Commit 3fb79c77 authored by ibuler's avatar ibuler

Merge branch 'dev' of github.com:jumpserver/jumpserver into dev

parents 02d3ae37 8cd8f41c
......@@ -13,8 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import time
from rest_framework import generics
from rest_framework.serializers import ValidationError
from rest_framework.views import APIView
......
......@@ -7,5 +7,7 @@ class AssetsConfig(AppConfig):
name = 'assets'
def ready(self):
from . import signals_handler
super().ready()
from . import signals_handler
from .models import Node
Node.initial_some_nodes()
......@@ -11,7 +11,7 @@ from django.utils.translation import ugettext
from django.core.cache import cache
from orgs.mixins.models import OrgModelMixin, OrgManager
from orgs.utils import set_current_org, get_current_org
from orgs.utils import set_current_org, get_current_org, tmp_to_root_org, tmp_to_org
from orgs.models import Organization
......@@ -25,37 +25,42 @@ class NodeQuerySet(models.QuerySet):
class TreeMixin:
tree_created_time = None
tree_updated_time_cache_key = 'NODE_TREE_CREATED_AT'
tree_update_time_cache_time = 3600
tree_updated_time_cache_key = 'NODE_TREE_UPDATED_AT'
tree_cache_time = 3600
tree_assets_cache_key = 'NODE_TREE_ASSETS_UPDATED_AT'
tree_assets_created_time = None
_tree_service = None
@classmethod
def tree(cls):
# Todo: 有待优化, 因为每次刷新都会导致其他节点的tree失效 完成
# TOdo: 游离的资产,在树上显示的数量不对
# Todo: ungroup node
# Todo: api key页面有bug 完成
from ..utils import TreeService
tree_updated_time = cache.get(cls.tree_updated_time_cache_key, 0)
if not cls.tree_created_time or \
tree_updated_time > cls.tree_created_time:
print("New tree")
tree = TreeService.new()
cls.tree_created_time = time.time()
cls.tree_assets_created_time = time.time()
cls._tree_service = tree
return tree
node_assets_updated_time = cache.get(cls.tree_assets_cache_key, 0)
if not cls.tree_assets_created_time or \
node_assets_updated_time > cls.tree_assets_created_time:
cls._tree_service.init_assets_async()
return cls._tree_service
@classmethod
def expire_cache_tree(cls):
def refresh_tree(cls):
key = cls.tree_updated_time_cache_key
ttl = cls.tree_update_time_cache_time
ttl = cls.tree_cache_time
value = time.time()
cache.set(key, value, ttl)
@classmethod
def refresh_tree(cls):
cls.expire_cache_tree()
def refresh_node_assets(cls):
key = cls.tree_assets_cache_key
ttl = cls.tree_cache_time
value = time.time()
cache.set(key, value, ttl)
@property
def _tree(self):
......@@ -183,6 +188,31 @@ class FamilyMixin:
key_list.pop()
return keys
def get_next_child_key(self):
mark = self.child_mark
self.child_mark += 1
self.save()
return "{}:{}".format(self.key, mark)
def get_next_child_preset_name(self):
name = ugettext("New node")
values = [
child.value[child.value.rfind(' '):]
for child in self.get_children()
if child.value.startswith(name)
]
values = [int(value) for value in values if value.strip().isdigit()]
count = max(values) + 1 if values else 1
return '{} {}'.format(name, count)
def create_child(self, value, _id=None):
with transaction.atomic():
child_key = self.get_next_child_key()
child = self.__class__.objects.create(
id=_id, key=child_key, value=value
)
return child
class FullValueMixin:
_full_value = None
......@@ -246,7 +276,85 @@ class NodeAssetsMixin:
return Asset.objects.filter(nodes__key__regex=pattern)
class Node(OrgModelMixin, TreeMixin, FamilyMixin, FullValueMixin, NodeAssetsMixin):
class SomeNodesMixin:
key = ''
default_key = '1'
default_value = 'Default'
ungrouped_key = '-10'
ungrouped_value = _('ungrouped')
empty_key = '-11'
empty_value = _("empty")
def is_default_node(self):
return self.key == self.default_key
def is_org_root(self):
if self.key.isdigit():
return True
else:
return False
@classmethod
def create_org_root_node(cls):
# 如果使用current_org 在set_current_org时会死循环
ori_org = get_current_org()
with transaction.atomic():
if not ori_org.is_real():
return cls.default_node()
set_current_org(Organization.root())
org_nodes_roots = cls.objects.filter(key__regex=r'^[0-9]+$')
org_nodes_roots_keys = org_nodes_roots.values_list('key', flat=True)
if not org_nodes_roots_keys:
org_nodes_roots_keys = ['1']
key = max([int(k) for k in org_nodes_roots_keys])
key = str(key + 1) if key != 0 else '2'
set_current_org(ori_org)
root = cls.objects.create(key=key, value=ori_org.name)
return root
@classmethod
def org_root(cls):
root = cls.objects.filter(key__regex=r'^[0-9]+$')
if root:
return root[0]
else:
return cls.create_org_root_node()
@classmethod
def ungrouped_node(cls):
with tmp_to_org(Organization.system()):
defaults = {'value': cls.ungrouped_key}
obj, created = cls.objects.get_or_create(
defaults=defaults, key=cls.ungrouped_key
)
return obj
@classmethod
def empty_node(cls):
with tmp_to_org(Organization.system()):
defaults = {'value': cls.empty_value}
obj, created = cls.objects.get_or_create(
defaults=defaults, key=cls.empty_key
)
return obj
@classmethod
def default_node(cls):
with tmp_to_org(Organization.default()):
defaults = {'value': cls.default_value}
obj, created = cls.objects.get_or_create(
defaults=defaults, key=cls.default_key,
)
return obj
@classmethod
def initial_some_nodes(cls):
cls.default_node()
cls.empty_node()
cls.ungrouped_node()
class Node(OrgModelMixin, SomeNodesMixin, TreeMixin, FamilyMixin, FullValueMixin, NodeAssetsMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
key = models.CharField(unique=True, max_length=64, verbose_name=_("Key")) # '1:1:1:1'
value = models.CharField(max_length=128, verbose_name=_("Value"))
......@@ -290,75 +398,13 @@ class Node(OrgModelMixin, TreeMixin, FamilyMixin, FullValueMixin, NodeAssetsMixi
def level(self):
return len(self.key.split(':'))
def get_next_child_key(self):
mark = self.child_mark
self.child_mark += 1
self.save()
return "{}:{}".format(self.key, mark)
def get_next_child_preset_name(self):
name = ugettext("New node")
values = [
child.value[child.value.rfind(' '):]
for child in self.get_children()
if child.value.startswith(name)
]
values = [int(value) for value in values if value.strip().isdigit()]
count = max(values) + 1 if values else 1
return '{} {}'.format(name, count)
def create_child(self, value, _id=None):
with transaction.atomic():
child_key = self.get_next_child_key()
child = self.__class__.objects.create(
id=_id, key=child_key, value=value
)
return child
@classmethod
def refresh_nodes(cls):
cls.refresh_tree()
def is_default_node(self):
return self.key == '1'
def is_org_root(self):
if self.key.isdigit():
return True
else:
return False
@classmethod
def create_org_root_node(cls):
# 如果使用current_org 在set_current_org时会死循环
ori_org = get_current_org()
with transaction.atomic():
if not ori_org.is_real():
return cls.default_node()
set_current_org(Organization.root())
org_nodes_roots = cls.objects.filter(key__regex=r'^[0-9]+$')
org_nodes_roots_keys = org_nodes_roots.values_list('key', flat=True)
if not org_nodes_roots_keys:
org_nodes_roots_keys = ['1']
key = max([int(k) for k in org_nodes_roots_keys])
key = str(key + 1) if key != 0 else '2'
set_current_org(ori_org)
root = cls.objects.create(key=key, value=ori_org.name)
return root
@classmethod
def org_root(cls):
root = cls.objects.filter(key__regex=r'^[0-9]+$')
if root:
return root[0]
else:
return cls.create_org_root_node()
@classmethod
def default_node(cls):
defaults = {'value': 'Default'}
obj, created = cls.objects.get_or_create(defaults=defaults, key='1')
return obj
def refresh_assets(cls):
cls.refresh_node_assets()
def as_tree_node(self):
from common.tree import TreeNode
......
......@@ -14,7 +14,9 @@ __all__ = [
class NodeSerializer(BulkOrgResourceModelSerializer):
name = serializers.ReadOnlyField(source='value')
value = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_("value"))
value = serializers.CharField(
required=False, allow_blank=True, allow_null=True, label=_("value")
)
class Meta:
model = Node
......
......@@ -2,8 +2,9 @@
#
from collections import defaultdict
from django.db.models.signals import (
post_save, m2m_changed, pre_delete, pre_save, pre_init, post_init
post_save, m2m_changed, post_delete
)
from django.db.models.aggregates import Count
from django.dispatch import receiver
from common.utils import get_logger
......@@ -29,29 +30,36 @@ def test_asset_conn_on_created(asset):
test_asset_connectivity_util.delay([asset])
@receiver(post_save, sender=Asset, dispatch_uid="my_unique_identifier")
@receiver(post_save, sender=Asset)
@on_transaction_commit
def on_asset_created_or_update(sender, instance=None, created=False, **kwargs):
"""
当资产创建时,更新硬件信息,更新可连接性
确保资产必须属于一个节点
"""
if created:
logger.info("Asset `{}` create signal received".format(instance))
logger.info("Asset create signal recv: {}".format(instance))
# 获取资产硬件信息
update_asset_hardware_info_on_created(instance)
test_asset_conn_on_created(instance)
# 确保资产存在一个节点
has_node = instance.nodes.all().exists()
if not has_node:
instance.nodes.add(Node.org_root())
@receiver(pre_delete, sender=Asset, dispatch_uid="my_unique_identifier")
@receiver(post_delete, sender=Asset)
def on_asset_delete(sender, instance=None, **kwargs):
"""
当资产删除时,刷新节点,节点中存在节点和资产的关系
"""
Node.refresh_nodes()
logger.debug("Asset delete signal recv: {}".format(instance))
Node.refresh_assets()
@receiver(post_save, sender=SystemUser, dispatch_uid="my_unique_identifier")
@receiver(post_save, sender=SystemUser, dispatch_uid="jms")
def on_system_user_update(sender, instance=None, created=True, **kwargs):
"""
当系统用户更新时,可能更新了秘钥,用户名等,这时要自动推送系统用户到资产上,
......@@ -60,61 +68,126 @@ def on_system_user_update(sender, instance=None, created=True, **kwargs):
关联到上面
"""
if instance and not created:
logger.info("System user `{}` update signal received".format(instance))
logger.info("System user update signal recv: {}".format(instance))
assets = instance.assets.all().valid()
push_system_user_to_assets.delay(instance, assets)
@receiver(m2m_changed, sender=SystemUser.assets.through, dispatch_uid="my_unique_identifier")
def on_system_user_assets_change(sender, instance=None, **kwargs):
@receiver(m2m_changed, sender=SystemUser.assets.through)
def on_system_user_assets_change(sender, instance=None, action='', model=None, pk_set=None, **kwargs):
"""
当系统用户和资产关系发生变化时,应该重新推送系统用户到新添加的资产中
"""
if instance and kwargs["action"] == "post_add":
assets = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
push_system_user_to_assets.delay(instance, assets)
@receiver(m2m_changed, sender=SystemUser.nodes.through, dispatch_uid="my_unique_identifier")
def on_system_user_nodes_change(sender, instance=None, **kwargs):
if action != "post_add":
return
logger.debug("System user assets change signal recv: {}".format(instance))
queryset = model.objects.filter(pk__in=pk_set)
if model == Asset:
system_users = [instance]
assets = queryset
else:
system_users = queryset
assets = [instance]
for system_user in system_users:
push_system_user_to_assets.delay(system_user, assets)
@receiver(m2m_changed, sender=SystemUser.nodes.through)
def on_system_user_nodes_change(sender, instance=None, action=None, model=None, pk_set=None, **kwargs):
"""
当系统用户和节点关系发生变化时,应该将节点下资产关联到新的系统用户上
"""
if action != "post_add":
return
logger.info("System user `{}` nodes update signal recv".format(instance))
queryset = model.objects.filter(pk__in=pk_set)
if model == Node:
nodes_keys = queryset.values_list('key', flat=True)
system_users = [instance]
else:
nodes_keys = [instance.key]
system_users = queryset
assets = Node.get_nodes_all_assets(nodes_keys)
for system_user in system_users:
system_user.assets.add(*tuple(assets))
@receiver(m2m_changed, sender=Asset.nodes.through)
def on_asset_nodes_change(sender, instance=None, action='', **kwargs):
"""
当系统用户和节点关系发生变化时,应该将节点关联到新的系统用户上
资产节点发生变化时,刷新节点
"""
if instance and kwargs["action"] == "post_add":
logger.info("System user `{}` nodes update signal received".format(instance))
nodes_keys = kwargs['model'].objects.filter(
pk__in=kwargs['pk_set']
).values_list('key', flat=True)
assets = Node.get_nodes_all_assets(nodes_keys)
instance.assets.add(*tuple(assets))
if action.startswith('post'):
logger.debug("Asset nodes change signal recv: {}".format(instance))
Node.refresh_assets()
@receiver(m2m_changed, sender=Asset.nodes.through, dispatch_uid="my_unique_identifier")
def on_asset_nodes_changed(sender, instance=None, **kwargs):
@receiver(m2m_changed, sender=Asset.nodes.through)
def on_asset_nodes_add(sender, instance=None, action='', model=None, pk_set=None, **kwargs):
"""
当资产的节点发生变化时,或者 当节点的资产关系发生变化时,
节点下新增的资产,添加到节点关联的系统用户中
并刷新节点
"""
if isinstance(instance, Asset):
logger.debug("Asset nodes change signal received: {}".format(instance))
# 节点资产发生变化时,将资产关联到节点关联的系统用户
if kwargs['action'] == 'post_add':
nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
system_users_assets = defaultdict(set)
system_users = SystemUser.objects.filter(nodes__in=nodes)
for system_user in system_users:
system_users_assets[system_user].add(instance)
for system_user, assets in system_users_assets.items():
system_user.assets.add(*tuple(assets))
if isinstance(instance, Node):
logger.debug("Node assets change signal received: {}".format(instance))
Node.refresh_nodes()
"""
if action != "post_add":
return
logger.debug("Assets node add signal recv: {}".format(action))
queryset = model.objects.filter(pk__in=pk_set)
if model == Node:
nodes = queryset
assets = [instance]
else:
nodes = [instance]
assets = queryset
# 节点资产发生变化时,将资产关联到节点关联的系统用户, 只关注新增的
system_users_assets = defaultdict(set)
system_users = SystemUser.objects.filter(nodes__in=nodes)
for system_user in system_users:
system_users_assets[system_user].update(set(assets))
for system_user, _assets in system_users_assets.items():
system_user.assets.add(*tuple(_assets))
@receiver(m2m_changed, sender=Asset.nodes.through)
def on_asset_nodes_remove(sender, instance=None, action='', model=None,
pk_set=None, **kwargs):
@receiver(post_save, sender=Node)
def on_node_update_or_created(sender, instance=None, created=False, **kwargs):
"""
监控资产删除节点关系, 或节点删除资产,避免产生游离资产
"""
if action not in ["post_remove", "pre_clear", "post_clear"]:
return
if action == "pre_clear":
if model == Node:
instance._nodes = list(instance.nodes.all())
else:
instance._assets = list(instance.assets.all())
return
logger.debug("Assets node remove signal recv: {}".format(action))
if action == "post_remove":
queryset = model.objects.filter(pk__in=pk_set)
else:
if model == Node:
queryset = instance._nodes
else:
queryset = instance._assets
if model == Node:
assets = [instance]
else:
assets = queryset
if isinstance(assets, list):
assets_not_has_node = []
for asset in assets:
if asset.nodes.all().count() == 0:
assets_not_has_node.append(asset.id)
else:
assets_not_has_node = assets.annotate(nodes_count=Count('nodes'))\
.filter(nodes_count=0).values_list('id', flat=True)
Node.org_root().assets.add(*tuple(assets_not_has_node))
@receiver([post_save, post_delete], sender=Node)
def on_node_update_or_created(sender, **kwargs):
# 刷新节点
Node.refresh_nodes()
......
......@@ -124,7 +124,6 @@
</div>
</div>
{% include 'assets/_node_tree.html' %}
{% include 'assets/_asset_update_modal.html' %}
{% include 'assets/_asset_import_modal.html' %}
{% include 'assets/_asset_list_modal.html' %}
......
......@@ -59,6 +59,8 @@ class TreeService(Tree):
tag_sep = ' / '
cache_key = '_NODE_FULL_TREE'
cache_time = 3600
has_empty_node = False
has_ungrouped_node = False
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
......@@ -119,9 +121,9 @@ class TreeService(Tree):
return [self.get_node(i, deep=deep) for i in ancestor_ids]
def get_node_full_tag(self, nid):
ancestors = self.ancestors(nid)
ancestors = self.ancestors(nid, with_self=True)
ancestors.reverse()
return self.tag_sep.join(n.tag for n in ancestors)
return self.tag_sep.join([n.tag for n in ancestors])
def get_family(self, nid, deep=False):
ancestors = self.ancestors(nid, with_self=False, deep=deep)
......
......@@ -290,10 +290,10 @@ LOGGING = {
'handlers': ['syslog'],
'level': 'INFO'
},
'django.db': {
'handlers': ['console', 'file'],
'level': 'DEBUG'
}
# 'django.db': {
# 'handlers': ['console', 'file'],
# 'level': 'DEBUG'
# }
}
}
......
......@@ -64,8 +64,11 @@ class OrgModelMixin(models.Model):
sep = '@'
def save(self, *args, **kwargs):
if current_org is not None and current_org.is_real():
self.org_id = current_org.id
org = get_current_org()
if org is not None and (org.is_real() or org.is_system()):
self.org_id = org.id
elif org is not None and org.is_default():
self.org_id = ''
return super().save(*args, **kwargs)
@property
......
......@@ -21,6 +21,8 @@ class Organization(models.Model):
ROOT_NAME = 'ROOT'
DEFAULT_ID = 'DEFAULT'
DEFAULT_NAME = 'DEFAULT'
SYSTEM_ID = '00000000-0000-0000-0000-000000000002'
SYSTEM_NAME = 'SYSTEM'
_user_admin_orgs = None
class Meta:
......@@ -55,6 +57,8 @@ class Organization(models.Model):
return cls.default()
elif id_or_name in [cls.ROOT_ID, cls.ROOT_NAME]:
return cls.root()
elif id_or_name in [cls.SYSTEM_ID, cls.SYSTEM_NAME]:
return cls.system()
try:
if is_uuid(id_or_name):
......@@ -89,7 +93,7 @@ class Organization(models.Model):
return False
def is_real(self):
return self.id not in (self.DEFAULT_NAME, self.ROOT_ID)
return self.id not in (self.DEFAULT_NAME, self.ROOT_ID, self.SYSTEM_ID)
@classmethod
def get_user_admin_orgs(cls, user):
......@@ -111,17 +115,18 @@ class Organization(models.Model):
def root(cls):
return cls(id=cls.ROOT_ID, name=cls.ROOT_NAME)
@classmethod
def system(cls):
return cls(id=cls.SYSTEM_ID, name=cls.SYSTEM_NAME)
def is_root(self):
if self.id is self.ROOT_ID:
return True
else:
return False
return self.id is self.ROOT_ID
def is_default(self):
if self.id is self.DEFAULT_ID:
return True
else:
return False
return self.id is self.DEFAULT_ID
def is_system(self):
return self.id is self.SYSTEM_ID
def change_to(self):
from .utils import set_current_org
......
# -*- coding: utf-8 -*-
#
from werkzeug.local import LocalProxy
from contextlib import contextmanager
from common.local import thread_local
from .models import Organization
......@@ -52,4 +53,22 @@ def get_current_org_id_for_serializer():
return org_id
@contextmanager
def tmp_to_root_org():
ori_org = get_current_org()
set_to_root_org()
yield
if ori_org is not None:
set_current_org(ori_org)
@contextmanager
def tmp_to_org(org):
ori_org = get_current_org()
set_current_org(org)
yield
if ori_org is not None:
set_current_org(ori_org)
current_org = LocalProxy(get_current_org)
......@@ -40,24 +40,14 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
return self.serializer_class
def filter_valid(self, queryset):
valid = self.request.query_params.get('is_valid', None)
if valid is None:
valid_query = self.request.query_params.get('is_valid', None)
if valid_query is None:
return queryset
if valid in ['0', 'N', 'false', 'False']:
valid = False
invalid = valid_query in ['0', 'N', 'false', 'False']
if invalid:
queryset = queryset.invalid()
else:
valid = True
now = timezone.now()
if valid:
queryset = queryset.filter(is_active=True).filter(
date_start__lt=now, date_expired__gt=now,
)
else:
queryset = queryset.filter(
Q(is_active=False) |
Q(date_start__gt=now) |
Q(date_expired__lt=now)
)
queryset = queryset.valid()
return queryset
def filter_system_user(self, queryset):
......
......@@ -15,10 +15,6 @@ from orgs.utils import set_to_root_org
from ..utils import (
ParserNode, AssetPermissionUtilV2
)
from .mixin import (
UserPermissionCacheMixin, GrantAssetsMixin, NodesWithUngroupMixin
)
from .. import const
from ..hands import User, Asset, Node, SystemUser, NodeSerializer
from .. import serializers
from ..models import Action
......@@ -66,8 +62,8 @@ class UserGrantedAssetsApi(UserPermissionMixin, ListAPIView):
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.AssetGrantedSerializer
only_fields = serializers.AssetGrantedSerializer.Meta.only_fields
filter_fields = ['hostname', 'ip']
search_fields = filter_fields
filter_fields = ['hostname', 'ip', 'id', 'comment']
search_fields = ['hostname', 'ip', 'comment']
def filter_by_nodes(self, queryset):
node_id = self.request.query_params.get("node")
......@@ -109,12 +105,21 @@ class UserGrantedNodesApi(UserPermissionMixin, ListAPIView):
查询用户授权的所有节点的API
"""
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.GrantedNodeSerializer
serializer_class = serializers.NodeGrantedSerializer
only_fields = NodeSerializer.Meta.only_fields
util = None
def get(self, request, *args, **kwargs):
self.util = AssetPermissionUtilV2(self.obj)
return super().get(request, *args, **kwargs)
def get_serializer_context(self):
context = super().get_serializer_context()
context["tree"] = self.util.user_tree
return context
def get_queryset(self):
util = AssetPermissionUtilV2(self.obj)
node_keys = util.get_nodes()
node_keys = self.util.get_nodes()
queryset = Node.objects.filter(key__in=node_keys)
return queryset
......@@ -131,7 +136,6 @@ class UserGrantedNodeChildrenApi(UserGrantedNodesApi):
system_user_id = self.request.query_params.get("system_user")
self.util = AssetPermissionUtilV2(self.obj)
if system_user_id:
system_user = get_object_or_404(SystemUser, id=system_user_id)
self.util.filter_permissions(system_users=system_user_id)
self.tree = self.util.get_user_tree()
......
# -*- coding: utf-8 -*-
#
from django.utils.translation import ugettext_lazy as _
UNGROUPED_NODE_ID = "00000000-0000-0000-0000-000000000002"
UNGROUPED_NODE_KEY = '-2'
UNGROUPED_NODE_VALUE = _("Ungrouped")
EMPTY_NODE_ID = "00000000-0000-0000-0000-000000000003"
EMPTY_NODE_KEY = "1:-2"
EMPTY_NODE_KEY = "-3"
EMPTY_NODE_VALUE = _("Empty")
......@@ -5,7 +5,7 @@ from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from common.utils import date_expired_default, set_or_append_attr_bulk
from common.utils import date_expired_default
from orgs.mixins.models import OrgModelMixin
from assets.models import Asset, SystemUser, Node
......
......@@ -4,6 +4,7 @@
import uuid
from django.utils.translation import ugettext_lazy as _
from django.db import models
from django.db.models import Q
from django.utils import timezone
from orgs.mixins.models import OrgModelMixin
......@@ -24,6 +25,18 @@ class BasePermissionQuerySet(models.QuerySet):
return self.active().filter(date_start__lt=timezone.now()) \
.filter(date_expired__gt=timezone.now())
def inactive(self):
return self.filter(is_active=False)
def invalid(self):
now = timezone.now
q = (
Q(is_active=False) |
Q(date_start__gt=now) |
Q(date_expired__lt=now)
)
return self.filter(q)
class BasePermissionManager(OrgManager):
def valid(self):
......
......@@ -9,8 +9,8 @@ from assets.serializers import ProtocolsField
from .asset_permission import ActionsField
__all__ = [
'GrantedNodeSerializer',
'NodeGrantedSerializer', 'AssetGrantedSerializer',
'NodeGrantedSerializer',
'AssetGrantedSerializer',
'ActionsSerializer', 'AssetSystemUserSerializer',
]
......@@ -43,36 +43,29 @@ class AssetGrantedSerializer(serializers.ModelSerializer):
"id", "hostname", "ip", "protocols", "os", 'domain',
"platform", "comment", "org_id",
]
fields = only_fields
fields = only_fields + ['org_name']
read_only_fields = fields
class NodeGrantedSerializer(serializers.ModelSerializer):
"""
授权资产组
"""
# assets_granted = AssetGrantedSerializer(many=True, read_only=True)
# assets_amount = serializers.ReadOnlyField()
name = serializers.ReadOnlyField(source='value')
# assets_only_fields = AssetGrantedSerializer.Meta.only_fields
# system_users_only_fields = AssetGrantedSerializer.system_users_only_fields
class Meta:
model = Node
only_fields = ['id', 'key', 'value', "org_id"]
fields = only_fields + ['name']
read_only_fields = fields
assets_amount = serializers.SerializerMethodField()
class GrantedNodeSerializer(serializers.ModelSerializer):
class Meta:
model = Node
fields = [
'id', 'name', 'key', 'value',
'id', 'name', 'key', 'value', 'org_id', "assets_amount"
]
read_only_fields = fields
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.tree = self.context.get("tree")
def get_assets_amount(self, obj):
if not self.tree:
return 0
return self.tree.assets_amount(obj.key)
class ActionsSerializer(serializers.Serializer):
actions = ActionsField(read_only=True)
......@@ -15,25 +15,23 @@ logger = get_logger(__file__)
@on_transaction_commit
def on_permission_created(sender, instance=None, created=False, **kwargs):
pass
# AssetPermissionUtil.expire_all_cache()
@receiver(post_save, sender=AssetPermission)
def on_permission_update(sender, **kwargs):
pass
# AssetPermissionUtil.expire_all_cache()
@receiver(post_delete, sender=AssetPermission)
def on_permission_delete(sender, **kwargs):
pass
# AssetPermissionUtil.expire_all_cache()
@receiver(m2m_changed, sender=AssetPermission.nodes.through)
def on_permission_nodes_changed(sender, instance=None, **kwargs):
# AssetPermissionUtil.expire_all_cache()
if isinstance(instance, AssetPermission) and kwargs['action'] == 'post_add':
def on_permission_nodes_changed(sender, instance=None, action='', **kwargs):
if action != 'post_add':
return
if isinstance(instance, AssetPermission):
logger.debug("Asset permission nodes change signal received")
nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
system_users = instance.system_users.all()
......@@ -42,9 +40,10 @@ def on_permission_nodes_changed(sender, instance=None, **kwargs):
@receiver(m2m_changed, sender=AssetPermission.assets.through)
def on_permission_assets_changed(sender, instance=None, **kwargs):
# AssetPermissionUtil.expire_all_cache()
if isinstance(instance, AssetPermission) and kwargs['action'] == 'post_add':
def on_permission_assets_changed(sender, instance=None, action='', **kwargs):
if action != 'post_add':
return
if isinstance(instance, AssetPermission):
logger.debug("Asset permission assets change signal received")
assets = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
system_users = instance.system_users.all()
......@@ -53,13 +52,15 @@ def on_permission_assets_changed(sender, instance=None, **kwargs):
@receiver(m2m_changed, sender=AssetPermission.system_users.through)
def on_permission_system_users_changed(sender, instance=None, **kwargs):
# AssetPermissionUtil.expire_all_cache()
if isinstance(instance, AssetPermission) and kwargs['action'] == 'post_add':
logger.debug("Asset permission system_users change signal received")
def on_permission_system_users_changed(sender, instance=None, action='', **kwargs):
if action != 'post_add':
return
if isinstance(instance, AssetPermission):
system_users = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
logger.debug("Asset permission system_users change signal received")
assets = instance.assets.all()
nodes = instance.nodes.all()
for system_user in system_users:
system_user.nodes.add(*tuple(nodes))
system_user.assets.add(*tuple(assets))
......@@ -4,6 +4,7 @@ from collections import defaultdict
from functools import reduce
from django.db.models import Q
from django.conf import settings
from orgs.utils import set_to_root_org
from common.utils import get_logger, timeit
......@@ -11,6 +12,7 @@ from common.tree import TreeNode
from assets.utils import TreeService
from ..models import AssetPermission
from ..hands import Node, Asset, SystemUser
from .. import const
logger = get_logger(__file__)
......@@ -129,6 +131,9 @@ class AssetPermissionUtilV2:
@timeit
def add_direct_nodes_to_user_tree(self, user_tree):
"""
将授权规则的节点放到用户树上, 从full tree中粘贴子树
"""
nodes_direct_keys = self.permissions \
.exclude(nodes__isnull=True) \
.values_list('nodes__key', flat=True) \
......@@ -153,6 +158,10 @@ class AssetPermissionUtilV2:
@timeit
def add_single_assets_node_to_user_tree(self, user_tree):
"""
将单独授权的资产放到树上,如果设置了单独资产到 未分组中,则放到未分组中
如果没有,则查询资产属于的资产组,放到树上
"""
# 添加单独授权资产的节点
nodes_single_assets = defaultdict(set)
queryset = self.permissions.exclude(assets__isnull=True) \
......@@ -161,13 +170,26 @@ class AssetPermissionUtilV2:
for item in queryset:
nodes_single_assets[item[1]].add(item[0])
# Todo: 游离资产
nodes_single_assets.pop(None, None)
for key in tuple(nodes_single_assets.keys()):
if user_tree.contains(key):
nodes_single_assets.pop(key)
# 如果要设置到ungroup中
if settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE:
node_key = Node.ungrouped_key
node_value = Node.ungrouped_value
user_tree.create_node(
identifier=node_key, tag=node_value,
parent=user_tree.root,
)
assets = set()
for _assets in nodes_single_assets.values():
assets.update(set(_assets))
user_tree.set_assets(node_key, assets)
return
# 获取单独授权资产,并没有在授权的节点上
for key, assets in nodes_single_assets.items():
node = self.full_tree.get_node(key, deep=True)
......@@ -180,11 +202,17 @@ class AssetPermissionUtilV2:
@timeit
def parse_user_tree_to_full_tree(self, user_tree):
"""
经过前面两个动作,用户授权的节点已放到树上,但是树不是完整的,
这里要讲树构造成一个完整的书
"""
# 开始修正user_tree,保证父节点都在树上
root_children = user_tree.children('')
for child in root_children:
if child.identifier.isdigit():
continue
if child.identifier.startswith('-'):
continue
ancestors = self.full_tree.ancestors(
child.identifier, with_self=False, deep=True
)
......@@ -194,6 +222,19 @@ class AssetPermissionUtilV2:
user_tree.safe_add_ancestors(ancestors)
user_tree.move_node(child.identifier, parent_id)
@staticmethod
def add_empty_node_if_need(user_tree):
"""
添加空节点,如果根节点没有子节点的话
"""
if not user_tree.children(user_tree.root):
node_key = Node.empty_key
node_value = Node.empty_value
user_tree.create_node(
identifier=node_key, tag=node_value,
parent=user_tree.root,
)
@timeit
def get_user_tree(self):
if self._user_tree:
......@@ -207,6 +248,7 @@ class AssetPermissionUtilV2:
self.add_direct_nodes_to_user_tree(user_tree)
self.add_single_assets_node_to_user_tree(user_tree)
self.parse_user_tree_to_full_tree(user_tree)
self.add_empty_node_if_need(user_tree)
self._user_tree = user_tree
return user_tree
......
......@@ -74,6 +74,14 @@ def construct_remote_apps_tree_root():
def parse_remote_app_to_tree_node(parent, remote_app):
system_user = remote_app.system_user
user = {
'id': system_user.id,
'name': system_user.name,
'username': system_user.username,
'protocol': system_user.protocol,
'login_mode': system_user.login_mode,
}
tree_node = {
'id': remote_app.id,
'name': remote_app.name,
......@@ -82,6 +90,6 @@ def parse_remote_app_to_tree_node(parent, remote_app):
'open': False,
'isParent': False,
'iconSkin': 'file',
'meta': {'type': 'remote_app'}
'meta': {'type': 'remote_app', 'user': user}
}
return TreeNode(**tree_node)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment