mirror of https://github.com/jumpserver/jumpserver
perf: 修改命令command input 长度问题 (#7996)
* perf: 修改命令command input max_length 1024 * perf: 修改命令command input 长度问题 * perf: 修改命令command input 长度问题 * perf: 修改命令command input 长度问题 * perf: 修改命令command input 长度问题 Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>pull/7999/head
parent
54d1996507
commit
e7af037513
|
@ -338,3 +338,24 @@ def get_file_by_arch(dir, filename):
|
||||||
settings.BASE_DIR, dir, platform_name, arch, filename
|
settings.BASE_DIR, dir, platform_name, arch, filename
|
||||||
)
|
)
|
||||||
return file_path
|
return file_path
|
||||||
|
|
||||||
|
|
||||||
|
def pretty_string(data: str, max_length=128, ellipsis_str='...'):
|
||||||
|
"""
|
||||||
|
params:
|
||||||
|
data: abcdefgh
|
||||||
|
max_length: 7
|
||||||
|
ellipsis_str: ...
|
||||||
|
return:
|
||||||
|
ab...gh
|
||||||
|
"""
|
||||||
|
if len(data) < max_length:
|
||||||
|
return data
|
||||||
|
remain_length = max_length - len(ellipsis_str)
|
||||||
|
half = remain_length // 2
|
||||||
|
if half <= 1:
|
||||||
|
return data[:max_length]
|
||||||
|
start = data[:half]
|
||||||
|
end = data[-half:]
|
||||||
|
data = f'{start}{ellipsis_str}{end}'
|
||||||
|
return data
|
||||||
|
|
|
@ -14,7 +14,7 @@ from terminal.filters import CommandFilter
|
||||||
from orgs.utils import current_org
|
from orgs.utils import current_org
|
||||||
from common.drf.api import JMSBulkModelViewSet
|
from common.drf.api import JMSBulkModelViewSet
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from terminal.serializers import InsecureCommandAlertSerializer
|
from terminal.backends.command.serializers import InsecureCommandAlertSerializer
|
||||||
from terminal.exceptions import StorageInvalid
|
from terminal.exceptions import StorageInvalid
|
||||||
from ..backends import (
|
from ..backends import (
|
||||||
get_command_storage, get_multi_command_storage,
|
get_command_storage, get_multi_command_storage,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import datetime
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.db.utils import OperationalError
|
from django.db.utils import OperationalError
|
||||||
|
from common.utils.common import pretty_string
|
||||||
|
|
||||||
from .base import CommandBase
|
from .base import CommandBase
|
||||||
|
|
||||||
|
@ -32,9 +33,11 @@ class CommandStore(CommandBase):
|
||||||
"""
|
"""
|
||||||
_commands = []
|
_commands = []
|
||||||
for c in commands:
|
for c in commands:
|
||||||
|
cmd_input = pretty_string(c['input'])
|
||||||
|
cmd_output = pretty_string(c['output'], max_length=1024)
|
||||||
_commands.append(self.model(
|
_commands.append(self.model(
|
||||||
user=c["user"], asset=c["asset"], system_user=c["system_user"],
|
user=c["user"], asset=c["asset"], system_user=c["system_user"],
|
||||||
input=c["input"], output=c["output"], session=c["session"],
|
input=cmd_input, output=cmd_output, session=c["session"],
|
||||||
risk_level=c.get("risk_level", 0), org_id=c["org_id"],
|
risk_level=c.get("risk_level", 0), org_id=c["org_id"],
|
||||||
timestamp=c["timestamp"]
|
timestamp=c["timestamp"]
|
||||||
))
|
))
|
||||||
|
|
|
@ -4,27 +4,19 @@ from rest_framework import serializers
|
||||||
|
|
||||||
from .models import AbstractSessionCommand
|
from .models import AbstractSessionCommand
|
||||||
|
|
||||||
|
__all__ = ['SessionCommandSerializer', 'InsecureCommandAlertSerializer']
|
||||||
|
|
||||||
class SessionCommandSerializer(serializers.Serializer):
|
|
||||||
"""使用这个类作为基础Command Log Serializer类, 用来序列化"""
|
|
||||||
|
|
||||||
id = serializers.UUIDField(read_only=True)
|
class SimpleSessionCommandSerializer(serializers.Serializer):
|
||||||
|
""" 简单Session命令序列类, 用来提取公共字段 """
|
||||||
user = serializers.CharField(label=_("User")) # 限制 64 字符,见 validate_user
|
user = serializers.CharField(label=_("User")) # 限制 64 字符,见 validate_user
|
||||||
asset = serializers.CharField(max_length=128, label=_("Asset"))
|
asset = serializers.CharField(max_length=128, label=_("Asset"))
|
||||||
system_user = serializers.CharField(max_length=64, label=_("System user"))
|
input = serializers.CharField(max_length=2048, label=_("Command"))
|
||||||
input = serializers.CharField(max_length=128, label=_("Command"))
|
|
||||||
output = serializers.CharField(max_length=1024, allow_blank=True, label=_("Output"))
|
|
||||||
session = serializers.CharField(max_length=36, label=_("Session ID"))
|
session = serializers.CharField(max_length=36, label=_("Session ID"))
|
||||||
risk_level = serializers.ChoiceField(required=False, label=_("Risk level"), choices=AbstractSessionCommand.RISK_LEVEL_CHOICES)
|
risk_level = serializers.ChoiceField(
|
||||||
risk_level_display = serializers.SerializerMethodField(label=_('Risk level display'))
|
required=False, label=_("Risk level"), choices=AbstractSessionCommand.RISK_LEVEL_CHOICES
|
||||||
|
)
|
||||||
org_id = serializers.CharField(max_length=36, required=False, default='', allow_null=True, allow_blank=True)
|
org_id = serializers.CharField(max_length=36, required=False, default='', allow_null=True, allow_blank=True)
|
||||||
timestamp = serializers.IntegerField(label=_('Timestamp'))
|
|
||||||
remote_addr = serializers.CharField(read_only=True, label=_('Remote Address'))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_risk_level_display(obj):
|
|
||||||
risk_mapper = dict(AbstractSessionCommand.RISK_LEVEL_CHOICES)
|
|
||||||
return risk_mapper.get(obj.risk_level)
|
|
||||||
|
|
||||||
def validate_user(self, value):
|
def validate_user(self, value):
|
||||||
if len(value) > 64:
|
if len(value) > 64:
|
||||||
|
@ -32,9 +24,21 @@ class SessionCommandSerializer(serializers.Serializer):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
class InsecureCommandAlertSerializer(serializers.Serializer):
|
class InsecureCommandAlertSerializer(SimpleSessionCommandSerializer):
|
||||||
input = serializers.CharField()
|
pass
|
||||||
asset = serializers.CharField()
|
|
||||||
user = serializers.CharField()
|
|
||||||
risk_level = serializers.IntegerField()
|
class SessionCommandSerializer(SimpleSessionCommandSerializer):
|
||||||
session = serializers.UUIDField()
|
"""使用这个类作为基础Command Log Serializer类, 用来序列化"""
|
||||||
|
|
||||||
|
id = serializers.UUIDField(read_only=True)
|
||||||
|
system_user = serializers.CharField(max_length=64, label=_("System user"))
|
||||||
|
output = serializers.CharField(max_length=2048, allow_blank=True, label=_("Output"))
|
||||||
|
risk_level_display = serializers.SerializerMethodField(label=_('Risk level display'))
|
||||||
|
timestamp = serializers.IntegerField(label=_('Timestamp'))
|
||||||
|
remote_addr = serializers.CharField(read_only=True, label=_('Remote Address'))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_risk_level_display(obj):
|
||||||
|
risk_mapper = dict(AbstractSessionCommand.RISK_LEVEL_CHOICES)
|
||||||
|
return risk_mapper.get(obj.risk_level)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
@ -18,6 +20,33 @@ class CommandManager(models.Manager):
|
||||||
class Command(AbstractSessionCommand):
|
class Command(AbstractSessionCommand):
|
||||||
objects = CommandManager()
|
objects = CommandManager()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def generate_fake(cls, count=100, org=None):
|
||||||
|
import uuid
|
||||||
|
import datetime
|
||||||
|
from orgs.models import Organization
|
||||||
|
from common.utils import random_string
|
||||||
|
|
||||||
|
if not org:
|
||||||
|
org = Organization.default()
|
||||||
|
|
||||||
|
d = datetime.datetime.now() - datetime.timedelta(days=1)
|
||||||
|
commands = [
|
||||||
|
cls(**{
|
||||||
|
'user': random_string(6),
|
||||||
|
'asset': random_string(10),
|
||||||
|
'system_user': random_string(6),
|
||||||
|
'session': str(uuid.uuid4()),
|
||||||
|
'input': random_string(16),
|
||||||
|
'output': random_string(64),
|
||||||
|
'timestamp': int(d.timestamp()),
|
||||||
|
'org_id': str(org.id)
|
||||||
|
})
|
||||||
|
for i in range(count)
|
||||||
|
]
|
||||||
|
cls.objects.bulk_create(commands)
|
||||||
|
print(f'Create {len(commands)} commands of org ({org})')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "terminal_command"
|
db_table = "terminal_command"
|
||||||
ordering = ('-timestamp',)
|
ordering = ('-timestamp',)
|
||||||
|
|
|
@ -3,5 +3,4 @@
|
||||||
from .terminal import *
|
from .terminal import *
|
||||||
from .session import *
|
from .session import *
|
||||||
from .storage import *
|
from .storage import *
|
||||||
from .command import *
|
|
||||||
from .sharing import *
|
from .sharing import *
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
|
||||||
from rest_framework import serializers
|
|
||||||
|
|
||||||
|
|
||||||
class InsecureCommandAlertSerializer(serializers.Serializer):
|
|
||||||
input = serializers.CharField()
|
|
||||||
asset = serializers.CharField()
|
|
||||||
user = serializers.CharField()
|
|
||||||
risk_level = serializers.IntegerField()
|
|
||||||
session = serializers.UUIDField()
|
|
||||||
org_id = serializers.CharField()
|
|
|
@ -4,7 +4,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
from common.drf.serializers import BulkModelSerializer, AdaptedBulkListSerializer
|
from common.drf.serializers import BulkModelSerializer, AdaptedBulkListSerializer
|
||||||
from common.utils import is_uuid
|
from common.utils import is_uuid
|
||||||
from users.serializers import ServiceAccountSerializer
|
from users.serializers import ServiceAccountSerializer
|
||||||
from common.utils import get_request_ip
|
from common.utils import get_request_ip, pretty_string
|
||||||
from .. import const
|
from .. import const
|
||||||
|
|
||||||
from ..models import (
|
from ..models import (
|
||||||
|
@ -111,12 +111,11 @@ class TerminalRegistrationSerializer(serializers.ModelSerializer):
|
||||||
valid = super().is_valid(raise_exception=raise_exception)
|
valid = super().is_valid(raise_exception=raise_exception)
|
||||||
if not valid:
|
if not valid:
|
||||||
return valid
|
return valid
|
||||||
name = self.validated_data.get('name')
|
raw_name = self.validated_data.get('name')
|
||||||
if len(name) > 128:
|
name = pretty_string(raw_name)
|
||||||
self.validated_data['comment'] = name
|
|
||||||
name = '{}...{}'.format(name[:32], name[-32:])
|
|
||||||
self.validated_data['name'] = name
|
self.validated_data['name'] = name
|
||||||
|
if len(raw_name) > 128:
|
||||||
|
self.validated_data['comment'] = raw_name
|
||||||
data = {'name': name}
|
data = {'name': name}
|
||||||
kwargs = {'data': data}
|
kwargs = {'data': data}
|
||||||
if self.instance and self.instance.user:
|
if self.instance and self.instance.user:
|
||||||
|
|
|
@ -401,10 +401,12 @@ class RoleMixin:
|
||||||
def is_staff(self, value):
|
def is_staff(self, value):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
service_account_email_suffix = '@local.domain'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_service_account(cls, name, comment):
|
def create_service_account(cls, name, email, comment):
|
||||||
app = cls.objects.create(
|
app = cls.objects.create(
|
||||||
username=name, name=name, email='{}@local.domain'.format(name),
|
username=name, name=name, email=email,
|
||||||
comment=comment, is_first_login=False,
|
comment=comment, is_first_login=False,
|
||||||
created_by='System', is_service_account=True,
|
created_by='System', is_service_account=True,
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,6 +6,7 @@ from rest_framework import serializers
|
||||||
|
|
||||||
from common.mixins import CommonBulkSerializerMixin
|
from common.mixins import CommonBulkSerializerMixin
|
||||||
from common.validators import PhoneValidator
|
from common.validators import PhoneValidator
|
||||||
|
from common.utils import pretty_string
|
||||||
from rbac.builtin import BuiltinRole
|
from rbac.builtin import BuiltinRole
|
||||||
from rbac.permissions import RBACPermission
|
from rbac.permissions import RBACPermission
|
||||||
from rbac.models import OrgRoleBinding, SystemRoleBinding, Role
|
from rbac.models import OrgRoleBinding, SystemRoleBinding, Role
|
||||||
|
@ -268,7 +269,9 @@ class ServiceAccountSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
def get_email(self):
|
def get_email(self):
|
||||||
name = self.initial_data.get('name')
|
name = self.initial_data.get('name')
|
||||||
return '{}@serviceaccount.local'.format(name)
|
name_max_length = 128 - len(User.service_account_email_suffix)
|
||||||
|
name = pretty_string(name, max_length=name_max_length, ellipsis_str='-')
|
||||||
|
return '{}{}'.format(name, User.service_account_email_suffix)
|
||||||
|
|
||||||
def validate_name(self, name):
|
def validate_name(self, name):
|
||||||
email = self.get_email()
|
email = self.get_email()
|
||||||
|
@ -283,6 +286,7 @@ class ServiceAccountSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
name = validated_data['name']
|
name = validated_data['name']
|
||||||
|
email = self.get_email()
|
||||||
comment = validated_data.get('comment', '')
|
comment = validated_data.get('comment', '')
|
||||||
user, ak = User.create_service_account(name, comment)
|
user, ak = User.create_service_account(name, email, comment)
|
||||||
return user
|
return user
|
||||||
|
|
|
@ -11,7 +11,7 @@ from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
|
||||||
from common.tasks import send_mail_async
|
from common.tasks import send_mail_async
|
||||||
from common.utils import reverse, get_object_or_none, ip
|
from common.utils import reverse, get_object_or_none, ip, pretty_string
|
||||||
from .models import User
|
from .models import User
|
||||||
|
|
||||||
logger = logging.getLogger('jumpserver')
|
logger = logging.getLogger('jumpserver')
|
||||||
|
@ -229,12 +229,14 @@ class LoginIpBlockUtil(BlockGlobalIpUtilBase):
|
||||||
BLOCK_KEY_TMPL = "_LOGIN_BLOCK_{}"
|
BLOCK_KEY_TMPL = "_LOGIN_BLOCK_{}"
|
||||||
|
|
||||||
|
|
||||||
def construct_user_email(username, email):
|
def construct_user_email(username, email, email_suffix=''):
|
||||||
if '@' not in email:
|
if '@' in email:
|
||||||
|
return email
|
||||||
if '@' in username:
|
if '@' in username:
|
||||||
email = username
|
return username
|
||||||
else:
|
if not email_suffix:
|
||||||
email = '{}@{}'.format(username, settings.EMAIL_SUFFIX)
|
email_suffix = settings.EMAIL_SUFFIX
|
||||||
|
email = f'{username}@{email_suffix}'
|
||||||
return email
|
return email
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ django.setup()
|
||||||
from resources.assets import AssetsGenerator, NodesGenerator, SystemUsersGenerator, AdminUsersGenerator
|
from resources.assets import AssetsGenerator, NodesGenerator, SystemUsersGenerator, AdminUsersGenerator
|
||||||
from resources.users import UserGroupGenerator, UserGenerator
|
from resources.users import UserGroupGenerator, UserGenerator
|
||||||
from resources.perms import AssetPermissionGenerator
|
from resources.perms import AssetPermissionGenerator
|
||||||
|
from resources.terminal import CommandGenerator
|
||||||
# from resources.system import StatGenerator
|
# from resources.system import StatGenerator
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,6 +27,7 @@ resource_generator_mapper = {
|
||||||
'user': UserGenerator,
|
'user': UserGenerator,
|
||||||
'user_group': UserGroupGenerator,
|
'user_group': UserGroupGenerator,
|
||||||
'asset_permission': AssetPermissionGenerator,
|
'asset_permission': AssetPermissionGenerator,
|
||||||
|
'command': CommandGenerator,
|
||||||
# 'stat': StatGenerator
|
# 'stat': StatGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
from .base import FakeDataGenerator
|
||||||
|
from terminal.models import Command
|
||||||
|
|
||||||
|
|
||||||
|
class CommandGenerator(FakeDataGenerator):
|
||||||
|
resource = 'command'
|
||||||
|
|
||||||
|
def do_generate(self, batch, batch_size):
|
||||||
|
Command.generate_fake(len(batch), self.org)
|
||||||
|
|
Loading…
Reference in New Issue