From 45bac09dc73944be65399cab4b92883d9fc316a9 Mon Sep 17 00:00:00 2001 From: Bai Date: Sun, 7 Apr 2024 20:09:17 +0800 Subject: [PATCH] perf: ROOT Org show orgs-and-roles in user-detail page --- .gitignore | 1 + apps/rbac/models/rolebinding.py | 1 + apps/users/api/user.py | 4 ++-- apps/users/models/user.py | 10 ++++++++++ apps/users/serializers/user.py | 19 +++++++++++++++++-- 5 files changed, 31 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 985f77580..b8fff0d67 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ releashe data/* test.py .history/ +.test/ diff --git a/apps/rbac/models/rolebinding.py b/apps/rbac/models/rolebinding.py index 11d91c911..ee019f34b 100644 --- a/apps/rbac/models/rolebinding.py +++ b/apps/rbac/models/rolebinding.py @@ -56,6 +56,7 @@ class RoleBinding(JMSBaseModel): on_delete=models.CASCADE, verbose_name=_('Organization') ) objects = RoleBindingManager() + objects_raw = models.Manager() class Meta: verbose_name = _('Role binding') diff --git a/apps/users/api/user.py b/apps/users/api/user.py index 473b08c04..7a67e9ddd 100644 --- a/apps/users/api/user.py +++ b/apps/users/api/user.py @@ -22,8 +22,7 @@ from ..models import User from ..notifications import ResetMFAMsg from ..permissions import UserObjectPermission from ..serializers import ( - UserSerializer, - MiniUserSerializer, InviteSerializer + UserSerializer, MiniUserSerializer, InviteSerializer, UserRetrieveSerializer ) from ..signals import post_user_create @@ -43,6 +42,7 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, SuggestionMixin, BulkModelV 'default': UserSerializer, 'suggestion': MiniUserSerializer, 'invite': InviteSerializer, + 'retrieve': UserRetrieveSerializer, } rbac_perms = { 'match': 'users.match_user', diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 0d05da844..ad90da0d1 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -5,6 +5,7 @@ import base64 import datetime import uuid from typing import Callable +from collections import defaultdict import sshpubkeys from django.conf import settings @@ -27,6 +28,7 @@ from common.utils import ( from labels.mixins import LabeledMixin from orgs.utils import current_org from rbac.const import Scope +from rbac.models import RoleBinding from ..signals import ( post_user_change_password, post_user_leave_org, pre_user_leave_org ) @@ -926,6 +928,14 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, LabeledMixin, JSONFilterM def is_local(self): return self.source == self.Source.local.value + @property + def orgs_roles(self): + orgs_roles = defaultdict(set) + rbs = RoleBinding.objects_raw.filter(user=self, scope='org').prefetch_related('role', 'org') + for rb in rbs: + orgs_roles[rb.org_name].add(str(rb.role.display_name)) + return orgs_roles + def is_password_authenticate(self): cas = self.Source.cas saml2 = self.Source.saml2 diff --git a/apps/users/serializers/user.py b/apps/users/serializers/user.py index db7efb4f9..24647483d 100644 --- a/apps/users/serializers/user.py +++ b/apps/users/serializers/user.py @@ -12,6 +12,7 @@ from common.serializers.fields import ( ) from common.utils import pretty_string, get_logger from common.validators import PhoneValidator +from orgs.utils import current_org from rbac.builtin import BuiltinRole from rbac.models import OrgRoleBinding, SystemRoleBinding, Role from rbac.permissions import RBACPermission @@ -23,6 +24,7 @@ __all__ = [ "MiniUserSerializer", "InviteSerializer", "ServiceAccountSerializer", + "UserRetrieveSerializer", ] logger = get_logger(__file__) @@ -46,6 +48,7 @@ class RolesSerializerMixin(serializers.Serializer): label=_("Org roles"), many=True, required=False, default=default_org_roles ) + orgs_roles = serializers.JSONField(read_only=True, label=_("Organization and roles relations")) def pop_roles_if_need(self, fields): request = self.context.get("request") @@ -58,7 +61,7 @@ class RolesSerializerMixin(serializers.Serializer): model_cls_field_mapper = { SystemRoleBinding: ["system_roles"], - OrgRoleBinding: ["org_roles"], + OrgRoleBinding: ["org_roles", "orgs_roles"], } update_actions = ("partial_bulk_update", "bulk_update", "partial_update", "update") @@ -156,6 +159,7 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, ResourceLa "is_first_login", "wecom_id", "dingtalk_id", "feishu_id", "lark_id", "date_api_key_last_used", ] + fields_only_root_org = ["orgs_roles"] disallow_self_update_fields = ["is_active", "system_roles", "org_roles"] extra_kwargs = { "password": { @@ -177,7 +181,18 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, ResourceLa "is_otp_secret_key_bound": {"label": _("Is OTP bound")}, 'mfa_level': {'label': _("MFA level")}, } + + def get_fields(self): + fields = super().get_fields() + self.pop_fields_if_need(fields) + return fields + def pop_fields_if_need(self, fields): + # pop only root org fields + if not current_org.is_root(): + for f in self.Meta.fields_only_root_org: + fields.pop(f, None) + def validate_password(self, password): password_strategy = self.initial_data.get("password_strategy") if self.instance is None and password_strategy != PasswordStrategy.custom: @@ -273,7 +288,7 @@ class UserRetrieveSerializer(UserSerializer): ) class Meta(UserSerializer.Meta): - fields = UserSerializer.Meta.fields + ["login_confirm_settings"] + fields = UserSerializer.Meta.fields + ["login_confirm_settings", "orgs_roles"] class MiniUserSerializer(serializers.ModelSerializer):