Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 接入管理类接口支持超管角色 #2264 #2575

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion saas/backend/api/admin/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
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 aenum import auto, skip
from aenum import auto, skip, LowerStrEnum

from backend.api.constants import BaseAPIEnum
from backend.util.enum import ChoicesEnum


class AdminAPIEnum(BaseAPIEnum):
Expand Down Expand Up @@ -44,6 +45,11 @@ class AdminAPIEnum(BaseAPIEnum):
# 是否有权限数据
SUBJECT_PERMISSION_EXISTS = auto()

# 分级管理员
GRADE_MANAGER_LIST = auto()
GRADE_MANAGER_CREATE = auto()
GRADE_MANAGER_UPDATE = auto()

_choices_labels = skip(
(
(SYSTEM_LIST, "获取系统列表"),
Expand All @@ -57,5 +63,22 @@ class AdminAPIEnum(BaseAPIEnum):
(SUBJECT_FREEZE_UNFREEZE, "冻结/解冻Subject"),
(SUBJECT_PERMISSION_CLEANUP, "权限清理"),
(SUBJECT_PERMISSION_EXISTS, "权限是否存在"),
(GRADE_MANAGER_LIST, "获取分级管理员列表"),
(GRADE_MANAGER_CREATE, "新建分级管理员"),
(GRADE_MANAGER_UPDATE, "更新分级管理员"),
)
)


class VerifyApiParamLocationEnum(ChoicesEnum, LowerStrEnum):
ROLE_IN_PATH = auto()
SYSTEM_IN_BODY = auto()
SYSTEM_IN_QUERY = auto()

_choices_labels = skip(
(
(ROLE_IN_PATH, "在URL里的role id参数"),
(SYSTEM_IN_BODY, "在body data里的system参数"),
(SYSTEM_IN_QUERY, "在get请求query里的system参数"),
)
)
12 changes: 11 additions & 1 deletion saas/backend/api/admin/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from django_filters import rest_framework as filters

from backend.apps.group.models import Group
from backend.apps.role.models import RoleRelatedObject
from backend.apps.role.models import RoleRelatedObject, Role
from backend.service.constants import RoleRelatedObjectType


Expand All @@ -28,3 +28,13 @@ class Meta:
def grade_manager_id_filter(self, queryset, name, value):
group_ids = RoleRelatedObject.objects.list_role_object_ids(value, RoleRelatedObjectType.GROUP.value)
return queryset.filter(id__in=group_ids)


class GradeManagerFilter(filters.FilterSet):
name = filters.CharFilter(label="名字", lookup_expr="icontains")

class Meta:
model = Role
fields = [
"name",
]
36 changes: 36 additions & 0 deletions saas/backend/api/admin/migrations/0002_auto_20240321_1557.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Generated by Django 3.2.16 on 2024-03-21 07:57

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api_admin', '0001_initial'),
]

operations = [
migrations.AlterField(
model_name='adminapiallowlistconfig',
name='api',
field=models.CharField(choices=[('system_list', '获取系统列表'), ('group_list', '获取用户组列表'), ('group_member_list', '获取用户组成员列表'), ('subject_joined_group_list', '获取Subject加入的用户组列表'), ('subject_role_list', '获取Subject角色列表'), ('role_super_manager_member_list', '获取超级管理员成员列表'), ('role_system_manager_member_list', '获取系统管理员及成员列表'), ('audit_event_list', '获取审计事件列表'), ('subject_freeze_unfreeze', '冻结/解冻Subject'), ('subject_permission_cleanup', '权限清理'), ('subject_permission_exists', '权限是否存在'), ('grade_manager_list', '获取分级管理员列表'), ('grade_manager_create', '新建分级管理员'), ('grade_manager_update', '更新分级管理员')], help_text='*代表任意', max_length=32, verbose_name='API'),
),
migrations.CreateModel(
name='SystemAllowAuthSystem',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('creator', models.CharField(max_length=64, verbose_name='创建者')),
('updater', models.CharField(max_length=64, verbose_name='更新者')),
('created_time', models.DateTimeField(auto_now_add=True)),
('updated_time', models.DateTimeField(auto_now=True)),
('system_id', models.CharField(max_length=32, verbose_name='接入系统')),
('auth_system_id', models.CharField(help_text='*代表任意', max_length=32, verbose_name='接入系统')),
],
options={
'verbose_name': '系统允许授权的系统',
'verbose_name_plural': '系统允许授权的系统',
'ordering': ['-id'],
'index_together': {('system_id', 'auth_system_id')},
},
),
]
52 changes: 52 additions & 0 deletions saas/backend/api/admin/mixins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# -*- 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 typing import List

from rest_framework import exceptions

from backend.api.constants import ALLOW_ANY
from backend.api.mixins import SystemClientCheckMixin

from backend.api.admin.models import SystemAllowAuthSystem


class SuperManagerAPIPermissionCheckMixin(SystemClientCheckMixin):
"""
管理类-超管角色API认证与鉴权
"""
def verify_system_scope(self, system_id: str, auth_system_ids: List[str]):
"""
API数据鉴权:查询系统可管控的授权系统表,校验接入系统是否越权提交了其他系统的数据
主要是针对分级管理员创建和更新时的可授权范围
"""
# 加速判断:大部分系统都只是管理自身系统的权限,即可授权范围里的每个系统都只能等于自身
if all([sys_id == system_id for sys_id in auth_system_ids]):
return

