perf: 优化 connect method acls 和登录 acls

This commit is contained in:
ibuler
2023-06-07 17:39:56 +08:00
parent a7ca9ccfe9
commit d6eb4bcbd2
24 changed files with 309 additions and 143 deletions

View File

@@ -1,4 +1,5 @@
from .command_acl import *
from .connect_method import *
from .login_acl import *
from .login_asset_acl import *
from .login_asset_check import *

View File

@@ -1,9 +1,8 @@
from rest_framework.decorators import action
from rest_framework.response import Response
from common.drf.filters import BaseFilterSet
from orgs.mixins.api import OrgBulkModelViewSet
from .common import ACLFiltersetMixin
from .common import ACLUserAssetFilterMixin
from .. import models, serializers
__all__ = ['CommandFilterACLViewSet', 'CommandGroupViewSet']
@@ -16,10 +15,10 @@ class CommandGroupViewSet(OrgBulkModelViewSet):
serializer_class = serializers.CommandGroupSerializer
class CommandACLFilter(ACLFiltersetMixin, BaseFilterSet):
class CommandACLFilter(ACLUserAssetFilterMixin):
class Meta:
model = models.CommandFilterACL
fields = ['name', 'users', 'assets']
fields = ['name', ]
class CommandFilterACLViewSet(OrgBulkModelViewSet):

View File

@@ -1,12 +1,12 @@
from django.db.models import Q
from django_filters import rest_framework as drf_filters
from common.drf.filters import BaseFilterSet
from common.utils import is_uuid
class ACLFiltersetMixin(BaseFilterSet):
class ACLUserFilterMixin(BaseFilterSet):
users = drf_filters.CharFilter(method='filter_user')
assets = drf_filters.CharFilter(method='filter_asset')
@staticmethod
def filter_user(queryset, name, value):
@@ -16,12 +16,17 @@ class ACLFiltersetMixin(BaseFilterSet):
if is_uuid(value):
user = User.objects.filter(id=value).first()
else:
user = User.objects.filter(name=value).first()
q = Q(name=value) | Q(username=value)
user = User.objects.filter(q).first()
if not user:
return queryset.none()
q = queryset.model.users.get_filter_q(user)
return queryset.filter(q).distinct()
class ACLUserAssetFilterMixin(ACLUserFilterMixin):
assets = drf_filters.CharFilter(method='filter_asset')
@staticmethod
def filter_asset(queryset, name, value):
from assets.models import Asset
@@ -31,7 +36,8 @@ class ACLFiltersetMixin(BaseFilterSet):
if is_uuid(value):
asset = Asset.objects.filter(id=value).first()
else:
asset = Asset.objects.filter(name=value).first()
q = Q(name=value) | Q(address=value)
asset = Asset.objects.filter(q).first()
if not asset:
return queryset.none()

View File

@@ -0,0 +1,23 @@
from django_filters import rest_framework as drf_filters
from common.api import JMSBulkModelViewSet
from .common import ACLUserFilterMixin
from .. import serializers
from ..models import ConnectMethodACL
__all__ = ['ConnectMethodACLViewSet']
class ConnectMethodFilter(ACLUserFilterMixin):
methods = drf_filters.CharFilter(field_name="methods__contains", lookup_expr='exact')
class Meta:
model = ConnectMethodACL
fields = ['name', ]
class ConnectMethodACLViewSet(JMSBulkModelViewSet):
queryset = ConnectMethodACL.objects.all()
filterset_class = ConnectMethodFilter
search_fields = ('name',)
serializer_class = serializers.ConnectMethodACLSerializer

View File

@@ -1,13 +1,19 @@
from common.api import JMSBulkModelViewSet
from .common import ACLUserFilterMixin
from .. import serializers
from ..filters import LoginAclFilter
from ..models import LoginACL
__all__ = ['LoginACLViewSet']
class LoginACLFilter(ACLUserFilterMixin):
class Meta:
model = LoginACL
fields = ('name', 'action')
class LoginACLViewSet(JMSBulkModelViewSet):
queryset = LoginACL.objects.all()
filterset_class = LoginAclFilter
filterset_class = LoginACLFilter
search_fields = ('name',)
serializer_class = serializers.LoginACLSerializer

View File

