perf: 修改 CommandFilterACL, CommandGroup Model 的 Meta 内部类; 修改 Command Model 的 system_user -> account 字段; 修改 ConnectionToken 的 command_filter_acls 返回字段;

pull/9155/head^2
Bai 2022-12-05 13:27:51 +08:00
parent 0cfcfacb6d
commit cc7424dbfe
16 changed files with 106 additions and 60 deletions

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.14 on 2022-12-05 03:22
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('acls', '0009_auto_20221204_0001'),
]
operations = [
migrations.AlterModelOptions(
name='commandfilteracl',
options={'ordering': ('priority', 'name'), 'verbose_name': 'Command acl'},
),
migrations.AlterModelOptions(
name='loginacl',
options={'ordering': ('priority', 'name'), 'verbose_name': 'Login acl'},
),
migrations.AlterModelOptions(
name='loginassetacl',
options={'ordering': ('priority', 'name'), 'verbose_name': 'Login asset acl'},
),
]

View File

@ -83,6 +83,7 @@ class BaseACL(CommonModelMixin):
objects = ACLManager.from_queryset(BaseACLQuerySet)()
class Meta:
ordering = ('priority', 'name')
abstract = True
@ -96,7 +97,8 @@ class UserAssetAccountBaseACL(BaseACL, OrgModelMixin):
objects = ACLManager.from_queryset(UserAssetAccountACLQuerySet)()
class Meta:
class Meta(BaseACL.Meta):
unique_together = ('name', 'org_id')
abstract = True
@classmethod
@ -118,4 +120,3 @@ class UserAssetAccountBaseACL(BaseACL, OrgModelMixin):
if kwargs:
queryset = queryset.filter(**kwargs)
return queryset

View File

@ -8,7 +8,7 @@ from django.utils.translation import ugettext_lazy as _
from common.utils import lazyproperty, get_logger
from orgs.mixins.models import JMSOrgBaseModel
from .base import UserAssetAccountBaseACL, UserAssetAccountACLQuerySet, ACLManager
from .base import UserAssetAccountBaseACL
logger = get_logger(__file__)
@ -94,23 +94,11 @@ class CommandGroup(JMSOrgBaseModel):
return '{} % {}'.format(self.name, self.type)
class CommandFilterACLQuerySet(UserAssetAccountACLQuerySet):
def get_command_groups(self):
ids = self.values_list('id', flat=True)
queryset = CommandFilterACL.command_groups.through.objects.filter(commandfilteracl_id__in=ids)
cmd_group_ids = queryset.values_list('commandgroup_id', flat=True)
command_groups = CommandGroup.objects.filter(id__in=cmd_group_ids)
return command_groups
class CommandFilterACL(UserAssetAccountBaseACL):
command_groups = models.ManyToManyField(CommandGroup, verbose_name=_('Commands'))
objects = ACLManager.from_queryset(CommandFilterACLQuerySet)()
class Meta:
unique_together = ('name', 'org_id')
ordering = ('priority', '-date_updated', 'name')
class Meta(UserAssetAccountBaseACL.Meta):
abstract = False
verbose_name = _('Command acl')
def __str__(self):

View File

@ -15,9 +15,9 @@ class LoginACL(BaseACL):
# 规则, ip_group, time_period
rules = models.JSONField(default=dict, verbose_name=_('Rule'))
class Meta:
ordering = ('priority', '-date_updated', 'name')
class Meta(BaseACL.Meta):
verbose_name = _('Login acl')
abstract = False
def __str__(self):
return self.name

View File

@ -6,10 +6,9 @@ from .base import UserAssetAccountBaseACL
class LoginAssetACL(UserAssetAccountBaseACL):
class Meta:
unique_together = ('name', 'org_id')
ordering = ('priority', '-date_updated', 'name')
class Meta(UserAssetAccountBaseACL.Meta):
verbose_name = _('Login asset acl')
abstract = False
def __str__(self):
return self.name

