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
fit2bot 2022-03-30 19:07:49 +08:00 committed by GitHub
parent 54d1996507
commit e7af037513
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 117 additions and 53 deletions

View File

@ -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

View File

@ -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,

View File

@ -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"]
)) ))

View File

@ -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)

View File

@ -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',)

View File

@ -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 *

View File

@ -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()

View File

@ -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:

View File

@ -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,
) )

View File

@ -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

View File

@ -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

View File

@ -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
} }

View File

@ -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)