From cf8bcabb49de72205272c6495d07d657329bc561 Mon Sep 17 00:00:00 2001 From: polo <826770122@qq.com> Date: Sun, 9 Oct 2022 17:44:11 +0800 Subject: [PATCH 001/644] =?UTF-8?q?feat:=20=E4=BF=AE=E5=A4=8D=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E6=9D=83=E9=99=90=E7=AE=A1=E7=90=86=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E6=97=A0=E9=99=90=E5=88=B6=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/views/resource-permiss/index.vue | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/src/views/resource-permiss/index.vue b/frontend/src/views/resource-permiss/index.vue index 98eba3588..e3114b92d 100644 --- a/frontend/src/views/resource-permiss/index.vue +++ b/frontend/src/views/resource-permiss/index.vue @@ -551,11 +551,15 @@ this.resourceInstances = []; } else { resItem.condition = data; - data.forEach(item => { - item.instance.forEach(e => { - resItem.resourceInstancesPath = e.path[0]; + if (data.length) { + data.forEach(item => { + item.instance.forEach(e => { + resItem.resourceInstancesPath = e.path[0]; + }); }); - }); + } else { + delete resItem.resourceInstancesPath; + } if (this.curResIndex !== -1) { this.resourceInstances.splice(this.curResIndex, 1, resItem); } From 7a98d6450541f142fd395338ff624fc9aa7646ff Mon Sep 17 00:00:00 2001 From: polo <826770122@qq.com> Date: Thu, 13 Oct 2022 18:15:28 +0800 Subject: [PATCH 002/644] feat: add externalSystemsLayout data --- frontend/src/App.vue | 13 +++-- frontend/src/store/index.js | 56 ++++++++++++++++++- frontend/src/views/group/add-perm/index.vue | 3 + .../group/common/render-template-item.vue | 12 +++- .../components/add-group-perm-sideslider.vue | 6 +- .../src/views/group/detail/group-perm-new.vue | 6 +- frontend/src/views/perm/index.vue | 16 ++++++ .../src/views/perm/perm-renewal/index.vue | 19 ++++++- frontend/src/views/transfer/index.vue | 13 +++-- 9 files changed, 126 insertions(+), 18 deletions(-) diff --git a/frontend/src/App.vue b/frontend/src/App.vue index fd8345a26..e23429bde 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -14,16 +14,21 @@ :style="processGuideStyle" :flag="processGuideShow" :content="$t(`m.guide['创建审批流程']`)" /> - + v-if="isRouterAlive && !externalSystemsLayout.hideIamBreadCrumbs"> - + +
@@ -78,7 +83,7 @@ }; }, computed: { - ...mapGetters(['mainContentLoading', 'user']) + ...mapGetters(['mainContentLoading', 'user', 'externalSystemsLayout']) }, watch: { '$route' (to, from) { diff --git a/frontend/src/store/index.js b/frontend/src/store/index.js index 8db5245a2..6ee40b7b5 100644 --- a/frontend/src/store/index.js +++ b/frontend/src/store/index.js @@ -312,7 +312,37 @@ const store = new Vuex.Store({ showNoviceGuide: false, - curRoleId: 0 + curRoleId: 0, + + externalSystemsLayout: { + hideIamHeader: false, // 第一层级头部导航 + hideIamSlider: false, // 第一层级侧边导航 + hideIamBreadCrumbs: false, // 第一层级面包屑 + myPerm: { // 我的权限 + hideCustomTab: true, // 自定义权限tab - 1 + hideApplyBtn: true, // 申请权限按钮 - 1 + hideTemporaryCustomTab: true, // 临时权限tab -1 + renewal: { // 我的权限-权限续期 + hideCustomTab: true // 自定义权限tab - 2 + }, + transfer: { // 我的权限-权限交接 + hideTextBtn: true, // 交接历史文本按钮 - 3 + hideCustomData: true, // 自定义权限交接-3 + hideManagerData: true, // 管理员交接数据-3 + showUserGroupSearch: true // 显示权限交接用户组查询-3 + } + }, + userGroup: { // 用户组 + addGroup: { // 用户组 - 添加用户组 - 添加权限抽屉 + hideAddTemplateTextBtn: true // 右侧抽屉新增文本按钮-7.1 + }, + groupDetail: { // 用户组 - 组详情 + hideAddBtn: true, // 用户组-组权限-添加权限按钮-6 + hideEditBtn: true, // 用户组-组权限-编辑权限按钮-6 + hideDeleteBtn: true // 用户组-组权限-删除权限按钮-6 + } + } + } }, getters: { mainContentLoading: state => state.mainContentLoading, @@ -337,7 +367,8 @@ const store = new Vuex.Store({ index: state => state.index, navCurRoleId: state => state.navCurRoleId, showNoviceGuide: state => state.showNoviceGuide, - curRoleId: state => state.curRoleId + curRoleId: state => state.curRoleId, + externalSystemsLayout: state => state.externalSystemsLayout }, mutations: { updateHost (state, params) { @@ -506,6 +537,10 @@ const store = new Vuex.Store({ updateCurRoleId (state, id) { state.curRoleId = id; + }, + + setExternalSystemsLayout (state, payload) { + state.externalSystemsLayout = payload; } }, actions: { @@ -670,6 +705,23 @@ const store = new Vuex.Store({ */ get ({ commit, state, dispatch }, params, config) { return http.get(`/app/index?invoke=get&${json2Query(params)}`, config); + }, + + /** + * 获取需要动态展示的按钮或文案 + * + * @param {Function} commit store commit mutation handler + * @param {Object} state store state + * @param {Function} dispatch store dispatch action handler + * @param {Object?} config http config + * + * @return {Promise} promise 对象 + */ + getExternalSystemsLayout ({ commit, state, dispatch }, config) { + return http.get(`${AJAX_URL_PREFIX}/systems/`, config).then(response => { + commit('setExternalSystemsLayout', response.data); + return response.data; + }); } } }); diff --git a/frontend/src/views/group/add-perm/index.vue b/frontend/src/views/group/add-perm/index.vue index c334b8259..f095e4914 100644 --- a/frontend/src/views/group/add-perm/index.vue +++ b/frontend/src/views/group/add-perm/index.vue @@ -43,6 +43,7 @@ :is-show.sync="isShowAddSideslider" :custom-perm="originalList" :template="tempalteDetailList" + :external-template="externalSystemsLayout.userGroup.addGroup.hideAddTemplateTextBtn" :aggregation="aggregationData" :authorization="authorizationData" :group-id="$route.params.id" @@ -78,6 +79,7 @@ import GroupPolicy from '@/model/group-policy'; import GroupAggregationPolicy from '@/model/group-aggregation-policy'; import Condition from '@/model/condition'; + import { mapGetters } from 'vuex'; export default { name: '', @@ -116,6 +118,7 @@ }; }, computed: { + ...mapGetters(['externalSystemsLayout']), isAggregateDisabled () { const aggregationIds = this.tableList.reduce((counter, item) => { return item.aggregationId !== '' ? counter.concat(item.aggregationId) : counter; diff --git a/frontend/src/views/group/common/render-template-item.vue b/frontend/src/views/group/common/render-template-item.vue index ccd8fc050..d053b70ed 100644 --- a/frontend/src/views/group/common/render-template-item.vue +++ b/frontend/src/views/group/common/render-template-item.vue @@ -9,7 +9,7 @@ ({{ count }}) - - From 3af54944134283051d6cc62531abbf8524dc2a0d Mon Sep 17 00:00:00 2001 From: zhu327 Date: Thu, 27 Oct 2022 10:56:37 +0800 Subject: [PATCH 014/644] feat: add subset manager readonly members --- saas/backend/api/management/v1/serializers.py | 5 + .../api/management/v1/views/grade_manager.py | 7 +- saas/backend/api/management/v2/serializers.py | 5 + saas/backend/apps/role/serializers.py | 17 +++- saas/backend/apps/role/tasks.py | 2 +- saas/backend/biz/handover.py | 4 +- saas/backend/biz/role.py | 21 +++- saas/backend/service/role.py | 99 ++++++++++++++++--- saas/backend/trans/role.py | 6 +- 9 files changed, 139 insertions(+), 27 deletions(-) diff --git a/saas/backend/api/management/v1/serializers.py b/saas/backend/api/management/v1/serializers.py index da4c7780b..40270c571 100644 --- a/saas/backend/api/management/v1/serializers.py +++ b/saas/backend/api/management/v1/serializers.py @@ -78,6 +78,11 @@ def validate(self, data): class ManagementGradeManagerCreateSLZ(ManagementSourceSystemSLZ, RatingMangerBaseInfoSZL): + members = serializers.ListField( + label="成员列表", + child=serializers.CharField(label="用户ID", max_length=64), + max_length=settings.SUBJECT_AUTHORIZATION_LIMIT["grade_manager_member_limit"], + ) authorization_scopes = serializers.ListField( label="可授权的权限范围", child=ManagementRoleScopeAuthorizationSLZ(label="系统操作"), allow_empty=False ) diff --git a/saas/backend/api/management/v1/views/grade_manager.py b/saas/backend/api/management/v1/views/grade_manager.py index 002604fe9..1b88bab82 100644 --- a/saas/backend/api/management/v1/views/grade_manager.py +++ b/saas/backend/api/management/v1/views/grade_manager.py @@ -32,7 +32,7 @@ RoleMemberDeleteAuditProvider, RoleUpdateAuditProvider, ) -from backend.apps.role.models import Role, RoleSource, RoleUser +from backend.apps.role.models import Role, RoleSource from backend.apps.role.serializers import RoleIdSLZ from backend.audit.audit import audit_context_setter, view_audit_decorator from backend.biz.role import RoleBiz, RoleCheckBiz @@ -85,6 +85,9 @@ def create(self, request, *args, **kwargs): # 检查该系统可创建的分级管理员数量是否超限 self.role_check_biz.check_grade_manager_of_system_limit(source_system_id) + # 兼容member格式 + data["members"] = [{"username": username} for username in data["members"]] + # 转换为RoleInfoBean,用于创建时使用 role_info = self.trans.to_role_info(data) @@ -232,7 +235,7 @@ def destroy(self, request, *args, **kwargs): serializer.is_valid(raise_exception=True) members = list(set(serializer.validated_data["members"])) - RoleUser.objects.delete_grade_manager_member(role.id, members) + self.biz.delete_grade_manager_member(role.id, members) # 审计 audit_context_setter(role=role, members=members) diff --git a/saas/backend/api/management/v2/serializers.py b/saas/backend/api/management/v2/serializers.py index e66075b10..6479a74f6 100644 --- a/saas/backend/api/management/v2/serializers.py +++ b/saas/backend/api/management/v2/serializers.py @@ -63,6 +63,11 @@ def validate(self, data): class ManagementGradeManagerCreateSLZ(ManagementSourceSystemSLZ, RatingMangerBaseInfoSZL): + members = serializers.ListField( + label="成员列表", + child=serializers.CharField(label="用户ID", max_length=64), + max_length=settings.SUBJECT_AUTHORIZATION_LIMIT["grade_manager_member_limit"], + ) authorization_scopes = serializers.ListField( label="可授权的权限范围", child=ManagementRoleScopeAuthorizationSLZ(label="系统操作"), allow_empty=False ) diff --git a/saas/backend/apps/role/serializers.py b/saas/backend/apps/role/serializers.py index aa983debf..25b078ca2 100644 --- a/saas/backend/apps/role/serializers.py +++ b/saas/backend/apps/role/serializers.py @@ -111,12 +111,17 @@ def validate(self, data): return data +class RoleMember(serializers.Serializer): + username = serializers.CharField(label="用户ID", max_length=128) + readonly = serializers.BooleanField(default=False) + + class RatingMangerBaseInfoSZL(serializers.Serializer): name = serializers.CharField(label="分级管理员名称", max_length=128) description = serializers.CharField(label="描述", allow_blank=True) members = serializers.ListField( label="成员列表", - child=serializers.CharField(label="用户ID", max_length=64), + child=RoleMember(label="成员"), max_length=settings.SUBJECT_AUTHORIZATION_LIMIT["grade_manager_member_limit"], ) @@ -142,7 +147,7 @@ class RoleIdSLZ(serializers.Serializer): class BaseRatingMangerSchemaSLZ(serializers.Serializer): - members = serializers.ListField(label="成员列表", child=serializers.CharField(label="用户ID", max_length=128)) + members = serializers.ListField(label="成员列表", child=RoleMember(label="成员")) class Meta: model = Role @@ -202,7 +207,9 @@ class Meta: fields = ("id", "name", "description", "creator", "created_time", "updated_time", "updater", "members") def get_members(self, obj): - return list(RoleUser.objects.filter(role_id=obj.id).values_list("username", flat=True)) + return [ + {"username": one.username, "readonly": one.readonly} for one in RoleUser.objects.filter(role_id=obj.id) + ] class RatingManagerListSLZ(BaseRatingMangerSLZ): @@ -234,7 +241,7 @@ def __init__(self, *args, **kwargs): # 查询role_users for one in RoleUser.objects.filter(role_id__id=role_ids): - self.role_users[one.role_id].append(one.username) + self.role_users[one.role_id].append({"username": one.username, "readonly": one.readonly}) # 查询role_subset_managers for one in RoleRelation.objects.filter(parent_id__in=role_ids): @@ -249,7 +256,7 @@ def get_members(self, obj): def get_is_member(self, obj): username = self.context["request"].user.username - return username in self.role_users[obj.id] + return username in {one["username"] for one in self.role_users[obj.id]} def get_has_subset_manager(self, obj): # 查询是否有子集管理员 diff --git a/saas/backend/apps/role/tasks.py b/saas/backend/apps/role/tasks.py index f724fd506..5d85e3b49 100644 --- a/saas/backend/apps/role/tasks.py +++ b/saas/backend/apps/role/tasks.py @@ -70,7 +70,7 @@ def sync_system_manager(): "name": f"{system.name}", "name_en": f"{system.name_en}", "description": "", - "members": members, + "members": [{"username": username} for username in members], "authorization_scopes": [{"system_id": system_id, "actions": [{"id": "*", "related_resource_types": []}]}], "subject_scopes": [{"type": "*", "id": "*"}], } diff --git a/saas/backend/biz/handover.py b/saas/backend/biz/handover.py index c51e9f9da..716104cb1 100644 --- a/saas/backend/biz/handover.py +++ b/saas/backend/biz/handover.py @@ -138,7 +138,7 @@ def grant_permission(self): return members.append(self.handover_to) self.biz.modify_system_manager_members(role_id=self.role_id, members=members) - elif self.role_type == RoleType.RATING_MANAGER.value: + elif self.role_type in [RoleType.RATING_MANAGER.value, RoleType.SUBSET_MANAGER.value]: self.biz.add_grade_manager_members(self.role_id, [self.handover_to]) if self.role_type != RoleType.SUPER_MANAGER.value: @@ -160,7 +160,7 @@ def revoke_permission(self): members = self._get_system_manager_members() members.remove(self.handover_from) self.biz.modify_system_manager_members(role_id=self.role_id, members=members) - elif self.role_type == RoleType.RATING_MANAGER.value: + elif self.role_type in [RoleType.RATING_MANAGER.value, RoleType.SUBSET_MANAGER.value]: self.biz.delete_member(self.role_id, self.handover_from) def _get_system_manager_members(self) -> List[str]: diff --git a/saas/backend/biz/role.py b/saas/backend/biz/role.py index 9f3187b71..612fa2cef 100644 --- a/saas/backend/biz/role.py +++ b/saas/backend/biz/role.py @@ -121,7 +121,6 @@ class RoleBiz: list_paging_user_role = RoleService.__dict__["list_paging_user_role"] list_user_role_for_system = RoleService.__dict__["list_user_role_for_system"] list_paging_role_for_system = RoleService.__dict__["list_paging_role_for_system"] - add_grade_manager_members = RoleService.__dict__["add_grade_manager_members"] list_subject_scope = RoleService.__dict__["list_subject_scope"] list_auth_scope = RoleService.__dict__["list_auth_scope"] list_by_ids = RoleService.__dict__["list_by_ids"] @@ -226,12 +225,32 @@ def delete_super_manager_member(self, username: str): """删除超级管理员成员""" self.svc.delete_super_manager_member(username) + def add_grade_manager_members(self, role_id: int, usernames: List[str]): + """ + 添加分级管理员成员 + """ + self.svc.add_grade_manager_members(role_id, usernames) + + self.svc.sync_subset_manager_members(role_id) + + def delete_grade_manager_member(self, role_id: int, usernames: List[str]): + """ + 批量删除分级管理员成员 + """ + RoleUser.objects.delete_grade_manager_member(role_id, usernames) + + self.svc.sync_subset_manager_members(role_id) + def delete_member(self, role_id: int, username: str): """ 角色删除成员 """ self.svc.delete_member(role_id, username) + # 同步删除子集管理员的成员 + for one in RoleRelation.objects.filter(parent_id=role_id): + self.svc.delete_member(one.role_id, username) + def update_super_manager_member_system_permission(self, username: str, need_sync_backend_role: bool): """ 更新超级管理员成员的系统权限 diff --git a/saas/backend/service/role.py b/saas/backend/service/role.py index c72dc3431..9432b73ad 100644 --- a/saas/backend/service/role.py +++ b/saas/backend/service/role.py @@ -96,6 +96,10 @@ class UserRoleMember(BaseModel): members: list +class RoleMember(BaseModel): + username: str + + class RoleInfo(PartialModel): code: str = "" name: str @@ -103,10 +107,14 @@ class RoleInfo(PartialModel): description: str type: str = RoleType.RATING_MANAGER.value - members: List[str] + members: List[RoleMember] subject_scopes: List[Subject] = [] authorization_scopes: List[AuthScopeSystem] = [] + @property + def member_usernames(self): + return [member.username for member in self.members] + class CommonAction(BaseModel): id: int = 0 @@ -159,10 +167,9 @@ def list_paging_user_role(self, user_id: str, limit: int, offset: int) -> Tuple[ role_ids = list(queryset.values_list("role_id", flat=True)[offset : offset + limit]) return count, self.list_by_ids(role_ids) - def list_members_by_role_id(self, role_id: int): + def list_members_by_role_id(self, role_id: int) -> List[str]: """查询指定角色的成员列表""" - members = list(RoleUser.objects.filter(role_id=role_id).values_list("username", flat=True)) - return members + return list(RoleUser.objects.filter(role_id=role_id).values_list("username", flat=True)) def list_by_ids(self, role_ids: List[int]) -> List[UserRole]: roles = Role.objects.filter(id__in=role_ids) @@ -178,7 +185,7 @@ def list_by_ids(self, role_ids: List[int]) -> List[UserRole]: sorted_data = sorted(data, key=lambda r: sort_index.index(r.type)) return sorted_data - def create(self, info: RoleInfo, creator: str) -> Role: + def create(self, info: RoleInfo, creator: str, add_member=True) -> Role: """创建Role""" with transaction.atomic(): role = Role( @@ -192,7 +199,9 @@ def create(self, info: RoleInfo, creator: str) -> Role: ) role.save(force_insert=True) - self._add_members(role.id, info.members) + if add_member: + self._add_members(role.id, info.member_usernames) + self._create_role_scope(role.id, info.subject_scopes, info.authorization_scopes) return role @@ -201,15 +210,31 @@ def create_subset_manager(self, grade_manager: Role, info: RoleInfo, creator: st """ 创建子集管理员 """ + # 创建子集管理员的同时加入分级管理员的人员名单 + grade_manager_members = self.list_members_by_role_id(grade_manager.id) + with transaction.atomic(): - role = self.create(info, creator) + role = self.create(info, creator, add_member=False) RoleRelation.objects.create(parent_id=grade_manager.id, role_id=role.id) + role_users = [ # 从上级分级管理员继承的成员是readonly的 + RoleUser(role_id=role.id, username=username, readonly=True) for username in grade_manager_members + ] + role_users.extend( + [ + RoleUser(role_id=role.id, username=username, readonly=False) + for username in info.member_usernames + if username not in grade_manager_members + ] + ) + if role_users: + RoleUser.objects.bulk_create(role_users, batch_size=100) + return role - def _add_members(self, role_id: int, usernames: List[str]): + def _add_members(self, role_id: int, members: List[str]): """Role增加成员""" - role_users = [RoleUser(role_id=role_id, username=username) for username in usernames] + role_users = [RoleUser(role_id=role_id, username=username) for username in members] if role_users: RoleUser.objects.bulk_create(role_users, batch_size=100) @@ -260,7 +285,9 @@ def update(self, role: Role, info: RoleInfo, updater: str): # 分级管理员成员 if "members" in update_fields: - self._update_members(role, info.members) + self._update_members(role, info.member_usernames) + if role.type == RoleType.RATING_MANAGER.value: + self.sync_subset_manager_members(role.id) # 可授权的权限范围 if "authorization_scopes" in update_fields: @@ -271,17 +298,30 @@ def update(self, role: Role, info: RoleInfo, updater: str): self.update_role_subject_scope(role.id, info.subject_scopes) def _update_members(self, role: Role, members: List[str], need_sync_backend_role: bool = False): - """更新Role成员""" + """ + 更新Role成员 + + NOTE: 只变更readonly为False的成员 + """ role_id = role.id - new_members = sorted(set(members), key=members.index) # 去重,但保持顺序不变 - old_members = list(RoleUser.objects.filter(role_id=role_id).values_list("username", flat=True)) + + # 查询用户的已设置为readonly的成员 + readonly_usernames = set( + RoleUser.objects.filter(role_id=role_id, readonly=True).values_list("username", flat=True) + ) + + # NOTE readonly的成员只能通过其它逻辑处理 + update_usernames = [username for username in members if username not in readonly_usernames] + + new_members = sorted(set(update_usernames), key=update_usernames.index) # 去重,但保持顺序不变 + old_members = list(RoleUser.objects.filter(role_id=role_id, readonly=False).values_list("username", flat=True)) # 由于需要保持顺序不变,所以需要看是否变化了包括顺序,如果改变了则直接全删除,然后全添加 if new_members != old_members: # 全删除 - RoleUser.objects.filter(role_id=role_id).delete() + RoleUser.objects.filter(role_id=role_id, readonly=False).delete() # 重新全部添加 - self._add_members(role_id, new_members) + self._add_members(role_id, [RoleMember(username=one) for one in new_members]) # 同步后端的role信息 if need_sync_backend_role: @@ -292,6 +332,35 @@ def _update_members(self, role: Role, members: List[str], need_sync_backend_role self._create_backend_role_member(role, list(created_members)) self._delete_backend_role_member(role, list(deleted_members)) + def sync_subset_manager_members(self, parent_id: int): + """ + 同步子集管理员成员 + """ + grade_manager_members = self.list_members_by_role_id(parent_id) + for relation in RoleRelation.objects.filter(parent_id=parent_id): + subset_manager_id = relation.role_id + subset_manager_members = List( + RoleUser.objects.filter(role_id=subset_manager_id, readonly=False).values_list("username", fields=True) + ) + + role_users = [ # 从上级分级管理员继承的成员是readonly的 + RoleUser(role_id=subset_manager_id, username=username, readonly=True) + for username in grade_manager_members + ] + role_users.extend( + [ + RoleUser(role_id=subset_manager_id, username=username, readonly=False) + for username in subset_manager_members + if username not in grade_manager_members + ] + ) + + # 全删除 + RoleUser.objects.filter(role_id=subset_manager_id).delete() + # 重新全部添加 + if role_users: + RoleUser.objects.bulk_create(role_users, batch_size=100) + def _create_backend_role_member(self, role: Role, created_members: List[str]): """创建后端role成员""" if created_members: diff --git a/saas/backend/trans/role.py b/saas/backend/trans/role.py index 797a9dd3e..4d94673b4 100644 --- a/saas/backend/trans/role.py +++ b/saas/backend/trans/role.py @@ -34,7 +34,11 @@ def from_role_data( data: { "name": str, "description": str, - "members": List[str], + "members": List[ + { + "username": str + } + ], "subject_scopes": [ { "type": str, From ccd319fe3e0a274dde87056b169514587ab833ff Mon Sep 17 00:00:00 2001 From: zhu327 Date: Thu, 27 Oct 2022 12:02:40 +0800 Subject: [PATCH 015/644] feat: subset manager inherit subject scopes --- .../0013_role_inherit_subject_scope.py | 18 +++++++++++++ saas/backend/apps/role/models.py | 11 +------- saas/backend/apps/role/serializers.py | 27 +++++++++++++++++++ saas/backend/apps/role/views/role.py | 24 ++++++++++++----- saas/backend/biz/role.py | 10 ++++++- saas/backend/service/role.py | 4 +++ 6 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 saas/backend/apps/role/migrations/0013_role_inherit_subject_scope.py diff --git a/saas/backend/apps/role/migrations/0013_role_inherit_subject_scope.py b/saas/backend/apps/role/migrations/0013_role_inherit_subject_scope.py new file mode 100644 index 000000000..e76abd92b --- /dev/null +++ b/saas/backend/apps/role/migrations/0013_role_inherit_subject_scope.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.28 on 2022-10-27 03:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('role', '0012_auto_20221026_1609'), + ] + + operations = [ + migrations.AddField( + model_name='role', + name='inherit_subject_scope', + field=models.BooleanField(default=False, verbose_name='继承人员管理范围'), + ), + ] diff --git a/saas/backend/apps/role/models.py b/saas/backend/apps/role/models.py index 6986cef5e..730de09ba 100644 --- a/saas/backend/apps/role/models.py +++ b/saas/backend/apps/role/models.py @@ -32,6 +32,7 @@ class Role(BaseModel): description = models.CharField("描述", max_length=255, default="") type = models.CharField("类型", max_length=32, choices=RoleType.get_choices()) code = models.CharField("标志", max_length=64, default="") + inherit_subject_scope = models.BooleanField("继承人员管理范围", default=False) class Meta: verbose_name = "角色" @@ -312,13 +313,3 @@ def delete(self): @property def permissions(self): return [] - - -""" -子集管理员同步分级管理员成员: - -1. 在RoleUser上增加readonly字段 -2. 子集管理员创建时默认需要查询分级管理员的成员, 并加入到成员中, 设置readonly=True -3. 子集管理员删除成员时, 不能删除readonly=True的成员 -4. 分级管理员的成员变更时, 需要同步变更子集管理员中readonly=True的成员 -""" diff --git a/saas/backend/apps/role/serializers.py b/saas/backend/apps/role/serializers.py index 25b078ca2..f9cccb936 100644 --- a/saas/backend/apps/role/serializers.py +++ b/saas/backend/apps/role/serializers.py @@ -413,3 +413,30 @@ class AuthorizedSubjectsSLZ(serializers.Serializer): type = serializers.CharField(label="Subject对象类型") id = serializers.CharField(label="Subject对象ID") name = serializers.CharField(label="Subject对象名称") + + +class SubsetMangerCreateSLZ(RatingMangerCreateSLZ): + subject_scopes = serializers.ListField(label="授权对象", child=RoleScopeSubjectSLZ(label="授权对象"), allow_empty=True) + inherit_subject_scope = serializers.BooleanField(label="继承分级管理员人员管理范围") + + def validate(self, data): + data = super().validate(data) + if not data["inherit_subject_scope"] and not data["subject_scopes"]: + raise serializers.ValidationError({"subject_scopes": ["must not be empty"]}) + return data + + +class SubsetMangerDetailSLZ(RatingMangerDetailSLZ): + class Meta: + model = Role + fields = ( + "id", + "name", + "description", + "updated_time", + "creator", + "members", + "authorization_scopes", + "inherit_subject_scope", + "subject_scopes", + ) diff --git a/saas/backend/apps/role/views/role.py b/saas/backend/apps/role/views/role.py index 7e8ebb482..e7d3e6107 100644 --- a/saas/backend/apps/role/views/role.py +++ b/saas/backend/apps/role/views/role.py @@ -60,6 +60,8 @@ RoleGroupMembersRenewSLZ, RoleIdSLZ, RoleScopeSubjectSLZ, + SubsetMangerCreateSLZ, + SubsetMangerDetailSLZ, SuperManagerMemberDeleteSLZ, SuperManagerMemberSLZ, SystemManagerMemberUpdateSLZ, @@ -725,13 +727,13 @@ def list(self, request, *args, **kwargs): ) def retrieve(self, request, *args, **kwargs): role = self.get_object() - serializer = RatingMangerDetailSLZ(instance=role) + serializer = SubsetMangerDetailSLZ(instance=role) data = serializer.data return Response(data) @swagger_auto_schema( operation_description="创建子集管理员", - request_body=RatingMangerCreateSLZ(label="创建子集管理员"), + request_body=SubsetMangerCreateSLZ(label="创建子集管理员"), responses={status.HTTP_201_CREATED: RoleIdSLZ(label="子集管理员ID")}, tags=["role"], ) @@ -740,7 +742,7 @@ def create(self, request, *args, **kwargs): """ 创建子集管理员 """ - serializer = RatingMangerCreateSLZ(data=request.data) + serializer = SubsetMangerCreateSLZ(data=request.data) serializer.is_valid(raise_exception=True) user_id = request.user.username @@ -756,8 +758,12 @@ def create(self, request, *args, **kwargs): # 检查授权范围 self.role_check_biz.check_subset_manager_auth_scope(grade_manager, info.authorization_scopes) - # 检查人员范围 - self.role_check_biz.check_subset_manager_subject_scope(grade_manager, info.subject_scopes) + if not info.inherit_subject_scope: + # 检查人员范围 + self.role_check_biz.check_subset_manager_subject_scope(grade_manager, info.subject_scopes) + else: + subject_scopes = self.biz.list_subject_scope(grade_manager.id) + info.subject_scopes = subject_scopes # 创建子集管理员, 并创建分级管理员与子集管理员的关系 role = self.biz.create_subset_manager(grade_manager, info, user_id) @@ -802,8 +808,12 @@ def update(self, request, *args, **kwargs): # 检查授权范围 self.role_check_biz.check_subset_manager_auth_scope(grade_manager, info.authorization_scopes) - # 检查人员范围 - self.role_check_biz.check_subset_manager_subject_scope(grade_manager, info.subject_scopes) + if not info.inherit_subject_scope: + # 检查人员范围 + self.role_check_biz.check_subset_manager_subject_scope(grade_manager, info.subject_scopes) + else: + subject_scopes = self.biz.list_subject_scope(grade_manager.id) + info.subject_scopes = subject_scopes self.biz.update(role, info, user_id) diff --git a/saas/backend/biz/role.py b/saas/backend/biz/role.py index 612fa2cef..db24e3ab3 100644 --- a/saas/backend/biz/role.py +++ b/saas/backend/biz/role.py @@ -199,7 +199,15 @@ def update(self, role: Role, info: RoleInfoBean, updater: str): """ 更新分级管理员 """ - return self.svc.update(role, info, updater) + self.svc.update(role, info, updater) + + # 同步更新自动跟随的子集管理员的人员选择范围 + if role.type == RoleType.RATING_MANAGER.value and "subject_scopes" in info.get_partial_fields(): + subject_scopes = self.svc.list_subject_scope(role.id) + + subset_manager_ids = list(RoleRelation.objects.filter(parent_id=role.id).values_list("role_id", flat=True)) + for subset_manager in Role.objects.filter(id__in=subset_manager_ids, inherit_subject_scope=True): + self.svc.update_role_subject_scope(subset_manager.id, subject_scopes) def create_subset_manager(self, grade_manager: Role, info: RoleInfoBean, creator: str) -> Role: """ diff --git a/saas/backend/service/role.py b/saas/backend/service/role.py index 9432b73ad..3583e8599 100644 --- a/saas/backend/service/role.py +++ b/saas/backend/service/role.py @@ -106,6 +106,7 @@ class RoleInfo(PartialModel): name_en: str = "" description: str type: str = RoleType.RATING_MANAGER.value + inherit_subject_scope: bool = False members: List[RoleMember] subject_scopes: List[Subject] = [] @@ -194,6 +195,7 @@ def create(self, info: RoleInfo, creator: str, add_member=True) -> Role: name_en=info.name_en, description=info.description, type=info.type, + inherit_subject_scope=info.inherit_subject_scope, creator=creator, updater=creator, ) @@ -279,6 +281,8 @@ def update(self, role: Role, info: RoleInfo, updater: str): role.name_en = info.name_en if "description" in update_fields: role.description = info.description + if role.type == RoleType.SUBSET_MANAGER.value and "inherit_subject_scope" in update_fields: + role.inherit_subject_scope = info.inherit_subject_scope role.updater = updater role.save() From becd9f71a5ddec231d15bcb6cb7a1400ec12a49c Mon Sep 17 00:00:00 2001 From: zhu327 Date: Thu, 27 Oct 2022 17:34:05 +0800 Subject: [PATCH 016/644] feat: add grade manager sync permission group --- .../api/management/v1/views/grade_manager.py | 10 ++ ...019_1610.py => 0011_auto_20221027_1731.py} | 24 ++- .../migrations/0012_auto_20221026_1609.py | 23 --- .../0013_role_inherit_subject_scope.py | 18 --- saas/backend/apps/role/models.py | 2 + saas/backend/apps/role/serializers.py | 3 + saas/backend/apps/role/views/role.py | 18 +++ saas/backend/apps/user/tasks.py | 3 + saas/backend/biz/group.py | 152 +++++++++++++++++- saas/backend/biz/handover.py | 5 + saas/backend/service/group.py | 6 +- saas/backend/service/role.py | 1 + 12 files changed, 217 insertions(+), 48 deletions(-) rename saas/backend/apps/role/migrations/{0011_auto_20221019_1610.py => 0011_auto_20221027_1731.py} (59%) delete mode 100644 saas/backend/apps/role/migrations/0012_auto_20221026_1609.py delete mode 100644 saas/backend/apps/role/migrations/0013_role_inherit_subject_scope.py diff --git a/saas/backend/api/management/v1/views/grade_manager.py b/saas/backend/api/management/v1/views/grade_manager.py index 1b88bab82..005ee8b30 100644 --- a/saas/backend/api/management/v1/views/grade_manager.py +++ b/saas/backend/api/management/v1/views/grade_manager.py @@ -35,6 +35,7 @@ from backend.apps.role.models import Role, RoleSource from backend.apps.role.serializers import RoleIdSLZ from backend.audit.audit import audit_context_setter, view_audit_decorator +from backend.biz.group import GroupBiz from backend.biz.role import RoleBiz, RoleCheckBiz from backend.common.pagination import CustomPageNumberPagination from backend.service.constants import RoleSourceTypeEnum, RoleType @@ -184,6 +185,7 @@ class ManagementGradeManagerMemberViewSet(GenericViewSet): queryset = Role.objects.filter(type=RoleType.RATING_MANAGER.value).order_by("-updated_time") biz = RoleBiz() + group_biz = GroupBiz() role_check_biz = RoleCheckBiz() @swagger_auto_schema( @@ -216,6 +218,10 @@ def create(self, request, *args, **kwargs): # 批量添加成员(添加时去重) self.biz.add_grade_manager_members(role.id, members) + # 同步权限用户组成员 + if role.sync_perm: + self.group_biz.update_sync_perm_group_by_role(role, request.user.username, sync_members=True) + # 审计 audit_context_setter(role=role, members=members) @@ -237,6 +243,10 @@ def destroy(self, request, *args, **kwargs): members = list(set(serializer.validated_data["members"])) self.biz.delete_grade_manager_member(role.id, members) + # 同步权限用户组成员 + if role.sync_perm: + self.group_biz.update_sync_perm_group_by_role(role, request.user.username, sync_members=True) + # 审计 audit_context_setter(role=role, members=members) diff --git a/saas/backend/apps/role/migrations/0011_auto_20221019_1610.py b/saas/backend/apps/role/migrations/0011_auto_20221027_1731.py similarity index 59% rename from saas/backend/apps/role/migrations/0011_auto_20221019_1610.py rename to saas/backend/apps/role/migrations/0011_auto_20221027_1731.py index 08de837cb..4f2ff6d95 100644 --- a/saas/backend/apps/role/migrations/0011_auto_20221019_1610.py +++ b/saas/backend/apps/role/migrations/0011_auto_20221027_1731.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.28 on 2022-10-19 08:10 +# Generated by Django 2.2.28 on 2022-10-27 09:31 from django.db import migrations, models @@ -10,6 +10,26 @@ class Migration(migrations.Migration): ] operations = [ + migrations.AddField( + model_name='role', + name='inherit_subject_scope', + field=models.BooleanField(default=False, verbose_name='继承人员管理范围'), + ), + migrations.AddField( + model_name='role', + name='sync_perm', + field=models.BooleanField(default=False, verbose_name='同步角色权限'), + ), + migrations.AddField( + model_name='rolerelatedobject', + name='sync_perm', + field=models.BooleanField(default=False, verbose_name='跟随角色同步'), + ), + migrations.AddField( + model_name='roleuser', + name='readonly', + field=models.BooleanField(default=False, verbose_name='用户组只读标识'), + ), migrations.AlterField( model_name='role', name='type', @@ -24,7 +44,7 @@ class Migration(migrations.Migration): ('created_time', models.DateTimeField(auto_now_add=True)), ('updated_time', models.DateTimeField(auto_now=True)), ('parent_id', models.IntegerField(verbose_name='父级角色ID')), - ('role_id', models.IntegerField(verbose_name='角色ID')), + ('role_id', models.IntegerField(db_index=True, verbose_name='角色ID')), ], options={ 'verbose_name': '角色关系', diff --git a/saas/backend/apps/role/migrations/0012_auto_20221026_1609.py b/saas/backend/apps/role/migrations/0012_auto_20221026_1609.py deleted file mode 100644 index 09c918c4f..000000000 --- a/saas/backend/apps/role/migrations/0012_auto_20221026_1609.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 2.2.28 on 2022-10-26 08:09 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('role', '0011_auto_20221019_1610'), - ] - - operations = [ - migrations.AddField( - model_name='roleuser', - name='readonly', - field=models.BooleanField(default=False, verbose_name='用户组只读标识'), - ), - migrations.AlterField( - model_name='rolerelation', - name='role_id', - field=models.IntegerField(db_index=True, verbose_name='角色ID'), - ), - ] diff --git a/saas/backend/apps/role/migrations/0013_role_inherit_subject_scope.py b/saas/backend/apps/role/migrations/0013_role_inherit_subject_scope.py deleted file mode 100644 index e76abd92b..000000000 --- a/saas/backend/apps/role/migrations/0013_role_inherit_subject_scope.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.28 on 2022-10-27 03:30 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('role', '0012_auto_20221026_1609'), - ] - - operations = [ - migrations.AddField( - model_name='role', - name='inherit_subject_scope', - field=models.BooleanField(default=False, verbose_name='继承人员管理范围'), - ), - ] diff --git a/saas/backend/apps/role/models.py b/saas/backend/apps/role/models.py index 730de09ba..1e2908943 100644 --- a/saas/backend/apps/role/models.py +++ b/saas/backend/apps/role/models.py @@ -33,6 +33,7 @@ class Role(BaseModel): type = models.CharField("类型", max_length=32, choices=RoleType.get_choices()) code = models.CharField("标志", max_length=64, default="") inherit_subject_scope = models.BooleanField("继承人员管理范围", default=False) + sync_perm = models.BooleanField("同步角色权限", default=False) class Meta: verbose_name = "角色" @@ -212,6 +213,7 @@ class RoleRelatedObject(BaseModel): role_id = models.IntegerField("角色ID") object_type = models.CharField("对象类型", max_length=32, choices=RoleRelatedObjectType.get_choices()) object_id = models.IntegerField("对象ID") + sync_perm = models.BooleanField("跟随角色同步", default=False) objects = RoleRelatedObjectManager() diff --git a/saas/backend/apps/role/serializers.py b/saas/backend/apps/role/serializers.py index f9cccb936..a7d4babd0 100644 --- a/saas/backend/apps/role/serializers.py +++ b/saas/backend/apps/role/serializers.py @@ -131,6 +131,7 @@ class RatingMangerCreateSLZ(RatingMangerBaseInfoSZL): label="系统操作", child=RoleScopeAuthorizationSLZ(label="系统操作"), allow_empty=False ) subject_scopes = serializers.ListField(label="授权对象", child=RoleScopeSubjectSLZ(label="授权对象"), allow_empty=False) + sync_perm = serializers.BooleanField(label="同步分级管理员权限到用户组", default=False) def validate(self, data): if len(data["authorization_scopes"]) != len({sys["system_id"] for sys in data["authorization_scopes"]}): @@ -196,6 +197,7 @@ class Meta: "members", "authorization_scopes", "subject_scopes", + "sync_perm", ) @@ -283,6 +285,7 @@ class Meta: "members", "authorization_scopes", "subject_scopes", + "sync_perm", ) def get_authorization_scopes(self, obj): diff --git a/saas/backend/apps/role/views/role.py b/saas/backend/apps/role/views/role.py index e7d3e6107..817e3381a 100644 --- a/saas/backend/apps/role/views/role.py +++ b/saas/backend/apps/role/views/role.py @@ -104,6 +104,7 @@ class GradeManagerViewSet(mixins.ListModelMixin, GenericViewSet): filterset_class = RatingMangerFilter biz = RoleBiz() + group_biz = GroupBiz() role_check_biz = RoleCheckBiz() role_trans = RoleTrans() @@ -136,6 +137,10 @@ def create(self, request, *args, **kwargs): info = self.role_trans.from_role_data(data) role = self.biz.create_grade_manager(info, user_id) + # 创建同步权限用户组 + if info.sync_perm: + self.group_biz.create_sync_perm_group_by_role(role, user_id) + audit_context_setter(role=role) return Response({"id": role.id}, status=status.HTTP_201_CREATED) @@ -192,6 +197,9 @@ def update(self, request, *args, **kwargs): info = self.role_trans.from_role_data(data, old_system_policy_list=old_system_policy_list) self.biz.update(role, info, user_id) + # 更新同步权限用户组信息 + self.group_biz.update_sync_perm_group_by_role(self.get_object(), user_id, sync_members=True, sync_prem=True) + audit_context_setter(role=role) return Response({}) @@ -234,6 +242,9 @@ def partial_update(self, request, *args, **kwargs): self.biz.update(role, RoleInfoBean.from_partial_data(data), user_id) + # 更新同步权限用户组信息 + self.group_biz.update_sync_perm_group_by_role(self.get_object(), user_id, sync_members=True) + audit_context_setter(role=role) return Response({}) @@ -245,6 +256,7 @@ class RoleMemberView(views.APIView): """ biz = RoleBiz() + group_biz = GroupBiz() @swagger_auto_schema( operation_description="退出角色", @@ -256,6 +268,12 @@ def delete(self, request, *args, **kwargs): role_id = kwargs["id"] user_id = request.user.username self.biz.delete_member(int(role_id), user_id) + + # 更新同步权限用户组信息 + role = Role.objects.filter(id=role_id).first() + if role and role.sync_perm: + self.group_biz.update_sync_perm_group_by_role(role, user_id, sync_members=True) + audit_context_setter(role_id=role_id) return Response({}) diff --git a/saas/backend/apps/user/tasks.py b/saas/backend/apps/user/tasks.py index 3c95011ec..27321cbcc 100644 --- a/saas/backend/apps/user/tasks.py +++ b/saas/backend/apps/user/tasks.py @@ -261,6 +261,9 @@ def _cleanup_role(self): ): self.role_biz.delete_member(role.id, username) + if role.sync_perm: + self.group_biz.update_sync_perm_group_by_role(role, "admin", sync_members=True) + elif role.type == RoleType.SUPER_MANAGER.value: self.role_biz.delete_super_manager_member(username) diff --git a/saas/backend/biz/group.py b/saas/backend/biz/group.py index 1a0917d97..bed512e9b 100644 --- a/saas/backend/biz/group.py +++ b/saas/backend/biz/group.py @@ -8,6 +8,7 @@ an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ +from collections import defaultdict from datetime import datetime from typing import Dict, List, Optional, Tuple @@ -38,7 +39,7 @@ from backend.service.group_saas_attribute import GroupAttributeService from backend.service.models import Policy, Subject from backend.service.policy.query import PolicyQueryService -from backend.service.role import RoleService, UserRole +from backend.service.role import AuthScopeSystem, RoleService, UserRole from backend.service.system import SystemService from backend.service.template import TemplateService from backend.util.time import utc_string_to_local @@ -151,7 +152,7 @@ def create_and_add_members( 创建用户组 """ with transaction.atomic(): - group = self.group_svc.create(name, description, creator) + group = self.group_svc.create(GroupCreate(name=name, description=description), creator) RoleRelatedObject.objects.create_group_relation(role_id, group.id) if subjects: self.group_svc.add_members(group.id, subjects, expired_at) @@ -567,6 +568,153 @@ def search_member_by_keyword(self, group_id: int, keyword: str) -> List[GroupMem return hit_members + def create_sync_perm_group_by_role(self, role: Role, creator: str): + """ + 创建与分级管理员授权范围同步的用户组 + + 同步数据: + 1. 分级管理员授权范围 -> 用户组的自定义权限 + 2. 分级管理员的成员 -> 用户组的成员 (过期时间永久) + """ + + # 创建用户组, 加成员 + with transaction.atomic(): + + # TODO 用户组的名字需要定义 + group = self.group_svc.create( + GroupCreate(name=role.name, description=Role.description, readonly=True), creator + ) + RoleRelatedObject.objects.create( + role_id=role.id, object_type=RoleRelatedObjectType.GROUP.value, object_id=group.id, sync_perm=True + ) + members = self.role_svc.list_members_by_role_id(role.id) + if members: + self.group_svc.add_members( + group.id, + [Subject(type=SubjectType.USER.value, id=username) for username in members], + PERMANENT_SECONDS, + ) + + # 查询角色的授权范围 + auth_scopes = self.role_svc.list_auth_scope(role.id) + + # 转换成group授权的自定义权限 + templates = [] + for scope in auth_scopes: + templates.append( + GroupTemplateGrantBean( + system_id=scope.system_id, template_id=0, policies=parse_obj_as(List[PolicyBean], scope.actions) + ) + ) + + self.grant(role, group, templates) + + def update_sync_perm_group_by_role( + self, role: Role, user_id: str, sync_members: bool = False, sync_prem: bool = False + ): + """ + 同步用户组的成员或权限 + """ + # 查询role的同步权限用户组 + relation = ( + RoleRelatedObject.objects.filter( + role_id=role.id, object_type=RoleRelatedObjectType.GROUP.value, sync_perm=True + ) + .only("object_id") + .first() + ) + + # 1. 如果role sync_perm 被修改为False, 同时存在同步权限用户组, 需要删除用户组 + if relation and not role.sync_perm: + self.delete(relation.object_id) + return + + # 2. 如果role sync_perm 被修改为True, 同时不存在同步权限用户组, 需要创建同步权限用户组 + if not relation and role.sync_perm: + self.create_sync_perm_group_by_role(role, user_id) + return + + # 3. 不存在同步权限用户组, 不需要处理 + if not relation and not role.sync_perm: + return + + # 4. 更新用户组的成员 + if sync_members: + self._update_sync_perm_group_member(role, relation.object_id) + + # 5. 更新用户组权限 + if sync_prem: + self._update_sync_perm_group_permission(role, relation.object_id) + + def _update_sync_perm_group_permission(self, role: Role, group_id: int): + # 查询角色的授权范围 + auth_scopes = self.role_svc.list_auth_scope(role.id) + + # 删除用户组超出授权范围的权限 + self._delete_sync_perm_group_permission_out_of_scope(group_id, auth_scopes) + + # 重新授权 + templates = [] + for scope in auth_scopes: + templates.append( + GroupTemplateGrantBean( + system_id=scope.system_id, template_id=0, policies=parse_obj_as(List[PolicyBean], scope.actions) + ) + ) + + group = Group.objects.get(id=group_id) + self.grant(role, group, templates) + + def _delete_sync_perm_group_permission_out_of_scope(self, group_id, auth_scopes: List[AuthScopeSystem]): + system_action_ids = defaultdict(list) + action_policy_id = {} + + queryset = PolicyModel.objects.filter(subject_type=SubjectType.GROUP.value, subject_id=str(group_id)).only( + "id", "system_id", "action_id" + ) + for p in queryset: + system_action_ids[p.system_id].append(p.action_id) + action_policy_id[(p.system_id, p.action_id)] = p.id + + subject = Subject(type=SubjectType.GROUP.value, id=str(group_id)) + + # 删除系统部分被删除的操作权限 + for scope in auth_scopes: + if scope.system_id not in system_action_ids: + continue + + delete_actions = set(system_action_ids[scope.system_id]) - {a.id for a in scope.actions} + policy_ids = [action_policy_id[(scope.system_id, action_id)] for action_id in delete_actions] + if policy_ids: + self.policy_operation_biz.delete_by_ids(scope.system_id, subject, policy_ids) + + # 删除系统被整体删除的权限 + auth_scope_systems = {scope.system_id for scope in auth_scopes} + for system_id in system_action_ids.keys(): + if system_id in auth_scope_systems: + continue + + policy_ids = [action_policy_id[(system_id, action_id)] for action_id in system_action_ids[system_id]] + if policy_ids: + self.policy_operation_biz.delete_by_ids(scope.system_id, subject, policy_ids) + + def _update_sync_perm_group_member(self, role: Role, group_id: int): + role_members = self.role_svc.list_members_by_role_id(role.id) + group_members = self.group_svc.list_all_group_member(group_id) + + role_subjects = {Subject(type=SubjectType.USER.value, id=username) for username in role_members} + group_subjects = {Subject.parse_obj(s) for s in group_members} + + # 需要新增的成员 + add_subjects = list(role_subjects - group_subjects) + if add_subjects: + self.group_svc.add_members(group_id, add_subjects, PERMANENT_SECONDS) + + # 需要删除的成员 + del_subjects = list(group_subjects - role_subjects) + if del_subjects: + self.group_svc.remove_members(group_id, del_subjects) + class GroupCheckBiz: svc = GroupService() diff --git a/saas/backend/biz/handover.py b/saas/backend/biz/handover.py index 716104cb1..a722beb13 100644 --- a/saas/backend/biz/handover.py +++ b/saas/backend/biz/handover.py @@ -118,6 +118,7 @@ def revoke_permission(self): class RoleHandoverHandler(BaseHandoverHandler): biz = RoleBiz() + group_biz = GroupBiz() def __init__(self, handover_task_id, handover_from, handover_to, object_detail): self.handover_task_id = handover_task_id @@ -141,6 +142,10 @@ def grant_permission(self): elif self.role_type in [RoleType.RATING_MANAGER.value, RoleType.SUBSET_MANAGER.value]: self.biz.add_grade_manager_members(self.role_id, [self.handover_to]) + role = Role.objects.get(id=self.role_id) + if role.sync_perm: + self.group_biz.update_sync_perm_group_by_role(role, "admin", sync_members=True) + if self.role_type != RoleType.SUPER_MANAGER.value: role = Role.objects.get(id=self.role_id) diff --git a/saas/backend/service/group.py b/saas/backend/service/group.py index ee6a2e3d4..d8f9c7a37 100644 --- a/saas/backend/service/group.py +++ b/saas/backend/service/group.py @@ -58,15 +58,15 @@ class GroupMemberExpiredAt(Subject): class GroupService: - def create(self, name: str, description: str, creator: str) -> Group: + def create(self, info: GroupCreate, creator: str) -> Group: """ 创建用户组 """ - group = Group(name=name, description=description, creator=creator) + group = Group(name=info.name, description=info.description, readonly=info.readonly, creator=creator) group.save(force_insert=True) # 创建后端的用户组 - iam.create_subjects([{"type": SubjectType.GROUP.value, "id": str(group.id), "name": name}]) + iam.create_subjects([{"type": SubjectType.GROUP.value, "id": str(group.id), "name": info.name}]) return group diff --git a/saas/backend/service/role.py b/saas/backend/service/role.py index 3583e8599..1e09a3908 100644 --- a/saas/backend/service/role.py +++ b/saas/backend/service/role.py @@ -107,6 +107,7 @@ class RoleInfo(PartialModel): description: str type: str = RoleType.RATING_MANAGER.value inherit_subject_scope: bool = False + sync_perm: bool = False members: List[RoleMember] subject_scopes: List[Subject] = [] From 23b3a53f70751192ddfb4561d4b37907359ceb03 Mon Sep 17 00:00:00 2001 From: "PACTERA_TGSC\\hao.li" Date: Thu, 27 Oct 2022 19:26:11 +0800 Subject: [PATCH 017/644] =?UTF-8?q?feature:=20=E7=AE=A1=E7=90=86=E7=A9=BA?= =?UTF-8?q?=E9=97=B4=E6=8A=98=E5=8F=A0=E5=88=97=E8=A1=A8=E7=BC=96=E5=86=99?= =?UTF-8?q?=20feature:=20=20=E6=B7=BB=E5=8A=A0=E6=BC=8F=E7=BC=BA=E7=9A=84?= =?UTF-8?q?=E5=9B=BD=E9=99=85=E5=8C=96=E6=9E=9A=E4=B8=BE=20fix:=20?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=E6=9D=83=E9=99=90=E7=AE=A1=E7=90=86=E7=9A=84?= =?UTF-8?q?=E4=B8=80=E7=BA=A7=E7=AE=A1=E7=90=86=E7=A9=BA=E9=97=B4=E8=B7=AF?= =?UTF-8?q?=E7=94=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/common/router-handle.js | 4 + frontend/src/language/lang/en.js | 22 +- frontend/src/language/lang/zh.js | 2 + .../components/basic-info-detail.vue | 12 +- .../first-manage-space/index.vue | 4 +- frontend/src/views/my-manage-space/index.vue | 340 +++++++++++++++--- 6 files changed, 326 insertions(+), 58 deletions(-) diff --git a/frontend/src/common/router-handle.js b/frontend/src/common/router-handle.js index d7d5f0f67..b25dd233b 100644 --- a/frontend/src/common/router-handle.js +++ b/frontend/src/common/router-handle.js @@ -213,6 +213,10 @@ export const getNavRouterDiff = (navIndex) => { ]; } + if (navIndex === 1) { + return ['firstManageSpace']; + } + if (navIndex === 2) { return [ 'systemAccess', diff --git a/frontend/src/language/lang/en.js b/frontend/src/language/lang/en.js index a43b8d926..cf10da491 100644 --- a/frontend/src/language/lang/en.js +++ b/frontend/src/language/lang/en.js @@ -271,7 +271,11 @@ export const m = { '只允许输入英文': 'Only English is allowed', '生效时间': 'Effective time', '生效条件': 'Effective condition', - '空间名称必填': 'Space name is required' + '空间名称必填': 'Space name is required', + '空间名称最长不超过32个字符': 'The space name cannot exceed 32 characters', + '空间名称不允许空格': 'Space name does not allow spaces', + '操作和资源边界范围不可为空': 'Operation and resource boundary range cannot be empty', + '可授权人员边界不可为空': 'The boundary of authorized personnel cannot be empty', }, info: { '加入用户组申请成功': 'Apply to join the group successfully', @@ -687,7 +691,21 @@ export const m = { '请输入空间名': 'Please enter the space name', '管理员': 'Administrators', '请输入管理员': 'Please enter the administrator', - '上级空间': 'Superior space' + '上级空间': 'Superior space', + '当前空间': 'Current Space', + '实例值': 'Instance Value', + '最大可授权范围操作和资源边界': 'Maximum authorized scope operation and resource boundary', + '选择操作和资源边界范围': 'Select operation and resource boundary range', + '最大可授权人员边界': 'Maximum authorized personnel boundary', + '选择可授权人员边界': 'Select authorized personnel boundary', + '最大可授权操作和资源边界': 'Maximum authorized operations and resource boundaries', + '授权人员边界': 'Authorized personnel boundary', + '搜索空间名、描述、创建人、创建时间': 'Search space name, description, creator and creation time', + '我有权限': 'I have authority', + '全部空间': 'All spaces', + '如需编辑授权边界的内容请点击': 'edit the content of the authorization boundary, click', + '释放': 'Release', + '一级管理空间可以编辑、管理二级管理空间的权限': 'The primary management space can edit and manage the permissions of the secondary management space' }, gradingDetail: { '分级管理员名称': 'Grading Manager Name:', diff --git a/frontend/src/language/lang/zh.js b/frontend/src/language/lang/zh.js index 6622cc736..d2df57a65 100644 --- a/frontend/src/language/lang/zh.js +++ b/frontend/src/language/lang/zh.js @@ -300,6 +300,8 @@ export const m = { '资源类型ID已被占用': '资源类型ID已被占用', '只允许输入英文': '只允许输入英文', '空间名称必填': '空间名称必填', + '空间名称最长不超过32个字符': '空间名称最长不超过32个字符', + '空间名称不允许空格': '空间名称不允许空格', '操作和资源边界范围不可为空': '操作和资源边界范围不可为空', '可授权人员边界不可为空': '可授权人员边界不可为空', /* eslint-disable no-dupe-keys */ diff --git a/frontend/src/views/manage-spaces/components/basic-info-detail.vue b/frontend/src/views/manage-spaces/components/basic-info-detail.vue index a6900e12f..4df5f4069 100644 --- a/frontend/src/views/manage-spaces/components/basic-info-detail.vue +++ b/frontend/src/views/manage-spaces/components/basic-info-detail.vue @@ -2,7 +2,7 @@
- + - + - + { return value.length <= 32; }, - message: this.$t(`m.verify['分级管理员名称最长不超过32个字符']`), + message: this.$t(`m.verify['空间名称最长不超过32个字符']`), trigger: 'blur' }, { validator: (value) => { return /^[^\s]*$/g.test(value); }, - message: this.$t(`m.verify['分级管理员名称不允许空格']`), + message: this.$t(`m.verify['空间名称不允许空格']`), trigger: 'blur' } ]; diff --git a/frontend/src/views/manage-spaces/first-manage-space/index.vue b/frontend/src/views/manage-spaces/first-manage-space/index.vue index 57ca17f2f..05705e874 100644 --- a/frontend/src/views/manage-spaces/first-manage-space/index.vue +++ b/frontend/src/views/manage-spaces/first-manage-space/index.vue @@ -235,7 +235,7 @@ }; - + diff --git a/frontend/src/components/nav/index.vue b/frontend/src/components/nav/index.vue index b5d966d6c..745cd452a 100644 --- a/frontend/src/components/nav/index.vue +++ b/frontend/src/components/nav/index.vue @@ -152,7 +152,7 @@ // 二级管理空间 [['secondaryManageSpace'], 'secondaryManageSpaceNav'], // 授权边界 - [['authorBoundary'], 'authorBoundaryNav'], + [['authorBoundary', 'authorBoundaryEditFirstLevel', 'authorBoundaryEditSecondLevel'], 'authorBoundaryNav'], // 资源权限 [['resourcePermiss'], 'resourcePermissNav'], // 管理员 @@ -216,7 +216,6 @@ this.curRole = newValue.role.type || 'staff'; if (newValue.role.id !== oldValue.role.id) { this.reload(); - console.log(newValue, '用户'); this.curRoleId = newValue.role.id; } }, diff --git a/frontend/src/router/ce.js b/frontend/src/router/ce.js index 1fe431981..7f26ec128 100644 --- a/frontend/src/router/ce.js +++ b/frontend/src/router/ce.js @@ -121,6 +121,14 @@ const FirstManageSpaceCreate = () => const AuthorizationBoundary = () => import(/* webpackChunkName: 'grading-admin' */ '../views/manage-spaces/authorization-boundary'); +// 授权边界一级管理空间编辑 +const AuthorizationBoundaryEditFirstLevel = () => + import(/* webpackChunkName: 'grading-admin' */ '../views/manage-spaces/authorization-boundary/edit/first-level'); + +// 授权边界二级管理空间编辑 +const AuthorizationBoundarySecondLevel = () => + import(/* webpackChunkName: 'grading-admin' */ '../views/manage-spaces/authorization-boundary/edit/second-level'); + // 二极管理空间 const SecondaryManageSpace = () => import(/* webpackChunkName: 'grading-admin' */ '../views/manage-spaces/secondary-manage-space'); @@ -300,6 +308,24 @@ export const routes = [ }, component: AuthorizationBoundary }, + { + path: 'manage-spaces/authorization-boundary/first-level/:id', + name: 'authorBoundaryEditFirstLevel', + meta: { + headerTitle: '', + backRouter: 'authorBoundary' + }, + component: AuthorizationBoundaryEditFirstLevel + }, + { + path: 'manage-spaces/authorization-boundary/second-level/:id', + name: 'authorBoundaryEditSecondLevel', + meta: { + headerTitle: '', + backRouter: 'authorBoundary' + }, + component: AuthorizationBoundarySecondLevel + }, { path: 'manage-spaces/secondary-manage-space', name: 'secondaryManageSpace', diff --git a/frontend/src/router/ee.js b/frontend/src/router/ee.js index 3891b2126..aeab54aab 100644 --- a/frontend/src/router/ee.js +++ b/frontend/src/router/ee.js @@ -121,6 +121,14 @@ const FirstManageSpaceCreate = () => const AuthorizationBoundary = () => import(/* webpackChunkName: 'grading-admin' */ '../views/manage-spaces/authorization-boundary'); +// 授权边界一级管理空间编辑 +const AuthorizationBoundaryEditFirstLevel = () => + import(/* webpackChunkName: 'grading-admin' */ '../views/manage-spaces/authorization-boundary/edit/first-level'); + +// 授权边界二级管理空间编辑 +const AuthorizationBoundarySecondLevel = () => + import(/* webpackChunkName: 'grading-admin' */ '../views/manage-spaces/authorization-boundary/edit/second-level'); + // 二极管理空间 const SecondaryManageSpace = () => import(/* webpackChunkName: 'grading-admin' */ '../views/manage-spaces/secondary-manage-space'); @@ -299,6 +307,24 @@ export const routes = [ }, component: AuthorizationBoundary }, + { + path: 'manage-spaces/authorization-boundary/first-level/:id', + name: 'authorBoundaryEditFirstLevel', + meta: { + headerTitle: '', + backRouter: 'authorBoundary' + }, + component: AuthorizationBoundaryEditFirstLevel + }, + { + path: 'manage-spaces/authorization-boundary/second-level/:id', + name: 'authorBoundaryEditSecondLevel', + meta: { + headerTitle: '', + backRouter: 'authorBoundary' + }, + component: AuthorizationBoundarySecondLevel + }, { path: 'manage-spaces/secondary-manage-space', name: 'secondaryManageSpace', diff --git a/frontend/src/router/ieod.js b/frontend/src/router/ieod.js index eecaf09aa..caefe2516 100644 --- a/frontend/src/router/ieod.js +++ b/frontend/src/router/ieod.js @@ -121,6 +121,14 @@ const FirstManageSpaceCreate = () => const AuthorizationBoundary = () => import(/* webpackChunkName: 'grading-admin' */ '../views/manage-spaces/authorization-boundary'); +// 授权边界一级管理空间编辑 +const AuthorizationBoundaryEditFirstLevel = () => + import(/* webpackChunkName: 'grading-admin' */ '../views/manage-spaces/authorization-boundary/edit/first-level'); + +// 授权边界二级管理空间编辑 +const AuthorizationBoundarySecondLevel = () => + import(/* webpackChunkName: 'grading-admin' */ '../views/manage-spaces/authorization-boundary/edit/second-level'); + // 二极管理空间 const SecondaryManageSpace = () => import(/* webpackChunkName: 'grading-admin' */ '../views/manage-spaces/secondary-manage-space'); @@ -300,6 +308,24 @@ export const routes = [ }, component: AuthorizationBoundary }, + { + path: 'manage-spaces/authorization-boundary/first-level/:id', + name: 'authorBoundaryEditFirstLevel', + meta: { + headerTitle: '', + backRouter: 'authorBoundary' + }, + component: AuthorizationBoundaryEditFirstLevel + }, + { + path: ':id/manage-spaces/authorization-boundary/second-level/:id', + name: 'authorBoundaryEditSecondLevel', + meta: { + headerTitle: '', + backRouter: 'authorBoundary' + }, + component: AuthorizationBoundarySecondLevel + }, { path: 'manage-spaces/secondary-manage-space', name: 'secondaryManageSpace', diff --git a/frontend/src/views/manage-spaces/authorization-boundary/edit/first-level.vue b/frontend/src/views/manage-spaces/authorization-boundary/edit/first-level.vue new file mode 100644 index 000000000..31188b580 --- /dev/null +++ b/frontend/src/views/manage-spaces/authorization-boundary/edit/first-level.vue @@ -0,0 +1,1042 @@ + + + diff --git a/frontend/src/views/manage-spaces/authorization-boundary/edit/second-level.vue b/frontend/src/views/manage-spaces/authorization-boundary/edit/second-level.vue new file mode 100644 index 000000000..a67305647 --- /dev/null +++ b/frontend/src/views/manage-spaces/authorization-boundary/edit/second-level.vue @@ -0,0 +1,1043 @@ + + + diff --git a/frontend/src/views/manage-spaces/authorization-boundary/index.vue b/frontend/src/views/manage-spaces/authorization-boundary/index.vue index 9981cca61..ef767816d 100644 --- a/frontend/src/views/manage-spaces/authorization-boundary/index.vue +++ b/frontend/src/views/manage-spaces/authorization-boundary/index.vue @@ -1,6 +1,6 @@ + diff --git a/frontend/src/views/my-manage-space/index.vue b/frontend/src/views/my-manage-space/index.vue index 25155954e..0ea562e29 100644 --- a/frontend/src/views/my-manage-space/index.vue +++ b/frontend/src/views/my-manage-space/index.vue @@ -293,12 +293,9 @@ handleCreate () { this.$store.commit('updateIndex', 3); - // this.$router.push({ - // name: 'firstManageSpaceCreate', - // params: { - // id: 0 - // } - // }); + this.$router.push({ + name: 'myManageSpaceCreate' + }); }, handleView ({ id, name }) { window.localStorage.setItem('iam-header-name-cache', name); From 8580baec1fed3b486704f6fd3a78d019d3eecd66 Mon Sep 17 00:00:00 2001 From: "PACTERA_TGSC\\hao.li" Date: Tue, 1 Nov 2022 15:17:56 +0800 Subject: [PATCH 034/644] =?UTF-8?q?feature:=E7=BA=A7=E5=88=AB=E7=A9=BA?= =?UTF-8?q?=E9=97=B4=E6=B7=BB=E5=8A=A0=E4=B8=80=E4=BA=9Btip=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=20feature:=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E8=A1=A8=E6=A0=BC=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/iam-edit/textarea.vue | 6 +- frontend/src/language/lang/en.js | 5 +- frontend/src/language/lang/zh.js | 5 +- .../edit/first-level.vue | 33 +++----- .../edit/second-level.vue | 44 ++-------- .../components/basic-info-detail.vue | 1 + .../components/render-instance-table.vue | 82 ++----------------- .../first-manage-space/create/index.vue | 4 +- .../secondary-manage-space/create/index.vue | 4 +- .../views/my-manage-space/create/index.vue | 4 +- 10 files changed, 44 insertions(+), 144 deletions(-) diff --git a/frontend/src/components/iam-edit/textarea.vue b/frontend/src/components/iam-edit/textarea.vue index 156578204..e3beac6f2 100644 --- a/frontend/src/components/iam-edit/textarea.vue +++ b/frontend/src/components/iam-edit/textarea.vue @@ -30,7 +30,7 @@ ref="input" type="textarea" :placeholder="placeholder" - maxlength="255" + :maxlength="maxLength" :rows="3" @input="handleInput" @blur="handleBlur" /> @@ -67,6 +67,10 @@ rules: { type: Array, default: () => [] + }, + maxLength: { + type: Number, + default: 255 } }, data () { diff --git a/frontend/src/language/lang/en.js b/frontend/src/language/lang/en.js index e97768def..0a0edd1d8 100644 --- a/frontend/src/language/lang/en.js +++ b/frontend/src/language/lang/en.js @@ -710,7 +710,10 @@ export const m = { '释放': 'Release', '一级管理空间可以编辑、管理二级管理空间的权限': 'The primary management space can edit and manage the permissions of the secondary management space', '提交审批': 'Submit for approval', - '新建我的管理空间': 'New My Management Space' + '新建我的管理空间': 'New My Management Space', + '一级管理空间缩小/修改授权边界时,同步修改相关的二级管理空间的授权边界': 'When the primary management space shrinks/modifies the authorization boundary, synchronously modify the authorization boundary of the related secondary management space', + '二级管理空间扩大自己的授权边界,需要走一级管理空间管理员审批': 'The secondary management space expands its own authorization boundary, which needs to be approved by the primary management space administrator', + '二级管理空间,授权边界(授权操作范围、授权人员范围)小于等于一级管理员空间': 'Secondary management space, authorization boundary (authorized operation range, authorized personnel range) is less than or equal to the primary administrator space' }, gradingDetail: { '分级管理员名称': 'Grading Manager Name:', diff --git a/frontend/src/language/lang/zh.js b/frontend/src/language/lang/zh.js index ef5b8ef02..c99bcbc14 100644 --- a/frontend/src/language/lang/zh.js +++ b/frontend/src/language/lang/zh.js @@ -777,7 +777,10 @@ export const m = { '释放': '释放', '一级管理空间可以编辑、管理二级管理空间的权限': '一级管理空间可以编辑、管理二级管理空间的权限', '提交审批': '提交审批', - '新建我的管理空间': '新建我的管理空间' + '新建我的管理空间': '新建我的管理空间', + '一级管理空间缩小/修改授权边界时,同步修改相关的二级管理空间的授权边界': '一级管理空间缩小/修改授权边界时,同步修改相关的二级管理空间的授权边界', + '二级管理空间扩大自己的授权边界,需要走一级管理空间管理员审批': '二级管理空间扩大自己的授权边界,需要走一级管理空间管理员审批', + '二级管理空间,授权边界(授权操作范围、授权人员范围)小于等于一级管理员空间': '二级管理空间,授权边界(授权操作范围、授权人员范围)小于等于一级管理员空间' }, gradingDetail: { '分级管理员名称': '分级管理员名称:', diff --git a/frontend/src/views/manage-spaces/authorization-boundary/edit/first-level.vue b/frontend/src/views/manage-spaces/authorization-boundary/edit/first-level.vue index 68f2729fa..6d5f0ee04 100644 --- a/frontend/src/views/manage-spaces/authorization-boundary/edit/first-level.vue +++ b/frontend/src/views/manage-spaces/authorization-boundary/edit/first-level.vue @@ -7,22 +7,22 @@ - +
- {{ $t(`m.grading['选择操作和资源实例范围']`) }} + {{ $t(`m.levelSpace['选择操作和资源边界范围']`) }}
+ v-bk-tooltips.top="{ content: addActionTips, width: 236, extCls: 'iam-tooltips-cls' }" />
{{ $t(`m.common['共']`) }} @@ -63,32 +63,21 @@

{{ $t(`m.verify['操作和资源实例范围不可为空']`) }}

{{ $t(`m.verify['可授权人员范围不可为空']`) }}

- -
- - -
-
-

{{ $t(`m.verify['理由不可为空']`) }}

{{ $t(`m.common['下一步']`) }} @@ -151,8 +140,8 @@ members: [] }, submitLoading: false, - addActionTips: this.$t(`m.grading['添加操作提示']`), - addMemberTips: this.$t(`m.grading['添加成员提示']`), + addActionTips: this.$t(`m.levelSpace['二级管理空间,授权边界(授权操作范围、授权人员范围)小于等于一级管理员空间']`), + addMemberTips: this.$t(`m.levelSpace['一级管理空间缩小/修改授权边界时,同步修改相关的二级管理空间的授权边界']`), isShowAddMemberDialog: false, users: [], departments: [], @@ -165,9 +154,8 @@ addMemberTitle: this.$t(`m.grading['选择可授权人员范围']`), originalList: [], isShowMemberEmptyError: false, - infoText: this.$t(`m.grading['选择提示']`), - tips: this.$t(`m.grading['添加操作提示']`), + tips: this.$t(`m.levelSpace['二级管理空间,授权边界(授权操作范围、授权人员范围)小于等于一级管理员空间']`), policyList: [], isLoading: false, isAllExpanded: false, @@ -175,7 +163,6 @@ aggregationsBackup: [], aggregationsTableData: [], curSystemId: [], - isShowReasonDialog: false, reason: '', dialogLoading: false, diff --git a/frontend/src/views/manage-spaces/authorization-boundary/edit/second-level.vue b/frontend/src/views/manage-spaces/authorization-boundary/edit/second-level.vue index 115e308de..fc9a16307 100644 --- a/frontend/src/views/manage-spaces/authorization-boundary/edit/second-level.vue +++ b/frontend/src/views/manage-spaces/authorization-boundary/edit/second-level.vue @@ -92,7 +92,7 @@ :all-checked="isAll" show-limit @on-cancel="handleCancelAdd" - @on-sumbit="handleSumbitAdd" /> + @on-sumbit="handleSubmitAdd" /> _); - }, - - checkReason () { - this.reasonEmptyError = this.reason === ''; } }, beforeRouteEnter (to, from, next) { @@ -915,13 +901,6 @@ font-size: 12px; color: #ff4d4d; } - .reason-empty-error{ - position: relative; - top: -45px; - left: 160px; - font-size: 12px; - color: #ff4d4d; - } .grade-admin-select-wrapper { .showTableClick { cursor: pointer; @@ -982,17 +961,4 @@ width: 130px; } } - .iam-edit-rate-manager-reason-dialog { - .content-wrapper { - display: flex; - justify-content: flex-start; - label { - display: block; - width: 70px; - span { - color: #ea3636; - } - } - } - } diff --git a/frontend/src/views/manage-spaces/components/basic-info-detail.vue b/frontend/src/views/manage-spaces/components/basic-info-detail.vue index 4df5f4069..3fc6bc7e0 100644 --- a/frontend/src/views/manage-spaces/components/basic-info-detail.vue +++ b/frontend/src/views/manage-spaces/components/basic-info-detail.vue @@ -20,6 +20,7 @@ diff --git a/frontend/src/views/manage-spaces/components/render-instance-table.vue b/frontend/src/views/manage-spaces/components/render-instance-table.vue index f94fdbab8..c6cb2d3e9 100644 --- a/frontend/src/views/manage-spaces/components/render-instance-table.vue +++ b/frontend/src/views/manage-spaces/components/render-instance-table.vue @@ -6,6 +6,7 @@ :header-border="false" :cell-class-name="getCellClass" :empty-text="$t(`m.verify['请选择操作']`)" + :max-height="maxHeight" @row-mouse-enter="handlerRowMouseEnter" @row-mouse-leave="handlerRowMouseLeave"> @@ -96,76 +97,7 @@
- - - - + - - - @@ -570,6 +570,14 @@ handleDelete () { this.$emit('on-delete', this.newRow); }, + handlerRemove (row, payload) { + window.changeDialog = true; + if (row.isAggregate) { + this.$emit('on-aggregate-delete', row.system_id, row.actions, payload); + return; + } + this.$emit('on-delete', row.system_id, row.id, `${row.system_id}&${row.id}`, payload); + }, handleViewResource (payload) { this.curId = payload.id; const params = []; @@ -1956,6 +1964,9 @@ & > td { background-color: transparent; } + .remove-icon { + display: inline-block; + } } } td:first-child .cell, @@ -1972,6 +1983,7 @@ } .relation-content-wrapper, .conditions-wrapper { + position: relative; height: 100%; padding: 17px 0; color: #63656e; @@ -1979,16 +1991,22 @@ display: block; margin-bottom: 9px; } + .iam-condition-item { + width: 90%; + } } .remove-icon { + display: none; position: absolute; - right: 2px; - top: 2px; - font-size: 20px; + top: 10px; + right: 10px; cursor: pointer; &:hover { color: #3a84ff; } + i { + font-size: 20px; + } } .relation-content-item { margin-top: 17px; diff --git a/frontend/src/views/manage-spaces/secondary-manage-space/create/index.vue b/frontend/src/views/manage-spaces/secondary-manage-space/create/index.vue index 96298ecd4..b1e956d70 100644 --- a/frontend/src/views/manage-spaces/secondary-manage-space/create/index.vue +++ b/frontend/src/views/manage-spaces/secondary-manage-space/create/index.vue @@ -62,6 +62,8 @@ :authorization="curAuthorizationData" :original-list="tableListBackup" :is-all-expanded="isAllExpanded" + @on-delete="handleDelete" + @on-aggregate-delete="handleAggregateDelete" @handleAggregateAction="handleAggregateAction" @on-select="handleAttrValueSelected" @on-resource-select="handleResSelect" /> @@ -882,7 +884,6 @@ name, description, members, - syncPerm, subject_scopes: subjects, authorization_scopes: data, sync_perm: syncPerm, @@ -986,6 +987,40 @@ this.isShowAddMemberDialog = false; }, + setAggregateExpanded () { + const flag = this.tableList.every(item => !item.isAggregate); + if (flag) { + this.isAllExpanded = false; + } + }, + + handleDelete (systemId, actionId, payload, index) { + window.changeDialog = true; + this.originalList = this.originalList.filter(item => payload !== item.$id); + this.tableList.splice(index, 1); + for (let i = 0; i < this.aggregations.length; i++) { + const item = this.aggregations[i]; + if (item.actions[0].system_id === systemId) { + item.actions = item.actions.filter(subItem => subItem.id !== actionId); + break; + } + } + this.aggregations = this.aggregations.filter(item => item.actions.length > 1); + this.setAggregateExpanded(); + }, + + handleAggregateDelete (systemId, actions, index) { + window.changeDialog = true; + this.tableList.splice(index, 1); + const deleteAction = actions.map(item => `${systemId}&${item.id}`); + this.originalList = this.originalList.filter(item => !deleteAction.includes(item.$id)); + this.aggregations = this.aggregations.filter(item => + !(item.actions[0].system_id === systemId + && _.isEqual(item.actions.map(_ => _.id).sort(), actions.map(_ => _.id).sort())) + ); + this.setAggregateExpanded(); + }, + /** * handleSubmitAdd */ From f773b6a907e5b77421150be84625ad8b068db23a Mon Sep 17 00:00:00 2001 From: Timmy Date: Mon, 14 Nov 2022 16:55:17 +0800 Subject: [PATCH 077/644] feat: subset manager add sync perm (#1629) --- saas/backend/apps/role/serializers.py | 1 + saas/backend/apps/role/views/role.py | 11 +++++++++++ saas/backend/service/role.py | 3 +++ 3 files changed, 15 insertions(+) diff --git a/saas/backend/apps/role/serializers.py b/saas/backend/apps/role/serializers.py index d884963a2..3f1e9527e 100644 --- a/saas/backend/apps/role/serializers.py +++ b/saas/backend/apps/role/serializers.py @@ -442,4 +442,5 @@ class Meta: "authorization_scopes", "inherit_subject_scope", "subject_scopes", + "sync_perm", ) diff --git a/saas/backend/apps/role/views/role.py b/saas/backend/apps/role/views/role.py index 9eb312695..2e224bd9c 100644 --- a/saas/backend/apps/role/views/role.py +++ b/saas/backend/apps/role/views/role.py @@ -719,6 +719,7 @@ class SubsetManagerViewSet(mixins.ListModelMixin, GenericViewSet): serializer_class = BaseGradeMangerSLZ biz = RoleBiz() + group_biz = GroupBiz() role_check_biz = RoleCheckBiz() role_trans = RoleTrans() @@ -786,6 +787,10 @@ def create(self, request, *args, **kwargs): # 创建子集管理员, 并创建分级管理员与子集管理员的关系 role = self.biz.create_subset_manager(grade_manager, info, user_id) + # 创建同步权限用户组 + if info.sync_perm: + self.group_biz.create_sync_perm_group_by_role(role, user_id) + audit_context_setter(role=role) return Response({"id": role.id}, status=status.HTTP_201_CREATED) @@ -836,6 +841,9 @@ def update(self, request, *args, **kwargs): self.biz.update(role, info, user_id) + # 更新同步权限用户组信息 + self.group_biz.update_sync_perm_group_by_role(self.get_object(), user_id, sync_members=True, sync_prem=True) + audit_context_setter(role=role) return Response({}) @@ -868,6 +876,9 @@ def partial_update(self, request, *args, **kwargs): self.biz.update(role, RoleInfoBean.from_partial_data(data), user_id) + # 更新同步权限用户组信息 + self.group_biz.update_sync_perm_group_by_role(self.get_object(), user_id, sync_members=True) + audit_context_setter(role=role) return Response({}) diff --git a/saas/backend/service/role.py b/saas/backend/service/role.py index aa67cf744..16f025bc6 100644 --- a/saas/backend/service/role.py +++ b/saas/backend/service/role.py @@ -198,6 +198,7 @@ def create(self, info: RoleInfo, creator: str, add_member=True) -> Role: description=info.description, type=info.type, inherit_subject_scope=info.inherit_subject_scope, + sync_perm=info.sync_perm, creator=creator, updater=creator, ) @@ -276,6 +277,8 @@ def update(self, role: Role, info: RoleInfo, updater: str): role.description = info.description if role.type == RoleType.SUBSET_MANAGER.value and "inherit_subject_scope" in update_fields: role.inherit_subject_scope = info.inherit_subject_scope + if "sync_perm" in update_fields: + role.sync_perm = info.sync_perm role.updater = updater role.save() From 7bf43e2bcc05aaf5f4d5ac30f30df98d73caa4a9 Mon Sep 17 00:00:00 2001 From: zhu327 Date: Mon, 14 Nov 2022 16:59:54 +0800 Subject: [PATCH 078/644] feat: subset manager add sync perm --- saas/backend/api/management/v2/serializers.py | 1 + saas/backend/api/management/v2/views/subset_manager.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/saas/backend/api/management/v2/serializers.py b/saas/backend/api/management/v2/serializers.py index 7a109b53e..6a3fcccb4 100644 --- a/saas/backend/api/management/v2/serializers.py +++ b/saas/backend/api/management/v2/serializers.py @@ -262,6 +262,7 @@ class ManagementSubsetMangerCreateSLZ(GradeMangerBaseInfoSLZ): ) subject_scopes = serializers.ListField(label="授权对象", child=RoleScopeSubjectSLZ(label="授权对象"), allow_empty=True) inherit_subject_scope = serializers.BooleanField(label="继承分级管理员人员管理范围", required=False, default=False) + sync_perm = serializers.BooleanField(label="同步分级管理员权限到用户组", required=False, default=False) def validate(self, data): data = super().validate(data) diff --git a/saas/backend/api/management/v2/views/subset_manager.py b/saas/backend/api/management/v2/views/subset_manager.py index ab643b643..2de86611a 100644 --- a/saas/backend/api/management/v2/views/subset_manager.py +++ b/saas/backend/api/management/v2/views/subset_manager.py @@ -22,6 +22,7 @@ from backend.apps.role.audit import RoleCreateAuditProvider from backend.apps.role.models import Role, RoleSource from backend.audit.audit import audit_context_setter, view_audit_decorator +from backend.biz.group import GroupBiz from backend.biz.role import RoleBiz, RoleCheckBiz from backend.service.constants import RoleSourceTypeEnum, RoleType from backend.trans.role import RoleTrans @@ -41,6 +42,7 @@ class ManagementSubsetManagerViewSet(GenericViewSet): lookup_field = "id" biz = RoleBiz() + group_biz = GroupBiz() role_check_biz = RoleCheckBiz() role_trans = RoleTrans() @@ -94,6 +96,10 @@ def create(self, request, *args, **kwargs): role_id=role.id, source_type=RoleSourceTypeEnum.API.value, source_system_id=source_system_id ) + # 创建同步权限用户组 + if info.sync_perm: + self.group_biz.create_sync_perm_group_by_role(role, "admin") + audit_context_setter(role=role) return Response({"id": role.id}) From e78aba5f3bf347e04bdec42371524ee1ef47e13d Mon Sep 17 00:00:00 2001 From: Timmy Date: Mon, 14 Nov 2022 17:23:21 +0800 Subject: [PATCH 079/644] fix: fix create_sync_perm_group_by_role (#1630) --- saas/backend/biz/group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/saas/backend/biz/group.py b/saas/backend/biz/group.py index f8a9e41c7..87a9b2579 100644 --- a/saas/backend/biz/group.py +++ b/saas/backend/biz/group.py @@ -582,7 +582,7 @@ def create_sync_perm_group_by_role(self, role: Role, creator: str): # TODO 用户组的名字需要定义 group = self.group_svc.create( - GroupCreation(name=role.name, description=Role.description, readonly=True), creator + GroupCreation(name=role.name, description=role.description, readonly=True), creator ) RoleRelatedObject.objects.create( role_id=role.id, object_type=RoleRelatedObjectType.GROUP.value, object_id=group.id, sync_perm=True From 19fe6e6a1e27b1d926a8ba92de226da71ef56617 Mon Sep 17 00:00:00 2001 From: "PACTERA_TGSC\\hao.li" Date: Mon, 14 Nov 2022 17:35:26 +0800 Subject: [PATCH 080/644] =?UTF-8?q?fix:=E5=8E=BB=E6=8E=89=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=BB=84=E5=A4=9A=E4=BD=99=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/views/group/index.vue | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/frontend/src/views/group/index.vue b/frontend/src/views/group/index.vue index 174f5c79a..f5bfce8ef 100644 --- a/frontend/src/views/group/index.vue +++ b/frontend/src/views/group/index.vue @@ -83,12 +83,8 @@ @@ -63,7 +63,7 @@ class="exception-wrap-item exception-part" type="search-empty" scene="part"> - +
diff --git a/frontend/src/views/grading-admin/components/basic-info-detail.vue b/frontend/src/views/grading-admin/components/basic-info-detail.vue index 734a19772..e3bb69fcc 100644 --- a/frontend/src/views/grading-admin/components/basic-info-detail.vue +++ b/frontend/src/views/grading-admin/components/basic-info-detail.vue @@ -2,8 +2,7 @@
- + - + { return value.length <= 32; }, - message: this.$t(`m.verify['分级管理员名称最长不超过32个字符']`), + message: this.$t(`m.verify['空间名称最长不超过32个字符']`), trigger: 'blur' }, { validator: (value) => { return /^[^\s]*$/g.test(value); }, - message: this.$t(`m.verify['分级管理员名称不允许空格']`), + message: this.$t(`m.verify['空间名称不允许空格']`), trigger: 'blur' } ]; diff --git a/frontend/src/views/grading-admin/components/member-edit.vue b/frontend/src/views/grading-admin/components/member-edit.vue index 196e7248c..5aae23ed7 100644 --- a/frontend/src/views/grading-admin/components/member-edit.vue +++ b/frontend/src/views/grading-admin/components/member-edit.vue @@ -42,7 +42,7 @@ :footer-position="footerPosition" @confirm="dropOut">

