mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-07-17 16:31:28 +00:00
refactor: 重构优化用户授权树工具类和用户授权树过期条件处理逻辑 <UserPermTreeRefreshUtil> <UserPermTreeExpireUtil>
This commit is contained in:
parent
4f5cc56b00
commit
1679efe2c9
@ -1,9 +1,10 @@
|
|||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
|
|
||||||
from users.models import User
|
from users.models import User
|
||||||
from perms.utils.user_permission import UserPermTreeUtil
|
|
||||||
from common.http import is_true
|
from common.http import is_true
|
||||||
|
|
||||||
|
from perms.utils.user_permission import UserPermTreeRefreshUtil
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['RebuildTreeMixin']
|
__all__ = ['RebuildTreeMixin']
|
||||||
|
|
||||||
@ -13,5 +14,5 @@ class RebuildTreeMixin:
|
|||||||
|
|
||||||
def get(self, request: Request, *args, **kwargs):
|
def get(self, request: Request, *args, **kwargs):
|
||||||
force = is_true(request.query_params.get('rebuild_tree'))
|
force = is_true(request.query_params.get('rebuild_tree'))
|
||||||
UserPermTreeUtil(self.user).refresh_if_need(force)
|
UserPermTreeRefreshUtil(self.user).refresh_if_need(force)
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
import logging
|
|
||||||
import uuid
|
import uuid
|
||||||
|
import logging
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from users.models import User
|
||||||
from assets.models import Asset, Account
|
from assets.models import Asset, Account
|
||||||
from common.db.models import UnionQuerySet
|
|
||||||
from common.utils import date_expired_default
|
|
||||||
from orgs.mixins.models import OrgManager
|
from orgs.mixins.models import OrgManager
|
||||||
from orgs.mixins.models import OrgModelMixin
|
from orgs.mixins.models import OrgModelMixin
|
||||||
|
from common.utils.timezone import local_now
|
||||||
|
from common.db.models import UnionQuerySet
|
||||||
|
from common.utils import date_expired_default
|
||||||
|
|
||||||
from perms.const import ActionChoices
|
from perms.const import ActionChoices
|
||||||
|
|
||||||
__all__ = ['AssetPermission', 'ActionChoices']
|
__all__ = ['AssetPermission', 'ActionChoices']
|
||||||
@ -131,3 +134,23 @@ class AssetPermission(OrgModelMixin):
|
|||||||
if not flat:
|
if not flat:
|
||||||
return accounts
|
return accounts
|
||||||
return accounts.values_list('id', flat=True)
|
return accounts.values_list('id', flat=True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_all_users_for_perms(cls, perm_ids, flat=False):
|
||||||
|
user_ids = cls.users.through.objects.filter(assetpermission_id__in=perm_ids)\
|
||||||
|
.values_list('user_id', flat=True).distinct()
|
||||||
|
group_ids = cls.user_groups.through.objects.filter(assetpermission_id__in=perm_ids)\
|
||||||
|
.values_list('usergroup_id', flat=True).distinct()
|
||||||
|
group_user_ids = User.groups.through.objects.filter(usergroup_id__in=group_ids)\
|
||||||
|
.values_list('user_id', flat=True).distinct()
|
||||||
|
user_ids = set(user_ids) | set(group_user_ids)
|
||||||
|
if flat:
|
||||||
|
return user_ids
|
||||||
|
return User.objects.filter(id__in=user_ids)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_expired_permissions(cls):
|
||||||
|
now = local_now()
|
||||||
|
return cls.objects.filter(Q(date_start__lte=now) | Q(date_expired__gte=now))
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,12 +5,12 @@ from django.dispatch import receiver
|
|||||||
|
|
||||||
from users.models import User, UserGroup
|
from users.models import User, UserGroup
|
||||||
from assets.models import Asset
|
from assets.models import Asset
|
||||||
from orgs.utils import current_org, tmp_to_org
|
from common.utils import get_logger, get_object_or_none
|
||||||
from common.utils import get_logger
|
|
||||||
from common.exceptions import M2MReverseNotAllowed
|
from common.exceptions import M2MReverseNotAllowed
|
||||||
from common.const.signals import POST_ADD, POST_REMOVE, POST_CLEAR
|
from common.const.signals import POST_ADD, POST_REMOVE, POST_CLEAR
|
||||||
|
|
||||||
from perms.models import AssetPermission
|
from perms.models import AssetPermission
|
||||||
from perms.utils.user_permission import UserPermTreeUtil
|
from perms.utils.user_permission import UserPermTreeExpireUtil
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
@ -21,10 +21,7 @@ def on_user_group_delete(sender, instance: UserGroup, using, **kwargs):
|
|||||||
exists = AssetPermission.user_groups.through.objects.filter(usergroup_id=instance.id).exists()
|
exists = AssetPermission.user_groups.through.objects.filter(usergroup_id=instance.id).exists()
|
||||||
if not exists:
|
if not exists:
|
||||||
return
|
return
|
||||||
|
UserPermTreeExpireUtil().expire_perm_tree_for_user_group(instance)
|
||||||
org_id = instance.org_id
|
|
||||||
user_ids = UserGroup.users.through.objects.filter(usergroup_id=instance.id).values_list('user_id', flat=True)
|
|
||||||
UserPermTreeUtil.add_need_refresh_orgs_for_users([org_id], list(user_ids))
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(m2m_changed, sender=User.groups.through)
|
@receiver(m2m_changed, sender=User.groups.through)
|
||||||
@ -41,39 +38,34 @@ def on_user_groups_change(sender, instance, action, reverse, pk_set, **kwargs):
|
|||||||
group = UserGroup.objects.get(id=list(group_ids)[0])
|
group = UserGroup.objects.get(id=list(group_ids)[0])
|
||||||
org_id = group.org_id
|
org_id = group.org_id
|
||||||
|
|
||||||
exists = AssetPermission.user_groups.through.objects.filter(usergroup_id__in=group_ids).exists()
|
has_group_perm = AssetPermission.user_groups.through.objects\
|
||||||
if not exists:
|
.filter(usergroup_id__in=group_ids).exists()
|
||||||
|
if not has_group_perm:
|
||||||
return
|
return
|
||||||
|
|
||||||
org_ids = [org_id]
|
UserPermTreeExpireUtil().expire_perm_tree_for_users_orgs(user_ids, [org_id])
|
||||||
UserPermTreeUtil.add_need_refresh_orgs_for_users(org_ids, user_ids)
|
|
||||||
|
|
||||||
|
|
||||||
@receiver([pre_delete], sender=AssetPermission)
|
@receiver([pre_delete], sender=AssetPermission)
|
||||||
def on_asset_perm_pre_delete(sender, instance, **kwargs):
|
def on_asset_perm_pre_delete(sender, instance, **kwargs):
|
||||||
# 授权删除之前,查出所有相关用户
|
UserPermTreeExpireUtil().expire_perm_tree_for_perms([instance.id])
|
||||||
with tmp_to_org(instance.org):
|
|
||||||
UserPermTreeUtil.add_need_refresh_by_asset_perm_ids([instance.id])
|
|
||||||
|
|
||||||
|
|
||||||
@receiver([pre_save], sender=AssetPermission)
|
@receiver([pre_save], sender=AssetPermission)
|
||||||
def on_asset_perm_pre_save(sender, instance, **kwargs):
|
def on_asset_perm_pre_save(sender, instance, **kwargs):
|
||||||
try:
|
old = get_object_or_none(AssetPermission, pk=instance.id)
|
||||||
old = AssetPermission.objects.get(id=instance.id)
|
if not old:
|
||||||
|
return
|
||||||
if old.is_valid != instance.is_valid:
|
if old.is_valid == instance.is_valid:
|
||||||
with tmp_to_org(instance.org):
|
return
|
||||||
UserPermTreeUtil.add_need_refresh_by_asset_perm_ids([instance.id])
|
UserPermTreeExpireUtil().expire_perm_tree_for_perms([instance.id])
|
||||||
except AssetPermission.DoesNotExist:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@receiver([post_save], sender=AssetPermission)
|
@receiver([post_save], sender=AssetPermission)
|
||||||
def on_asset_perm_post_save(sender, instance, created, **kwargs):
|
def on_asset_perm_post_save(sender, instance, created, **kwargs):
|
||||||
if not created:
|
if not created:
|
||||||
return
|
return
|
||||||
with tmp_to_org(instance.org):
|
UserPermTreeExpireUtil().expire_perm_tree_for_perms([instance.id])
|
||||||
UserPermTreeUtil.add_need_refresh_by_asset_perm_ids([instance.id])
|
|
||||||
|
|
||||||
|
|
||||||
def need_rebuild_mapping_node(action):
|
def need_rebuild_mapping_node(action):
|
||||||
@ -82,69 +74,52 @@ def need_rebuild_mapping_node(action):
|
|||||||
|
|
||||||
@receiver(m2m_changed, sender=AssetPermission.nodes.through)
|
@receiver(m2m_changed, sender=AssetPermission.nodes.through)
|
||||||
def on_permission_nodes_changed(sender, instance, action, reverse, **kwargs):
|
def on_permission_nodes_changed(sender, instance, action, reverse, **kwargs):
|
||||||
if reverse:
|
|
||||||
raise M2MReverseNotAllowed
|
|
||||||
|
|
||||||
if not need_rebuild_mapping_node(action):
|
if not need_rebuild_mapping_node(action):
|
||||||
return
|
return
|
||||||
|
if reverse:
|
||||||
with tmp_to_org(instance.org):
|
raise M2MReverseNotAllowed
|
||||||
UserPermTreeUtil.add_need_refresh_by_asset_perm_ids([instance.id])
|
UserPermTreeExpireUtil().expire_perm_tree_for_perms([instance.id])
|
||||||
|
|
||||||
|
|
||||||
@receiver(m2m_changed, sender=AssetPermission.assets.through)
|
@receiver(m2m_changed, sender=AssetPermission.assets.through)
|
||||||
def on_permission_assets_changed(sender, instance, action, reverse, pk_set, model, **kwargs):
|
def on_permission_assets_changed(sender, instance, action, reverse, pk_set, model, **kwargs):
|
||||||
if reverse:
|
|
||||||
raise M2MReverseNotAllowed
|
|
||||||
|
|
||||||
if not need_rebuild_mapping_node(action):
|
if not need_rebuild_mapping_node(action):
|
||||||
return
|
return
|
||||||
with tmp_to_org(instance.org):
|
if reverse:
|
||||||
UserPermTreeUtil.add_need_refresh_by_asset_perm_ids([instance.id])
|
raise M2MReverseNotAllowed
|
||||||
|
UserPermTreeExpireUtil().expire_perm_tree_for_perms([instance.id])
|
||||||
|
|
||||||
|
|
||||||
@receiver(m2m_changed, sender=AssetPermission.users.through)
|
@receiver(m2m_changed, sender=AssetPermission.users.through)
|
||||||
def on_asset_permission_users_changed(sender, action, reverse, instance, pk_set, **kwargs):
|
def on_asset_permission_users_changed(sender, action, reverse, instance, pk_set, **kwargs):
|
||||||
if reverse:
|
if reverse:
|
||||||
raise M2MReverseNotAllowed
|
raise M2MReverseNotAllowed
|
||||||
|
|
||||||
if not need_rebuild_mapping_node(action):
|
if not need_rebuild_mapping_node(action):
|
||||||
return
|
return
|
||||||
|
user_ids = pk_set
|
||||||
with tmp_to_org(instance.org):
|
UserPermTreeExpireUtil().expire_perm_tree_for_users_orgs(user_ids, [instance.org.id])
|
||||||
UserPermTreeUtil.add_need_refresh_orgs_for_users(
|
|
||||||
[current_org.id], pk_set
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(m2m_changed, sender=AssetPermission.user_groups.through)
|
@receiver(m2m_changed, sender=AssetPermission.user_groups.through)
|
||||||
def on_asset_permission_user_groups_changed(sender, instance, action, pk_set, reverse, **kwargs):
|
def on_asset_permission_user_groups_changed(sender, instance, action, pk_set, reverse, **kwargs):
|
||||||
|
if not need_rebuild_mapping_node(action):
|
||||||
|
return
|
||||||
if reverse:
|
if reverse:
|
||||||
raise M2MReverseNotAllowed
|
raise M2MReverseNotAllowed
|
||||||
|
|
||||||
if not need_rebuild_mapping_node(action):
|
group_ids = pk_set
|
||||||
return
|
UserPermTreeExpireUtil().expire_perm_tree_for_user_groups_orgs(group_ids, [instance.org.id])
|
||||||
|
|
||||||
user_ids = User.groups.through.objects.filter(usergroup_id__in=pk_set) \
|
|
||||||
.values_list('user_id', flat=True) \
|
|
||||||
.distinct()
|
|
||||||
with tmp_to_org(instance.org):
|
|
||||||
UserPermTreeUtil.add_need_refresh_orgs_for_users(
|
|
||||||
[current_org.id], user_ids
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(m2m_changed, sender=Asset.nodes.through)
|
@receiver(m2m_changed, sender=Asset.nodes.through)
|
||||||
def on_node_asset_change(action, instance, reverse, pk_set, **kwargs):
|
def on_node_asset_change(action, instance, reverse, pk_set, **kwargs):
|
||||||
if not need_rebuild_mapping_node(action):
|
if not need_rebuild_mapping_node(action):
|
||||||
return
|
return
|
||||||
|
|
||||||
if reverse:
|
if reverse:
|
||||||
asset_pk_set = pk_set
|
asset_ids = pk_set
|
||||||
node_pk_set = [instance.id]
|
node_ids = [instance.id]
|
||||||
else:
|
else:
|
||||||
asset_pk_set = [instance.id]
|
asset_ids = [instance.id]
|
||||||
node_pk_set = pk_set
|
node_ids = pk_set
|
||||||
|
|
||||||
with tmp_to_org(instance.org):
|
UserPermTreeExpireUtil().expire_perm_tree_for_nodes_assets(node_ids, asset_ids)
|
||||||
UserPermTreeUtil.add_need_refresh_on_nodes_assets_relate_change(node_pk_set, asset_pk_set)
|
|
||||||
|
@ -7,16 +7,18 @@ from django.db.transaction import atomic
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
|
|
||||||
|
from ops.celery.decorator import register_as_period_task
|
||||||
from orgs.utils import tmp_to_root_org
|
from orgs.utils import tmp_to_root_org
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from common.utils.timezone import local_now, dt_formatter, dt_parser
|
from common.utils.timezone import local_now, dt_parser
|
||||||
from common.const.crontab import CRONTAB_AT_AM_TEN
|
from common.const.crontab import CRONTAB_AT_AM_TEN
|
||||||
from ops.celery.decorator import register_as_period_task
|
|
||||||
from perms.notifications import (
|
|
||||||
PermedAssetsWillExpireUserMsg, AssetPermsWillExpireForOrgAdminMsg,
|
|
||||||
)
|
|
||||||
from perms.models import AssetPermission
|
from perms.models import AssetPermission
|
||||||
from perms.utils.user_permission import UserPermTreeUtil
|
from perms.utils.user_permission import UserPermTreeExpireUtil
|
||||||
|
from perms.notifications import (
|
||||||
|
PermedAssetsWillExpireUserMsg,
|
||||||
|
AssetPermsWillExpireForOrgAdminMsg,
|
||||||
|
)
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
@ -26,33 +28,11 @@ logger = get_logger(__file__)
|
|||||||
@atomic()
|
@atomic()
|
||||||
@tmp_to_root_org()
|
@tmp_to_root_org()
|
||||||
def check_asset_permission_expired():
|
def check_asset_permission_expired():
|
||||||
"""
|
""" 这里的任务要足够短,不要影响周期任务 """
|
||||||
这里的任务要足够短,不要影响周期任务
|
perms = AssetPermission.get_expired_permissions()
|
||||||
"""
|
perm_ids = list(perms.distinct().values_list('id', flat=True))
|
||||||
from settings.models import Setting
|
logger.info(f'Checking expired permissions: {perm_ids}')
|
||||||
|
UserPermTreeExpireUtil().expire_perm_tree_for_perms(perm_ids)
|
||||||
setting_name = 'last_asset_perm_expired_check'
|
|
||||||
|
|
||||||
end = local_now()
|
|
||||||
default_start = end - timedelta(days=36000) # Long long ago in china
|
|
||||||
|
|
||||||
defaults = {'value': dt_formatter(default_start)}
|
|
||||||
setting, created = Setting.objects.get_or_create(
|
|
||||||
name=setting_name, defaults=defaults
|
|
||||||
)
|
|
||||||
if created:
|
|
||||||
start = default_start
|
|
||||||
else:
|
|
||||||
start = dt_parser(setting.value)
|
|
||||||
setting.value = dt_formatter(end)
|
|
||||||
setting.save()
|
|
||||||
|
|
||||||
asset_perm_ids = AssetPermission.objects.filter(
|
|
||||||
date_expired__gte=start, date_expired__lte=end
|
|
||||||
).distinct().values_list('id', flat=True)
|
|
||||||
asset_perm_ids = list(asset_perm_ids)
|
|
||||||
logger.info(f'>>> checking {start} to {end} have {asset_perm_ids} expired')
|
|
||||||
UserPermTreeUtil.add_need_refresh_by_asset_perm_ids_cross_orgs(asset_perm_ids)
|
|
||||||
|
|
||||||
|
|
||||||
@register_as_period_task(crontab=CRONTAB_AT_AM_TEN)
|
@register_as_period_task(crontab=CRONTAB_AT_AM_TEN)
|
||||||
|
@ -7,24 +7,32 @@ from django.core.cache import cache
|
|||||||
from django.db.models import Q, QuerySet
|
from django.db.models import Q, QuerySet
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from assets.models import (
|
from users.models import User
|
||||||
Asset, FavoriteAsset, AssetQuerySet, NodeQuerySet
|
|
||||||
)
|
|
||||||
from assets.utils import NodeAssetsUtil
|
from assets.utils import NodeAssetsUtil
|
||||||
|
from assets.models import (
|
||||||
|
Asset,
|
||||||
|
FavoriteAsset,
|
||||||
|
AssetQuerySet,
|
||||||
|
NodeQuerySet
|
||||||
|
)
|
||||||
|
from orgs.models import Organization
|
||||||
|
from orgs.utils import (
|
||||||
|
tmp_to_org,
|
||||||
|
current_org,
|
||||||
|
ensure_in_real_or_default_org,
|
||||||
|
tmp_to_root_org
|
||||||
|
)
|
||||||
from common.db.models import output_as_string, UnionQuerySet
|
from common.db.models import output_as_string, UnionQuerySet
|
||||||
from common.decorator import on_transaction_commit
|
from common.decorator import on_transaction_commit
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from common.utils.common import lazyproperty, timeit
|
from common.utils.common import lazyproperty, timeit
|
||||||
from orgs.models import Organization
|
|
||||||
from orgs.utils import (
|
|
||||||
tmp_to_org, current_org,
|
|
||||||
ensure_in_real_or_default_org, tmp_to_root_org
|
|
||||||
)
|
|
||||||
from perms.locks import UserGrantedTreeRebuildLock
|
from perms.locks import UserGrantedTreeRebuildLock
|
||||||
from perms.models import (
|
from perms.models import (
|
||||||
AssetPermission, PermNode, UserAssetGrantedTreeNodeRelation
|
AssetPermission,
|
||||||
|
PermNode,
|
||||||
|
UserAssetGrantedTreeNodeRelation
|
||||||
)
|
)
|
||||||
from users.models import User
|
|
||||||
from .permission import AssetPermissionUtil
|
from .permission import AssetPermissionUtil
|
||||||
|
|
||||||
NodeFrom = UserAssetGrantedTreeNodeRelation.NodeFrom
|
NodeFrom = UserAssetGrantedTreeNodeRelation.NodeFrom
|
||||||
@ -33,50 +41,53 @@ NODE_ONLY_FIELDS = ('id', 'key', 'parent_key', 'org_id')
|
|||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class UserPermTreeUtil2:
|
class UserPermTreeCacheMixin:
|
||||||
|
""" 缓存数据 users: {org_id, org_id }, 记录用户授权树已经构建完成的组织集合 """
|
||||||
|
|
||||||
cache_key_template = 'perms.user.node_tree.built_orgs.user_id:{user_id}'
|
cache_key_template = 'perms.user.node_tree.built_orgs.user_id:{user_id}'
|
||||||
|
|
||||||
|
def get_cache_key(self, user_id):
|
||||||
|
return self.cache_key_template.format(user_id=user_id)
|
||||||
|
|
||||||
|
@lazyproperty
|
||||||
|
def client(self):
|
||||||
|
return cache.client.get_client(write=True)
|
||||||
|
|
||||||
|
|
||||||
|
class UserPermTreeRefreshUtil(UserPermTreeCacheMixin):
|
||||||
|
""" 用户授权树刷新工具, 针对某一个用户授权树的刷新 """
|
||||||
|
|
||||||
def __init__(self, user):
|
def __init__(self, user):
|
||||||
self.user = user
|
self.user = user
|
||||||
self.orgs = self.user.orgs.distinct()
|
self.orgs = self.user.orgs.distinct()
|
||||||
self.org_ids = [str(o.id) for o in self.orgs]
|
self.org_ids = [str(o.id) for o in self.orgs]
|
||||||
|
|
||||||
def get_cache_key(self, user_id):
|
|
||||||
return self.cache_key_template.format(user_id=user_id)
|
|
||||||
|
|
||||||
@lazyproperty
|
@lazyproperty
|
||||||
def cache_key_user(self):
|
def cache_key_user(self):
|
||||||
return self.get_cache_key(self.user.id)
|
return self.get_cache_key(self.user.id)
|
||||||
|
|
||||||
@lazyproperty
|
|
||||||
def cache_key_all_user(self):
|
|
||||||
return self.get_cache_key('*')
|
|
||||||
|
|
||||||
@lazyproperty
|
|
||||||
def client(self):
|
|
||||||
return cache.client.get_client(write=True)
|
|
||||||
|
|
||||||
@timeit
|
@timeit
|
||||||
def refresh_if_need(self, force=False):
|
def refresh_if_need(self, force=False):
|
||||||
self.clean_user_perm_tree_nodes_for_legacy_org()
|
self.clean_user_perm_tree_nodes_for_legacy_org()
|
||||||
|
|
||||||
to_refresh_orgs = self.orgs if force else self.get_user_need_refresh_orgs()
|
to_refresh_orgs = self.orgs if force else self.get_user_need_refresh_orgs()
|
||||||
if not to_refresh_orgs:
|
if not to_refresh_orgs:
|
||||||
logger.info('Not have to refresh orgs')
|
logger.info('Not have to refresh orgs')
|
||||||
return
|
return
|
||||||
|
|
||||||
with UserGrantedTreeRebuildLock(self.user.id):
|
with UserGrantedTreeRebuildLock(self.user.id):
|
||||||
for org in to_refresh_orgs:
|
for org in to_refresh_orgs:
|
||||||
with tmp_to_org(org):
|
self.rebuild_user_perm_tree_for_org(org)
|
||||||
start = time.time()
|
|
||||||
UserGrantedTreeBuildUtils(self.user).rebuild_user_granted_tree()
|
|
||||||
end = time.time()
|
|
||||||
logger.info(
|
|
||||||
'Refresh user [{user}] org [{org}] perm tree, user {use_time:.2f}s'
|
|
||||||
''.format(user=self.user, org=org, use_time=end-start)
|
|
||||||
)
|
|
||||||
self.mark_user_orgs_refresh_finished(to_refresh_orgs)
|
self.mark_user_orgs_refresh_finished(to_refresh_orgs)
|
||||||
|
|
||||||
|
def rebuild_user_perm_tree_for_org(self, org):
|
||||||
|
with tmp_to_org(org):
|
||||||
|
start = time.time()
|
||||||
|
UserGrantedTreeBuildUtils(self.user).rebuild_user_granted_tree()
|
||||||
|
end = time.time()
|
||||||
|
logger.info(
|
||||||
|
'Refresh user [{user}] org [{org}] perm tree, user {use_time:.2f}s'
|
||||||
|
''.format(user=self.user, org=org, use_time=end-start)
|
||||||
|
)
|
||||||
|
|
||||||
def clean_user_perm_tree_nodes_for_legacy_org(self):
|
def clean_user_perm_tree_nodes_for_legacy_org(self):
|
||||||
with tmp_to_root_org():
|
with tmp_to_root_org():
|
||||||
""" Clean user legacy org node relations """
|
""" Clean user legacy org node relations """
|
||||||
@ -95,7 +106,14 @@ class UserPermTreeUtil2:
|
|||||||
def mark_user_orgs_refresh_finished(self, org_ids):
|
def mark_user_orgs_refresh_finished(self, org_ids):
|
||||||
self.client.sadd(self.cache_key_user, *org_ids)
|
self.client.sadd(self.cache_key_user, *org_ids)
|
||||||
|
|
||||||
# cls
|
|
||||||
|
class UserPermTreeExpireUtil(UserPermTreeCacheMixin):
|
||||||
|
""" 用户授权树过期工具 """
|
||||||
|
|
||||||
|
@lazyproperty
|
||||||
|
def cache_key_all_user(self):
|
||||||
|
return self.get_cache_key('*')
|
||||||
|
|
||||||
def expire_perm_tree_for_all_user(self):
|
def expire_perm_tree_for_all_user(self):
|
||||||
keys = self.client.keys(self.cache_key_all_user)
|
keys = self.client.keys(self.cache_key_all_user)
|
||||||
with self.client.pipline() as p:
|
with self.client.pipline() as p:
|
||||||
@ -103,6 +121,34 @@ class UserPermTreeUtil2:
|
|||||||
p.delete(k)
|
p.delete(k)
|
||||||
p.execute()
|
p.execute()
|
||||||
|
|
||||||
|
def expire_perm_tree_for_nodes_assets(self, node_ids, asset_ids):
|
||||||
|
node_perm_ids = AssetPermissionUtil().get_permissions_for_nodes(node_ids, flat=True)
|
||||||
|
asset_perm_ids = AssetPermissionUtil().get_permissions_for_assets(asset_ids, flat=True)
|
||||||
|
perm_ids = set(node_perm_ids) | set(asset_perm_ids)
|
||||||
|
self.expire_perm_tree_for_perms(perm_ids)
|
||||||
|
|
||||||
|
@tmp_to_root_org()
|
||||||
|
def expire_perm_tree_for_perms(self, perm_ids):
|
||||||
|
org_perm_ids = AssetPermission.objects.filter(id__in=perm_ids).values_list('org_id', 'id')
|
||||||
|
org_perms_mapper = defaultdict(set)
|
||||||
|
for org_id, perm_id in org_perm_ids:
|
||||||
|
org_perms_mapper[org_id].add(perm_id)
|
||||||
|
for org_id, perms_id in org_perms_mapper.items():
|
||||||
|
org_ids = [org_id]
|
||||||
|
user_ids = AssetPermission.get_all_users_for_perms(perm_ids, flat=True)
|
||||||
|
self.expire_perm_tree_for_users_orgs(user_ids, org_ids)
|
||||||
|
|
||||||
|
def expire_perm_tree_for_user_group(self, user_group):
|
||||||
|
group_ids = [user_group.id]
|
||||||
|
org_ids = [user_group.org_id]
|
||||||
|
self.expire_perm_tree_for_user_groups_orgs(group_ids, org_ids)
|
||||||
|
|
||||||
|
def expire_perm_tree_for_user_groups_orgs(self, group_ids, org_ids):
|
||||||
|
user_ids = User.groups.through.objects.filter(usergroup_id__in=group_ids)\
|
||||||
|
.values_list('user_id', flat=True).distinct()
|
||||||
|
self.expire_perm_tree_for_users_orgs(user_ids, org_ids)
|
||||||
|
|
||||||
|
@on_transaction_commit
|
||||||
def expire_perm_tree_for_users_orgs(self, user_ids, org_ids):
|
def expire_perm_tree_for_users_orgs(self, user_ids, org_ids):
|
||||||
org_ids = [str(oid) for oid in org_ids]
|
org_ids = [str(oid) for oid in org_ids]
|
||||||
with self.client.pipline() as p:
|
with self.client.pipline() as p:
|
||||||
@ -112,183 +158,6 @@ class UserPermTreeUtil2:
|
|||||||
p.execute()
|
p.execute()
|
||||||
logger.info('Expire perm tree for users: [{}], orgs: [{}]'.format(user_ids, org_ids))
|
logger.info('Expire perm tree for users: [{}], orgs: [{}]'.format(user_ids, org_ids))
|
||||||
|
|
||||||
def expire_perm_tree_for_nodes_assets(self, node_ids, asset_ids):
|
|
||||||
node_perm_ids = AssetPermissionUtil().get_permissions_for_nodes(node_ids, flat=True)
|
|
||||||
asset_perm_ids = AssetPermissionUtil().get_permissions_for_assets(asset_ids, flat=True)
|
|
||||||
perm_ids = set(node_perm_ids) | set(asset_perm_ids)
|
|
||||||
|
|
||||||
|
|
||||||
class UserPermTreeUtil:
|
|
||||||
key_template = 'perms.user.node_tree.built_orgs.user_id:{user_id}'
|
|
||||||
|
|
||||||
def __init__(self, user):
|
|
||||||
self.user = user
|
|
||||||
self.key = self.key_template.format(user_id=user.id)
|
|
||||||
self.client = self.get_redis_client()
|
|
||||||
|
|
||||||
@timeit
|
|
||||||
def refresh_if_need(self, force=False):
|
|
||||||
user = self.user
|
|
||||||
orgs = user.orgs.all().distinct()
|
|
||||||
org_ids = [str(o.id) for o in orgs]
|
|
||||||
|
|
||||||
with tmp_to_root_org():
|
|
||||||
user_relations = UserAssetGrantedTreeNodeRelation.objects.filter(user=user)
|
|
||||||
user_legacy_org_relations = user_relations.exclude(org_id__in=org_ids)
|
|
||||||
user_legacy_org_relations.delete()
|
|
||||||
|
|
||||||
need_refresh_orgs = []
|
|
||||||
|
|
||||||
if not force and not self.have_need_refresh_orgs():
|
|
||||||
return
|
|
||||||
|
|
||||||
with UserGrantedTreeRebuildLock(user_id=user.id):
|
|
||||||
if force:
|
|
||||||
orgs = self.orgs
|
|
||||||
self.set_all_orgs_as_built()
|
|
||||||
else:
|
|
||||||
orgs = self.get_need_refresh_orgs_and_fill_up()
|
|
||||||
|
|
||||||
for org in orgs:
|
|
||||||
with tmp_to_org(org):
|
|
||||||
t_start = time.time()
|
|
||||||
logger.info(f'Rebuild user tree: user={self.user} org={current_org}')
|
|
||||||
utils = UserGrantedTreeBuildUtils(user)
|
|
||||||
utils.rebuild_user_granted_tree()
|
|
||||||
logger.info(
|
|
||||||
f'Rebuild user tree ok: cost={time.time() - t_start} '
|
|
||||||
f'user={self.user} org={current_org}'
|
|
||||||
)
|
|
||||||
|
|
||||||
@lazyproperty
|
|
||||||
def org_ids(self):
|
|
||||||
ret = {str(org.id) for org in self.orgs}
|
|
||||||
return ret
|
|
||||||
|
|
||||||
@lazyproperty
|
|
||||||
def orgs(self):
|
|
||||||
orgs = {*self.user.orgs.all().distinct()}
|
|
||||||
return orgs
|
|
||||||
|
|
||||||
def set_all_orgs_as_built(self):
|
|
||||||
self.client.sadd(self.key, *self.org_ids)
|
|
||||||
|
|
||||||
def have_need_refresh_orgs(self):
|
|
||||||
built_org_ids = self.client.smembers(self.key)
|
|
||||||
built_org_ids = {org_id.decode() for org_id in built_org_ids}
|
|
||||||
have = self.org_ids - built_org_ids
|
|
||||||
return have
|
|
||||||
|
|
||||||
def get_need_refresh_orgs_and_fill_up(self):
|
|
||||||
org_ids = self.org_ids
|
|
||||||
|
|
||||||
with self.client.pipeline() as p:
|
|
||||||
p.smembers(self.key)
|
|
||||||
p.sadd(self.key, *org_ids)
|
|
||||||
old_org_ids, new_orgs_count = p.execute()
|
|
||||||
old_org_ids = {oid.decode() for oid in old_org_ids}
|
|
||||||
need_refresh_org_ids = org_ids - old_org_ids
|
|
||||||
need_refresh_orgs = Organization.objects.filter(id__in=need_refresh_org_ids)
|
|
||||||
logger.info(f'Need refresh orgs: {need_refresh_orgs}')
|
|
||||||
return need_refresh_orgs
|
|
||||||
|
|
||||||
# cls
|
|
||||||
@classmethod
|
|
||||||
def get_redis_client(cls):
|
|
||||||
return cache.client.get_client(write=True)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def clean_all_user_tree_built_mark(cls):
|
|
||||||
""" 清除所有用户已构建树的标记 """
|
|
||||||
client = cls.get_redis_client()
|
|
||||||
key_match = cls.key_template.format(user_id='*')
|
|
||||||
keys = client.keys(key_match)
|
|
||||||
with client.pipeline() as p:
|
|
||||||
for key in keys:
|
|
||||||
p.delete(key)
|
|
||||||
p.execute()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
@on_transaction_commit
|
|
||||||
def remove_built_orgs_from_users(cls, org_ids, user_ids):
|
|
||||||
client = cls.get_redis_client()
|
|
||||||
org_ids = [str(org_id) for org_id in org_ids]
|
|
||||||
|
|
||||||
with client.pipeline() as p:
|
|
||||||
for user_id in user_ids:
|
|
||||||
key = cls.key_template.format(user_id=user_id)
|
|
||||||
p.srem(key, *org_ids)
|
|
||||||
p.execute()
|
|
||||||
logger.info(f'Remove orgs from users built tree: users:{user_ids} orgs:{org_ids}')
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def add_need_refresh_orgs_for_users(cls, org_ids, user_ids):
|
|
||||||
cls.remove_built_orgs_from_users(org_ids, user_ids)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
@ensure_in_real_or_default_org
|
|
||||||
def add_need_refresh_on_nodes_assets_relate_change(cls, node_ids, asset_ids):
|
|
||||||
"""
|
|
||||||
1,计算与这些资产有关的授权
|
|
||||||
2,计算与这些节点以及祖先节点有关的授权
|
|
||||||
"""
|
|
||||||
|
|
||||||
node_ids = set(node_ids)
|
|
||||||
ancestor_node_keys = set()
|
|
||||||
asset_perm_ids = set()
|
|
||||||
|
|
||||||
nodes = PermNode.objects.filter(id__in=node_ids).only('id', 'key')
|
|
||||||
for node in nodes:
|
|
||||||
ancestor_node_keys.update(node.get_ancestor_keys())
|
|
||||||
|
|
||||||
ancestor_id = PermNode.objects.filter(key__in=ancestor_node_keys).values_list('id', flat=True)
|
|
||||||
node_ids.update(ancestor_id)
|
|
||||||
|
|
||||||
assets_related_perm_ids = AssetPermission.nodes.through.objects.filter(
|
|
||||||
node_id__in=node_ids
|
|
||||||
).values_list('assetpermission_id', flat=True)
|
|
||||||
asset_perm_ids.update(assets_related_perm_ids)
|
|
||||||
|
|
||||||
nodes_related_perm_ids = AssetPermission.assets.through.objects.filter(
|
|
||||||
asset_id__in=asset_ids
|
|
||||||
).values_list('assetpermission_id', flat=True)
|
|
||||||
asset_perm_ids.update(nodes_related_perm_ids)
|
|
||||||
|
|
||||||
cls.add_need_refresh_by_asset_perm_ids(asset_perm_ids)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def add_need_refresh_by_asset_perm_ids_cross_orgs(cls, asset_perm_ids):
|
|
||||||
org_id_perm_ids_mapper = defaultdict(set)
|
|
||||||
pairs = AssetPermission.objects.filter(id__in=asset_perm_ids).values_list('org_id', 'id')
|
|
||||||
for org_id, perm_id in pairs:
|
|
||||||
org_id_perm_ids_mapper[org_id].add(perm_id)
|
|
||||||
for org_id, perm_ids in org_id_perm_ids_mapper.items():
|
|
||||||
with tmp_to_org(org_id):
|
|
||||||
cls.add_need_refresh_by_asset_perm_ids(perm_ids)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
@ensure_in_real_or_default_org
|
|
||||||
def add_need_refresh_by_asset_perm_ids(cls, asset_perm_ids):
|
|
||||||
|
|
||||||
group_ids = AssetPermission.user_groups.through.objects.filter(
|
|
||||||
assetpermission_id__in=asset_perm_ids
|
|
||||||
).values_list('usergroup_id', flat=True)
|
|
||||||
|
|
||||||
user_ids = set()
|
|
||||||
direct_user_id = AssetPermission.users.through.objects.filter(
|
|
||||||
assetpermission_id__in=asset_perm_ids
|
|
||||||
).values_list('user_id', flat=True)
|
|
||||||
user_ids.update(direct_user_id)
|
|
||||||
|
|
||||||
group_user_ids = User.groups.through.objects.filter(
|
|
||||||
usergroup_id__in=group_ids
|
|
||||||
).values_list('user_id', flat=True)
|
|
||||||
user_ids.update(group_user_ids)
|
|
||||||
|
|
||||||
cls.remove_built_orgs_from_users(
|
|
||||||
[current_org.id], user_ids
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class UserGrantedUtilsBase:
|
class UserGrantedUtilsBase:
|
||||||
user: User
|
user: User
|
||||||
|
@ -43,6 +43,9 @@ class Setting(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
def is_name(self, name):
|
||||||
|
return self.name == name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cleaned_value(self):
|
def cleaned_value(self):
|
||||||
try:
|
try:
|
||||||
|
@ -8,11 +8,13 @@ from django.db.utils import ProgrammingError, OperationalError
|
|||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils.functional import LazyObject
|
from django.utils.functional import LazyObject
|
||||||
|
|
||||||
|
from jumpserver.utils import current_request
|
||||||
|
|
||||||
from common.decorator import on_transaction_commit
|
from common.decorator import on_transaction_commit
|
||||||
from common.signals import django_ready
|
from common.signals import django_ready
|
||||||
from common.utils import get_logger, ssh_key_gen
|
from common.utils import get_logger, ssh_key_gen
|
||||||
from common.utils.connection import RedisPubSub
|
from common.utils.connection import RedisPubSub
|
||||||
from jumpserver.utils import current_request
|
|
||||||
from .models import Setting
|
from .models import Setting
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
@ -31,15 +33,12 @@ setting_pub_sub = SettingSubPub()
|
|||||||
def refresh_settings_on_changed(sender, instance=None, **kwargs):
|
def refresh_settings_on_changed(sender, instance=None, **kwargs):
|
||||||
if not instance:
|
if not instance:
|
||||||
return
|
return
|
||||||
|
|
||||||
setting_pub_sub.publish(instance.name)
|
setting_pub_sub.publish(instance.name)
|
||||||
|
if instance.is_name('PERM_SINGLE_ASSET_TO_UNGROUP_NODE'):
|
||||||
# 配置变化: PERM_SINGLE_ASSET_TO_UNGROUP_NODE
|
""" 过期所有用户授权树 """
|
||||||
if instance.name == 'PERM_SINGLE_ASSET_TO_UNGROUP_NODE':
|
logger.debug('Expire all user perm tree')
|
||||||
# 清除所有用户授权树已构建的标记,下次访问重新生成
|
from perms.utils.user_permission import UserPermTreeExpireUtil
|
||||||
logger.debug('Clean ALL User perm tree built mark')
|
UserPermTreeExpireUtil().expire_perm_tree_for_all_user()
|
||||||
from perms.utils.user_permission import UserPermTreeUtil
|
|
||||||
UserPermTreeUtil.clean_all_user_tree_built_mark()
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(django_ready)
|
@receiver(django_ready)
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from django.db import models, IntegrityError
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from common.utils import lazyproperty
|
|
||||||
from orgs.mixins.models import OrgModelMixin
|
from orgs.mixins.models import OrgModelMixin
|
||||||
|
from common.utils import lazyproperty
|
||||||
|
|
||||||
__all__ = ['UserGroup']
|
__all__ = ['UserGroup']
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user