fix(perms): 用户添加到用户组报错

This commit is contained in:
xinwen 2020-09-27 19:47:13 +08:00 committed by 老广
parent b8ff3b38bf
commit 6701a1b604
8 changed files with 54 additions and 35 deletions

View File

@ -1,12 +1,14 @@
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist as DJObjectDoesNotExist from django.core.exceptions import PermissionDenied, ObjectDoesNotExist as DJObjectDoesNotExist
from django.http import Http404 from django.http import Http404
from django.utils.translation import gettext from django.utils.translation import gettext
from rest_framework import exceptions from rest_framework import exceptions
from rest_framework.views import set_rollback from rest_framework.views import set_rollback
from rest_framework.response import Response from rest_framework.response import Response
from common.exceptions import JMSObjectDoesNotExist from common.exceptions import JMSObjectDoesNotExist
from common.utils import get_logger
logger = get_logger(__name__)
def extract_object_name(exc, index=0): def extract_object_name(exc, index=0):
@ -20,6 +22,8 @@ def extract_object_name(exc, index=0):
def common_exception_handler(exc, context): def common_exception_handler(exc, context):
logger.exception('')
if isinstance(exc, Http404): if isinstance(exc, Http404):
exc = JMSObjectDoesNotExist(object_name=extract_object_name(exc, 1)) exc = JMSObjectDoesNotExist(object_name=extract_object_name(exc, 1))
elif isinstance(exc, PermissionDenied): elif isinstance(exc, PermissionDenied):

View File

@ -3,6 +3,8 @@
from django.http import HttpResponse from django.http import HttpResponse
from django.utils.encoding import iri_to_uri from django.utils.encoding import iri_to_uri
from rest_framework.serializers import BooleanField
class HttpResponseTemporaryRedirect(HttpResponse): class HttpResponseTemporaryRedirect(HttpResponse):
status_code = 307 status_code = 307
@ -14,3 +16,7 @@ class HttpResponseTemporaryRedirect(HttpResponse):
def get_remote_addr(request): def get_remote_addr(request):
return request.META.get("HTTP_X_FORWARDED_HOST") or request.META.get("REMOTE_ADDR") return request.META.get("HTTP_X_FORWARDED_HOST") or request.META.get("REMOTE_ADDR")
def is_true(value):
return value in BooleanField.TRUE_VALUES

View File

