mirror of https://github.com/jumpserver/jumpserver
Dev beta (#3167)
* [Update] 添加loading * [Update] 修改避免游离资产 * [Update] 修改nodes * [Update] 修改支持未分组pull/3171/head
parent
1fe18e8073
commit
8cd8f41cb0
|
@ -13,8 +13,6 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import time
|
|
||||||
|
|
||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
|
@ -7,5 +7,7 @@ class AssetsConfig(AppConfig):
|
||||||
name = 'assets'
|
name = 'assets'
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
from . import signals_handler
|
|
||||||
super().ready()
|
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 django.core.cache import cache
|
||||||
|
|
||||||
from orgs.mixins.models import OrgModelMixin, OrgManager
|
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
|
from orgs.models import Organization
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,37 +25,42 @@ class NodeQuerySet(models.QuerySet):
|
||||||
|
|
||||||
class TreeMixin:
|
class TreeMixin:
|
||||||
tree_created_time = None
|
tree_created_time = None
|
||||||
tree_updated_time_cache_key = 'NODE_TREE_CREATED_AT'
|
tree_updated_time_cache_key = 'NODE_TREE_UPDATED_AT'
|
||||||
tree_update_time_cache_time = 3600
|
tree_cache_time = 3600
|
||||||
|
tree_assets_cache_key = 'NODE_TREE_ASSETS_UPDATED_AT'
|
||||||
|
tree_assets_created_time = None
|
||||||
_tree_service = None
|
_tree_service = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tree(cls):
|
def tree(cls):
|
||||||
# Todo: 有待优化, 因为每次刷新都会导致其他节点的tree失效 完成
|
|
||||||
# TOdo: 游离的资产,在树上显示的数量不对
|
|
||||||
# Todo: ungroup node
|
|
||||||
# Todo: api key页面有bug 完成
|
|
||||||
from ..utils import TreeService
|
from ..utils import TreeService
|
||||||
tree_updated_time = cache.get(cls.tree_updated_time_cache_key, 0)
|
tree_updated_time = cache.get(cls.tree_updated_time_cache_key, 0)
|
||||||
if not cls.tree_created_time or \
|
if not cls.tree_created_time or \
|
||||||
tree_updated_time > cls.tree_created_time:
|
tree_updated_time > cls.tree_created_time:
|
||||||
print("New tree")
|
|
||||||
tree = TreeService.new()
|
tree = TreeService.new()
|
||||||
cls.tree_created_time = time.time()
|
cls.tree_created_time = time.time()
|
||||||
|
cls.tree_assets_created_time = time.time()
|
||||||
cls._tree_service = tree
|
cls._tree_service = tree
|
||||||
return 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
|
return cls._tree_service
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def expire_cache_tree(cls):
|
def refresh_tree(cls):
|
||||||
key = cls.tree_updated_time_cache_key
|
key = cls.tree_updated_time_cache_key
|
||||||
ttl = cls.tree_update_time_cache_time
|
ttl = cls.tree_cache_time
|
||||||
value = time.time()
|
value = time.time()
|
||||||
cache.set(key, value, ttl)
|
cache.set(key, value, ttl)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def refresh_tree(cls):
|
def refresh_node_assets(cls):
|
||||||
cls.expire_cache_tree()
|
key = cls.tree_assets_cache_key
|
||||||
|
ttl = cls.tree_cache_time
|
||||||
|
value = time.time()
|
||||||
|
cache.set(key, value, ttl)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _tree(self):
|
def _tree(self):
|
||||||
|
@ -183,6 +188,31 @@ class FamilyMixin:
|
||||||
key_list.pop()
|
key_list.pop()
|
||||||
return keys
|
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:
|
class FullValueMixin:
|
||||||
_full_value = None
|
_full_value = None
|
||||||
|
@ -246,7 +276,85 @@ class NodeAssetsMixin:
|
||||||
return Asset.objects.filter(nodes__key__regex=pattern)
|
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)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
key = models.CharField(unique=True, max_length=64, verbose_name=_("Key")) # '1:1:1:1'
|
key = models.CharField(unique=True, max_length=64, verbose_name=_("Key")) # '1:1:1:1'
|
||||||
value = models.CharField(max_length=128, verbose_name=_("Value"))
|
value = models.CharField(max_length=128, verbose_name=_("Value"))
|
||||||
|
@ -290,75 +398,13 @@ class Node(OrgModelMixin, TreeMixin, FamilyMixin, FullValueMixin, NodeAssetsMixi
|
||||||
def level(self):
|
def level(self):
|
||||||
return len(self.key.split(':'))
|
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
|
@classmethod
|
||||||
def refresh_nodes(cls):
|
def refresh_nodes(cls):
|
||||||
cls.refresh_tree()
|
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
|
@classmethod
|
||||||
def create_org_root_node(cls):
|
def refresh_assets(cls):
|
||||||
# 如果使用current_org 在set_current_org时会死循环
|
cls.refresh_node_assets()
|
||||||
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 as_tree_node(self):
|
def as_tree_node(self):
|
||||||
from common.tree import TreeNode
|
from common.tree import TreeNode
|
||||||
|
|
|
@ -14,7 +14,9 @@ __all__ = [
|
||||||
|
|
||||||
class NodeSerializer(BulkOrgResourceModelSerializer):
|
class NodeSerializer(BulkOrgResourceModelSerializer):
|
||||||
name = serializers.ReadOnlyField(source='value')
|
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:
|
class Meta:
|
||||||
model = Node
|
model = Node
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
#
|
#
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from django.db.models.signals import (
|
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 django.dispatch import receiver
|
||||||
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
|
@ -29,29 +30,36 @@ def test_asset_conn_on_created(asset):
|
||||||
test_asset_connectivity_util.delay([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
|
@on_transaction_commit
|
||||||
def on_asset_created_or_update(sender, instance=None, created=False, **kwargs):
|
def on_asset_created_or_update(sender, instance=None, created=False, **kwargs):
|
||||||
"""
|
"""
|
||||||
当资产创建时,更新硬件信息,更新可连接性
|
当资产创建时,更新硬件信息,更新可连接性
|
||||||
|
确保资产必须属于一个节点
|
||||||
"""
|
"""
|
||||||
if created:
|
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)
|
update_asset_hardware_info_on_created(instance)
|
||||||
test_asset_conn_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):
|
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):
|
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:
|
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()
|
assets = instance.assets.all().valid()
|
||||||
push_system_user_to_assets.delay(instance, assets)
|
push_system_user_to_assets.delay(instance, assets)
|
||||||
|
|
||||||
|
|
||||||
@receiver(m2m_changed, sender=SystemUser.assets.through, dispatch_uid="my_unique_identifier")
|
@receiver(m2m_changed, sender=SystemUser.assets.through)
|
||||||
def on_system_user_assets_change(sender, instance=None, **kwargs):
|
def on_system_user_assets_change(sender, instance=None, action='', model=None, pk_set=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
当系统用户和资产关系发生变化时,应该重新推送系统用户到新添加的资产中
|
当系统用户和资产关系发生变化时,应该重新推送系统用户到新添加的资产中
|
||||||
"""
|
"""
|
||||||
if instance and kwargs["action"] == "post_add":
|
if action != "post_add":
|
||||||
assets = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
|
return
|
||||||
push_system_user_to_assets.delay(instance, assets)
|
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, dispatch_uid="my_unique_identifier")
|
@receiver(m2m_changed, sender=SystemUser.nodes.through)
|
||||||
def on_system_user_nodes_change(sender, instance=None, **kwargs):
|
def on_system_user_nodes_change(sender, instance=None, action=None, model=None, pk_set=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
当系统用户和节点关系发生变化时,应该将节点关联到新的系统用户上
|
当系统用户和节点关系发生变化时,应该将节点下资产关联到新的系统用户上
|
||||||
"""
|
"""
|
||||||
if instance and kwargs["action"] == "post_add":
|
if action != "post_add":
|
||||||
logger.info("System user `{}` nodes update signal received".format(instance))
|
return
|
||||||
nodes_keys = kwargs['model'].objects.filter(
|
logger.info("System user `{}` nodes update signal recv".format(instance))
|
||||||
pk__in=kwargs['pk_set']
|
|
||||||
).values_list('key', flat=True)
|
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)
|
assets = Node.get_nodes_all_assets(nodes_keys)
|
||||||
instance.assets.add(*tuple(assets))
|
for system_user in system_users:
|
||||||
|
system_user.assets.add(*tuple(assets))
|
||||||
|
|
||||||
|
|
||||||
@receiver(m2m_changed, sender=Asset.nodes.through, dispatch_uid="my_unique_identifier")
|
@receiver(m2m_changed, sender=Asset.nodes.through)
|
||||||
def on_asset_nodes_changed(sender, instance=None, **kwargs):
|
def on_asset_nodes_change(sender, instance=None, action='', **kwargs):
|
||||||
|
"""
|
||||||
|
资产节点发生变化时,刷新节点
|
||||||
|
"""
|
||||||
|
if action.startswith('post'):
|
||||||
|
logger.debug("Asset nodes change signal recv: {}".format(instance))
|
||||||
|
Node.refresh_assets()
|
||||||
|
|
||||||
|
|
||||||
|
@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):
|
if action != "post_add":
|
||||||
logger.debug("Asset nodes change signal received: {}".format(instance))
|
return
|
||||||
# 节点资产发生变化时,将资产关联到节点关联的系统用户
|
logger.debug("Assets node add signal recv: {}".format(action))
|
||||||
if kwargs['action'] == 'post_add':
|
queryset = model.objects.filter(pk__in=pk_set)
|
||||||
nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
|
if model == Node:
|
||||||
|
nodes = queryset
|
||||||
|
assets = [instance]
|
||||||
|
else:
|
||||||
|
nodes = [instance]
|
||||||
|
assets = queryset
|
||||||
|
# 节点资产发生变化时,将资产关联到节点关联的系统用户, 只关注新增的
|
||||||
system_users_assets = defaultdict(set)
|
system_users_assets = defaultdict(set)
|
||||||
system_users = SystemUser.objects.filter(nodes__in=nodes)
|
system_users = SystemUser.objects.filter(nodes__in=nodes)
|
||||||
for system_user in system_users:
|
for system_user in system_users:
|
||||||
system_users_assets[system_user].add(instance)
|
system_users_assets[system_user].update(set(assets))
|
||||||
for system_user, assets in system_users_assets.items():
|
for system_user, _assets in system_users_assets.items():
|
||||||
system_user.assets.add(*tuple(assets))
|
system_user.assets.add(*tuple(_assets))
|
||||||
if isinstance(instance, Node):
|
|
||||||
logger.debug("Node assets change signal received: {}".format(instance))
|
|
||||||
|
|
||||||
Node.refresh_nodes()
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Node)
|
@receiver(m2m_changed, sender=Asset.nodes.through)
|
||||||
def on_node_update_or_created(sender, instance=None, created=False, **kwargs):
|
def on_asset_nodes_remove(sender, instance=None, action='', model=None,
|
||||||
|
pk_set=None, **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()
|
Node.refresh_nodes()
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
<div class="ibox-content mailbox-content" style="padding-top: 0;padding-left: 1px">
|
<div class="ibox-content mailbox-content" style="padding-top: 0;padding-left: 1px">
|
||||||
<div class="file-manager" id="tree-node-id">
|
<div class="file-manager" id="tree-node-id">
|
||||||
<div id="{% block treeID %}nodeTree{% endblock %}" class="ztree">
|
<div id="{% block treeID %}nodeTree{% endblock %}" class="ztree">
|
||||||
|
{% trans 'Loading' %} ...
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -124,7 +124,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% include 'assets/_node_tree.html' %}
|
|
||||||
{% include 'assets/_asset_update_modal.html' %}
|
{% include 'assets/_asset_update_modal.html' %}
|
||||||
{% include 'assets/_asset_import_modal.html' %}
|
{% include 'assets/_asset_import_modal.html' %}
|
||||||
{% include 'assets/_asset_list_modal.html' %}
|
{% include 'assets/_asset_list_modal.html' %}
|
||||||
|
|
|
@ -59,6 +59,8 @@ class TreeService(Tree):
|
||||||
tag_sep = ' / '
|
tag_sep = ' / '
|
||||||
cache_key = '_NODE_FULL_TREE'
|
cache_key = '_NODE_FULL_TREE'
|
||||||
cache_time = 3600
|
cache_time = 3600
|
||||||
|
has_empty_node = False
|
||||||
|
has_ungrouped_node = False
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*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]
|
return [self.get_node(i, deep=deep) for i in ancestor_ids]
|
||||||
|
|
||||||
def get_node_full_tag(self, nid):
|
def get_node_full_tag(self, nid):
|
||||||
ancestors = self.ancestors(nid)
|
ancestors = self.ancestors(nid, with_self=True)
|
||||||
ancestors.reverse()
|
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):
|
def get_family(self, nid, deep=False):
|
||||||
ancestors = self.ancestors(nid, with_self=False, deep=deep)
|
ancestors = self.ancestors(nid, with_self=False, deep=deep)
|
||||||
|
|
|
@ -290,10 +290,10 @@ LOGGING = {
|
||||||
'handlers': ['syslog'],
|
'handlers': ['syslog'],
|
||||||
'level': 'INFO'
|
'level': 'INFO'
|
||||||
},
|
},
|
||||||
'django.db': {
|
# 'django.db': {
|
||||||
'handlers': ['console', 'file'],
|
# 'handlers': ['console', 'file'],
|
||||||
'level': 'DEBUG'
|
# 'level': 'DEBUG'
|
||||||
}
|
# }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,8 +64,11 @@ class OrgModelMixin(models.Model):
|
||||||
sep = '@'
|
sep = '@'
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if current_org is not None and current_org.is_real():
|
org = get_current_org()
|
||||||
self.org_id = current_org.id
|
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)
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -21,6 +21,8 @@ class Organization(models.Model):
|
||||||
ROOT_NAME = 'ROOT'
|
ROOT_NAME = 'ROOT'
|
||||||
DEFAULT_ID = 'DEFAULT'
|
DEFAULT_ID = 'DEFAULT'
|
||||||
DEFAULT_NAME = 'DEFAULT'
|
DEFAULT_NAME = 'DEFAULT'
|
||||||
|
SYSTEM_ID = '00000000-0000-0000-0000-000000000002'
|
||||||
|
SYSTEM_NAME = 'SYSTEM'
|
||||||
_user_admin_orgs = None
|
_user_admin_orgs = None
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -55,6 +57,8 @@ class Organization(models.Model):
|
||||||
return cls.default()
|
return cls.default()
|
||||||
elif id_or_name in [cls.ROOT_ID, cls.ROOT_NAME]:
|
elif id_or_name in [cls.ROOT_ID, cls.ROOT_NAME]:
|
||||||
return cls.root()
|
return cls.root()
|
||||||
|
elif id_or_name in [cls.SYSTEM_ID, cls.SYSTEM_NAME]:
|
||||||
|
return cls.system()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if is_uuid(id_or_name):
|
if is_uuid(id_or_name):
|
||||||
|
@ -89,7 +93,7 @@ class Organization(models.Model):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_real(self):
|
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
|
@classmethod
|
||||||
def get_user_admin_orgs(cls, user):
|
def get_user_admin_orgs(cls, user):
|
||||||
|
@ -111,17 +115,18 @@ class Organization(models.Model):
|
||||||
def root(cls):
|
def root(cls):
|
||||||
return cls(id=cls.ROOT_ID, name=cls.ROOT_NAME)
|
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):
|
def is_root(self):
|
||||||
if self.id is self.ROOT_ID:
|
return self.id is self.ROOT_ID
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def is_default(self):
|
def is_default(self):
|
||||||
if self.id is self.DEFAULT_ID:
|
return self.id is self.DEFAULT_ID
|
||||||
return True
|
|
||||||
else:
|
def is_system(self):
|
||||||
return False
|
return self.id is self.SYSTEM_ID
|
||||||
|
|
||||||
def change_to(self):
|
def change_to(self):
|
||||||
from .utils import set_current_org
|
from .utils import set_current_org
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
from werkzeug.local import LocalProxy
|
from werkzeug.local import LocalProxy
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from common.local import thread_local
|
from common.local import thread_local
|
||||||
from .models import Organization
|
from .models import Organization
|
||||||
|
@ -52,4 +53,22 @@ def get_current_org_id_for_serializer():
|
||||||
return org_id
|
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)
|
current_org = LocalProxy(get_current_org)
|
||||||
|
|
|
@ -40,24 +40,14 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
|
||||||
return self.serializer_class
|
return self.serializer_class
|
||||||
|
|
||||||
def filter_valid(self, queryset):
|
def filter_valid(self, queryset):
|
||||||
valid = self.request.query_params.get('is_valid', None)
|
valid_query = self.request.query_params.get('is_valid', None)
|
||||||
if valid is None:
|
if valid_query is None:
|
||||||
return queryset
|
return queryset
|
||||||
if valid in ['0', 'N', 'false', 'False']:
|
invalid = valid_query in ['0', 'N', 'false', 'False']
|
||||||
valid = False
|
if invalid:
|
||||||
|
queryset = queryset.invalid()
|
||||||
else:
|
else:
|
||||||
valid = True
|
queryset = queryset.valid()
|
||||||
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)
|
|
||||||
)
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def filter_system_user(self, queryset):
|
def filter_system_user(self, queryset):
|
||||||
|
|
|
@ -15,10 +15,6 @@ from orgs.utils import set_to_root_org
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ParserNode, AssetPermissionUtilV2
|
ParserNode, AssetPermissionUtilV2
|
||||||
)
|
)
|
||||||
from .mixin import (
|
|
||||||
UserPermissionCacheMixin, GrantAssetsMixin, NodesWithUngroupMixin
|
|
||||||
)
|
|
||||||
from .. import const
|
|
||||||
from ..hands import User, Asset, Node, SystemUser, NodeSerializer
|
from ..hands import User, Asset, Node, SystemUser, NodeSerializer
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
from ..models import Action
|
from ..models import Action
|
||||||
|
@ -66,8 +62,8 @@ class UserGrantedAssetsApi(UserPermissionMixin, ListAPIView):
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = serializers.AssetGrantedSerializer
|
serializer_class = serializers.AssetGrantedSerializer
|
||||||
only_fields = serializers.AssetGrantedSerializer.Meta.only_fields
|
only_fields = serializers.AssetGrantedSerializer.Meta.only_fields
|
||||||
filter_fields = ['hostname', 'ip']
|
filter_fields = ['hostname', 'ip', 'id', 'comment']
|
||||||
search_fields = filter_fields
|
search_fields = ['hostname', 'ip', 'comment']
|
||||||
|
|
||||||
def filter_by_nodes(self, queryset):
|
def filter_by_nodes(self, queryset):
|
||||||
node_id = self.request.query_params.get("node")
|
node_id = self.request.query_params.get("node")
|
||||||
|
@ -109,12 +105,21 @@ class UserGrantedNodesApi(UserPermissionMixin, ListAPIView):
|
||||||
查询用户授权的所有节点的API
|
查询用户授权的所有节点的API
|
||||||
"""
|
"""
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = serializers.GrantedNodeSerializer
|
serializer_class = serializers.NodeGrantedSerializer
|
||||||
only_fields = NodeSerializer.Meta.only_fields
|
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):
|
def get_queryset(self):
|
||||||
util = AssetPermissionUtilV2(self.obj)
|
node_keys = self.util.get_nodes()
|
||||||
node_keys = util.get_nodes()
|
|
||||||
queryset = Node.objects.filter(key__in=node_keys)
|
queryset = Node.objects.filter(key__in=node_keys)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
@ -131,7 +136,6 @@ class UserGrantedNodeChildrenApi(UserGrantedNodesApi):
|
||||||
system_user_id = self.request.query_params.get("system_user")
|
system_user_id = self.request.query_params.get("system_user")
|
||||||
self.util = AssetPermissionUtilV2(self.obj)
|
self.util = AssetPermissionUtilV2(self.obj)
|
||||||
if system_user_id:
|
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.util.filter_permissions(system_users=system_user_id)
|
||||||
self.tree = self.util.get_user_tree()
|
self.tree = self.util.get_user_tree()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
UNGROUPED_NODE_ID = "00000000-0000-0000-0000-000000000002"
|
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_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.db.models import Q
|
||||||
from django.utils.translation import ugettext_lazy as _
|
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 orgs.mixins.models import OrgModelMixin
|
||||||
from assets.models import Asset, SystemUser, Node
|
from assets.models import Asset, SystemUser, Node
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import uuid
|
import uuid
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models import Q
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from orgs.mixins.models import OrgModelMixin
|
from orgs.mixins.models import OrgModelMixin
|
||||||
|
|
||||||
|
@ -24,6 +25,18 @@ class BasePermissionQuerySet(models.QuerySet):
|
||||||
return self.active().filter(date_start__lt=timezone.now()) \
|
return self.active().filter(date_start__lt=timezone.now()) \
|
||||||
.filter(date_expired__gt=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):
|
class BasePermissionManager(OrgManager):
|
||||||
def valid(self):
|
def valid(self):
|
||||||
|
|
|
@ -9,8 +9,8 @@ from assets.serializers import ProtocolsField
|
||||||
from .asset_permission import ActionsField
|
from .asset_permission import ActionsField
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'GrantedNodeSerializer',
|
'NodeGrantedSerializer',
|
||||||
'NodeGrantedSerializer', 'AssetGrantedSerializer',
|
'AssetGrantedSerializer',
|
||||||
'ActionsSerializer', 'AssetSystemUserSerializer',
|
'ActionsSerializer', 'AssetSystemUserSerializer',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -43,36 +43,29 @@ class AssetGrantedSerializer(serializers.ModelSerializer):
|
||||||
"id", "hostname", "ip", "protocols", "os", 'domain',
|
"id", "hostname", "ip", "protocols", "os", 'domain',
|
||||||
"platform", "comment", "org_id",
|
"platform", "comment", "org_id",
|
||||||
]
|
]
|
||||||
fields = only_fields
|
fields = only_fields + ['org_name']
|
||||||
read_only_fields = fields
|
read_only_fields = fields
|
||||||
|
|
||||||
|
|
||||||
class NodeGrantedSerializer(serializers.ModelSerializer):
|
class NodeGrantedSerializer(serializers.ModelSerializer):
|
||||||
"""
|
assets_amount = serializers.SerializerMethodField()
|
||||||
授权资产组
|
|
||||||
"""
|
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
class GrantedNodeSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Node
|
model = Node
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'name', 'key', 'value',
|
'id', 'name', 'key', 'value', 'org_id', "assets_amount"
|
||||||
]
|
]
|
||||||
read_only_fields = fields
|
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):
|
class ActionsSerializer(serializers.Serializer):
|
||||||
actions = ActionsField(read_only=True)
|
actions = ActionsField(read_only=True)
|
||||||
|
|
|
@ -15,25 +15,23 @@ logger = get_logger(__file__)
|
||||||
@on_transaction_commit
|
@on_transaction_commit
|
||||||
def on_permission_created(sender, instance=None, created=False, **kwargs):
|
def on_permission_created(sender, instance=None, created=False, **kwargs):
|
||||||
pass
|
pass
|
||||||
# AssetPermissionUtil.expire_all_cache()
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=AssetPermission)
|
@receiver(post_save, sender=AssetPermission)
|
||||||
def on_permission_update(sender, **kwargs):
|
def on_permission_update(sender, **kwargs):
|
||||||
pass
|
pass
|
||||||
# AssetPermissionUtil.expire_all_cache()
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=AssetPermission)
|
@receiver(post_delete, sender=AssetPermission)
|
||||||
def on_permission_delete(sender, **kwargs):
|
def on_permission_delete(sender, **kwargs):
|
||||||
pass
|
pass
|
||||||
# AssetPermissionUtil.expire_all_cache()
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(m2m_changed, sender=AssetPermission.nodes.through)
|
@receiver(m2m_changed, sender=AssetPermission.nodes.through)
|
||||||
def on_permission_nodes_changed(sender, instance=None, **kwargs):
|
def on_permission_nodes_changed(sender, instance=None, action='', **kwargs):
|
||||||
# AssetPermissionUtil.expire_all_cache()
|
if action != 'post_add':
|
||||||
if isinstance(instance, AssetPermission) and kwargs['action'] == 'post_add':
|
return
|
||||||
|
if isinstance(instance, AssetPermission):
|
||||||
logger.debug("Asset permission nodes change signal received")
|
logger.debug("Asset permission nodes change signal received")
|
||||||
nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
|
nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
|
||||||
system_users = instance.system_users.all()
|
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)
|
@receiver(m2m_changed, sender=AssetPermission.assets.through)
|
||||||
def on_permission_assets_changed(sender, instance=None, **kwargs):
|
def on_permission_assets_changed(sender, instance=None, action='', **kwargs):
|
||||||
# AssetPermissionUtil.expire_all_cache()
|
if action != 'post_add':
|
||||||
if isinstance(instance, AssetPermission) and kwargs['action'] == 'post_add':
|
return
|
||||||
|
if isinstance(instance, AssetPermission):
|
||||||
logger.debug("Asset permission assets change signal received")
|
logger.debug("Asset permission assets change signal received")
|
||||||
assets = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
|
assets = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
|
||||||
system_users = instance.system_users.all()
|
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)
|
@receiver(m2m_changed, sender=AssetPermission.system_users.through)
|
||||||
def on_permission_system_users_changed(sender, instance=None, **kwargs):
|
def on_permission_system_users_changed(sender, instance=None, action='', **kwargs):
|
||||||
# AssetPermissionUtil.expire_all_cache()
|
if action != 'post_add':
|
||||||
if isinstance(instance, AssetPermission) and kwargs['action'] == 'post_add':
|
return
|
||||||
logger.debug("Asset permission system_users change signal received")
|
if isinstance(instance, AssetPermission):
|
||||||
system_users = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
|
system_users = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
|
||||||
|
logger.debug("Asset permission system_users change signal received")
|
||||||
assets = instance.assets.all()
|
assets = instance.assets.all()
|
||||||
nodes = instance.nodes.all()
|
nodes = instance.nodes.all()
|
||||||
for system_user in system_users:
|
for system_user in system_users:
|
||||||
system_user.nodes.add(*tuple(nodes))
|
system_user.nodes.add(*tuple(nodes))
|
||||||
system_user.assets.add(*tuple(assets))
|
system_user.assets.add(*tuple(assets))
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ from collections import defaultdict
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from orgs.utils import set_to_root_org
|
from orgs.utils import set_to_root_org
|
||||||
from common.utils import get_logger, timeit
|
from common.utils import get_logger, timeit
|
||||||
|
@ -11,6 +12,7 @@ from common.tree import TreeNode
|
||||||
from assets.utils import TreeService
|
from assets.utils import TreeService
|
||||||
from ..models import AssetPermission
|
from ..models import AssetPermission
|
||||||
from ..hands import Node, Asset, SystemUser
|
from ..hands import Node, Asset, SystemUser
|
||||||
|
from .. import const
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
@ -129,6 +131,9 @@ class AssetPermissionUtilV2:
|
||||||
|
|
||||||
@timeit
|
@timeit
|
||||||
def add_direct_nodes_to_user_tree(self, user_tree):
|
def add_direct_nodes_to_user_tree(self, user_tree):
|
||||||
|
"""
|
||||||
|
将授权规则的节点放到用户树上, 从full tree中粘贴子树
|
||||||
|
"""
|
||||||
nodes_direct_keys = self.permissions \
|
nodes_direct_keys = self.permissions \
|
||||||
.exclude(nodes__isnull=True) \
|
.exclude(nodes__isnull=True) \
|
||||||
.values_list('nodes__key', flat=True) \
|
.values_list('nodes__key', flat=True) \
|
||||||
|
@ -153,6 +158,10 @@ class AssetPermissionUtilV2:
|
||||||
|
|
||||||
@timeit
|
@timeit
|
||||||
def add_single_assets_node_to_user_tree(self, user_tree):
|
def add_single_assets_node_to_user_tree(self, user_tree):
|
||||||
|
"""
|
||||||
|
将单独授权的资产放到树上,如果设置了单独资产到 未分组中,则放到未分组中
|
||||||
|
如果没有,则查询资产属于的资产组,放到树上
|
||||||
|
"""
|
||||||
# 添加单独授权资产的节点
|
# 添加单独授权资产的节点
|
||||||
nodes_single_assets = defaultdict(set)
|
nodes_single_assets = defaultdict(set)
|
||||||
queryset = self.permissions.exclude(assets__isnull=True) \
|
queryset = self.permissions.exclude(assets__isnull=True) \
|
||||||
|
@ -161,13 +170,26 @@ class AssetPermissionUtilV2:
|
||||||
|
|
||||||
for item in queryset:
|
for item in queryset:
|
||||||
nodes_single_assets[item[1]].add(item[0])
|
nodes_single_assets[item[1]].add(item[0])
|
||||||
# Todo: 游离资产
|
|
||||||
nodes_single_assets.pop(None, None)
|
nodes_single_assets.pop(None, None)
|
||||||
|
|
||||||
for key in tuple(nodes_single_assets.keys()):
|
for key in tuple(nodes_single_assets.keys()):
|
||||||
if user_tree.contains(key):
|
if user_tree.contains(key):
|
||||||
nodes_single_assets.pop(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():
|
for key, assets in nodes_single_assets.items():
|
||||||
node = self.full_tree.get_node(key, deep=True)
|
node = self.full_tree.get_node(key, deep=True)
|
||||||
|
@ -180,11 +202,17 @@ class AssetPermissionUtilV2:
|
||||||
|
|
||||||
@timeit
|
@timeit
|
||||||
def parse_user_tree_to_full_tree(self, user_tree):
|
def parse_user_tree_to_full_tree(self, user_tree):
|
||||||
|
"""
|
||||||
|
经过前面两个动作,用户授权的节点已放到树上,但是树不是完整的,
|
||||||
|
这里要讲树构造成一个完整的书
|
||||||
|
"""
|
||||||
# 开始修正user_tree,保证父节点都在树上
|
# 开始修正user_tree,保证父节点都在树上
|
||||||
root_children = user_tree.children('')
|
root_children = user_tree.children('')
|
||||||
for child in root_children:
|
for child in root_children:
|
||||||
if child.identifier.isdigit():
|
if child.identifier.isdigit():
|
||||||
continue
|
continue
|
||||||
|
if child.identifier.startswith('-'):
|
||||||
|
continue
|
||||||
ancestors = self.full_tree.ancestors(
|
ancestors = self.full_tree.ancestors(
|
||||||
child.identifier, with_self=False, deep=True
|
child.identifier, with_self=False, deep=True
|
||||||
)
|
)
|
||||||
|
@ -194,6 +222,19 @@ class AssetPermissionUtilV2:
|
||||||
user_tree.safe_add_ancestors(ancestors)
|
user_tree.safe_add_ancestors(ancestors)
|
||||||
user_tree.move_node(child.identifier, parent_id)
|
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
|
@timeit
|
||||||
def get_user_tree(self):
|
def get_user_tree(self):
|
||||||
if self._user_tree:
|
if self._user_tree:
|
||||||
|
@ -207,6 +248,7 @@ class AssetPermissionUtilV2:
|
||||||
self.add_direct_nodes_to_user_tree(user_tree)
|
self.add_direct_nodes_to_user_tree(user_tree)
|
||||||
self.add_single_assets_node_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.parse_user_tree_to_full_tree(user_tree)
|
||||||
|
self.add_empty_node_if_need(user_tree)
|
||||||
self._user_tree = user_tree
|
self._user_tree = user_tree
|
||||||
return 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):
|
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 = {
|
tree_node = {
|
||||||
'id': remote_app.id,
|
'id': remote_app.id,
|
||||||
'name': remote_app.name,
|
'name': remote_app.name,
|
||||||
|
@ -82,6 +90,6 @@ def parse_remote_app_to_tree_node(parent, remote_app):
|
||||||
'open': False,
|
'open': False,
|
||||||
'isParent': False,
|
'isParent': False,
|
||||||
'iconSkin': 'file',
|
'iconSkin': 'file',
|
||||||
'meta': {'type': 'remote_app'}
|
'meta': {'type': 'remote_app', 'user': user}
|
||||||
}
|
}
|
||||||
return TreeNode(**tree_node)
|
return TreeNode(**tree_node)
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
<div class="ibox-content mailbox-content" style="padding-top: 0">
|
<div class="ibox-content mailbox-content" style="padding-top: 0">
|
||||||
<div class="file-manager ">
|
<div class="file-manager ">
|
||||||
<div id="assetTree" class="ztree">
|
<div id="assetTree" class="ztree">
|
||||||
|
{% trans 'Loading' %} ...
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue