mirror of https://github.com/jumpserver/jumpserver
perf: Connect methods acl allow accept action
parent
ae9956ff91
commit
4f79abe678
|
@ -1,4 +1,4 @@
|
||||||
from common.serializers import CommonBulkModelSerializer
|
from common.serializers.mixin import CommonBulkModelSerializer
|
||||||
from .base import BaseUserAssetAccountACLSerializer as BaseSerializer
|
from .base import BaseUserAssetAccountACLSerializer as BaseSerializer
|
||||||
from ..const import ActionChoices
|
from ..const import ActionChoices
|
||||||
from ..models import ConnectMethodACL
|
from ..models import ConnectMethodACL
|
||||||
|
@ -11,11 +11,10 @@ class ConnectMethodACLSerializer(BaseSerializer, CommonBulkModelSerializer):
|
||||||
model = ConnectMethodACL
|
model = ConnectMethodACL
|
||||||
fields = [
|
fields = [
|
||||||
i for i in BaseSerializer.Meta.fields + ['connect_methods']
|
i for i in BaseSerializer.Meta.fields + ['connect_methods']
|
||||||
if i not in ['assets', 'accounts']
|
if i not in ['assets', 'accounts', 'org_id']
|
||||||
]
|
]
|
||||||
action_choices_exclude = BaseSerializer.Meta.action_choices_exclude + [
|
action_choices_exclude = BaseSerializer.Meta.action_choices_exclude + [
|
||||||
ActionChoices.review,
|
ActionChoices.review,
|
||||||
ActionChoices.accept,
|
|
||||||
ActionChoices.notice,
|
ActionChoices.notice,
|
||||||
ActionChoices.face_verify,
|
ActionChoices.face_verify,
|
||||||
ActionChoices.face_online,
|
ActionChoices.face_online,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
from common.serializers import CommonBulkModelSerializer
|
||||||
from common.serializers import MethodSerializer
|
from common.serializers import MethodSerializer
|
||||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
|
||||||
from .base import BaseUserACLSerializer
|
from .base import BaseUserACLSerializer
|
||||||
from .rules import RuleSerializer
|
from .rules import RuleSerializer
|
||||||
from ..const import ActionChoices
|
from ..const import ActionChoices
|
||||||
|
@ -12,12 +12,12 @@ __all__ = ["LoginACLSerializer"]
|
||||||
common_help_text = _("With * indicating a match all. ")
|
common_help_text = _("With * indicating a match all. ")
|
||||||
|
|
||||||
|
|
||||||
class LoginACLSerializer(BaseUserACLSerializer):
|
class LoginACLSerializer(BaseUserACLSerializer, CommonBulkModelSerializer):
|
||||||
rules = MethodSerializer(label=_('Rule'))
|
rules = MethodSerializer(label=_('Rule'))
|
||||||
|
|
||||||
class Meta(BaseUserACLSerializer.Meta):
|
class Meta(BaseUserACLSerializer.Meta):
|
||||||
model = LoginACL
|
model = LoginACL
|
||||||
fields = BaseUserACLSerializer.Meta.fields + ['rules', ]
|
fields = list((set(BaseUserACLSerializer.Meta.fields) | {'rules'}) - {'org_id'})
|
||||||
action_choices_exclude = [
|
action_choices_exclude = [
|
||||||
ActionChoices.warning,
|
ActionChoices.warning,
|
||||||
ActionChoices.notify_and_warn,
|
ActionChoices.notify_and_warn,
|
||||||
|
|
|
@ -5,7 +5,7 @@ from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.db.models import Model
|
from django.db.models import Model
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.fields import ChoiceField, empty
|
from rest_framework.fields import empty
|
||||||
|
|
||||||
from common.db.fields import TreeChoices, JSONManyToManyField as ModelJSONManyToManyField
|
from common.db.fields import TreeChoices, JSONManyToManyField as ModelJSONManyToManyField
|
||||||
from common.utils import decrypt_password, is_uuid
|
from common.utils import decrypt_password, is_uuid
|
||||||
|
@ -54,54 +54,19 @@ class EncryptedField(serializers.CharField):
|
||||||
|
|
||||||
|
|
||||||
class LabeledChoiceField(serializers.ChoiceField):
|
class LabeledChoiceField(serializers.ChoiceField):
|
||||||
def __init__(self, **kwargs):
|
def to_representation(self, key):
|
||||||
self.attrs = kwargs.pop("attrs", None) or ("value", "label")
|
if key is None:
|
||||||
super().__init__(**kwargs)
|
return key
|
||||||
|
label = self.choices.get(key, key)
|
||||||
def to_representation(self, value):
|
return {"value": key, "label": label}
|
||||||
if not value:
|
|
||||||
return value
|
|
||||||
data = {}
|
|
||||||
for attr in self.attrs:
|
|
||||||
if not hasattr(value, attr):
|
|
||||||
continue
|
|
||||||
data[attr] = getattr(value, attr)
|
|
||||||
return data
|
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def to_internal_value(self, data):
|
||||||
if not data:
|
|
||||||
return data
|
|
||||||
if isinstance(data, dict):
|
if isinstance(data, dict):
|
||||||
return data.get("value") or data.get("label")
|
data = data.get("value")
|
||||||
return data
|
|
||||||
|
|
||||||
def get_schema(self):
|
if isinstance(data, str) and "(" in data and data.endswith(")"):
|
||||||
"""
|
data = data.strip(")").split('(')[-1]
|
||||||
为 drf-spectacular 提供 OpenAPI schema
|
return super(LabeledChoiceField, self).to_internal_value(data)
|
||||||
"""
|
|
||||||
if getattr(self, 'many', False):
|
|
||||||
return {
|
|
||||||
'type': 'array',
|
|
||||||
'items': {
|
|
||||||
'type': 'object',
|
|
||||||
'properties': {
|
|
||||||
'value': {'type': 'string'},
|
|
||||||
'label': {'type': 'string'}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'description': getattr(self, 'help_text', ''),
|
|
||||||
'title': getattr(self, 'label', ''),
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
return {
|
|
||||||
'type': 'object',
|
|
||||||
'properties': {
|
|
||||||
'value': {'type': 'string'},
|
|
||||||
'label': {'type': 'string'}
|
|
||||||
},
|
|
||||||
'description': getattr(self, 'help_text', ''),
|
|
||||||
'title': getattr(self, 'label', ''),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class LabeledMultipleChoiceField(serializers.MultipleChoiceField):
|
class LabeledMultipleChoiceField(serializers.MultipleChoiceField):
|
||||||
|
@ -221,7 +186,7 @@ class ObjectRelatedField(serializers.RelatedField):
|
||||||
"""
|
"""
|
||||||
# 获取字段的基本信息
|
# 获取字段的基本信息
|
||||||
field_type = 'array' if self.many else 'object'
|
field_type = 'array' if self.many else 'object'
|
||||||
|
|
||||||
if field_type == 'array':
|
if field_type == 'array':
|
||||||
# 如果是多对多关系
|
# 如果是多对多关系
|
||||||
return {
|
return {
|
||||||
|
@ -250,7 +215,7 @@ class ObjectRelatedField(serializers.RelatedField):
|
||||||
获取对象的 OpenAPI schema
|
获取对象的 OpenAPI schema
|
||||||
"""
|
"""
|
||||||
properties = {}
|
properties = {}
|
||||||
|
|
||||||
# 动态分析 attrs 中的属性类型
|
# 动态分析 attrs 中的属性类型
|
||||||
for attr in self.attrs:
|
for attr in self.attrs:
|
||||||
# 尝试从 queryset 的 model 中获取字段信息
|
# 尝试从 queryset 的 model 中获取字段信息
|
||||||
|
@ -259,7 +224,7 @@ class ObjectRelatedField(serializers.RelatedField):
|
||||||
'type': field_type,
|
'type': field_type,
|
||||||
'description': f'{attr} field'
|
'description': f'{attr} field'
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'properties': properties,
|
'properties': properties,
|
||||||
|
@ -280,7 +245,7 @@ class ObjectRelatedField(serializers.RelatedField):
|
||||||
return self._map_django_field_type(field)
|
return self._map_django_field_type(field)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# 如果没有 queryset 或无法获取字段信息,使用启发式规则
|
# 如果没有 queryset 或无法获取字段信息,使用启发式规则
|
||||||
return self._heuristic_field_type(attr_name)
|
return self._heuristic_field_type(attr_name)
|
||||||
|
|
||||||
|
@ -289,7 +254,7 @@ class ObjectRelatedField(serializers.RelatedField):
|
||||||
将 Django 字段类型映射到 OpenAPI 类型
|
将 Django 字段类型映射到 OpenAPI 类型
|
||||||
"""
|
"""
|
||||||
field_type = type(field).__name__
|
field_type = type(field).__name__
|
||||||
|
|
||||||
# 整数类型
|
# 整数类型
|
||||||
if 'Integer' in field_type or 'BigInteger' in field_type or 'SmallInteger' in field_type:
|
if 'Integer' in field_type or 'BigInteger' in field_type or 'SmallInteger' in field_type:
|
||||||
return 'integer'
|
return 'integer'
|
||||||
|
@ -314,7 +279,7 @@ class ObjectRelatedField(serializers.RelatedField):
|
||||||
启发式推断字段类型
|
启发式推断字段类型
|
||||||
"""
|
"""
|
||||||
# 基于属性名的启发式规则
|
# 基于属性名的启发式规则
|
||||||
|
|
||||||
if attr_name in ['is_active', 'enabled', 'visible'] or attr_name.startswith('is_'):
|
if attr_name in ['is_active', 'enabled', 'visible'] or attr_name.startswith('is_'):
|
||||||
return 'boolean'
|
return 'boolean'
|
||||||
elif attr_name in ['count', 'number', 'size', 'amount']:
|
elif attr_name in ['count', 'number', 'size', 'amount']:
|
||||||
|
|
|
@ -240,9 +240,17 @@ class ConnectMethodUtil:
|
||||||
def get_user_allowed_connect_methods(cls, os, user):
|
def get_user_allowed_connect_methods(cls, os, user):
|
||||||
from acls.models import ConnectMethodACL
|
from acls.models import ConnectMethodACL
|
||||||
methods = cls.get_filtered_protocols_connect_methods(os)
|
methods = cls.get_filtered_protocols_connect_methods(os)
|
||||||
acls = ConnectMethodACL.get_user_acls(user)
|
accept_methods = set(itertools.chain.from_iterable(
|
||||||
disabled_connect_methods = acls.values_list('connect_methods', flat=True)
|
ConnectMethodACL.get_user_acls(user)
|
||||||
disabled_connect_methods = set(itertools.chain.from_iterable(disabled_connect_methods))
|
.filter(action=ConnectMethodACL.ActionChoices.accept)
|
||||||
|
.values_list('connect_methods', flat=True)
|
||||||
|
))
|
||||||
|
# 在禁用的基础上放行一些连接方法
|
||||||
|
disabled_connect_methods = set(itertools.chain.from_iterable(
|
||||||
|
ConnectMethodACL.get_user_acls(user)
|
||||||
|
.filter(action=ConnectMethodACL.ActionChoices.reject)
|
||||||
|
.values_list('connect_methods', flat=True)
|
||||||
|
)) - accept_methods
|
||||||
|
|
||||||
new_queryset = {}
|
new_queryset = {}
|
||||||
for protocol, methods in methods.items():
|
for protocol, methods in methods.items():
|
||||||
|
|
Loading…
Reference in New Issue