diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index d6f4130ea..ec51dac30 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -495,6 +495,11 @@
.main-scroller {
height: calc(100% + 91px);
}
+ .my-perm-container {
+ .main-scroller {
+ height: calc(100% + 51px);
+ }
+ }
.user-org-perm-container {
.main-scroller {
height: calc(100% + 312px);
diff --git a/frontend/src/common/router-handle.js b/frontend/src/common/router-handle.js
index 8bb1d1f28..342c54bc5 100644
--- a/frontend/src/common/router-handle.js
+++ b/frontend/src/common/router-handle.js
@@ -298,7 +298,6 @@ export const getNavRouterDiff = (navIndex, managerPerm = '') => {
'addGroupPerm',
'resourcePermiss',
'ratingManager',
- 'gradingAdminCreate',
'gradingAdminDetail',
'gradingAdminEdit',
'gradingAdminUpdateTemplate',
diff --git a/frontend/src/components/header-nav/index.vue b/frontend/src/components/header-nav/index.vue
index 445307f0f..43a9ab64a 100644
--- a/frontend/src/components/header-nav/index.vue
+++ b/frontend/src/components/header-nav/index.vue
@@ -664,10 +664,7 @@
window.localStorage.removeItem('iam-header-title-cache');
window.localStorage.removeItem('iam-header-name-cache');
window.localStorage.removeItem('applyGroupList');
- const loginUrl = new URL(`${window.LOGIN_SERVICE_URL}/`);
- loginUrl.searchParams.append('c_url', encodeURIComponent(window.location.href));
- loginUrl.searchParams.append('is_from_logout', 1);
- window.location = loginUrl.href;
+ window.location = `${window.LOGIN_SERVICE_URL}/?c_url=${encodeURIComponent(window.location.href)}&is_from_logout=1`;
},
handleManager () {
diff --git a/frontend/src/store/index.js b/frontend/src/store/index.js
index 2e16120b8..e318cc55f 100644
--- a/frontend/src/store/index.js
+++ b/frontend/src/store/index.js
@@ -471,7 +471,8 @@ const store = new Vuex.Store({
// 设置项目最大可授权范围
addMemberBoundary: {
customFooterClass: false, // 设置项目最大可授权范围, 底部插槽自定义样式
- hideInfiniteTreeCount: false // 隐藏设置项目最大可授权范围左边拓扑树显示成员个数
+ hideInfiniteTreeCount: false, // 隐藏设置项目最大可授权范围左边拓扑树显示成员个数
+ hideScopeRangeEntry: false // 隐藏跳转到授权边界页面入口
}
},
curTreeTableDataIndex: 0,
@@ -950,7 +951,8 @@ const store = new Vuex.Store({
// 设置项目最大可授权范围
addMemberBoundary: {
customFooterClass: true, // 设置项目最大可授权范围, 底部插槽自定义样式
- hideInfiniteTreeCount: true// 隐藏设置项目最大可授权范围左边拓扑树显示成员个数
+ hideInfiniteTreeCount: true, // 隐藏设置项目最大可授权范围左边拓扑树显示成员个数
+ hideScopeRangeEntry: true // 隐藏跳转到授权边界页面入口
}
};
commit('setExternalSystemsLayout', externalSystemsLayout);
diff --git a/frontend/src/views/group/components/iam-add-member.vue b/frontend/src/views/group/components/iam-add-member.vue
index 04a2acb72..b51668839 100644
--- a/frontend/src/views/group/components/iam-add-member.vue
+++ b/frontend/src/views/group/components/iam-add-member.vue
@@ -196,7 +196,7 @@
{{ $t(`m.common['手动输入提示1']`) }}
{{ $t(`m.common['手动输入提示2']`) }}
-
+
{{ $t(`m.common[',']`) }}{{ $t(`m.common['请尝试']`)
}}{{ $t(`m.common['修改授权人员范围']`) }}
@@ -610,7 +610,7 @@
};
},
computed: {
- ...mapGetters(['user', 'externalSystemId']),
+ ...mapGetters(['user', 'externalSystemId', 'externalSystemsLayout']),
isLoading () {
return this.requestQueue.length > 0;
},
@@ -714,6 +714,9 @@
// const roleId = navCurRoleId || curRoleId;
return this.user.role || {};
},
+ isShowScopeEntry () {
+ return this.isHierarchicalAdmin.type === 'rating_manager' && !this.externalSystemsLayout.addMemberBoundary.hideScopeRangeEntry;
+ },
nameType () {
return (payload) => {
const { name, type, username, full_name: fullName } = payload;
diff --git a/frontend/src/views/manage-spaces/secondary-manage-space/components/iam-add-member.vue b/frontend/src/views/manage-spaces/secondary-manage-space/components/iam-add-member.vue
index b0851f2a8..8393b0077 100644
--- a/frontend/src/views/manage-spaces/secondary-manage-space/components/iam-add-member.vue
+++ b/frontend/src/views/manage-spaces/secondary-manage-space/components/iam-add-member.vue
@@ -170,8 +170,8 @@
{{ $t(`m.common['手动输入提示1']`) }}
{{ $t(`m.common['手动输入提示2']`) }}
-
- ,{{ $t(`m.common['请尝试']`) }}{{ $t(`m.common['修改授权人员范围']`) }}
+
+ {{ $t(`m.common[',']`) }}{{ $t(`m.common['请尝试']`) }}{{ $t(`m.common['修改授权人员范围']`) }}
0;
},
@@ -442,6 +442,9 @@
isSelectedEmpty () {
return this.hasSelectedDepartments.length < 1 && this.hasSelectedUsers.length < 1;
},
+ isShowScopeEntry () {
+ return this.isHierarchicalAdmin.type === 'rating_manager' && !this.externalSystemsLayout.addMemberBoundary.hideScopeRangeEntry;
+ },
style () {
if (this.showExpiredAt) {
if (this.isPrev) {
diff --git a/frontend/src/views/my-manage-space/add-member-boundary/index.vue b/frontend/src/views/my-manage-space/add-member-boundary/index.vue
index e9819a201..40b257614 100644
--- a/frontend/src/views/my-manage-space/add-member-boundary/index.vue
+++ b/frontend/src/views/my-manage-space/add-member-boundary/index.vue
@@ -182,7 +182,7 @@
{{ $t(`m.common['手动输入提示2']`) }}
-
+
{{ $t(`m.common[',']`) }}
{{ $t(`m.common['请尝试']`)
}}
@@ -577,6 +577,9 @@
isOrganization () {
return this.tabActive === 'organization';
},
+ isShowScopeEntry () {
+ return this.isHierarchicalAdmin.type === 'rating_manager' && !this.externalSystemsLayout.addMemberBoundary.hideScopeRangeEntry;
+ },
isManualInputOverLimit () {
if (!this.manualValue) {
return false;
diff --git a/saas/VERSION b/saas/VERSION
index 63221cd37..f169d883c 100644
--- a/saas/VERSION
+++ b/saas/VERSION
@@ -1 +1 @@
-1.10.30
+1.10.31
diff --git a/saas/backend/api/admin/constants.py b/saas/backend/api/admin/constants.py
index 9e934441f..b889e6d52 100644
--- a/saas/backend/api/admin/constants.py
+++ b/saas/backend/api/admin/constants.py
@@ -25,11 +25,13 @@ class AdminAPIEnum(BaseAPIEnum):
# 用户组成员
GROUP_MEMBER_LIST = auto()
+ GROUP_MEMBER_ADD = auto()
# 用户组权限
GROUP_POLICY_GRANT = auto()
# 模板
+ TEMPLATE_LIST = auto()
TEMPLATE_CREATE = auto()
# Subject
diff --git a/saas/backend/api/admin/serializers.py b/saas/backend/api/admin/serializers.py
index 734703944..f1dd3374b 100644
--- a/saas/backend/api/admin/serializers.py
+++ b/saas/backend/api/admin/serializers.py
@@ -12,10 +12,10 @@
from backend.api.management.v2.serializers import ManagementGradeManagerGroupCreateSLZ
from backend.apps.group.models import Group
-from backend.apps.group.serializers import GroupAuthorizationSLZ
+from backend.apps.group.serializers import GroupAddMemberSLZ, GroupAuthorizationSLZ
from backend.apps.role.models import Role
from backend.apps.role.serializers import BaseGradeMangerSLZ
-from backend.apps.template.serializers import TemplateCreateSLZ, TemplateIdSLZ
+from backend.apps.template.serializers import TemplateCreateSLZ, TemplateIdSLZ, TemplateListSchemaSLZ, TemplateListSLZ
from backend.service.constants import GroupMemberType, RoleType
@@ -36,6 +36,10 @@ class AdminGroupMemberSLZ(serializers.Serializer):
expired_at = serializers.IntegerField(label="过期时间戳(单位秒)")
+class AdminGroupAddMemberSLZ(GroupAddMemberSLZ):
+ pass
+
+
class AdminSubjectGroupSLZ(serializers.Serializer):
id = serializers.CharField(label="用户组id")
name = serializers.CharField(label="用户组名称")
@@ -91,6 +95,14 @@ class FreezeSubjectResponseSLZ(serializers.Serializer):
id = serializers.CharField(label="SubjectID")
+class AdminTemplateListSchemaSLZ(TemplateListSchemaSLZ):
+ pass
+
+
+class AdminTemplateListSLZ(TemplateListSLZ):
+ pass
+
+
class AdminTemplateCreateSLZ(TemplateCreateSLZ):
pass
diff --git a/saas/backend/api/admin/urls.py b/saas/backend/api/admin/urls.py
index 5edcfd91e..5faa712b2 100644
--- a/saas/backend/api/admin/urls.py
+++ b/saas/backend/api/admin/urls.py
@@ -24,7 +24,7 @@
# 用户组成员
path(
"groups//members/",
- views.AdminGroupMemberViewSet.as_view({"get": "list"}),
+ views.AdminGroupMemberViewSet.as_view({"get": "list", "post": "create"}),
name="open.admin.group_member",
),
# 用户组授权
@@ -34,7 +34,11 @@
name="open.admin.group_policy",
),
# 模板
- path("templates/", views.AdminTemplateViewSet.as_view({"post": "create"}), name="open.admin.template"),
+ path(
+ "templates/",
+ views.AdminTemplateViewSet.as_view({"get": "list", "post": "create"}),
+ name="open.admin.template",
+ ),
# Subject
path(
"subjects///groups/",
diff --git a/saas/backend/api/admin/views/group.py b/saas/backend/api/admin/views/group.py
index f9879d99f..e8e624762 100644
--- a/saas/backend/api/admin/views/group.py
+++ b/saas/backend/api/admin/views/group.py
@@ -20,6 +20,7 @@
from backend.api.admin.filters import GroupFilter
from backend.api.admin.permissions import AdminAPIPermission
from backend.api.admin.serializers import (
+ AdminGroupAddMemberSLZ,
AdminGroupAuthorizationSLZ,
AdminGroupBasicSLZ,
AdminGroupCreateSLZ,
@@ -27,7 +28,11 @@
)
from backend.api.authentication import ESBAuthentication
from backend.api.management.v2.views import ManagementGroupViewSet
-from backend.apps.group.audit import GroupCreateAuditProvider, GroupTemplateCreateAuditProvider
+from backend.apps.group.audit import (
+ GroupCreateAuditProvider,
+ GroupMemberCreateAuditProvider,
+ GroupTemplateCreateAuditProvider,
+)
from backend.apps.group.constants import OperateEnum
from backend.apps.group.models import Group
from backend.apps.group.views import check_readonly_group
@@ -36,9 +41,11 @@
from backend.audit.constants import AuditSourceType
from backend.biz.group import GroupBiz, GroupCheckBiz, GroupCreationBean
from backend.biz.role import RoleBiz
+from backend.biz.utils import remove_not_exist_subject
from backend.common.lock import gen_group_upsert_lock
from backend.common.pagination import CompatiblePagination
from backend.service.constants import GroupSaaSAttributeEnum, RoleType
+from backend.service.models import Subject
from backend.trans.group import GroupTrans
@@ -130,13 +137,17 @@ class AdminGroupMemberViewSet(GenericViewSet):
authentication_classes = [ESBAuthentication]
permission_classes = [AdminAPIPermission]
- admin_api_permission = {"list": AdminAPIEnum.GROUP_MEMBER_LIST.value}
+ admin_api_permission = {
+ "list": AdminAPIEnum.GROUP_MEMBER_LIST.value,
+ "create": AdminAPIEnum.GROUP_MEMBER_ADD.value,
+ }
queryset = Group.objects.all()
lookup_field = "id"
pagination_class = CompatiblePagination
biz = GroupBiz()
+ group_check_biz = GroupCheckBiz()
@swagger_auto_schema(
operation_description="用户组成员列表",
@@ -153,6 +164,41 @@ def list(self, request, *args, **kwargs):
results = [one.dict(include={"type", "id", "name", "expired_at"}) for one in group_members]
return Response({"count": count, "results": results})
+ @swagger_auto_schema(
+ operation_description="用户组添加成员",
+ request_body=AdminGroupAddMemberSLZ(label="用户组成员"),
+ responses={status.HTTP_200_OK: serializers.Serializer()},
+ tags=["admin.group.member"],
+ )
+ @view_audit_decorator(GroupMemberCreateAuditProvider)
+ def create(self, request, *args, **kwargs):
+ group = self.get_object()
+
+ serializer = AdminGroupAddMemberSLZ(data=request.data)
+ serializer.is_valid(raise_exception=True)
+ data = serializer.validated_data
+
+ members_data = data["members"]
+ expired_at = data["expired_at"]
+ # 成员Dict结构转换为Subject结构,并去重
+ members = list(set(parse_obj_as(List[Subject], members_data)))
+
+ # 检测成员是否满足管理的授权范围
+ role = Role.objects.get(type=RoleType.SUPER_MANAGER.value)
+ self.group_check_biz.check_role_subject_scope(role, members)
+ self.group_check_biz.check_member_count(group.id, len(members))
+
+ # 排除组织架构中不存在的成员
+ members = remove_not_exist_subject(members)
+ if members:
+ # 添加成员
+ self.biz.add_members(group.id, members, expired_at)
+
+ # 写入审计上下文
+ audit_context_setter(group=group, members=[m.dict() for m in members])
+
+ return Response({})
+
class AdminGroupPolicyViewSet(GenericViewSet):
"""用户组授权"""
diff --git a/saas/backend/api/admin/views/template.py b/saas/backend/api/admin/views/template.py
index c499d68b8..5fc94bbd8 100644
--- a/saas/backend/api/admin/views/template.py
+++ b/saas/backend/api/admin/views/template.py
@@ -1,3 +1,13 @@
+# -*- coding: utf-8 -*-
+"""
+TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available.
+Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved.
+Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
+You may obtain a copy of the License at http://opensource.org/licenses/MIT
+Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+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 drf_yasg.utils import swagger_auto_schema
from rest_framework import status
from rest_framework.response import Response
@@ -5,28 +15,62 @@
from backend.api.admin.constants import AdminAPIEnum
from backend.api.admin.permissions import AdminAPIPermission
-from backend.api.admin.serializers import AdminTemplateCreateSLZ, AdminTemplateIdSLZ
+from backend.api.admin.serializers import (
+ AdminTemplateCreateSLZ,
+ AdminTemplateIdSLZ,
+ AdminTemplateListSchemaSLZ,
+ AdminTemplateListSLZ,
+)
from backend.api.authentication import ESBAuthentication
from backend.apps.role.models import Role
from backend.apps.template.audit import TemplateCreateAuditProvider
+from backend.apps.template.views import TemplateQueryMixin
from backend.audit.audit import audit_context_setter, view_audit_decorator
-from backend.biz.role import RoleAuthorizationScopeChecker
+from backend.biz.role import RoleAuthorizationScopeChecker, RoleListQuery
from backend.biz.template import TemplateBiz, TemplateCheckBiz, TemplateCreateBean
from backend.common.lock import gen_template_upsert_lock
from backend.service.constants import RoleType
-class AdminTemplateViewSet(GenericViewSet):
+class AdminTemplateViewSet(TemplateQueryMixin, GenericViewSet):
"""模板"""
authentication_classes = [ESBAuthentication]
permission_classes = [AdminAPIPermission]
- admin_api_permission = {"create": AdminAPIEnum.TEMPLATE_CREATE.value}
+ admin_api_permission = {
+ "list": AdminAPIEnum.TEMPLATE_LIST.value,
+ "create": AdminAPIEnum.TEMPLATE_CREATE.value,
+ }
template_biz = TemplateBiz()
template_check_biz = TemplateCheckBiz()
+ @swagger_auto_schema(
+ operation_description="模板列表",
+ responses={status.HTTP_200_OK: AdminTemplateListSchemaSLZ(label="模板", many=True)},
+ tags=["admin.template"],
+ )
+ def list(self, request, *args, **kwargs):
+ role = Role.objects.get(type=RoleType.SUPER_MANAGER.value)
+ queryset = RoleListQuery(role, request.user).query_template()
+
+ # 查询 role 的 system-actions set
+ role_system_actions = RoleListQuery(role).get_scope_system_actions()
+
+ # 强制分页
+ paginator = self.pagination_class()
+ page = paginator.paginate_queryset(queryset, request, view=self)
+
+ if page is None:
+ return Response(
+ {"detail": "Pagination is required, but no valid page parameters were provided."},
+ status=status.HTTP_400_BAD_REQUEST,
+ )
+
+ serializer = AdminTemplateListSLZ(page, many=True, role_system_actions=role_system_actions)
+ return paginator.get_paginated_response(serializer.data)
+
@swagger_auto_schema(
operation_description="创建模板",
request_body=AdminTemplateCreateSLZ(label="模板"),
diff --git a/saas/resources/apigateway/bk_apigw_resources_bk-iam.yaml b/saas/resources/apigateway/bk_apigw_resources_bk-iam.yaml
index 103fb6d82..38b5f3a33 100644
--- a/saas/resources/apigateway/bk_apigw_resources_bk-iam.yaml
+++ b/saas/resources/apigateway/bk_apigw_resources_bk-iam.yaml
@@ -3233,6 +3233,58 @@ paths:
disabledStages: [ ]
descriptionEn:
/api/v1/open/admin/groups/{id}/policies/:
+ post:
+ operationId: admin_groups_policies_grant
+ description: 超管授权用户组
+ tags:
+ - open
+ responses:
+ default:
+ description: ''
+ x-bk-apigateway-resource:
+ isPublic: true
+ allowApplyPermission: true
+ matchSubpath: false
+ backend:
+ type: HTTP
+ method: post
+ path: /api/v1/open/admin/groups/{id}/policies/
+ matchSubpath: false
+ timeout: 0
+ upstreams: { }
+ transformHeaders: { }
+ authConfig:
+ userVerifiedRequired: false
+ resourcePermissionRequired: false
+ disabledStages: [ ]
+ descriptionEn:
+ /api/v1/open/admin/groups/{id}/members/:
+ post:
+ operationId: admin_add_group_members
+ description: 超管用户组添加成员
+ tags:
+ - open
+ - v2
+ responses:
+ default:
+ description: ''
+ x-bk-apigateway-resource:
+ isPublic: true
+ allowApplyPermission: true
+ matchSubpath: false
+ backend:
+ type: HTTP
+ method: post
+ path: /api/v1/open/admin/groups/{id}/members/
+ matchSubpath: false
+ timeout: 0
+ upstreams: {}
+ transformHeaders: {}
+ authConfig:
+ userVerifiedRequired: false
+ resourcePermissionRequired: false
+ disabledStages: []
+ descriptionEn:
post:
operationId: admin_groups_policies_grant
description: 超管授权用户组
@@ -3394,3 +3446,53 @@ paths:
resourcePermissionRequired: false
disabledStages: [ ]
descriptionEn:
+ get:
+ operationId: admin_list_templates
+ description: 超管获取模板列表
+ tags:
+ - open
+ responses:
+ default:
+ description: ''
+ x-bk-apigateway-resource:
+ isPublic: true
+ allowApplyPermission: true
+ matchSubpath: false
+ backend:
+ type: HTTP
+ method: get
+ path: /api/v1/open/admin/templates/
+ matchSubpath: false
+ timeout: 0
+ upstreams: { }
+ transformHeaders: { }
+ authConfig:
+ userVerifiedRequired: false
+ resourcePermissionRequired: false
+ disabledStages: [ ]
+ descriptionEn:
+ post:
+ operationId: admin_create_templates
+ description: 超管创建模板
+ tags:
+ - open
+ responses:
+ default:
+ description: ''
+ x-bk-apigateway-resource:
+ isPublic: true
+ allowApplyPermission: true
+ matchSubpath: false
+ backend:
+ type: HTTP
+ method: post
+ path: /api/v1/open/admin/templates/
+ matchSubpath: false
+ timeout: 0
+ upstreams: { }
+ transformHeaders: { }
+ authConfig:
+ userVerifiedRequired: false
+ resourcePermissionRequired: false
+ disabledStages: [ ]
+ descriptionEn:
diff --git a/saas/resources/version_log/change_log.md b/saas/resources/version_log/change_log.md
index e692f630f..9c0d57b24 100644
--- a/saas/resources/version_log/change_log.md
+++ b/saas/resources/version_log/change_log.md
@@ -1,3 +1,17 @@
+
+# V1.10.31 版本更新日志
+
+### 新增功能
+
+* 超级管理类 API - 模板列表和用户组添加成员
+
+### 缺陷修复
+
+* 蓝盾内嵌页面隐藏跳转到授权边界入口
+* 修复退出登录重定向地址转义多次
+
+---
+
# V1.10.30 版本更新日志
diff --git a/saas/resources/version_log/change_log_en.md b/saas/resources/version_log/change_log_en.md
index c6e0d0549..c7ba4c872 100644
--- a/saas/resources/version_log/change_log_en.md
+++ b/saas/resources/version_log/change_log_en.md
@@ -1,3 +1,17 @@
+
+# V1.10.31 Version Update Log
+
+### New Features
+
+* Super Admin API: Added template list and the ability to add members to user groups.
+
+### Bug Fixes
+
+* Embedded pages in BlueKing now hide the link to the authorization boundary entry.
+* Fixed multiple escapes of the redirect URL upon logout.
+
+---
+
# V1.10.30 Version Update Log