From 0255f4d659ef00d0643464f72599f9d7fc8eb186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E4=BD=A9=E7=8F=8A=5Bshiisa=5D?= Date: Thu, 27 Jun 2024 14:18:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E8=B6=85=E7=AE=A1=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=A8=A1=E6=9D=BF=E5=88=97=E8=A1=A8=E5=92=8C?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=BB=84=E6=B7=BB=E5=8A=A0=E6=88=90=E5=91=98?= =?UTF-8?q?API=20#2409?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- saas/backend/api/admin/constants.py | 2 + saas/backend/api/admin/serializers.py | 16 +++++- saas/backend/api/admin/urls.py | 8 ++- saas/backend/api/admin/views/group.py | 50 +++++++++++++++++- saas/backend/api/admin/views/template.py | 47 +++++++++++++++-- .../apigateway/bk_apigw_resources_bk-iam.yaml | 52 +++++++++++++++++++ 6 files changed, 165 insertions(+), 10 deletions(-) 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..5ea930e22 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,57 @@ 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() + page = self.paginate_queryset(queryset) + + if page is not None: + serializer = AdminTemplateListSLZ(page, many=True, role_system_actions=role_system_actions) + return self.get_paginated_response(serializer.data) + + serializer = AdminTemplateListSLZ(queryset, many=True, role_system_actions=role_system_actions) + return 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 35e04ed12..adc79d21f 100644 --- a/saas/resources/apigateway/bk_apigw_resources_bk-iam.yaml +++ b/saas/resources/apigateway/bk_apigw_resources_bk-iam.yaml @@ -3258,6 +3258,33 @@ paths: 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: /api/v1/open/admin/systems/{system_id}/provider_config/: get: operationId: admin_list_provider_config @@ -3285,6 +3312,31 @@ paths: disabledStages: [ ] descriptionEn: /api/v1/open/admin/templates/: + 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: 超管创建模板