View File

@ -160,15 +160,15 @@ class ConnectionToken(OrgModelMixin, JMSBaseModel):
return self.domain.random_gateway()
@lazyproperty
def acl_command_groups(self):
def command_filter_acls(self):
from acls.models import CommandFilterACL
kwargs = {
'user': self.user,
'asset': self.asset,
'account': self.account,
}
command_groups = CommandFilterACL.filter_queryset(**kwargs).get_command_groups()
return command_groups
acls = CommandFilterACL.filter_queryset(**kwargs).valid()
return acls
class SuperConnectionToken(ConnectionToken):

View File

@ -1,13 +1,15 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from assets.models import Asset, CommandFilterRule, Account, Platform
from acls.models import CommandGroup
from assets.serializers import PlatformSerializer, AssetProtocolsSerializer
from authentication.models import ConnectionToken
from orgs.mixins.serializers import OrgResourceModelSerializerMixin
from perms.serializers.permission import ActionChoicesField
from users.models import User
from assets.models import Asset, Account, Platform
from assets.serializers import PlatformSerializer, AssetProtocolsSerializer
from perms.serializers.permission import ActionChoicesField
from acls.models import CommandGroup, CommandFilterACL
from orgs.mixins.serializers import OrgResourceModelSerializerMixin
from common.drf.fields import ObjectRelatedField
from ..models import ConnectionToken
__all__ = [
'ConnectionTokenSerializer', 'ConnectionTokenSecretSerializer',
@ -125,13 +127,20 @@ class ConnectionTokenGatewaySerializer(serializers.ModelSerializer):
]
class ConnectionTokenACLCmdGroupSerializer(serializers.ModelSerializer):
""" ACL command group"""
class ConnectionTokenACLSerializer(serializers.ModelSerializer):
command_groups = ObjectRelatedField(
many=True, required=False, queryset=CommandGroup.objects,
attrs=('id', 'name', 'type', 'content', 'ignore_case', 'pattern'),
label=_('Command group')
)
reviewers = ObjectRelatedField(
many=True, queryset=User.objects, label=_("Reviewers"), required=False
)
class Meta:
model = CommandGroup
model = CommandFilterACL
fields = [
'id', 'type', 'content', 'ignore_case', 'pattern'
'id', 'name', 'command_groups', 'action', 'reviewers', 'priority', 'is_active'
]
@ -151,7 +160,7 @@ class ConnectionTokenSecretSerializer(OrgResourceModelSerializerMixin):
account = ConnectionTokenAccountSerializer(read_only=True)
gateway = ConnectionTokenGatewaySerializer(read_only=True)
platform = ConnectionTokenPlatform(read_only=True)
acl_command_groups = ConnectionTokenACLCmdGroupSerializer(read_only=True, many=True)
command_filter_acls = ConnectionTokenACLSerializer(read_only=True, many=True)
actions = ActionChoicesField()
expire_at = serializers.IntegerField()
expire_now = serializers.BooleanField(label=_('Expired now'), write_only=True, default=True)
@ -160,7 +169,7 @@ class ConnectionTokenSecretSerializer(OrgResourceModelSerializerMixin):
model = ConnectionToken
fields = [
'id', 'value', 'user', 'asset', 'account', 'platform',
'acl_command_groups',
'command_filter_acls',
'protocol', 'gateway', 'actions', 'expire_at', 'expire_now',
]
extra_kwargs = {

View File

@ -26,7 +26,7 @@ __all__ = ['CommandViewSet', 'InsecureCommandAlertAPI']
class CommandQueryMixin:
command_store = get_command_storage()
filterset_fields = [
"asset", "system_user", "user", "session",
"asset", "account", "user", "session",
"risk_level", "input"
]
default_days_ago = 5
@ -56,7 +56,7 @@ class CommandQueryMixin:
multi_command_storage = get_multi_command_storage()
queryset = multi_command_storage.filter(
date_from=date_from, date_to=date_to,
user=q.get("user"), asset=q.get("asset"), system_user=q.get("system_user"),
user=q.get("user"), asset=q.get("asset"), account=q.get("account"),
input=q.get("input"), session=q.get("session_id", q.get('session')),
risk_level=self.get_query_risk_level(), org_id=self.get_org_id(),
)
@ -91,7 +91,7 @@ class CommandViewSet(JMSBulkModelViewSet):
{
"user": "admin",
"asset": "localhost",
"system_user": "web",
"account": "web",
"session": "xxxxxx",
"input": "whoami",
"output": "d2hvbWFp", # base64.b64encode(s)

View File

@ -15,13 +15,13 @@ class CommandBase(object):
@abc.abstractmethod
def filter(self, date_from=None, date_to=None,
user=None, asset=None, system_user=None,
user=None, asset=None, account=None,
input=None, session=None, risk_level=None, org_id=None):
pass
@abc.abstractmethod
def count(self, date_from=None, date_to=None,
user=None, asset=None, system_user=None,
user=None, asset=None, account=None,
input=None, session=None):
pass

View File

@ -21,7 +21,7 @@ class CommandStore(CommandBase):
"""
self.model.objects.create(
user=command["user"], asset=command["asset"],
system_user=command["system_user"], input=command["input"],
account=command["account"], input=command["input"],
output=command["output"], session=command["session"],
risk_level=command.get("risk_level", 0), org_id=command["org_id"],
timestamp=command["timestamp"]
@ -36,7 +36,7 @@ class CommandStore(CommandBase):
cmd_input = pretty_string(c['input'])
cmd_output = pretty_string(c['output'], max_length=1024)
_commands.append(self.model(
user=c["user"], asset=c["asset"], system_user=c["system_user"],
user=c["user"], asset=c["asset"], account=c["account"],
input=cmd_input, output=cmd_output, session=c["session"],
risk_level=c.get("risk_level", 0), org_id=c["org_id"],
timestamp=c["timestamp"]
@ -64,7 +64,7 @@ class CommandStore(CommandBase):
@staticmethod
def make_filter_kwargs(
date_from=None, date_to=None,
user=None, asset=None, system_user=None,
user=None, asset=None, account=None,
input=None, session=None, risk_level=None, org_id=None):
filter_kwargs = {}
date_from_default = timezone.now() - datetime.timedelta(days=7)
@ -87,8 +87,8 @@ class CommandStore(CommandBase):
filter_kwargs["user__startswith"] = user
if asset:
filter_kwargs['asset'] = asset
if system_user:
filter_kwargs['system_user'] = system_user
if account:
filter_kwargs['account'] = account
if input:
filter_kwargs['input__icontains'] = input
if session:
@ -100,22 +100,22 @@ class CommandStore(CommandBase):
return filter_kwargs
def filter(self, date_from=None, date_to=None,
user=None, asset=None, system_user=None,
user=None, asset=None, account=None,
input=None, session=None, risk_level=None, org_id=None):
filter_kwargs = self.make_filter_kwargs(
date_from=date_from, date_to=date_to, user=user,
asset=asset, system_user=system_user, input=input,
asset=asset, account=account, input=input,
session=session, risk_level=risk_level, org_id=org_id,
)
queryset = self.model.objects.filter(**filter_kwargs)
return queryset
def count(self, date_from=None, date_to=None,
user=None, asset=None, system_user=None,
user=None, asset=None, account=None,
input=None, session=None):
filter_kwargs = self.make_filter_kwargs(
date_from=date_from, date_to=date_to, user=user,
asset=asset, system_user=system_user, input=input,
asset=asset, account=account, input=input,
session=session,
)
count = self.model.objects.filter(**filter_kwargs).count()

View File

@ -49,7 +49,7 @@ class CommandStore(object):
self.es = Elasticsearch(hosts=hosts, max_retries=0, **kwargs)
self.exact_fields = set()
self.match_fields = {'input', 'risk_level', 'user', 'asset', 'system_user'}
self.match_fields = {'input', 'risk_level', 'user', 'asset', 'account'}
may_exact_fields = {'session', 'org_id'}
if self.is_new_index_type():
@ -142,7 +142,7 @@ class CommandStore(object):
def make_data(command):
data = dict(
user=command["user"], asset=command["asset"],
system_user=command["system_user"], input=command["input"],
account=command["account"], input=command["input"],
output=command["output"], risk_level=command["risk_level"],
session=command["session"], timestamp=command["timestamp"],
org_id=command["org_id"]

View File

@ -19,7 +19,7 @@ class AbstractSessionCommand(OrgModelMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
user = models.CharField(max_length=64, db_index=True, verbose_name=_("User"))
asset = models.CharField(max_length=128, db_index=True, verbose_name=_("Asset"))
system_user = models.CharField(max_length=64, db_index=True, verbose_name=_("System user"))
account = models.CharField(max_length=64, db_index=True, verbose_name=_("Account"))
input = models.CharField(max_length=128, db_index=True, verbose_name=_("Input"))
output = models.CharField(max_length=1024, blank=True, verbose_name=_("Output"))
session = models.CharField(max_length=36, db_index=True, verbose_name=_("Session"))

View File

@ -33,7 +33,8 @@ class SessionCommandSerializer(SimpleSessionCommandSerializer):
"""使用这个类作为基础Command Log Serializer类, 用来序列化"""
id = serializers.UUIDField(read_only=True)
system_user = serializers.CharField(label=_("System user")) # 限制 64 字符,不能直接迁移成 128 字符,命令表数据量会比较大
# 限制 64 字符,不能直接迁移成 128 字符,命令表数据量会比较大
account = serializers.CharField(label=_("Account "))
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'))
@ -45,7 +46,7 @@ class SessionCommandSerializer(SimpleSessionCommandSerializer):
risk_mapper = dict(AbstractSessionCommand.RISK_LEVEL_CHOICES)
return risk_mapper.get(obj.risk_level)
def validate_system_user(self, value):
def validate_account(self, value):
if len(value) > 64:
value = pretty_string(value, 64)
return value

View File

@ -16,7 +16,7 @@ class CommandFilter(filters.FilterSet):
class Meta:
model = Command
fields = [
'asset', 'system_user', 'user', 'session', 'risk_level', 'input',
'asset', 'account', 'user', 'session', 'risk_level', 'input',
'date_from', 'date_to', 'session_id', 'risk_level', 'command_storage_id',
]
@ -49,14 +49,14 @@ class CommandFilter(filters.FilterSet):
class CommandFilterForStorageTree(CommandFilter):
asset = filters.CharFilter(method='do_nothing')
system_user = filters.CharFilter(method='do_nothing')
account = filters.CharFilter(method='do_nothing')
session = filters.CharFilter(method='do_nothing')
risk_level = filters.NumberFilter(method='do_nothing')
class Meta:
model = CommandStorage
fields = [
'asset', 'system_user', 'user', 'session', 'risk_level', 'input',
'asset', 'account', 'user', 'session', 'risk_level', 'input',
'date_from', 'date_to', 'session_id', 'risk_level', 'command_storage_id',
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.14 on 2022-12-05 05:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('terminal', '0060_alter_applethostdeployment_options'),
]
operations = [
migrations.RenameField(
model_name='command',
old_name='system_user',
new_name='account',
),
migrations.AlterField(
model_name='command',
name='account',
field=models.CharField(db_index=True, max_length=64, verbose_name='Account'),
),
]

View File

@ -33,7 +33,7 @@ class Command(AbstractSessionCommand):
cls(**{
'user': random_string(6),
'asset': random_string(10),
'system_user': random_string(6),
'account': random_string(6),
'session': str(uuid.uuid4()),
'input': random_string(16),
'output': random_string(64),