{{ $t(`m.common['退出将不在具备相应的管理权限']`) }}

-

{{ $t(`m.common['确定退出分级管理员']`) }}

+

{{ $t(`m.common['确定退出一级管理空间']`) }}

diff --git a/frontend/src/views/grading-admin/components/render-instance-table.vue b/frontend/src/views/grading-admin/components/render-instance-table.vue index 558af79c1..fa79395e4 100644 --- a/frontend/src/views/grading-admin/components/render-instance-table.vue +++ b/frontend/src/views/grading-admin/components/render-instance-table.vue @@ -316,7 +316,7 @@ } }, created () { - console.log('1.我的分级管理员-最大可授权资源范围'); + console.log('1.我的一级管理空间-最大可授权资源范围'); // 判断数组是否被另外一个数组包含 this.isArrayInclude = (target, origin) => { const itemAry = []; diff --git a/frontend/src/views/grading-admin/components/render-member.vue b/frontend/src/views/grading-admin/components/render-member.vue index 09b2db510..33b03b218 100644 --- a/frontend/src/views/grading-admin/components/render-member.vue +++ b/frontend/src/views/grading-admin/components/render-member.vue @@ -1,9 +1,9 @@
- 该模板无法选择的原因是:分级管理员缩小了授权范围,但是没有同步删除模板里的操作,如需选择请重新编辑模板或者创建新的模板。 + 该模板无法选择的原因是:一级管理空间缩小了授权范围,但是没有同步删除模板里的操作,如需选择请重新编辑模板或者创建新的模板。
-
{{ $t(`m.grading['分级管理员列表']`) }}
+
{{ $t(`m.grading['一级管理空间列表']`) }}
- +
@@ -903,7 +903,7 @@ try { await this.$store.dispatch(`role/${dispatchMethod}`, params); await this.$store.dispatch('roleList'); - this.messageSuccess(this.$t(`m.info['编辑分级管理员成功']`), 1000); + this.messageSuccess(this.$t(`m.info['编辑一级管理空间成功']`), 1000); this.$router.push({ name: 'gradingAdminDetail', params: { diff --git a/frontend/src/views/manage-spaces/authorization-boundary/edit/second-level.vue b/frontend/src/views/manage-spaces/authorization-boundary/edit/second-level.vue index fc9a16307..44cf05472 100644 --- a/frontend/src/views/manage-spaces/authorization-boundary/edit/second-level.vue +++ b/frontend/src/views/manage-spaces/authorization-boundary/edit/second-level.vue @@ -845,7 +845,7 @@ try { await this.$store.dispatch(`role/${dispatchMethod}`, params); await this.$store.dispatch('roleList'); - this.messageSuccess(this.$t(`m.info['编辑分级管理员成功']`), 1000); + this.messageSuccess(this.$t(`m.info['编辑二级管理空间成功']`), 1000); this.$router.push({ name: 'gradingAdminDetail', params: { diff --git a/frontend/src/views/manage-spaces/components/add-action-side-slider.vue b/frontend/src/views/manage-spaces/components/add-action-side-slider.vue index 19e70858f..8633d57eb 100644 --- a/frontend/src/views/manage-spaces/components/add-action-side-slider.vue +++ b/frontend/src/views/manage-spaces/components/add-action-side-slider.vue @@ -49,10 +49,10 @@
@@ -63,7 +63,7 @@ class="exception-wrap-item exception-part" type="search-empty" scene="part"> - +
diff --git a/frontend/src/views/manage-spaces/components/basic-info-detail.vue b/frontend/src/views/manage-spaces/components/basic-info-detail.vue index 2f9949bdd..94ee6f9ec 100644 --- a/frontend/src/views/manage-spaces/components/basic-info-detail.vue +++ b/frontend/src/views/manage-spaces/components/basic-info-detail.vue @@ -10,7 +10,7 @@ :value="formData.name" :remote-hander="handleUpdateRatingManager" /> - +

{{ nameValidateText }}

- -
- +
+ @@ -19,7 +23,7 @@ {{ $t(`m.grading['同时具备空间下操作和资源权限']`) }} @@ -27,13 +31,14 @@
-
- diff --git a/frontend/src/views/manage-spaces/secondary-manage-space/components/render-instance-table.vue b/frontend/src/views/manage-spaces/secondary-manage-space/components/render-instance-table.vue index 8068eef66..27b1ec6a3 100644 --- a/frontend/src/views/manage-spaces/secondary-manage-space/components/render-instance-table.vue +++ b/frontend/src/views/manage-spaces/secondary-manage-space/components/render-instance-table.vue @@ -145,6 +145,7 @@ :ext-cls="'relate-instance-sideslider'" @update:isShow="handleResourceCancel">
+ {{curScopeAction}} item.id === data.id)); this.curIndex = index; + console.log(this.authorization[this.params.system_id], this.curScopeAction, 454545545); this.curResIndex = resIndex; this.curGroupIndex = groupIndex; this.resourceInstanceSidesliderTitle = `${this.$t(`m.common['关联操作']`)}【${data.name}】${this.$t(`m.common['的资源实例']`)}`; @@ -955,6 +957,7 @@ try { const res = await this.$store.dispatch('permTemplate/getAuthorizationScopeActions', { systemId }); this.authorization[systemId] = res.data.filter(item => item.id !== '*'); + console.log(this.authorization[systemId], 5555); } catch (e) { console.error(e); this.bkMessageInstance = this.$bkMessage({ @@ -2014,7 +2017,7 @@ .remove-icon { display: none; position: absolute; - top: 50%; + top: 50% !important; right: 0; transform: translate(-50%, -50%); cursor: pointer; diff --git a/frontend/src/views/manage-spaces/secondary-manage-space/components/transfer-out-dialog.vue b/frontend/src/views/manage-spaces/secondary-manage-space/components/transfer-out-dialog.vue index 69aab2e96..c8ef5db9f 100644 --- a/frontend/src/views/manage-spaces/secondary-manage-space/components/transfer-out-dialog.vue +++ b/frontend/src/views/manage-spaces/secondary-manage-space/components/transfer-out-dialog.vue @@ -8,7 +8,7 @@ ext-cls="iam-group-transfer-dialog" @after-leave="handleAfterLeave">
-
{{ $t(`m.grading['分级管理员列表']`) }}
+
{{ $t(`m.grading['一级管理空间列表']`) }}
@@ -26,7 +26,7 @@
@@ -330,6 +330,7 @@ }, methods: { async fetchPageData () { + console.log(this.user); const propsId = Number(this.id); const headerTitle = propsId ? '二级管理空间克隆' : '新建二级管理空间'; this.$store.commit('setHeaderTitle', headerTitle); @@ -805,7 +806,6 @@ }, async fetchAggregationAction (payload) { - console.log(555555555555555, payload); this.isLoading = true; try { const res = await this.$store.dispatch('aggregate/getAggregateAction', { system_ids: payload }); @@ -1084,7 +1084,7 @@ try { await this.$store.dispatch('spaceManage/addSecondManager', params); await this.$store.dispatch('roleList'); - this.messageSuccess(this.$t(`m.levelSpace['新建二级管理空间成功']`), 1000); + this.messageSuccess(this.$t(`m.info['新建二级管理空间成功']`), 1000); this.$router.go(-1); } catch (e) { console.error(e); @@ -1273,7 +1273,7 @@ } .action-empty-error { position: relative; - top: -50px; + top: -45px; left: 150px; font-size: 12px; color: #ff4d4d; diff --git a/frontend/src/views/manage-spaces/secondary-manage-space/detail/index.vue b/frontend/src/views/manage-spaces/secondary-manage-space/detail/index.vue index 572e0a6ff..e78ba7c44 100644 --- a/frontend/src/views/manage-spaces/secondary-manage-space/detail/index.vue +++ b/frontend/src/views/manage-spaces/secondary-manage-space/detail/index.vue @@ -1,7 +1,7 @@ - @@ -280,32 +294,49 @@ } }, - handleUpdateManageSpace (payload, index) { + handleUpdateMembers (payload, index) { + this.handleUpdateManageSpace(payload, index); + }, + + handleUpdateSubMembers (payload, index) { + this.handleUpdateSubManageSpace(payload, index); + }, + + async handleUpdateManageSpace (payload, index) { + this.formData = this.tableList.find((e, i) => i === index); + await this.fetchManageTable(payload, 'role/updateRatingManager'); + }, + + async handleUpdateSubManageSpace (payload, index) { this.formData = this.subTableList.find((e, i) => i === index); + await this.fetchManageTable(payload, 'spaceManage/updateSecondManagerManager'); + }, + + async fetchManageTable (payload, url) { + const { name, description, members } = payload; const params = { - name: payload.name || this.formData.name, - description: payload.description || this.formData.description, - members: payload.members || this.formData.members, + name: name || this.formData.name, + description: description || this.formData.description, + members: members || this.formData.members, id: this.formData.id }; - return this.$store.dispatch('spaceManage/updateSecondManagerManager', params) - .then(async () => { - this.messageSuccess(this.$t(`m.info['编辑成功']`), 2000); - this.formData.name = params.name; - this.formData.description = params.description; - this.formData.members = [...params.members]; - }, (e) => { - console.warn('error'); - this.bkMessageInstance = this.$bkMessage({ - limit: 1, - theme: 'error', - message: e.message || e.data.msg || e.statusText - }); + try { + await this.$store.dispatch(url, params); + this.messageSuccess(this.$t(`m.info['编辑成功']`), 2000); + this.formData = Object.assign(this.formData, { + name: params.name, + description: params.description, + members: [...params.members] }); - }, - - handleUpdateMembers (payload, index) { - this.handleUpdateManageSpace(payload, index); + } catch (e) { + console.error(e); + this.bkMessageInstance = this.$bkMessage({ + limit: 1, + theme: 'error', + message: e.message || e.data.msg || e.statusText, + ellipsisCopy: true + }); + } }, handleRowClick (row, column, cell, event, rowIndex, columnIndex) { diff --git a/frontend/src/views/perm-apply/apply-custom-perm/index.vue b/frontend/src/views/perm-apply/apply-custom-perm/index.vue index 337cee4ee..2551cdcac 100644 --- a/frontend/src/views/perm-apply/apply-custom-perm/index.vue +++ b/frontend/src/views/perm-apply/apply-custom-perm/index.vue @@ -281,7 +281,7 @@ {{ row.description || '--' }} - +
@@ -763,7 +763,7 @@ handleViewDetail (payload) { if (payload.role && payload.role.name) { this.isShowGradeSlider = true; - this.gradeSliderTitle = `【${payload.role.name}】${this.$t(`m.grading['分级管理员']`)} ${this.$t(`m.common['成员']`)}`; + this.gradeSliderTitle = `【${payload.role.name}】${this.$t(`m.grading['一级管理空间']`)} ${this.$t(`m.common['成员']`)}`; this.fetchRoles(payload.role.id); } }, diff --git a/frontend/src/views/perm-apply/apply-join-user-group/index.vue b/frontend/src/views/perm-apply/apply-join-user-group/index.vue index 4aef9bd6e..6ed01a346 100644 --- a/frontend/src/views/perm-apply/apply-join-user-group/index.vue +++ b/frontend/src/views/perm-apply/apply-join-user-group/index.vue @@ -49,7 +49,7 @@ - +
@@ -209,10 +209,10 @@ name: this.$t(`m.common['系统包含']`), remoteMethod: this.handleRemoteSystem }, - // 分级管理员 + // 一级管理空间 { id: 'role_id', - name: this.$t(`m.grading['分级管理员']`), + name: this.$t(`m.grading['一级管理空间']`), remoteMethod: this.handleGradeAdmin } ]; @@ -396,7 +396,7 @@ return data.map(({ id, name }) => ({ id, name })).filter(item => item.name.indexOf(value) > -1); }); }, - // 分级管理员数据 + // 一级管理空间数据 handleGradeAdmin (value) { return this.$store.dispatch('role/getScopeHasUser') .then(({ data }) => { @@ -425,7 +425,7 @@ handleViewDetail (payload) { if (payload.role && payload.role.name) { this.isShowGradeSlider = true; - this.gradeSliderTitle = `【${payload.role.name}】${this.$t(`m.grading['分级管理员']`)} ${this.$t(`m.common['成员']`)}`; + this.gradeSliderTitle = `【${payload.role.name}】${this.$t(`m.grading['一级管理空间']`)} ${this.$t(`m.common['成员']`)}`; this.fetchRoles(payload.role.id); } }, diff --git a/frontend/src/views/perm-template/components/render-action.vue b/frontend/src/views/perm-template/components/render-action.vue index ccc43949e..532d504df 100644 --- a/frontend/src/views/perm-template/components/render-action.vue +++ b/frontend/src/views/perm-template/components/render-action.vue @@ -61,7 +61,7 @@
- 由于分级管理员的授权范围没有包含此操作,
+ 由于一级管理空间的授权范围没有包含此操作,
如需使用该模板进行新的授权必须先删除该操作。
@@ -128,7 +128,7 @@
- 由于分级管理员的授权范围没有包含此操作,
+ 由于一级管理空间的授权范围没有包含此操作,
如需使用该模板进行新的授权必须先删除该操作。
diff --git a/frontend/src/views/perm/department-group-perm/index.vue b/frontend/src/views/perm/department-group-perm/index.vue index 0d0661d35..e939b2023 100644 --- a/frontend/src/views/perm/department-group-perm/index.vue +++ b/frontend/src/views/perm/department-group-perm/index.vue @@ -21,8 +21,8 @@ - - + +
@@ -303,7 +303,7 @@ }, /** - * 调用接口获取分级管理员各项数据 + * 调用接口获取一级管理空间各项数据 */ async fetchRoles (id) { this.sliderLoading = true; @@ -324,12 +324,12 @@ } }, /** - * 点击分级管理员中的项弹出侧边框且显示数据 + * 点击一级管理空间中的项弹出侧边框且显示数据 */ handleViewDetail (payload) { if (payload.role && payload.role.name) { this.isShowGradeSlider = true; - this.gradeSliderTitle = `【${payload.role.name}】${this.$t(`m.grading['分级管理员']`)} ${this.$t(`m.common['成员']`)}`; + this.gradeSliderTitle = `【${payload.role.name}】${this.$t(`m.grading['一级管理空间']`)} ${this.$t(`m.common['成员']`)}`; this.fetchRoles(payload.role.id); } } diff --git a/frontend/src/views/perm/group-perm/index.vue b/frontend/src/views/perm/group-perm/index.vue index 34799f6a4..fa4bf9247 100644 --- a/frontend/src/views/perm/group-perm/index.vue +++ b/frontend/src/views/perm/group-perm/index.vue @@ -21,8 +21,8 @@ - - + +
@@ -325,7 +325,7 @@ }, /** - * 调用接口获取分级管理员各项数据 + * 调用接口获取一级管理空间各项数据 */ async fetchRoles (id) { this.sliderLoading = true; @@ -346,12 +346,12 @@ } }, /** - * 点击分级管理员中的项弹出侧边框且显示数据 + * 点击一级管理空间中的项弹出侧边框且显示数据 */ handleViewDetail (payload) { if (payload.role && payload.role.name) { this.isShowGradeSlider = true; - this.gradeSliderTitle = `【${payload.role.name}】${this.$t(`m.grading['分级管理员']`)} ${this.$t(`m.common['成员']`)}`; + this.gradeSliderTitle = `【${payload.role.name}】${this.$t(`m.grading['一级管理空间']`)} ${this.$t(`m.common['成员']`)}`; this.fetchRoles(payload.role.id); } } diff --git a/frontend/src/views/tempora-perm-apply/apply-custom-perm/index.vue b/frontend/src/views/tempora-perm-apply/apply-custom-perm/index.vue index 0eac9e7fe..61fc957ef 100644 --- a/frontend/src/views/tempora-perm-apply/apply-custom-perm/index.vue +++ b/frontend/src/views/tempora-perm-apply/apply-custom-perm/index.vue @@ -235,7 +235,7 @@ {{ item }}
-

{{ $t(`m.info['分级管理员成员提示']`) }}

+

{{ $t(`m.info['一级管理空间成员提示']`) }}

@@ -480,7 +480,7 @@ handleViewDetail (payload) { if (payload.role && payload.role.name) { this.isShowGradeSlider = true; - this.gradeSliderTitle = `【${payload.role.name}】${this.$t(`m.grading['分级管理员']`)} ${this.$t(`m.common['成员']`)}`; + this.gradeSliderTitle = `【${payload.role.name}】${this.$t(`m.grading['一级管理空间']`)} ${this.$t(`m.common['成员']`)}`; this.fetchRoles(payload.role.id); } }, diff --git a/frontend/src/views/tempora-perm-apply/apply-join-user-group/index.vue b/frontend/src/views/tempora-perm-apply/apply-join-user-group/index.vue index d44de6032..0cc87305a 100644 --- a/frontend/src/views/tempora-perm-apply/apply-join-user-group/index.vue +++ b/frontend/src/views/tempora-perm-apply/apply-join-user-group/index.vue @@ -49,7 +49,7 @@ - +
@@ -205,10 +205,10 @@ name: this.$t(`m.common['系统包含']`), remoteMethod: this.handleRemoteSystem }, - // 分级管理员 + // 一级管理空间 { id: 'role_id', - name: this.$t(`m.grading['分级管理员']`), + name: this.$t(`m.grading['一级管理空间']`), remoteMethod: this.handleGradeAdmin } ]; @@ -392,7 +392,7 @@ return data.map(({ id, name }) => ({ id, name })).filter(item => item.name.indexOf(value) > -1); }); }, - // 分级管理员数据 + // 一级管理空间数据 handleGradeAdmin (value) { return this.$store.dispatch('role/getScopeHasUser') .then(({ data }) => { @@ -421,7 +421,7 @@ handleViewDetail (payload) { if (payload.role && payload.role.name) { this.isShowGradeSlider = true; - this.gradeSliderTitle = `【${payload.role.name}】${this.$t(`m.grading['分级管理员']`)} ${this.$t(`m.common['成员']`)}`; + this.gradeSliderTitle = `【${payload.role.name}】${this.$t(`m.grading['一级管理空间']`)} ${this.$t(`m.common['成员']`)}`; this.fetchRoles(payload.role.id); } }, diff --git a/frontend/src/views/transfer/history-detail.vue b/frontend/src/views/transfer/history-detail.vue index 3826bb59e..ab10c7f64 100644 --- a/frontend/src/views/transfer/history-detail.vue +++ b/frontend/src/views/transfer/history-detail.vue @@ -88,7 +88,7 @@ item.title = this.$t(`m.permTransfer['系统管理员交接:']`); item.info = item.objectDetail.name; } else if (item.objectDetail.type === 'rating_manager') { - item.title = this.$t(`m.permTransfer['分级管理员交接:']`); + item.title = this.$t(`m.permTransfer['一级管理空间交接:']`); item.info = item.objectDetail.name; } } else { diff --git a/frontend/src/views/transfer/index.vue b/frontend/src/views/transfer/index.vue index 0a45353c3..5e29f0b4f 100644 --- a/frontend/src/views/transfer/index.vue +++ b/frontend/src/views/transfer/index.vue @@ -41,7 +41,9 @@ @blur="handleRtxBlur" @change="handleRtxChange"> -

{{ $t(`m.verify['请选择成员']`) }}

+

+ {{ $t(`m.verify['请选择空间管理员']`) }} +

{{ $t(`m.verify['目标交接人不能为本人']`) }}

diff --git a/frontend/src/views/transfer/manager.vue b/frontend/src/views/transfer/manager.vue index 503eb1fb4..915ebfc54 100644 --- a/frontend/src/views/transfer/manager.vue +++ b/frontend/src/views/transfer/manager.vue @@ -36,7 +36,7 @@ {{$t(`m.nav['系统管理员']`)}} diff --git a/frontend/src/views/transfer/rating-manager.vue b/frontend/src/views/transfer/rating-manager.vue index 00bb87b10..029c36c09 100644 --- a/frontend/src/views/transfer/rating-manager.vue +++ b/frontend/src/views/transfer/rating-manager.vue @@ -5,7 +5,7 @@
- +
@@ -22,7 +22,7 @@ - + @@ -60,7 +60,7 @@ isEmpty: false, isLoading: false, rateListRender: [], - rateListAll: [], // 分级管理员权限交接 + rateListAll: [], // 一级管理空间权限交接 rateExpanded: true, isSelectAllChecked: false, rateSelectData: [] diff --git a/frontend/src/views/transfer/system-manager.vue b/frontend/src/views/transfer/system-manager.vue index e5ae51193..ac16739ad 100644 --- a/frontend/src/views/transfer/system-manager.vue +++ b/frontend/src/views/transfer/system-manager.vue @@ -60,7 +60,7 @@ isEmpty: false, isLoading: false, systemListRender: [], - systemListAll: [], // 分级管理员权限交接 + systemListAll: [], // 一级管理空间权限交接 systemExpanded: true, isSelectAllChecked: false, systemSelectData: [] diff --git a/frontend/src/views/user/components/department-group-perm.vue b/frontend/src/views/user/components/department-group-perm.vue index 93b5d2282..3eace7e26 100644 --- a/frontend/src/views/user/components/department-group-perm.vue +++ b/frontend/src/views/user/components/department-group-perm.vue @@ -21,8 +21,8 @@ - - + +
@@ -301,7 +301,7 @@ }, /** - * 调用接口获取分级管理员各项数据 + * 调用接口获取一级管理空间各项数据 */ async fetchRoles (id) { this.sliderLoading = true; @@ -322,12 +322,12 @@ } }, /** - * 点击分级管理员中的项弹出侧边框且显示数据 + * 点击一级管理空间中的项弹出侧边框且显示数据 */ handleViewDetail (payload) { if (payload.role && payload.role.name) { this.isShowGradeSlider = true; - this.gradeSliderTitle = `【${payload.role.name}】${this.$t(`m.grading['分级管理员']`)} ${this.$t(`m.common['成员']`)}`; + this.gradeSliderTitle = `【${payload.role.name}】${this.$t(`m.grading['一级管理空间']`)} ${this.$t(`m.common['成员']`)}`; this.fetchRoles(payload.role.id); } } From c4cf9c78d2e29f98498a22d1584f7d5a7da199c8 Mon Sep 17 00:00:00 2001 From: polo <826770122@qq.com> Date: Thu, 17 Nov 2022 17:28:31 +0800 Subject: [PATCH 106/644] feat: add userGroup AddUserGroupDiaLogUrl --- frontend/src/store/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/store/index.js b/frontend/src/store/index.js index 5f0d65bba..cde9d4dad 100644 --- a/frontend/src/store/index.js +++ b/frontend/src/store/index.js @@ -375,7 +375,8 @@ const store = new Vuex.Store({ }, userGroup: { // 用户组 addGroup: { // 用户组 - 添加用户组 - 添加权限抽屉 - hideAddTemplateTextBtn: false // 右侧抽屉新增文本按钮-7.1 + hideAddTemplateTextBtn: false, // 右侧抽屉新增文本按钮-7.1 + AddUserGroupDiaLogUrl: '' // 用户组 - 添加用户组 - 组成员链接跳转 }, groupDetail: { // 用户组 - 组详情 hideAddBtn: false, // 用户组-组权限-添加权限按钮-6 From 8971b65149a1114f92baf922a09fc4802df60a25 Mon Sep 17 00:00:00 2001 From: "PACTERA_TGSC\\hao.li" Date: Thu, 17 Nov 2022 17:33:15 +0800 Subject: [PATCH 107/644] =?UTF-8?q?feature:=E4=B8=80=E7=BA=A7=E3=80=81?= =?UTF-8?q?=E4=BA=8C=E7=BA=A7=E7=AE=A1=E7=90=86=E7=A9=BA=E9=97=B4=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=BF=9B=E5=85=A5=E6=93=8D=E4=BD=9C=E9=A1=B9=EF=BC=8C?= =?UTF-8?q?=E5=8E=BB=E6=8E=89=E5=A4=9A=E4=BD=99log,=20=E4=B8=80=E7=BA=A7?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E7=A9=BA=E9=97=B4=E5=88=97=E8=A1=A8=E5=AF=B9?= =?UTF-8?q?=E9=BD=90tag=E6=A0=87=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/header/index.vue | 10 ++-- frontend/src/views/grading-admin/index.vue | 57 ++++++++++++++----- .../secondary-manage-space/create/index.vue | 7 --- .../secondary-manage-space/index.vue | 13 ++--- 4 files changed, 52 insertions(+), 35 deletions(-) diff --git a/frontend/src/components/header/index.vue b/frontend/src/components/header/index.vue index 4a2048aeb..090d85495 100644 --- a/frontend/src/components/header/index.vue +++ b/frontend/src/components/header/index.vue @@ -7,7 +7,7 @@ direction="right" :flag="showGuide" :style="{ top: '5px', right: '125px' }" - :content="$t(`m.guide['切换分级管理员']`)" /> + :content="$t(`m.guide['切换一级管理空间']`)" /> @@ -73,7 +83,8 @@ import IamEditInput from '@/components/iam-edit/input'; import IamEditMemberSelector from '@/views/my-manage-space/components/iam-edit/member-selector'; import RenderItem from '../common/render-item'; - import { getWindowHeight } from '@/common/util'; + import { getWindowHeight, formatCodeData } from '@/common/util'; + export default { name: '', components: { @@ -86,7 +97,13 @@ return { subTitle: this.$t(`m.set['系统管理员提示']`), systemUserList: [], - userApi: window.BK_USER_API + userApi: window.BK_USER_API, + emptyData: { + type: '', + text: '', + tip: '', + tipType: '' + } }; }, computed: { @@ -108,9 +125,9 @@ async fetchSystemManager () { this.$emit('data-ready', false); try { - const res = await this.$store.dispatch('role/getSystemManager'); + const { code, data } = await this.$store.dispatch('role/getSystemManager'); const tempArr = []; - res.data.forEach(item => { + data.forEach(item => { tempArr.push({ ...item, memberBackup: _.cloneDeep(item.members), @@ -119,12 +136,15 @@ }); }); this.systemUserList.splice(0, this.systemUserList.length, ...tempArr); + this.emptyData = formatCodeData(code, this.emptyData, this.systemUserList.length === 0); } catch (e) { console.error(e); + const { code, data, message, statusText } = e; + this.emptyData = formatCodeData(code, this.emptyData); this.bkMessageInstance = this.$bkMessage({ limit: 1, theme: 'error', - message: e.message || e.data.msg || e.statusText, + message: message || data.msg || statusText, ellipsisLine: 2, ellipsisCopy: true }); diff --git a/frontend/src/views/system-access/index.vue b/frontend/src/views/system-access/index.vue index 02c6d353f..92a793795 100644 --- a/frontend/src/views/system-access/index.vue +++ b/frontend/src/views/system-access/index.vue @@ -1,361 +1,382 @@ - - - + + + diff --git a/frontend/src/views/tempora-perm-apply/apply-custom-perm/index.vue b/frontend/src/views/tempora-perm-apply/apply-custom-perm/index.vue index c4ad1c85f..83fc7514b 100644 --- a/frontend/src/views/tempora-perm-apply/apply-custom-perm/index.vue +++ b/frontend/src/views/tempora-perm-apply/apply-custom-perm/index.vue @@ -165,8 +165,15 @@
@@ -324,8 +331,13 @@ tagActionList: [], hoverActionData: { actions: [] + }, + emptyData: { + type: 'search-empty', + text: '', + tip: '', + tipType: 'search' } - }; }, computed: { diff --git a/frontend/src/views/tempora-perm-apply/components/render-resource.vue b/frontend/src/views/tempora-perm-apply/components/render-resource.vue index 279b42db2..443c8b893 100644 --- a/frontend/src/views/tempora-perm-apply/components/render-resource.vue +++ b/frontend/src/views/tempora-perm-apply/components/render-resource.vue @@ -1,924 +1,924 @@ - - - + + + diff --git a/frontend/src/views/transfer/group.vue b/frontend/src/views/transfer/group.vue index c76236594..05c81a3db 100644 --- a/frontend/src/views/transfer/group.vue +++ b/frontend/src/views/transfer/group.vue @@ -70,16 +70,24 @@
- +
- --> +
- + + + diff --git a/frontend/src/views/transfer/system-manager.vue b/frontend/src/views/transfer/system-manager.vue index ac16739ad..2a6f336f6 100644 --- a/frontend/src/views/transfer/system-manager.vue +++ b/frontend/src/views/transfer/system-manager.vue @@ -44,13 +44,22 @@
- + + + + diff --git a/frontend/src/views/user/components/department-group-perm.vue b/frontend/src/views/user/components/department-group-perm.vue index 3eace7e26..505f82063 100644 --- a/frontend/src/views/user/components/department-group-perm.vue +++ b/frontend/src/views/user/components/department-group-perm.vue @@ -57,6 +57,15 @@ +

{{ $t(`m.info['一级管理空间成员提示']`) }}

+
+ +
@@ -99,6 +117,7 @@ import { mapGetters } from 'vuex'; import DeleteDialog from '@/components/iam-confirm-dialog/index.vue'; import RenderPermSideslider from '../../perm/components/render-group-perm-sideslider'; + import { formatCodeData } from '@/common/util'; export default { name: '', @@ -136,7 +155,21 @@ // 控制侧边弹出层显示 isShowGradeSlider: false, sliderLoading: false, - gradeSliderTitle: '' + gradeSliderTitle: '', + gradeMembers: [], + curRoleId: -1, + emptyData: { + type: '', + text: '', + tip: '', + tipType: '' + }, + emptySliderData: { + type: '', + text: '', + tip: '', + tipType: '' + } }; }, computed: { @@ -150,19 +183,22 @@ this.pageLoading = true; const { type } = this.data; try { - const res = await this.$store.dispatch('perm/getDepartPermGroups', { + const { code, data } = await this.$store.dispatch('perm/getDepartPermGroups', { subjectType: type === 'user' ? type : 'department', subjectId: type === 'user' ? this.data.username : this.data.id }); - this.dataList = res.data || []; + this.dataList = data || []; this.pageConf.count = this.dataList.length; this.curPageData = this.getDataByPage(this.pageConf.current); + this.emptyData = formatCodeData(code, this.emptyData, this.dataList.length === 0); } catch (e) { console.error(e); + const { code, data, message, statusText } = e; + this.emptyData = formatCodeData(code, this.emptyData); this.bkMessageInstance = this.$bkMessage({ limit: 1, theme: 'error', - message: e.message || e.data.msg || e.statusText, + message: message || data.msg || statusText, ellipsisLine: 2, ellipsisCopy: true }); @@ -171,6 +207,21 @@ } }, + handleEmptyRefresh () { + this.pageConf = Object.assign( + {}, + { + current: 1, + count: 0, + limit: 10 + }); + this.fetchSystems(); + }, + + handleEmptySliderRefresh () { + this.fetchRoles(this.curRoleId); + }, + handleAnimationEnd () { this.curGroupName = ''; this.curGroupId = ''; @@ -306,14 +357,17 @@ async fetchRoles (id) { this.sliderLoading = true; try { - const res = await this.$store.dispatch('role/getGradeMembers', { id }); - this.gradeMembers = [...res.data]; + const { code, data } = await this.$store.dispatch('role/getGradeMembers', { id }); + this.gradeMembers = [...data]; + this.emptySliderData = formatCodeData(code, this.emptySliderData, data.length === 0); } catch (e) { console.error(e); + const { code, data, message, statusText } = e; + this.emptySliderData = formatCodeData(code, this.emptySliderData); this.bkMessageInstance = this.$bkMessage({ limit: 1, theme: 'error', - message: e.message || e.data.msg || e.statusText, + message: message || data.msg || statusText, ellipsisLine: 2, ellipsisCopy: true }); @@ -326,9 +380,11 @@ */ handleViewDetail (payload) { if (payload.role && payload.role.name) { + const { name, id } = payload.role; this.isShowGradeSlider = true; - this.gradeSliderTitle = `【${payload.role.name}】${this.$t(`m.grading['一级管理空间']`)} ${this.$t(`m.common['成员']`)}`; - this.fetchRoles(payload.role.id); + this.gradeSliderTitle = `【${name}】${this.$t(`m.grading['一级管理空间']`)} ${this.$t(`m.common['成员']`)}`; + this.curRoleId = id; + this.fetchRoles(id); } } } diff --git a/frontend/src/views/user/components/group-perm.vue b/frontend/src/views/user/components/group-perm.vue index 0be386075..a9f7a0087 100644 --- a/frontend/src/views/user/components/group-perm.vue +++ b/frontend/src/views/user/components/group-perm.vue @@ -56,6 +56,15 @@ + @@ -106,6 +115,16 @@ +

{{ $t(`m.permApply['请选择用户组']`) }}

@@ -149,7 +168,7 @@ - + + + diff --git a/frontend/src/views/user/components/record-list.vue b/frontend/src/views/user/components/record-list.vue index cb8f1a30e..a6ee0cef0 100644 --- a/frontend/src/views/user/components/record-list.vue +++ b/frontend/src/views/user/components/record-list.vue @@ -1,322 +1,343 @@ - - - + + + + diff --git a/frontend/src/views/user/components/render-depart.vue b/frontend/src/views/user/components/render-depart.vue index 81614356f..137dab5ad 100644 --- a/frontend/src/views/user/components/render-depart.vue +++ b/frontend/src/views/user/components/render-depart.vue @@ -1,138 +1,138 @@ - - - + + + diff --git a/frontend/src/views/user/components/template-perm.vue b/frontend/src/views/user/components/template-perm.vue index 40110ea70..5bec848f8 100644 --- a/frontend/src/views/user/components/template-perm.vue +++ b/frontend/src/views/user/components/template-perm.vue @@ -1,399 +1,422 @@ - - - + + + diff --git a/frontend/src/views/user/components/teporary-custom-perm.vue b/frontend/src/views/user/components/teporary-custom-perm.vue index 1b7445306..df5b43581 100644 --- a/frontend/src/views/user/components/teporary-custom-perm.vue +++ b/frontend/src/views/user/components/teporary-custom-perm.vue @@ -1,132 +1,151 @@ - - - + + + diff --git a/frontend/src/views/user/index.vue b/frontend/src/views/user/index.vue index 992b0edc6..68cc52e1e 100644 --- a/frontend/src/views/user/index.vue +++ b/frontend/src/views/user/index.vue @@ -68,12 +68,14 @@ :all-data="treeList" :style="contentStyle" :is-sync="isSync" + :empty-data="emptyData" location="page" @async-load-nodes="handleRemoteLoadNode" @expand-node="handleExpanded" @on-select="handleOnSelected" - @on-click="handleOnClick"> - + @on-click="handleOnClick" + @on-refresh="handleEmptyRefresh" + />