# 接入系统可管控的系统表[system_id/auth_system_id]来实现管控更多接入系统权限
allowed_auth_system = SystemAllowAuthSystem.list_auth_system_id(system_id)
allowed_system_ids = set(allowed_auth_system)
# 任何系统都允许访问自身
allowed_system_ids.add(system_id)

# 如果允许管理的系统存在任意,则说明该系统可管理所有系统,鉴权直接通过
if ALLOW_ANY in allowed_system_ids:
return

# 遍历校验
for sys_id in auth_system_ids:
if sys_id not in allowed_system_ids:
raise exceptions.PermissionDenied(
detail=(
f"system[{system_id}] is not allow to operate system[{sys_id}]'s permission data, "
"please contact the developer to add a whitelist"
)
)
21 changes: 21 additions & 0 deletions saas/backend/api/admin/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from django.db import models

from backend.api.constants import ALLOW_ANY
from backend.common.cache import cachedmethod
from backend.common.models import BaseModel

from .constants import AdminAPIEnum
Expand All @@ -36,3 +37,23 @@ def is_allowed(cls, app_code: str, api: str):
由于支持配置任意,所以判断还需要判断是否包含了任意
"""
return cls.objects.filter(app_code=app_code, api__in=[ALLOW_ANY, api]).exists()


class SystemAllowAuthSystem(BaseModel):
"""系统允许授权的系统
即可配置某个系统管理其他系统的权限
"""

system_id = models.CharField("接入系统", max_length=32)
auth_system_id = models.CharField("接入系统", max_length=32, help_text="*代表任意")

class Meta:
verbose_name = "系统允许授权的系统"
verbose_name_plural = "系统允许授权的系统"
ordering = ["-id"]
index_together = ["system_id", "auth_system_id"]

@classmethod
@cachedmethod(timeout=5 * 60) # 缓存5分钟
def list_auth_system_id(cls, system_id: str):
return list(cls.objects.filter(system_id=system_id).values_list("auth_system_id", flat=True))
30 changes: 29 additions & 1 deletion saas/backend/api/admin/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,17 @@
"""
from rest_framework import serializers

from backend.api.management.v1.serializers import (
ManagementGradeManagerBasicInfoSLZ,
ManagementGradeManagerUpdateSLZ,
ManagementGradeManagerCreateSLZ,
ManagementSourceSystemSLZ,
)
from backend.apps.group.models import Group
from backend.apps.role.models import Role
from backend.apps.role.serializers import BaseGradeMangerSLZ
from backend.apps.role.serializers import (
BaseGradeMangerSLZ,
)
from backend.service.constants import GroupMemberType, RoleType


Expand Down Expand Up @@ -73,3 +81,23 @@ class SubjectSLZ(serializers.Serializer):
class FreezeSubjectResponseSLZ(serializers.Serializer):
type = serializers.CharField(label="SubjectType")
id = serializers.CharField(label="SubjectID")


class SuperManagerSourceSystemSLZ(ManagementSourceSystemSLZ):
class Meta:
ref_name = "V1SuperManagerSourceSystemSLZ"


class SuperManagerGradeManagerCreateSLZ(ManagementGradeManagerCreateSLZ):
class Meta:
ref_name = "V1SuperManagerGradeManagerCreateSLZ"


class SuperManagerGradeManagerUpdateSLZ(ManagementGradeManagerUpdateSLZ):
class Meta:
ref_name = "V1SuperManagerGradeManagerUpdateSLZ"


class SuperManagerGradeManagerBasicInfoSLZ(ManagementGradeManagerBasicInfoSLZ):
class Meta:
ref_name = "V1SuperManagerGradeManagerBasicInfoSLZ"
20 changes: 18 additions & 2 deletions saas/backend/api/admin/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,28 @@
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 django.urls import path
from django.urls import path, include

from . import views

urlpatterns = [
# 用户组
path(
"super_managers/",
include(
[
path(
"grade_managers/",
views.SuperManagerGradeManagerViewSet.as_view({"get": "list", "post": "create"}),
name="open.admin.super_manager.grade_manager",
),
path(
"grade_managers/<int:id>/",
views.SuperManagerGradeManagerViewSet.as_view({"put": "update"}),
name="open.admin.super_manager.grade_manager",
),
]
),
),
path("groups/", views.AdminGroupViewSet.as_view({"get": "list"}), name="open.admin.group"),
# 用户组成员
path(
Expand Down
2 changes: 2 additions & 0 deletions saas/backend/api/admin/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
specific language governing permissions and limitations under the License.
"""
from .audit import AdminAuditEventViewSet
from .grade_manager import SuperManagerGradeManagerViewSet
from .group import AdminGroupMemberViewSet, AdminGroupViewSet
from .role import AdminSuperManagerMemberViewSet, AdminSystemManagerMemberViewSet
from .subject import (
Expand All @@ -32,4 +33,5 @@
"AdminSubjectFreezeViewSet",
"AdminSubjectPermissionCleanupViewSet",
"AdminSubjectPermissionExistsViewSet",
"SuperManagerGradeManagerViewSet"
]
Loading
Loading