@@ -1,19 +1,18 @@
from common.drf.filters import BaseFilterSet
from orgs.mixins.api import OrgBulkModelViewSet
from .common import ACLFiltersetMixin
from .common import ACLUserAssetFilterMixin
from .. import models, serializers
__all__ = ['LoginAssetACLViewSet']
class CommandACLFilter(ACLFiltersetMixin, BaseFilterSet):
class LoginAssetACLFilter(ACLUserAssetFilterMixin):
class Meta:
model = models.LoginAssetACL
fields = ['name', 'users', 'assets']
fields = ['name', ]
class LoginAssetACLViewSet(OrgBulkModelViewSet):
model = models.LoginAssetACL
filterset_class = CommandACLFilter
filterset_class = LoginAssetACLFilter
search_fields = ['name']
serializer_class = serializers.LoginAssetACLSerializer

View File

@@ -1,15 +0,0 @@
from django_filters import rest_framework as filters
from common.drf.filters import BaseFilterSet
from acls.models import LoginACL
class LoginAclFilter(BaseFilterSet):
user = filters.UUIDFilter(field_name='user_id')
user_display = filters.CharFilter(field_name='user__name')
class Meta:
model = LoginACL
fields = (
'name', 'user', 'user_display', 'action'
)

View File

@@ -0,0 +1,46 @@
# Generated by Django 3.2.17 on 2023-06-06 06:23
import uuid
import django.core.validators
from django.conf import settings
from django.db import migrations, models
import common.db.fields
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('acls', '0014_loginassetacl_rules'),
]
operations = [
migrations.CreateModel(
name='ConnectMethodACL',
fields=[
('created_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by')),
('updated_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by')),
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
('priority', models.IntegerField(default=50, help_text='1-100, the lower the value will be match first',
validators=[django.core.validators.MinValueValidator(1),
django.core.validators.MaxValueValidator(100)],
verbose_name='Priority')),
('action', models.CharField(default='reject', max_length=64, verbose_name='Action')),
('is_active', models.BooleanField(default=True, verbose_name='Active')),
('users', common.db.fields.JSONManyToManyField(default=dict, to='users.User', verbose_name='Users')),
('connect_methods', models.JSONField(default=list, verbose_name='Connect methods')),
(
'reviewers',
models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Reviewers')),
],
options={
'ordering': ('priority', 'date_updated', 'name'),
'abstract': False,
},
),
]

View File

@@ -0,0 +1,37 @@
# Generated by Django 3.2.17 on 2023-06-06 10:57
from django.db import migrations, models
import common.db.fields
def migrate_users_login_acls(apps, schema_editor):
login_acl_model = apps.get_model('acls', 'LoginACL')
for login_acl in login_acl_model.objects.all():
login_acl.users = {
"type": "ids", "ids": [str(login_acl.user_id)]
}
login_acl.save()
class Migration(migrations.Migration):
dependencies = [
('acls', '0015_connectmethodacl'),
]
operations = [
migrations.AddField(
model_name='loginacl',
name='users',
field=common.db.fields.JSONManyToManyField(default=dict, to='users.User', verbose_name='Users'),
),
migrations.RemoveField(
model_name='loginacl',
name='user',
),
migrations.AlterField(
model_name='loginacl',
name='name',
field=models.CharField(max_length=128, unique=True, verbose_name='Name'),
),
]

View File

@@ -1,3 +1,4 @@
from .command_acl import *
from .connect_method import *
from .login_acl import *
from .login_asset_acl import *
from .command_acl import *

View File

