mirror of https://github.com/jumpserver/jumpserver
perf: 修改 json field query
parent
1ec4cbdf38
commit
7c850a8a1e
|
@ -1,6 +1,7 @@
|
|||
from rest_framework.generics import CreateAPIView
|
||||
from rest_framework.response import Response
|
||||
|
||||
from common.db.fields import JSONManyToManyField
|
||||
from common.utils import reverse, lazyproperty
|
||||
from orgs.utils import tmp_to_org
|
||||
from .. import serializers
|
||||
|
@ -30,14 +31,20 @@ class LoginAssetCheckAPI(CreateAPIView):
|
|||
return serializer
|
||||
|
||||
def check_review(self):
|
||||
user = self.serializer.user
|
||||
asset = self.serializer.asset
|
||||
|
||||
# 用户满足的 acls
|
||||
queryset = LoginAssetACL.objects.all()
|
||||
q = JSONManyToManyField.get_filter_q(LoginAssetACL, 'users', user)
|
||||
queryset = queryset.filter(q)
|
||||
q = JSONManyToManyField.get_filter_q(LoginAssetACL, 'assets', asset)
|
||||
queryset = queryset.filter(q)
|
||||
account_username = self.serializer.validated_data.get('account_username')
|
||||
queryset = queryset.filter(accounts__contains=account_username)
|
||||
|
||||
with tmp_to_org(self.serializer.asset.org):
|
||||
kwargs = {
|
||||
'user': self.serializer.user,
|
||||
'asset': self.serializer.asset,
|
||||
'account_username': self.serializer.validated_data.get('account_username'),
|
||||
'action': LoginAssetACL.ActionChoices.review
|
||||
}
|
||||
acl = LoginAssetACL.objects.filter(**kwargs).valid().first()
|
||||
acl = queryset.order_by('priority').valid().first()
|
||||
|
||||
if acl:
|
||||
need_review = True
|
||||
|
|
|
@ -21,7 +21,7 @@ def migrate_base_acl_users_assets_accounts(apps, *args):
|
|||
asset_attrs.append({"name": "name", "value": asset_names, "match": "in"})
|
||||
asset_address = (obj.assets or {}).get('address_group', [])
|
||||
if asset_address:
|
||||
asset_attrs.append({"name": "address", "value": asset_address, "match": "ip_in", "rel": "or"})
|
||||
asset_attrs.append({"name": "address", "value": asset_address, "match": "ip_in"})
|
||||
obj.new_assets = {"type": "attrs", "attrs": asset_attrs}
|
||||
|
||||
account_usernames = (obj.accounts or {}).get('username_group', [])
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import ipaddress
|
||||
import json
|
||||
import re
|
||||
|
||||
from django.apps import apps
|
||||
from django.core.exceptions import ValidationError
|
||||
|
@ -344,10 +345,6 @@ class RelatedManager:
|
|||
if name is None or val is None:
|
||||
continue
|
||||
|
||||
if val == '*':
|
||||
filters = Q()
|
||||
break
|
||||
|
||||
if match == 'ip_in':
|
||||
q = self.get_ip_in_q(name, val)
|
||||
elif match in ("exact", "contains", "startswith", "endswith", "regex"):
|
||||
|
@ -362,7 +359,10 @@ class RelatedManager:
|
|||
else:
|
||||
q = Q()
|
||||
else:
|
||||
q = Q(**{name: val})
|
||||
if val == '*':
|
||||
q = Q()
|
||||
else:
|
||||
q = Q(**{name: val})
|
||||
|
||||
if rel == 'or':
|
||||
filters |= q
|
||||
|
@ -415,6 +415,59 @@ class JSONManyToManyDescriptor:
|
|||
value = value.value
|
||||
manager.set(value)
|
||||
|
||||
def test_is(self):
|
||||
print("Self.field is", self.field)
|
||||
print("Self.field to", self.field.to)
|
||||
print("Self.field model", self.field.model)
|
||||
print("Self.field column", self.field.column)
|
||||
print("Self.field to", self.field.__dict__)
|
||||
|
||||
@staticmethod
|
||||
def attr_to_regex(attr):
|
||||
"""将属性规则转换为正则表达式"""
|
||||
name, value, match = attr['name'], attr['value'], attr['match']
|
||||
if match == 'contains':
|
||||
return r'.*{}.*'.format(escape_regex(value))
|
||||
elif match == 'startswith':
|
||||
return r'^{}.*'.format(escape_regex(value))
|
||||
elif match == 'endswith':
|
||||
return r'.*{}$'.format(escape_regex(value))
|
||||
elif match == 'regex':
|
||||
return value
|
||||
elif match == 'not':
|
||||
return r'^(?!^{}$)'.format(escape_regex(value))
|
||||
elif match == 'in':
|
||||
values = '|'.join(map(escape_regex, value))
|
||||
return r'^(?:{})$'.format(values)
|
||||
else:
|
||||
return r'^{}$'.format(escape_regex(value))
|
||||
|
||||
def is_match(self, attr_dict, attr_rules):
|
||||
for rule in attr_rules:
|
||||
value = attr_dict.get(rule['name'], '')
|
||||
regex = self.attr_to_regex(rule)
|
||||
if not re.match(regex, value):
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_filter_q(self, instance):
|
||||
model_cls = self.field.model
|
||||
field_name = self.field.column
|
||||
q = Q(users__type='all') | Q(users__type='ids', users__ids__contains=[str(instance.id)])
|
||||
queryset_id_attrs = model_cls.objects \
|
||||
.filter(**{'{}__type'.format(field_name): 'attrs'}) \
|
||||
.values_list('id', '{}__attrs'.format(field_name))
|
||||
instance_attr = {k: v for k, v in instance.__dict__.items() if not k.startswith('_')}
|
||||
ids = [str(_id) for _id, attr_rules in queryset_id_attrs if self.is_match(instance_attr, attr_rules)]
|
||||
if ids:
|
||||
q |= Q(id__in=ids)
|
||||
return q
|
||||
|
||||
|
||||
def escape_regex(s):
|
||||
"""转义字符串中的正则表达式特殊字符"""
|
||||
return re.sub('[.*+?^${}()|[\\]]', r'\\\g<0>', s)
|
||||
|
||||
|
||||
class JSONManyToManyField(models.JSONField):
|
||||
def __init__(self, to, *args, **kwargs):
|
||||
|
@ -455,18 +508,15 @@ class JSONManyToManyField(models.JSONField):
|
|||
if 'name' not in attr or 'value' not in attr:
|
||||
raise ValueError(_("Invalid attrs, should be has name and value"))
|
||||
|
||||
def get_db_prep_value(self, value, connection, prepared=False):
|
||||
return self.get_prep_value(value)
|
||||
|
||||
def get_prep_value(self, value):
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, RelatedManager):
|
||||
value = value.value
|
||||
self.check_value(value)
|
||||
return json.dumps(value)
|
||||
|
||||
def validate(self, value, model_instance):
|
||||
super().validate(value, model_instance)
|
||||
if not isinstance(value, dict):
|
||||
raise ValidationError("Invalid JSON data for JSONManyToManyField.")
|
||||
self.check_value(value)
|
||||
|
|
Loading…
Reference in New Issue