@ -17,7 +17,7 @@ from ...utils.user_asset_permission import (
get_indirect_granted_node_children, get_indirect_granted_node_children,
get_user_granted_nodes_list_via_mapping_node, get_user_granted_nodes_list_via_mapping_node,
get_top_level_granted_nodes, 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() @tmp_to_root_org()
def list(self, request, *args, **kwargs): 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() nodes = self.get_nodes()
serializer = self.get_serializer(nodes, many=True) serializer = self.get_serializer(nodes, many=True)
return Response(serializer.data) return Response(serializer.data)
@ -73,8 +73,8 @@ class BaseNodeChildrenApi(NodeChildrenMixin, BaseGrantedNodeApi, metaclass=abc.A
class BaseGrantedNodeAsTreeApi(SerializeToTreeNodeMixin, _GrantedNodeStructApi, metaclass=abc.ABCMeta): class BaseGrantedNodeAsTreeApi(SerializeToTreeNodeMixin, _GrantedNodeStructApi, metaclass=abc.ABCMeta):
@tmp_to_root_org() @tmp_to_root_org()
def list(self, request, *args, **kwargs): def list(self, request: Request, *args, **kwargs):
init_user_tree_if_need(self.user) rebuild_user_tree_if_need(request, self.user)
nodes = self.get_nodes() nodes = self.get_nodes()
nodes = self.serialize_nodes(nodes, with_asset_amount=True) nodes = self.serialize_nodes(nodes, with_asset_amount=True)
return Response(data=nodes) return Response(data=nodes)

View File

@ -12,7 +12,7 @@ from ...utils.user_asset_permission import (
get_indirect_granted_node_children, UNGROUPED_NODE_KEY, FAVORITE_NODE_KEY, get_indirect_granted_node_children, UNGROUPED_NODE_KEY, FAVORITE_NODE_KEY,
get_user_direct_granted_assets, get_top_level_granted_nodes, get_user_direct_granted_assets, get_top_level_granted_nodes,
get_user_granted_nodes_list_via_mapping_node, 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, get_user_all_assetpermission_ids,
) )
@ -38,7 +38,7 @@ class MyGrantedNodesWithAssetsAsTreeApi(SerializeToTreeNodeMixin, ListAPIView):
""" """
user = request.user 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_nodes = get_user_granted_nodes_list_via_mapping_node(user)
all_assets = get_user_granted_all_assets(user) all_assets = get_user_granted_all_assets(user)
@ -106,7 +106,7 @@ class UserGrantedNodeChildrenWithAssetsAsTreeForAdminApi(ForAdminMixin, UserNode
key = self.id2key_if_have() key = self.id2key_if_have()
user = self.user user = self.user
init_user_tree_if_need(user) rebuild_user_tree_if_need(request, user)
nodes, assets = self.get_data(key, user) nodes, assets = self.get_data(key, user)
tree_nodes = self.serialize_nodes(nodes, with_asset_amount=True) tree_nodes = self.serialize_nodes(nodes, with_asset_amount=True)

View File

@ -4,16 +4,16 @@ from itertools import chain
from django.db.models.signals import m2m_changed, pre_delete, pre_save from django.db.models.signals import m2m_changed, pre_delete, pre_save
from django.dispatch import receiver from django.dispatch import receiver
from django.db import transaction
from django.db.models import Q 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 users.models import User, UserGroup
from assets.models import Asset 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.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 .models import AssetPermission, RemoteAppPermission, RebuildUserTreeTask from .models import AssetPermission, RemoteAppPermission
logger = get_logger(__file__) logger = get_logger(__file__)
@ -21,9 +21,12 @@ logger = get_logger(__file__)
@receiver([pre_save], sender=AssetPermission) @receiver([pre_save], sender=AssetPermission)
def on_asset_perm_deactive(instance: AssetPermission, **kwargs): def on_asset_perm_deactive(instance: AssetPermission, **kwargs):
old = AssetPermission.objects.only('is_active').get(id=instance.id) try:
if instance.is_active != old.is_active: old = AssetPermission.objects.only('is_active').get(id=instance.id)
create_rebuild_user_tree_task_by_asset_perm(instance) 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) @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) 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): def create_rebuild_user_tree_task_by_asset_perm(asset_perm: AssetPermission):
user_ids = set() user_ids = set()
user_ids.update( user_ids.update(

View File

@ -2,6 +2,7 @@
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
from datetime import timedelta from datetime import timedelta
from django.db import transaction
from django.db.models import Q from django.db.models import Q
from django.conf import settings from django.conf import settings
from celery import shared_task from celery import shared_task
@ -9,23 +10,25 @@ from common.utils import get_logger
from common.utils.timezone import now from common.utils.timezone import now
from users.models import User from users.models import User
from perms.models import RebuildUserTreeTask, AssetPermission 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__) logger = get_logger(__file__)
@shared_task(queue='node_tree') @shared_task(queue='node_tree')
def rebuild_user_mapping_nodes_celery_task(user_id): 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) 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') @shared_task(queue='node_tree')
def dispatch_mapping_node_tasks(): def dispatch_mapping_node_tasks():
user_ids = RebuildUserTreeTask.objects.all().values_list('user_id', flat=True).distinct() 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: for id in user_ids:
logger.info(f'>>> dispatch mapping node task for user[{id}]')
rebuild_user_mapping_nodes_celery_task.delay(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() 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)

View File

@ -8,6 +8,7 @@ from django.conf import settings
from django.db.models import F, Q, Value, BooleanField from django.db.models import F, Q, Value, BooleanField
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from common.http import is_true
from common.utils import get_logger from common.utils import get_logger
from common.const.distributed_lock_key import UPDATE_MAPPING_NODE_TASK_LOCK_KEY from common.const.distributed_lock_key import UPDATE_MAPPING_NODE_TASK_LOCK_KEY
from orgs.utils import tmp_to_root_org 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): 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) tmp_nodes = compute_tmp_mapping_node_from_perm(user)
for _node in tmp_nodes: for _node in tmp_nodes:
set_node_granted_assets_amount(user, _node) set_node_granted_assets_amount(user, _node)
create_mapping_nodes(user, tmp_nodes) 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): 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 如果没有数据同步创建用户授权树 先检查 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: try:
rebuild_user_mapping_nodes_with_lock(user) rebuild_user_mapping_nodes_with_lock(user)
except lock.SomeoneIsDoingThis: except lock.SomeoneIsDoingThis:

View File

@ -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 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 common.utils import get_logger
from .signals import post_user_create from .signals import post_user_create
from .models import User from .models import User
@ -27,14 +28,12 @@ def on_user_create(sender, user=None, **kwargs):
@receiver(m2m_changed, sender=User.groups.through) @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'): if action.startswith('post'):
logger.debug("User group member change signal recv: {}".format(instance)) if reverse:
from perms.utils import AssetPermissionUtil create_rebuild_user_tree_task(pk_set)
AssetPermissionUtil.expire_all_user_tree_cache() else:
create_rebuild_user_tree_task([instance.id])
@receiver(cas_user_authenticated) @receiver(cas_user_authenticated)