@@ -9,7 +9,7 @@ from common.utils.time_period import contains_time_period
from orgs.mixins.models import OrgModelMixin
__all__ = [
'BaseACL', 'UserAssetAccountBaseACL',
'BaseACL', 'UserBaseACL', 'UserAssetAccountBaseACL',
]
@@ -34,7 +34,7 @@ class BaseACLQuerySet(models.QuerySet):
class BaseACL(JMSBaseModel):
name = models.CharField(max_length=128, verbose_name=_('Name'))
name = models.CharField(max_length=128, verbose_name=_('Name'), unique=True)
priority = models.IntegerField(
default=50, verbose_name=_("Priority"),
help_text=_("1-100, the lower the value will be match first"),
@@ -79,13 +79,27 @@ class BaseACL(JMSBaseModel):
return None
class UserAssetAccountBaseACL(BaseACL, OrgModelMixin):
class UserBaseACL(BaseACL):
users = JSONManyToManyField('users.User', default=dict, verbose_name=_('Users'))
class Meta:
abstract = True
@classmethod
def get_user_acls(cls, user):
queryset = cls.objects.all()
q = cls.users.get_filter_q(user)
queryset = queryset.filter(q)
return queryset.valid().distinct()
class UserAssetAccountBaseACL(UserBaseACL, OrgModelMixin):
name = models.CharField(max_length=128, verbose_name=_('Name'))
assets = JSONManyToManyField('assets.Asset', default=dict, verbose_name=_('Assets'))
accounts = models.JSONField(default=list, verbose_name=_("Accounts"))
class Meta(BaseACL.Meta):
unique_together = ('name', 'org_id')
class Meta(UserBaseACL.Meta):
unique_together = [('name', 'org_id')]
abstract = True
@classmethod

View File

@@ -0,0 +1,10 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from .base import UserBaseACL
__all__ = ['ConnectMethodACL']
class ConnectMethodACL(UserBaseACL):
connect_methods = models.JSONField(default=list, verbose_name=_('Connect methods'))

View File

@@ -3,18 +3,14 @@ from django.utils.translation import ugettext_lazy as _
from common.utils import get_request_ip, get_ip_city
from common.utils.timezone import local_now_display
from .base import BaseACL
from .base import UserBaseACL
class LoginACL(BaseACL):
user = models.ForeignKey(
'users.User', on_delete=models.CASCADE,
related_name='login_acls', verbose_name=_('User')
)
class LoginACL(UserBaseACL):
# 规则, ip_group, time_period
rules = models.JSONField(default=dict, verbose_name=_('Rule'))
class Meta(BaseACL.Meta):
class Meta(UserBaseACL.Meta):
verbose_name = _('Login acl')
abstract = False
@@ -28,10 +24,6 @@ class LoginACL(BaseACL):
def filter_acl(cls, user):
return user.login_acls.all().valid().distinct()
@classmethod
def get_user_acls(cls, user):
return cls.filter_acl(user)
def create_confirm_ticket(self, request):
from tickets import const
from tickets.models import ApplyLoginTicket

View File

@@ -1,4 +1,5 @@
from .command_acl import *
from .connect_method import *
from .login_acl import *
from .login_asset_acl import *
from .login_asset_check import *
from .command_acl import *

View File

@@ -1,11 +1,10 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from acls.models.base import ActionChoices
from acls.models.base import ActionChoices, BaseACL
from common.serializers.fields import JSONManyToManyField, LabeledChoiceField
from jumpserver.utils import has_valid_xpack_license
from common.serializers.fields import JSONManyToManyField, ObjectRelatedField, LabeledChoiceField
from orgs.models import Organization
from users.models import User
common_help_text = _(
"With * indicating a match all. "
@@ -71,25 +70,16 @@ class ActionAclSerializer(serializers.Serializer):
action._choices = choices
class BaseUserAssetAccountACLSerializerMixin(ActionAclSerializer, serializers.Serializer):
users = JSONManyToManyField(label=_('User'))
assets = JSONManyToManyField(label=_('Asset'))
accounts = serializers.ListField(label=_('Account'))
reviewers = ObjectRelatedField(
queryset=User.objects, many=True, required=False, label=_('Reviewers')
)
reviewers_amount = serializers.IntegerField(
read_only=True, source="reviewers.count", label=_('Reviewers amount')
)
class BaserACLSerializer(ActionAclSerializer, serializers.Serializer):
class Meta:
model = BaseACL
fields_mini = ["id", "name"]
fields_small = fields_mini + [
"users", "accounts", "assets", "is_active",
"date_created", "date_updated", "priority",
"action", "comment", "created_by", "org_id",
"is_active", "priority", "action",
"date_created", "date_updated",
"comment", "created_by", "org_id",
]
fields_m2m = ["reviewers", "reviewers_amount"]
fields_m2m = ["reviewers", ]
fields = fields_small + fields_m2m
extra_kwargs = {
"priority": {"default": 50},
@@ -115,3 +105,18 @@ class BaseUserAssetAccountACLSerializerMixin(ActionAclSerializer, serializers.Se
)
raise serializers.ValidationError(error)
return valid_reviewers
class BaserUserACLSerializer(BaserACLSerializer):
users = JSONManyToManyField(label=_('User'))
class Meta(BaserACLSerializer.Meta):
fields = BaserACLSerializer.Meta.fields + ['users']
class BaseUserAssetAccountACLSerializer(BaserUserACLSerializer):
assets = JSONManyToManyField(label=_('Asset'))
accounts = serializers.ListField(label=_('Account'))
class Meta(BaserUserACLSerializer.Meta):
fields = BaserUserACLSerializer.Meta.fields + ['assets', 'accounts']

View File

@@ -1,13 +1,13 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from terminal.models import Session
from acls.models import CommandGroup, CommandFilterACL
from common.utils import lazyproperty, get_object_or_none
from common.serializers.fields import ObjectRelatedField, LabeledChoiceField
from orgs.utils import tmp_to_root_org
from common.utils import lazyproperty, get_object_or_none
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from .base import BaseUserAssetAccountACLSerializerMixin as BaseSerializer
from orgs.utils import tmp_to_root_org
from terminal.models import Session
from .base import BaseUserAssetAccountACLSerializer as BaseSerializer
__all__ = ["CommandFilterACLSerializer", "CommandGroupSerializer", "CommandReviewSerializer"]

View File

@@ -0,0 +1,23 @@
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from .base import BaseUserAssetAccountACLSerializer as BaseSerializer
from ..models import ConnectMethodACL
__all__ = ["ConnectMethodACLSerializer"]
class ConnectMethodACLSerializer(BaseSerializer, BulkOrgResourceModelSerializer):
class Meta(BaseSerializer.Meta):
model = ConnectMethodACL
fields = [
i for i in BaseSerializer.Meta.fields + ['connect_methods']
if i not in ['assets', 'accounts']
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
field_action = self.fields.get('action')
if not field_action:
return
# 仅支持拒绝
for k in ['review', 'accept']:
field_action._choices.pop(k, None)

View File

@@ -1,47 +1,22 @@
from django.utils.translation import ugettext as _
from rest_framework import serializers
from common.serializers import BulkModelSerializer, MethodSerializer
from common.serializers.fields import ObjectRelatedField
from users.models import User
from .base import ActionAclSerializer
from common.serializers import MethodSerializer
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from .base import BaserUserACLSerializer
from .rules import RuleSerializer
from ..models import LoginACL
__all__ = [
"LoginACLSerializer",
]
__all__ = ["LoginACLSerializer"]
common_help_text = _(
"With * indicating a match all. "
)
common_help_text = _("With * indicating a match all. ")
class LoginACLSerializer(ActionAclSerializer, BulkModelSerializer):
user = ObjectRelatedField(queryset=User.objects, label=_("User"))
reviewers = ObjectRelatedField(
queryset=User.objects, label=_("Reviewers"), many=True, required=False
)
reviewers_amount = serializers.IntegerField(
read_only=True, source="reviewers.count", label=_("Reviewers amount")
)
class LoginACLSerializer(BaserUserACLSerializer, BulkOrgResourceModelSerializer):
rules = MethodSerializer(label=_('Rule'))
class Meta:
class Meta(BaserUserACLSerializer.Meta):
model = LoginACL
fields_mini = ["id", "name"]
fields_small = fields_mini + [
"priority", "user", "rules", "action",
"is_active", "date_created", "date_updated",
"comment", "created_by",
]
fields_fk = ["user"]
fields_m2m = ["reviewers", "reviewers_amount"]
fields = fields_small + fields_fk + fields_m2m
extra_kwargs = {
"priority": {"default": 50},
"is_active": {"default": True},
}
fields = BaserUserACLSerializer.Meta.fields + ['rules', ]
def get_rules_serializer(self):
return RuleSerializer()

View File

@@ -2,7 +2,7 @@ from django.utils.translation import gettext_lazy as _
from common.serializers import MethodSerializer
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from .base import BaseUserAssetAccountACLSerializerMixin as BaseSerializer
from .base import BaseUserAssetAccountACLSerializer as BaseSerializer
from .rules import RuleSerializer
from ..models import LoginAssetACL

View File

@@ -10,6 +10,7 @@ router.register(r'login-acls', api.LoginACLViewSet, 'login-acl')
router.register(r'login-asset-acls', api.LoginAssetACLViewSet, 'login-asset-acl')
router.register(r'command-filter-acls', api.CommandFilterACLViewSet, 'command-filter-acl')
router.register(r'command-groups', api.CommandGroupViewSet, 'command-group')
router.register(r'connect-method-acls', api.ConnectMethodACLViewSet, 'connect-method-acl')
urlpatterns = [
path('login-asset/check/', api.LoginAssetCheckAPI.as_view(), name='login-asset-check'),