diff --git a/apps/common/drf/exc_handlers.py b/apps/common/drf/exc_handlers.py index 8515c95ec..906224615 100644 --- a/apps/common/drf/exc_handlers.py +++ b/apps/common/drf/exc_handlers.py @@ -1,12 +1,14 @@ from django.core.exceptions import PermissionDenied, ObjectDoesNotExist as DJObjectDoesNotExist from django.http import Http404 from django.utils.translation import gettext - from rest_framework import exceptions from rest_framework.views import set_rollback from rest_framework.response import Response from common.exceptions import JMSObjectDoesNotExist +from common.utils import get_logger + +logger = get_logger(__name__) def extract_object_name(exc, index=0): @@ -20,6 +22,8 @@ def extract_object_name(exc, index=0): def common_exception_handler(exc, context): + logger.exception('') + if isinstance(exc, Http404): exc = JMSObjectDoesNotExist(object_name=extract_object_name(exc, 1)) elif isinstance(exc, PermissionDenied): diff --git a/apps/common/http.py b/apps/common/http.py index f3a743045..5a3d85524 100644 --- a/apps/common/http.py +++ b/apps/common/http.py @@ -3,6 +3,8 @@ from django.http import HttpResponse from django.utils.encoding import iri_to_uri +from rest_framework.serializers import BooleanField + class HttpResponseTemporaryRedirect(HttpResponse): status_code = 307 @@ -14,3 +16,7 @@ class HttpResponseTemporaryRedirect(HttpResponse): def get_remote_addr(request): return request.META.get("HTTP_X_FORWARDED_HOST") or request.META.get("REMOTE_ADDR") + + +def is_true(value): + return value in BooleanField.TRUE_VALUES diff --git a/apps/perms/api/user_permission/user_permission_nodes.py b/apps/perms/api/user_permission/user_permission_nodes.py index c6c98a1f2..d54765d51 100644 --- a/apps/perms/api/user_permission/user_permission_nodes.py +++ b/apps/perms/api/user_permission/user_permission_nodes.py @@ -17,7 +17,7 @@ from ...utils.user_asset_permission import ( get_indirect_granted_node_children, get_user_granted_nodes_list_via_mapping_node, get_top_level_granted_nodes, - init_user_tree_if_need, + rebuild_user_tree_if_need, ) @@ -61,7 +61,7 @@ class BaseGrantedNodeApi(_GrantedNodeStructApi, metaclass=abc.ABCMeta): @tmp_to_root_org() def list(self, request, *args, **kwargs): - init_user_tree_if_need(self.user) + rebuild_user_tree_if_need(request, self.user) nodes = self.get_nodes() serializer = self.get_serializer(nodes, many=True) return Response(serializer.data) @@ -73,8 +73,8 @@ class BaseNodeChildrenApi(NodeChildrenMixin, BaseGrantedNodeApi, metaclass=abc.A class BaseGrantedNodeAsTreeApi(SerializeToTreeNodeMixin, _GrantedNodeStructApi, metaclass=abc.ABCMeta): @tmp_to_root_org() - def list(self, request, *args, **kwargs): - init_user_tree_if_need(self.user) + def list(self, request: Request, *args, **kwargs): + rebuild_user_tree_if_need(request, self.user) nodes = self.get_nodes() nodes = self.serialize_nodes(nodes, with_asset_amount=True) return Response(data=nodes) diff --git a/apps/perms/api/user_permission/user_permission_nodes_with_assets.py b/apps/perms/api/user_permission/user_permission_nodes_with_assets.py index 09cfbe75f..e0591b7df 100644 --- a/apps/perms/api/user_permission/user_permission_nodes_with_assets.py +++ b/apps/perms/api/user_permission/user_permission_nodes_with_assets.py @@ -12,7 +12,7 @@ from ...utils.user_asset_permission import ( get_indirect_granted_node_children, UNGROUPED_NODE_KEY, FAVORITE_NODE_KEY, get_user_direct_granted_assets, get_top_level_granted_nodes, get_user_granted_nodes_list_via_mapping_node, - get_user_granted_all_assets, init_user_tree_if_need, + get_user_granted_all_assets, rebuild_user_tree_if_need, get_user_all_assetpermission_ids, ) @@ -38,7 +38,7 @@ class MyGrantedNodesWithAssetsAsTreeApi(SerializeToTreeNodeMixin, ListAPIView): """ user = request.user - init_user_tree_if_need(user) + rebuild_user_tree_if_need(request, user) all_nodes = get_user_granted_nodes_list_via_mapping_node(user) all_assets = get_user_granted_all_assets(user) @@ -106,7 +106,7 @@ class UserGrantedNodeChildrenWithAssetsAsTreeForAdminApi(ForAdminMixin, UserNode key = self.id2key_if_have() user = self.user - init_user_tree_if_need(user) + rebuild_user_tree_if_need(request, user) nodes, assets = self.get_data(key, user) tree_nodes = self.serialize_nodes(nodes, with_asset_amount=True) diff --git a/apps/perms/signals_handler.py b/apps/perms/signals_handler.py index f2ba8e803..cff5140f2 100644 --- a/apps/perms/signals_handler.py +++ b/apps/perms/signals_handler.py @@ -4,16 +4,16 @@ from itertools import chain from django.db.models.signals import m2m_changed, pre_delete, pre_save from django.dispatch import receiver -from django.db import transaction + from django.db.models import Q -from perms.tasks import dispatch_mapping_node_tasks +from perms.tasks import create_rebuild_user_tree_task from users.models import User, UserGroup from assets.models import Asset -from common.utils import get_logger +from common.utils import get_logger, get_object_or_none from common.exceptions import M2MReverseNotAllowed from common.const.signals import POST_ADD, POST_REMOVE, POST_CLEAR -from .models import AssetPermission, RemoteAppPermission, RebuildUserTreeTask +from .models import AssetPermission, RemoteAppPermission logger = get_logger(__file__) @@ -21,9 +21,12 @@ logger = get_logger(__file__) @receiver([pre_save], sender=AssetPermission) def on_asset_perm_deactive(instance: AssetPermission, **kwargs): - old = AssetPermission.objects.only('is_active').get(id=instance.id) - if instance.is_active != old.is_active: - create_rebuild_user_tree_task_by_asset_perm(instance) + try: + old = AssetPermission.objects.only('is_active').get(id=instance.id) + if instance.is_active != old.is_active: + create_rebuild_user_tree_task_by_asset_perm(instance) + except AssetPermission.DoesNotExist: + pass @receiver([pre_delete], sender=AssetPermission) @@ -32,13 +35,6 @@ def on_asset_permission_delete(instance, **kwargs): create_rebuild_user_tree_task_by_asset_perm(instance) -def create_rebuild_user_tree_task(user_ids): - RebuildUserTreeTask.objects.bulk_create( - [RebuildUserTreeTask(user_id=i) for i in user_ids] - ) - transaction.on_commit(dispatch_mapping_node_tasks.delay) - - def create_rebuild_user_tree_task_by_asset_perm(asset_perm: AssetPermission): user_ids = set() user_ids.update( diff --git a/apps/perms/tasks.py b/apps/perms/tasks.py index f427b684c..fb871d16e 100644 --- a/apps/perms/tasks.py +++ b/apps/perms/tasks.py @@ -2,6 +2,7 @@ from __future__ import absolute_import, unicode_literals from datetime import timedelta +from django.db import transaction from django.db.models import Q from django.conf import settings from celery import shared_task @@ -9,23 +10,25 @@ from common.utils import get_logger from common.utils.timezone import now from users.models import User from perms.models import RebuildUserTreeTask, AssetPermission -from perms.utils.user_asset_permission import rebuild_user_mapping_nodes_if_need_with_lock +from perms.utils.user_asset_permission import rebuild_user_mapping_nodes_if_need_with_lock, lock logger = get_logger(__file__) @shared_task(queue='node_tree') def rebuild_user_mapping_nodes_celery_task(user_id): - logger.info(f'>>> rebuild user[{user_id}] mapping nodes') user = User.objects.get(id=user_id) - rebuild_user_mapping_nodes_if_need_with_lock(user) + try: + rebuild_user_mapping_nodes_if_need_with_lock(user) + except lock.SomeoneIsDoingThis: + pass @shared_task(queue='node_tree') def dispatch_mapping_node_tasks(): user_ids = RebuildUserTreeTask.objects.all().values_list('user_id', flat=True).distinct() + logger.info(f'>>> dispatch_mapping_node_tasks for users {list(user_ids)}') for id in user_ids: - logger.info(f'>>> dispatch mapping node task for user[{id}]') rebuild_user_mapping_nodes_celery_task.delay(id) @@ -55,3 +58,10 @@ def dispatch_process_expired_asset_permission(asset_perm_ids): ) dispatch_mapping_node_tasks.delay() + + +def create_rebuild_user_tree_task(user_ids): + RebuildUserTreeTask.objects.bulk_create( + [RebuildUserTreeTask(user_id=i) for i in user_ids] + ) + transaction.on_commit(dispatch_mapping_node_tasks.delay) diff --git a/apps/perms/utils/user_asset_permission.py b/apps/perms/utils/user_asset_permission.py index 80e4778ea..01a558ae2 100644 --- a/apps/perms/utils/user_asset_permission.py +++ b/apps/perms/utils/user_asset_permission.py @@ -8,6 +8,7 @@ from django.conf import settings from django.db.models import F, Q, Value, BooleanField from django.utils.translation import gettext as _ +from common.http import is_true from common.utils import get_logger from common.const.distributed_lock_key import UPDATE_MAPPING_NODE_TASK_LOCK_KEY from orgs.utils import tmp_to_root_org @@ -247,10 +248,12 @@ def set_node_granted_assets_amount(user, node): def rebuild_user_mapping_nodes(user): + logger.info(f'>>> {dt_formater(now())} start rebuild {user} mapping nodes') tmp_nodes = compute_tmp_mapping_node_from_perm(user) for _node in tmp_nodes: set_node_granted_assets_amount(user, _node) create_mapping_nodes(user, tmp_nodes) + logger.info(f'>>> {dt_formater(now())} end rebuild {user} mapping nodes') def get_user_granted_nodes_list_via_mapping_node(user): @@ -486,12 +489,13 @@ def get_favorite_node(user): ) -def init_user_tree_if_need(user): +def rebuild_user_tree_if_need(request, user): """ 升级授权树策略后,用户的数据可能还未初始化,为防止用户显示没有数据 先检查 MappingNode 如果没有数据,同步创建用户授权树 """ - if not UserGrantedMappingNode.objects.filter(user=user).exists(): + if is_true(request.query_params.get('rebuild_tree')) or \ + not UserGrantedMappingNode.objects.filter(user=user).exists(): try: rebuild_user_mapping_nodes_with_lock(user) except lock.SomeoneIsDoingThis: diff --git a/apps/users/signals_handler.py b/apps/users/signals_handler.py index 37f7c42fc..3fdf6ddc6 100644 --- a/apps/users/signals_handler.py +++ b/apps/users/signals_handler.py @@ -9,6 +9,7 @@ from django_cas_ng.signals import cas_user_authenticated from jms_oidc_rp.signals import openid_create_or_update_user +from perms.tasks import create_rebuild_user_tree_task from common.utils import get_logger from .signals import post_user_create from .models import User @@ -27,14 +28,12 @@ def on_user_create(sender, user=None, **kwargs): @receiver(m2m_changed, sender=User.groups.through) -def on_user_groups_change(sender, instance=None, action='', **kwargs): - """ - 资产节点发生变化时,刷新节点 - """ +def on_user_groups_change(instance, action, reverse, pk_set, **kwargs): if action.startswith('post'): - logger.debug("User group member change signal recv: {}".format(instance)) - from perms.utils import AssetPermissionUtil - AssetPermissionUtil.expire_all_user_tree_cache() + if reverse: + create_rebuild_user_tree_task(pk_set) + else: + create_rebuild_user_tree_task([instance.id]) @receiver(cas_user_authenticated)