diff --git a/dbm-ui/backend/bk_web/viewsets.py b/dbm-ui/backend/bk_web/viewsets.py index c5907f4b21..3b127f1826 100644 --- a/dbm-ui/backend/bk_web/viewsets.py +++ b/dbm-ui/backend/bk_web/viewsets.py @@ -9,11 +9,11 @@ specific language governing permissions and limitations under the License. """ import copy -from typing import Any, Dict, Optional +from typing import Any, Dict, List, Optional, Tuple, Union from blueapps.account.decorators import login_exempt from django.utils.decorators import classonlymethod -from rest_framework import serializers, status, viewsets +from rest_framework import permissions, serializers, status, viewsets from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet @@ -23,7 +23,11 @@ class GenericMixin: queryset = "" # 是否支持全局豁免 - global_login_exempt = False + global_login_exempt: bool = False + # 权限动作映射表。如果权限映射逻辑复杂,可考虑覆写_get_custom_permissions + action_permission_map: Dict[Union[Tuple[str], str], List[permissions.BasePermission]] = {} + # ⚠️为了避免权限泄露,希望默认权限是永假来兜底,所以请定义好每个视图的权限类 + default_permission_class: List[permissions.BasePermission] = [RejectPermission()] @staticmethod def get_request_data(request, **kwargs) -> Dict[str, Any]: @@ -31,6 +35,12 @@ def get_request_data(request, **kwargs) -> Dict[str, Any]: request_data.update(**kwargs) return request_data + def get_action_permission_map(self) -> dict: + return self.action_permission_map + + def get_default_permission_class(self) -> list: + return self.default_permission_class + @property def validated_data(self): """ @@ -98,7 +108,10 @@ def get_permissions(self): def _get_custom_permissions(self): """用户自定义的permission类,由子类继承覆写""" # ⚠️为了避免权限泄露,希望默认权限是永假来兜底,所以请写每一个视图的时候都覆写该方法 - return [RejectPermission()] + for actions, custom_perms in self.get_action_permission_map().items(): + if actions == self.action or self.action in actions: + return custom_perms + return self.get_default_permission_class() @classmethod def _get_login_exempt_view_func(cls): diff --git a/dbm-ui/backend/components/mysql_priv_manager/client.py b/dbm-ui/backend/components/mysql_priv_manager/client.py index 27ca3ae1fa..e3fb458526 100644 --- a/dbm-ui/backend/components/mysql_priv_manager/client.py +++ b/dbm-ui/backend/components/mysql_priv_manager/client.py @@ -67,6 +67,11 @@ def __init__(self): url="/priv/modify_account", description=_("修改账号的密码"), ) + self.get_account = self.generate_data_api( + method="POST", + url="/priv/get_account", + description=_("查询账号列表"), + ) # 授权规则相关 self.pre_check_authorize_rules = self.generate_data_api( diff --git a/dbm-ui/backend/configuration/constants.py b/dbm-ui/backend/configuration/constants.py index f6e81e61d6..384ca83be9 100644 --- a/dbm-ui/backend/configuration/constants.py +++ b/dbm-ui/backend/configuration/constants.py @@ -185,4 +185,8 @@ class BizSettingsEnum(str, StructuredEnum): DOMAIN_RESOLUTION_SUPPORT = "DOMAIN_RESOLUTION_SUPPORT" # DB组件和admin用户的映射 -DB_ADMIN_USER_MAP = {DBType.MySQL: MYSQL_ADMIN_USER, DBType.Sqlserver: SQLSERVER_ADMIN_USER} +DB_ADMIN_USER_MAP = { + DBType.TenDBCluster: MYSQL_ADMIN_USER, + DBType.MySQL: MYSQL_ADMIN_USER, + DBType.Sqlserver: SQLSERVER_ADMIN_USER, +} diff --git a/dbm-ui/backend/configuration/serializers.py b/dbm-ui/backend/configuration/serializers.py index c6c797c92c..f1870b72c8 100644 --- a/dbm-ui/backend/configuration/serializers.py +++ b/dbm-ui/backend/configuration/serializers.py @@ -95,7 +95,7 @@ class GetMySQLAdminPasswordSerializer(serializers.Serializer): begin_time = DBTimezoneField(help_text=_("开始时间"), required=False) end_time = DBTimezoneField(help_text=_("结束时间"), required=False) - instances = serializers.CharField(help_text=_("过滤的实例列表(通过,分割,实例格式为--ip:port)"), required=False) + instances = serializers.CharField(help_text=_("过滤的实例列表(通过,分割,实例格式为--cloud:ip:port)"), required=False) class GetMySQLAdminPasswordResponseSerializer(serializers.Serializer): @@ -103,7 +103,7 @@ class Meta: swagger_schema_fields = {"example": mock_data.MYSQL_ADMIN_PASSWORD_DATA} -class ModifyMySQLAdminPasswordSerializer(serializers.Serializer): +class ModifyAdminPasswordSerializer(serializers.Serializer): class InstanceInfoSerializer(serializers.Serializer): ip = serializers.CharField(help_text=_("实例ip")) port = serializers.CharField(help_text=_("实例port")) @@ -116,10 +116,16 @@ class InstanceInfoSerializer(serializers.Serializer): instance_list = serializers.ListSerializer(help_text=_("实例信息"), child=InstanceInfoSerializer()) def validate(self, attrs): + # 校验密码中的特殊字符 invalid_characters = re.compile(r"[`\'\"]") if invalid_characters.findall(attrs["password"]): raise serializers.ValidationError(_("修改密码中不允许包含单引号,双引号和反引号")) + # 目前只允许一次性修改一种类型 + cluster_type = [inst["cluster_type"] for inst in attrs["instance_list"]] + if len(set(cluster_type)) > 1: + raise serializers.ValidationError(_("一次只允许修改同种集群类型的密码")) + return attrs diff --git a/dbm-ui/backend/configuration/views/dba.py b/dbm-ui/backend/configuration/views/dba.py index 03f4539857..234b37ad4c 100644 --- a/dbm-ui/backend/configuration/views/dba.py +++ b/dbm-ui/backend/configuration/views/dba.py @@ -27,14 +27,11 @@ class DBAdminViewSet(viewsets.SystemViewSet): def _get_custom_permissions(self): if self.action == "list_admins": return [] - if not int(self.request.data.get("bk_biz_id", 0)): return [ResourceActionPermission([ActionEnum.GLOBAL_DBA_ADMINISTRATOR_EDIT])] else: - instance_getter = lambda request, view: [request.data["bk_biz_id"]] # noqa: E731 - return [ - ResourceActionPermission([ActionEnum.DBA_ADMINISTRATOR_EDIT], ResourceEnum.BUSINESS, instance_getter) - ] + inst_getter = lambda request, view: [request.data["bk_biz_id"]] # noqa: E731 + return [ResourceActionPermission([ActionEnum.DBA_ADMINISTRATOR_EDIT], ResourceEnum.BUSINESS, inst_getter)] @common_swagger_auto_schema( operation_summary=_("查询DBA人员列表"), query_serializer=ListDBAdminSerializer, tags=[SWAGGER_TAG] diff --git a/dbm-ui/backend/configuration/views/function_controller.py b/dbm-ui/backend/configuration/views/function_controller.py index 2c973816ac..2b2665305b 100644 --- a/dbm-ui/backend/configuration/views/function_controller.py +++ b/dbm-ui/backend/configuration/views/function_controller.py @@ -26,8 +26,7 @@ class FunctionControllerViewSet(viewsets.AuditedModelViewSet): serializer_class = FunctionControllerSerializer queryset = FunctionController.objects.all() - def _get_custom_permissions(self): - return [] + default_permission_class = [] @common_swagger_auto_schema( operation_summary=_("功能开关列表"), diff --git a/dbm-ui/backend/configuration/views/ip_whitelist.py b/dbm-ui/backend/configuration/views/ip_whitelist.py index 3de5321010..01399347d1 100644 --- a/dbm-ui/backend/configuration/views/ip_whitelist.py +++ b/dbm-ui/backend/configuration/views/ip_whitelist.py @@ -72,7 +72,7 @@ class IPWhitelistViewSet(viewsets.AuditedModelViewSet): pagination_class = AuditedLimitOffsetPagination @staticmethod - def instance_getter(request, view): + def inst_getter(request, view): # 批量删除的白名单一定在一个业务下 if view.action == "batch_delete": return [IPWhitelist.objects.filter(pk__in=request.data["ids"]).first().bk_biz_id] @@ -84,11 +84,10 @@ def instance_getter(request, view): def _get_custom_permissions(self): if self.action in ["retrieve", "iplist"]: return [] - - bk_biz_id = self.instance_getter(self.request, self)[0] + bk_biz_id = self.inst_getter(self.request, self)[0] if int(bk_biz_id): return [ - ResourceActionPermission([ActionEnum.IP_WHITELIST_MANAGE], ResourceEnum.BUSINESS, self.instance_getter) + ResourceActionPermission([ActionEnum.IP_WHITELIST_MANAGE], ResourceEnum.BUSINESS, self.inst_getter) ] else: return [ResourceActionPermission([ActionEnum.GLOBAL_IP_WHITELIST_MANAGE])] diff --git a/dbm-ui/backend/configuration/views/password_policy.py b/dbm-ui/backend/configuration/views/password_policy.py index c2ce975077..d1bc08aad8 100644 --- a/dbm-ui/backend/configuration/views/password_policy.py +++ b/dbm-ui/backend/configuration/views/password_policy.py @@ -25,7 +25,7 @@ from backend.configuration.serializers import ( GetMySQLAdminPasswordResponseSerializer, GetMySQLAdminPasswordSerializer, - ModifyMySQLAdminPasswordSerializer, + ModifyAdminPasswordSerializer, ModifyMySQLPasswordRandomCycleSerializer, PasswordPolicySerializer, VerifyPasswordResponseSerializer, @@ -34,6 +34,7 @@ from backend.db_periodic_task.models import DBPeriodicTask from backend.iam_app.dataclass.actions import ActionEnum from backend.iam_app.handlers.drf_perm.base import ResourceActionPermission +from backend.iam_app.handlers.drf_perm.cluster import ModifyActionPermission SWAGGER_TAG = _("密码安全策略") @@ -41,15 +42,13 @@ class PasswordPolicyViewSet(viewsets.SystemViewSet): pagination_class = None - def _get_custom_permissions(self): - if self.action in [ - self.get_password_policy.__name__, - self.verify_password_strength.__name__, - self.get_random_password.__name__, - self.query_random_cycle.__name__, - ]: - return [] - return [ResourceActionPermission([ActionEnum.PASSWORD_POLICY_SET])] + action_permission_map = { + ("get_password_policy", "verify_password_strength", "get_random_password", "query_random_cycle"): [], + ("modify_admin_password", "query_mysql_admin_password"): [ModifyActionPermission()], + ("update_password_policy", "modify_random_cycle"): [ + ResourceActionPermission([ActionEnum.PASSWORD_POLICY_SET]) + ], + } @common_swagger_auto_schema( operation_summary=_("查询密码安全策略"), @@ -141,10 +140,10 @@ def query_mysql_admin_password(self, request, *args, **kwargs): @common_swagger_auto_schema( operation_summary=_("修改db实例密码(admin)"), - request_body=ModifyMySQLAdminPasswordSerializer(), + request_body=ModifyAdminPasswordSerializer(), tags=[SWAGGER_TAG], ) - @action(methods=["POST"], detail=False, serializer_class=ModifyMySQLAdminPasswordSerializer) + @action(methods=["POST"], detail=False, serializer_class=ModifyAdminPasswordSerializer) def modify_admin_password(self, request, *args, **kwargs): validated_data = self.params_validate(self.get_serializer_class()) validated_data["operator"] = request.user.username diff --git a/dbm-ui/backend/configuration/views/profile.py b/dbm-ui/backend/configuration/views/profile.py index a75f4d312b..c1ab732a50 100644 --- a/dbm-ui/backend/configuration/views/profile.py +++ b/dbm-ui/backend/configuration/views/profile.py @@ -24,9 +24,7 @@ class ProfileViewSet(viewsets.SystemViewSet): serializer_class = ProfileSerializer - - def _get_custom_permissions(self): - return [] + default_permission_class = [] @common_swagger_auto_schema(operation_summary=_("查询个人配置列表"), tags=[SWAGGER_TAG]) @action(methods=["GET"], detail=False) diff --git a/dbm-ui/backend/configuration/views/system.py b/dbm-ui/backend/configuration/views/system.py index a2d3e59177..38b1fe4002 100644 --- a/dbm-ui/backend/configuration/views/system.py +++ b/dbm-ui/backend/configuration/views/system.py @@ -38,16 +38,12 @@ class SystemSettingsViewSet(viewsets.SystemViewSet): """系统设置视图""" - def _get_custom_permissions(self): - # 非超级用户拒绝访问敏感信息 - if self.action == "sensitive_environ": - return [RejectPermission()] - if self.action == "update_duty_notice_config": - return [ResourceActionPermission([ActionEnum.UPDATE_DUTY_NOTICE_CONFIG])] - if self.action in ["disk_classes", "device_classes", "duty_notice_config", "environ"]: - return [] - - return [ResourceActionPermission([ActionEnum.GLOBAL_MANAGE])] + action_permission_map = { + ("sensitive_environ",): [RejectPermission()], + ("update_duty_notice_config",): [ResourceActionPermission([ActionEnum.UPDATE_DUTY_NOTICE_CONFIG])], + ("disk_classes", "device_classes", "duty_notice_config", "environ"): [], + } + default_permission_class = [ResourceActionPermission([ActionEnum.GLOBAL_MANAGE])] @common_swagger_auto_schema( operation_summary=_("查询磁盘类型"), @@ -129,8 +125,8 @@ class BizSettingsViewSet(viewsets.AuditedModelViewSet): serializer_class = BizSettingsSerializer queryset = BizSettings.objects.all() - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("业务设置列表"), diff --git a/dbm-ui/backend/core/encrypt/views.py b/dbm-ui/backend/core/encrypt/views.py index 5421e4a07e..0b1975ae3b 100644 --- a/dbm-ui/backend/core/encrypt/views.py +++ b/dbm-ui/backend/core/encrypt/views.py @@ -23,10 +23,8 @@ class EncryptViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - if self.action == "fetch_public_keys": - return [] - return [ResourceActionPermission([ActionEnum.GLOBAL_MANAGE])] + action_permission_map = {("fetch_public_keys",): []} + default_permission_class = [ResourceActionPermission([ActionEnum.GLOBAL_MANAGE])] @common_swagger_auto_schema( operation_summary=_("查询公钥列表"), request_body=FetchPublicKeysSerializer(), tags=[SWAGGER_TAG] diff --git a/dbm-ui/backend/core/storages/views.py b/dbm-ui/backend/core/storages/views.py index 90ae421a53..c444fbf91f 100644 --- a/dbm-ui/backend/core/storages/views.py +++ b/dbm-ui/backend/core/storages/views.py @@ -21,8 +21,7 @@ class StorageViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [] + default_permission_class = [] @common_swagger_auto_schema( operation_summary=_("批量获取文件内容"), request_body=BatchDownloadFileSerializer(), tags=[SWAGGER_TAG] diff --git a/dbm-ui/backend/db_dirty/views.py b/dbm-ui/backend/db_dirty/views.py index fb733bbc8b..526fa2efa0 100644 --- a/dbm-ui/backend/db_dirty/views.py +++ b/dbm-ui/backend/db_dirty/views.py @@ -39,10 +39,8 @@ class DBDirtyMachineViewSet(viewsets.SystemViewSet): pagination_class = None filter_class = None - def _get_custom_permissions(self): - if self.action == "query_operation_list": - return [] - return [ResourceActionPermission([ActionEnum.DIRTY_POLL_MANAGE])] + action_permission_map = {("query_operation_list",): []} + default_permission_class = [ResourceActionPermission([ActionEnum.DIRTY_POLL_MANAGE])] @common_swagger_auto_schema( operation_summary=_("查询污点池列表"), diff --git a/dbm-ui/backend/db_event/views/dbha.py b/dbm-ui/backend/db_event/views/dbha.py index b3c0363c5c..b8f69aa64b 100644 --- a/dbm-ui/backend/db_event/views/dbha.py +++ b/dbm-ui/backend/db_event/views/dbha.py @@ -28,16 +28,16 @@ class DBHAEventViewSet(viewsets.SystemViewSet): + def get_action_permission_map(self): + return {("cat",): []} + + def get_default_permission_class(self): + return [ResourceActionPermission([ActionEnum.DBHA_SWITCH_EVENT_VIEW], ResourceEnum.BUSINESS, self.inst_getter)] + @staticmethod - def biz_getter(request, view): + def inst_getter(request, view): return [get_request_key_id(request, "app")] - def _get_custom_permissions(self): - if self.action == "cat": - # TODO: 暂时豁免,后续cat接口带上业务属性后再放开 - return [] - return [ResourceActionPermission([ActionEnum.DBHA_SWITCH_EVENT_VIEW], ResourceEnum.BUSINESS, self.biz_getter)] - @common_swagger_auto_schema( operation_summary=_("DBHA切换事件列表"), query_serializer=QueryListSerializer, diff --git a/dbm-ui/backend/db_monitor/views/duty_rule.py b/dbm-ui/backend/db_monitor/views/duty_rule.py index 60cb7575d5..7ca1918d75 100644 --- a/dbm-ui/backend/db_monitor/views/duty_rule.py +++ b/dbm-ui/backend/db_monitor/views/duty_rule.py @@ -10,6 +10,7 @@ """ from django.utils.decorators import method_decorator from django.utils.translation import ugettext_lazy as _ +from django_filters.rest_framework import DjangoFilterBackend from rest_framework import filters from rest_framework.decorators import action from rest_framework.response import Response @@ -66,36 +67,39 @@ class MonitorDutyRuleViewSet(viewsets.AuditedModelViewSet): queryset = DutyRule.objects.all().order_by("-update_at") serializer_class = DutyRuleSerializer pagination_class = AuditedLimitOffsetPagination - filter_backends = [filters.SearchFilter] + filter_backends = [filters.SearchFilter, DjangoFilterBackend] filter_fields = {"db_type": ["exact"], "name": ["exact"]} search_fields = ["name"] + default_permission_class = [ResourceActionPermission([ActionEnum.GLOBAL_MANAGE])] + + def get_action_permission_map(self): + return { + ("list",): [ResourceActionPermission([ActionEnum.DUTY_RULE_LIST], ResourceEnum.DBTYPE, self.inst_getter)], + ("create",): [ + ResourceActionPermission([ActionEnum.DUTY_RULE_CREATE], ResourceEnum.DBTYPE, self.inst_getter) + ], + ("update", "partial_update"): [ + ResourceActionPermission([ActionEnum.DUTY_RULE_UPDATE], ResourceEnum.DBTYPE, self.inst_getter) + ], + ("destroy",): [ + ResourceActionPermission([ActionEnum.DUTY_RULE_DESTROY], ResourceEnum.DBTYPE, self.inst_getter) + ], + ("priority_distinct",): [], + } + @staticmethod def inst_getter(request, view): if view.action in ["list", "create"]: return [get_request_key_id(request, key="db_type")] if view.action in ["update", "partial_update", "destroy"]: - return [view.kwargs.get("pk")] - - def _get_custom_permissions(self): - if self.action == "list": - return [ResourceActionPermission([ActionEnum.DUTY_RULE_LIST], ResourceEnum.DBTYPE, self.inst_getter)] - elif self.action == "create": - return [ResourceActionPermission([ActionEnum.DUTY_RULE_CREATE], ResourceEnum.DBTYPE, self.inst_getter)] - elif self.action in ["update", "partial_update"]: - return [ResourceActionPermission([ActionEnum.DUTY_RULE_UPDATE], ResourceEnum.DUTY_RULE, self.inst_getter)] - elif self.action == "destroy": - return [ResourceActionPermission([ActionEnum.DUTY_RULE_DESTROY], ResourceEnum.DUTY_RULE, self.inst_getter)] - elif self.action in ["priority_distinct"]: - return [] - - return [ResourceActionPermission([ActionEnum.GLOBAL_MANAGE])] + return [DutyRule.objects.get(id=view.kwargs.get("pk")).db_type] @Permission.decorator_permission_field( - id_field=lambda d: d["id"], + id_field=lambda d: d["db_type"], data_field=lambda d: d["results"], - actions=ActionEnum.get_actions_by_resource(ResourceEnum.DUTY_RULE.id), - resource_meta=ResourceEnum.DUTY_RULE, + actions=[ActionEnum.DUTY_RULE_CREATE, ActionEnum.DUTY_RULE_UPDATE, ActionEnum.DUTY_RULE_DESTROY], + resource_meta=ResourceEnum.DBTYPE, ) def list(self, request, *args, **kwargs): return super().list(request, *args, **kwargs) diff --git a/dbm-ui/backend/db_monitor/views/grafana.py b/dbm-ui/backend/db_monitor/views/grafana.py index 8d52dc7b3c..c6ded2c459 100644 --- a/dbm-ui/backend/db_monitor/views/grafana.py +++ b/dbm-ui/backend/db_monitor/views/grafana.py @@ -32,8 +32,6 @@ def _get_custom_permissions(self): else: return [ClusterDetailPermission()] - return super()._get_custom_permissions() - @common_swagger_auto_schema( operation_summary=_("查询内嵌仪表盘地址"), query_serializer=GetDashboardSerializer, diff --git a/dbm-ui/backend/db_monitor/views/notice_group.py b/dbm-ui/backend/db_monitor/views/notice_group.py index 9f4a3d5f53..be616765db 100644 --- a/dbm-ui/backend/db_monitor/views/notice_group.py +++ b/dbm-ui/backend/db_monitor/views/notice_group.py @@ -100,13 +100,23 @@ class MonitorNoticeGroupViewSet(viewsets.AuditedModelViewSet): pagination_class = AuditedLimitOffsetPagination filter_class = MonitorGroupListFilter - def _get_custom_permissions(self): - if self.action in ["list", "destroy", "create", "update"]: - return [NotifyGroupPermission(view_action=self.action)] - elif self.action in ["get_msg_type", "list_group_name"]: - return [] - - return [DBManagePermission()] + default_permission_class = DBManagePermission() + + def get_action_permission_map(self): + return { + ( + "list", + "create", + "destroy", + "update", + "partial_update", + ): [NotifyGroupPermission(view_action=self.action)], + ( + "get_msg_type", + "list_group_name", + "list_default_group", + ): [], + } def get_serializer_context(self): context = super().get_serializer_context() @@ -114,26 +124,20 @@ def get_serializer_context(self): context["group_used"] = dict(Counter([item for group in notify_groups for item in group])) return context + @Permission.decorator_permission_field( + id_field=lambda d: d["id"], + data_field=lambda d: d["results"], + actions=ActionEnum.get_actions_by_resource(ResourceEnum.NOTIFY_GROUP.id), + resource_meta=ResourceEnum.NOTIFY_GROUP, + ) + def list(self, request, *args, **kwargs): + return super().list(request, *args, **kwargs) + @common_swagger_auto_schema(operation_summary=_("查询通知类型"), tags=[SWAGGER_TAG]) @action(methods=["GET"], detail=False) def get_msg_type(self, request, *args, **kwargs): return Response(CmsiApi.get_msg_type()) - @Permission.decorator_external_permission_field( - param_field=lambda d: d["bk_biz_id"], - actions=[ - ActionEnum.GLOBAL_NOTIFY_GROUP_CREATE, - ActionEnum.GLOBAL_NOTIFY_GROUP_DESTROY, - ActionEnum.GLOBAL_NOTIFY_GROUP_UPDATE, - ActionEnum.NOTIFY_GROUP_CREATE, - ActionEnum.NOTIFY_GROUP_DESTROY, - ActionEnum.NOTIFY_GROUP_UPDATE, - ], - resource_meta=ResourceEnum.BUSINESS, - ) - def list(self, request, *args, **kwargs): - return super().list(request, *args, **kwargs) - @common_swagger_auto_schema(operation_summary=_("查询告警组名称"), tags=[SWAGGER_TAG]) @action(methods=["GET"], detail=False, filter_class=MonitorGroupListFilter) def list_group_name(self, request, *args, **kwargs): diff --git a/dbm-ui/backend/db_package/views.py b/dbm-ui/backend/db_package/views.py index a6d1d4a1e4..9f06449103 100644 --- a/dbm-ui/backend/db_package/views.py +++ b/dbm-ui/backend/db_package/views.py @@ -47,6 +47,20 @@ class DBPackageViewSet(viewsets.AuditedModelViewSet): filter_class = PackageListFilter serializer_class = PackageSerializer + def get_action_permission_map(self): + return { + ( + "list_install_pkg_types", + "list_install_packages", + ): [], + ("list",): [ + ResourceActionPermission([ActionEnum.PACKAGE_VIEW], ResourceEnum.DBTYPE, self.instance_getter) + ], + } + + def get_default_permission_class(self): + return [ResourceActionPermission([ActionEnum.PACKAGE_MANAGE], ResourceEnum.DBTYPE, self.instance_getter)] + @staticmethod def instance_getter(request, view): if view.action == "destroy": @@ -54,14 +68,6 @@ def instance_getter(request, view): else: return [get_request_key_id(request, "db_type")] - def _get_custom_permissions(self): - if self.action in ["list_install_pkg_types", "list_install_packages"]: - return [] - elif self.action == "list": - return [ResourceActionPermission([ActionEnum.PACKAGE_VIEW], ResourceEnum.DBTYPE, self.instance_getter)] - else: - return [ResourceActionPermission([ActionEnum.PACKAGE_MANAGE], ResourceEnum.DBTYPE, self.instance_getter)] - @common_swagger_auto_schema( operation_summary=_("新建版本文件"), tags=[DB_PACKAGE_TAG], diff --git a/dbm-ui/backend/db_services/cluster_entry/views.py b/dbm-ui/backend/db_services/cluster_entry/views.py index 2042414150..97d810427d 100644 --- a/dbm-ui/backend/db_services/cluster_entry/views.py +++ b/dbm-ui/backend/db_services/cluster_entry/views.py @@ -30,12 +30,8 @@ class ClusterEntryViewSet(viewsets.SystemViewSet): permission_classes = [] - - def _get_custom_permissions(self): - if self.action == "get_cluster_entries": - return [DBManagePermission()] - - return [ResourceActionPermission([ActionEnum.GLOBAL_MANAGE])] + action_permission_map = {("get_cluster_entries",): [DBManagePermission()]} + default_permission_class = [ResourceActionPermission([ActionEnum.GLOBAL_MANAGE])] @common_swagger_auto_schema(operation_summary=_("修改集群访问入口"), tags=[SWAGGER_TAG]) @action(methods=["POST"], detail=False, serializer_class=ModifyClusterEntrySerializer) diff --git a/dbm-ui/backend/db_services/cmdb/views.py b/dbm-ui/backend/db_services/cmdb/views.py index cef69b03d7..b15f82b5ad 100644 --- a/dbm-ui/backend/db_services/cmdb/views.py +++ b/dbm-ui/backend/db_services/cmdb/views.py @@ -30,11 +30,14 @@ class CMDBViewSet(viewsets.SystemViewSet): lookup_field = "bk_biz_id" - def _get_custom_permissions(self): - if self.action in ["list_bizs", "list_modules", "list_cc_obj_user"]: - return [] - - return [DBManagePermission()] + action_permission_map = { + ( + "list_bizs", + "list_modules", + "list_cc_obj_user", + ): [] + } + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("业务列表"), diff --git a/dbm-ui/backend/db_services/dbbase/cluster/views.py b/dbm-ui/backend/db_services/dbbase/cluster/views.py index 38931546d6..e93c150b36 100644 --- a/dbm-ui/backend/db_services/dbbase/cluster/views.py +++ b/dbm-ui/backend/db_services/dbbase/cluster/views.py @@ -29,8 +29,8 @@ class ClusterViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("通过集群查询同机关联集群"), diff --git a/dbm-ui/backend/db_services/dbbase/instances/views.py b/dbm-ui/backend/db_services/dbbase/instances/views.py index de156beb9e..fc54b6c750 100644 --- a/dbm-ui/backend/db_services/dbbase/instances/views.py +++ b/dbm-ui/backend/db_services/dbbase/instances/views.py @@ -22,8 +22,8 @@ class InstanceViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("根据用户手动输入的 ip:port 查询真实的实例"), diff --git a/dbm-ui/backend/db_services/dbbase/resources/views.py b/dbm-ui/backend/db_services/dbbase/resources/views.py index b0c59137b7..f3c1cfc6ac 100644 --- a/dbm-ui/backend/db_services/dbbase/resources/views.py +++ b/dbm-ui/backend/db_services/dbbase/resources/views.py @@ -55,8 +55,8 @@ class BaseListResourceViewSet(AuditedModelViewSet): filterset_class = ResourceFilter pagination_class = None - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] def get_filterset_kwargs(self): return {"bk_biz_id": self.kwargs["bk_biz_id"]} diff --git a/dbm-ui/backend/db_services/dbbase/views.py b/dbm-ui/backend/db_services/dbbase/views.py index b3730c6850..67821e7e35 100644 --- a/dbm-ui/backend/db_services/dbbase/views.py +++ b/dbm-ui/backend/db_services/dbbase/views.py @@ -52,10 +52,14 @@ class DBBaseViewSet(viewsets.SystemViewSet): pagination_class = AuditedLimitOffsetPagination - def _get_custom_permissions(self): - if self.action in ["verify_duplicated_cluster_name", "simple_query_cluster", "common_query_cluster"]: - return [] - return [DBManagePermission()] + action_permission_map = { + ( + "verify_duplicated_cluster_name", + "simple_query_cluster", + "common_query_cluster", + ): [] + } + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("查询集群名字是否重复"), diff --git a/dbm-ui/backend/db_services/dbconfig/views.py b/dbm-ui/backend/db_services/dbconfig/views.py index f4fa94022d..7c43a1aa91 100644 --- a/dbm-ui/backend/db_services/dbconfig/views.py +++ b/dbm-ui/backend/db_services/dbconfig/views.py @@ -22,37 +22,33 @@ from backend.iam_app.dataclass import ResourceEnum from backend.iam_app.dataclass.actions import ActionEnum from backend.iam_app.handlers.drf_perm.base import ResourceActionPermission -from backend.iam_app.handlers.drf_perm.dbconfig import ( - BizDBConfigPermission, - GlobalConfigPermission, - decorator_biz_config_permission_field, -) +from backend.iam_app.handlers.drf_perm.dbconfig import BizDBConfigPermission, GlobalConfigPermission from backend.iam_app.handlers.permission import Permission SWAGGER_TAG = "config" class ConfigViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - if self.action in [ + action_permission_map = { + ( "list_biz_configs", "get_level_config", "list_config_version_history", "get_config_version_detail", - ]: - return [BizDBConfigPermission([ActionEnum.DBCONFIG_VIEW])] - elif self.action in ["upsert_level_config", "save_module_deploy_info"]: - return [BizDBConfigPermission([ActionEnum.DBCONFIG_EDIT])] - elif self.action in ["list_platform_configs", "get_platform_config"]: - return [GlobalConfigPermission([ActionEnum.GLOBAL_DBCONFIG_VIEW])] - elif self.action in ["create_platform_config"]: - return [GlobalConfigPermission([ActionEnum.GLOBAL_DBCONFIG_CREATE])] - elif self.action in ["upsert_platform_config"]: - return [GlobalConfigPermission([ActionEnum.GLOBAL_DBCONFIG_EDIT])] - elif self.action in ["list_config_names"]: - return [] - - return [ResourceActionPermission([ActionEnum.GLOBAL_MANAGE])] + ): [BizDBConfigPermission([ActionEnum.DBCONFIG_VIEW])], + ( + "upsert_level_config", + "save_module_deploy_info", + ): [BizDBConfigPermission([ActionEnum.DBCONFIG_EDIT])], + ( + "list_platform_configs", + "get_platform_config", + ): [GlobalConfigPermission([ActionEnum.GLOBAL_DBCONFIG_VIEW])], + ("create_platform_config",): [GlobalConfigPermission([ActionEnum.GLOBAL_DBCONFIG_CREATE])], + ("upsert_platform_config",): [GlobalConfigPermission([ActionEnum.GLOBAL_DBCONFIG_EDIT])], + ("list_config_names",): [], + } + default_permission_class = [ResourceActionPermission([ActionEnum.GLOBAL_MANAGE])] @common_swagger_auto_schema( operation_summary=_("查询配置项名称列表"), @@ -129,7 +125,14 @@ def get_platform_config(self, request): responses={status.HTTP_200_OK: serializers.ListPublicConfigResponseSerializer(many=True)}, tags=[SWAGGER_TAG], ) - @decorator_biz_config_permission_field() + @Permission.decorator_external_permission_field( + param_field=lambda d: { + ResourceEnum.DBTYPE.id: ClusterType.cluster_type_to_db_type(d["meta_cluster_type"]), + ResourceEnum.BUSINESS.id: d["bk_biz_id"], + }, + actions=[ActionEnum.DBCONFIG_EDIT], + resource_meta=[ResourceEnum.DBTYPE, ResourceEnum.BUSINESS], + ) @action(methods=["GET"], detail=False, serializer_class=serializers.ListBizConfigRequestSerializer) def list_biz_configs(self, request): validated_data = self.params_validate(self.get_serializer_class()) diff --git a/dbm-ui/backend/db_services/dbpermission/db_account/handlers.py b/dbm-ui/backend/db_services/dbpermission/db_account/handlers.py index a9a0a3707c..6fea1c5726 100644 --- a/dbm-ui/backend/db_services/dbpermission/db_account/handlers.py +++ b/dbm-ui/backend/db_services/dbpermission/db_account/handlers.py @@ -14,12 +14,14 @@ from typing import Any, Dict, List, Optional from django.utils.translation import ugettext as _ +from iam.resource.utils import FancyDict from backend.components.mysql_priv_manager.client import DBPrivManagerApi from backend.core.encrypt.constants import AsymmetricCipherConfigType from backend.core.encrypt.handlers import AsymmetricHandler from backend.db_services.dbpermission.constants import AccountType from backend.db_services.dbpermission.db_account.dataclass import AccountMeta, AccountRuleMeta +from backend.db_services.dbpermission.db_account.signals import create_account_signal from backend.db_services.mysql.open_area.models import TendbOpenAreaConfig from backend.db_services.mysql.permission.exceptions import DBPermissionBaseException @@ -79,6 +81,14 @@ def create_account(self, account: AccountMeta) -> Optional[Any]: "psw": account.password, } ) + # 获取新建账号信息 TODO: 这段逻辑在create_account返回账号信息后可以去掉 + account_data = DBPrivManagerApi.get_account( + params={"bk_biz_id": self.bk_biz_id, "cluster_type": self.account_type, "user_like": account.user} + )["results"] + account = {info["user"]: info for info in account_data}.get(account.user) + account.update(account_type=self.account_type) + create_account_signal.send(sender=None, account=FancyDict(account)) + return resp def delete_account(self, account: AccountMeta) -> Optional[Any]: diff --git a/dbm-ui/backend/db_services/dbpermission/db_account/signals.py b/dbm-ui/backend/db_services/dbpermission/db_account/signals.py new file mode 100644 index 0000000000..a97f5c378d --- /dev/null +++ b/dbm-ui/backend/db_services/dbpermission/db_account/signals.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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 django.dispatch import Signal + +# 创建账号的信号 +create_account_signal = Signal(providing_args=["account"]) diff --git a/dbm-ui/backend/db_services/dbpermission/db_account/views.py b/dbm-ui/backend/db_services/dbpermission/db_account/views.py index b4d7210d4a..00bdc11ab6 100644 --- a/dbm-ui/backend/db_services/dbpermission/db_account/views.py +++ b/dbm-ui/backend/db_services/dbpermission/db_account/views.py @@ -17,6 +17,7 @@ from backend.bk_web import viewsets from backend.bk_web.swagger import common_swagger_auto_schema +from backend.db_services.dbpermission.constants import AccountType from backend.db_services.dbpermission.db_account.dataclass import AccountMeta, AccountRuleMeta from backend.db_services.dbpermission.db_account.handlers import AccountHandler from backend.db_services.dbpermission.db_account.serializers import ( @@ -32,7 +33,9 @@ ) from backend.iam_app.dataclass import ResourceEnum from backend.iam_app.dataclass.actions import ActionEnum +from backend.iam_app.handlers.drf_perm.account import AccountPermission from backend.iam_app.handlers.drf_perm.base import DBManagePermission, ResourceActionPermission, get_request_key_id +from backend.iam_app.handlers.permission import Permission SWAGGER_TAG = "db_services/permission/account" @@ -48,12 +51,14 @@ def instance_getter(request, view): return [get_request_key_id(request, "bk_biz_id")] def _get_custom_permissions(self): - if self.action not in ["create_account", "delete_account", "add_account_rule"]: + if self.action in ["query_account_rules"]: return [DBManagePermission()] - - account_type = self.request.data.get("account_type", self.account_type) - account_action = getattr(ActionEnum, f"{account_type}_{self.action}".upper()) - return [ResourceActionPermission([account_action], ResourceEnum.BUSINESS, self.instance_getter)] + elif self.action in ["list_account_rules"]: + account_type = self.request.query_params.get("account_type", AccountType.MYSQL) + view_action = getattr(ActionEnum, f"{account_type}_account_rules_view".upper()) + return [ResourceActionPermission([view_action], ResourceEnum.BUSINESS, self.instance_getter)] + else: + return [AccountPermission(account_type=self.account_type, view_action=self.action)] def _view_common_handler( self, request, bk_biz_id: int, meta: Union[AccountMeta, AccountRuleMeta], func: str @@ -130,6 +135,14 @@ def add_account_rule(self, request, bk_biz_id): tags=[SWAGGER_TAG], ) @action(methods=["GET"], detail=False, serializer_class=FilterAccountRulesSerializer) + @Permission.decorator_permission_field( + id_field=lambda d: d["account"]["account_id"], + data_field=lambda d: d["results"], + action_filed=lambda k: [ + getattr(ActionEnum, f'{k["view_class"].account_type.upper()}_DELETE_ACCOUNT'), + getattr(ActionEnum, f'{k["view_class"].account_type.upper()}_ADD_ACCOUNT_RULE'), + ], + ) def list_account_rules(self, request, bk_biz_id): return self._view_common_handler( request=request, diff --git a/dbm-ui/backend/db_services/dbpermission/db_authorize/handlers.py b/dbm-ui/backend/db_services/dbpermission/db_authorize/handlers.py index 6222957cb6..8144dd5d68 100644 --- a/dbm-ui/backend/db_services/dbpermission/db_authorize/handlers.py +++ b/dbm-ui/backend/db_services/dbpermission/db_authorize/handlers.py @@ -17,8 +17,9 @@ from django.utils.translation import ugettext as _ from backend import env +from backend.components import DBPrivManagerApi from backend.db_meta.enums import ClusterType -from backend.db_services.dbpermission.constants import AUTHORIZE_DATA_EXPIRE_TIME +from backend.db_services.dbpermission.constants import AUTHORIZE_DATA_EXPIRE_TIME, AccountType from backend.db_services.dbpermission.db_authorize.dataclass import AuthorizeMeta, ExcelAuthorizeMeta from backend.db_services.dbpermission.db_authorize.models import AuthorizeRecord from backend.utils.cache import data_cache @@ -33,6 +34,7 @@ class AuthorizeHandler(object): EXCEL_ERROR_TEMPLATE = "" authorize_meta: AuthorizeMeta = None excel_authorize_meta: ExcelAuthorizeMeta = None + account_type: AccountType = None def __init__(self, bk_biz_id: int, operator: str = None): """ @@ -43,6 +45,17 @@ def __init__(self, bk_biz_id: int, operator: str = None): self.bk_biz_id = bk_biz_id self.operator = operator + @staticmethod + def _get_user_info_map(account_type: str, bk_biz_id: int): + """ + 获取业务下制定账号类型的账号信息,返回用户名与用户信息的映射 + @param account_type: 账号类型 + @param bk_biz_id: 业务ID + """ + user_data = DBPrivManagerApi.get_account(params={"cluster_type": account_type, "bk_biz_id": bk_biz_id}) + user_info_map = {user["user"]: user for user in user_data["results"]} + return user_info_map + def _pre_check_rules(self, authorize: AuthorizeMeta, **kwargs) -> Tuple[bool, str, Dict]: """ 前置检查的具体逻辑,需要返回三个参数 @@ -50,7 +63,8 @@ def _pre_check_rules(self, authorize: AuthorizeMeta, **kwargs) -> Tuple[bool, st @param user_db__rules: 当前的授权规则 :return pre_check: 前置校验是否通过 :return message: 检查信息 - :return authorize_data: 授权序列化数据 + :return authorize_data: 授权序列化数据. + 注:返回的数据需要带上account_id用于鉴权 """ raise NotImplementedError @@ -82,9 +96,7 @@ def pre_check_excel_rules(self, excel_authorize: ExcelAuthorizeMeta, **kwargs) - """ authorize_excel_data_list = excel_authorize.authorize_excel_data - - # 并发请求数据校验接口 - # 这里使用并发的原因是考虑有些pre-check需要请求IO接口 + # 并发请求数据校验接口,这里使用并发的原因是考虑有些pre-check需要请求IO接口 tasks = [] with ThreadPoolExecutor(max_workers=min(len(authorize_excel_data_list), settings.CONCURRENT_NUMBER)) as ex: for index, excel_data in enumerate(authorize_excel_data_list): diff --git a/dbm-ui/backend/db_services/dbpermission/db_authorize/views.py b/dbm-ui/backend/db_services/dbpermission/db_authorize/views.py index bdbf15e07c..2bbac7be8e 100644 --- a/dbm-ui/backend/db_services/dbpermission/db_authorize/views.py +++ b/dbm-ui/backend/db_services/dbpermission/db_authorize/views.py @@ -40,8 +40,8 @@ class DBAuthorizeViewSet(viewsets.SystemViewSet): authorize_meta = AuthorizeMeta excel_authorize_meta = ExcelAuthorizeMeta - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] def _view_common_handler( self, diff --git a/dbm-ui/backend/db_services/dbresource/views/resource.py b/dbm-ui/backend/db_services/dbresource/views/resource.py index eaa6ae169f..c96117c2ae 100644 --- a/dbm-ui/backend/db_services/dbresource/views/resource.py +++ b/dbm-ui/backend/db_services/dbresource/views/resource.py @@ -70,26 +70,25 @@ class DBResourceViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - if self.action in [ + action_permission_map = { + ( "resource_import", "resource_apply", "resource_pre_apply", "resource_confirm", "resource_delete", "resource_update", - ]: - return [ResourceActionPermission([ActionEnum.RESOURCE_POLL_MANAGE])] - elif self.action in [ + ): [ResourceActionPermission([ActionEnum.RESOURCE_POLL_MANAGE])], + ( "spec_resource_count", "get_mountpoints", "get_disktypes", "get_subzones", "get_device_class", - ]: - return [] - - return [ResourceActionPermission([ActionEnum.RESOURCE_MANAGE])] + ): [], + ("query_operation_list",): [ResourceActionPermission([ActionEnum.RESOURCE_OPERATION_VIEW])], + } + default_permission_class = [ResourceActionPermission([ActionEnum.RESOURCE_MANAGE])] @common_swagger_auto_schema( operation_summary=_("资源池资源列表"), diff --git a/dbm-ui/backend/db_services/dbresource/views/sepc.py b/dbm-ui/backend/db_services/dbresource/views/sepc.py index 8bbccc696a..339876a3ca 100644 --- a/dbm-ui/backend/db_services/dbresource/views/sepc.py +++ b/dbm-ui/backend/db_services/dbresource/views/sepc.py @@ -54,6 +54,24 @@ class DBSpecViewSet(viewsets.AuditedModelViewSet): serializer_class = SpecSerializer filter_class = SpecListFilter + def get_action_permission_map(self): + return { + ( + "delete", + "batch_delete", + ): [ResourceActionPermission([ActionEnum.SPEC_DESTROY], ResourceEnum.DBTYPE, self.instance_getter)], + ("create",): [ + ResourceActionPermission([ActionEnum.SPEC_CREATE], ResourceEnum.DBTYPE, self.instance_getter) + ], + ("update",): [ + ResourceActionPermission([ActionEnum.SPEC_UPDATE], ResourceEnum.DBTYPE, self.instance_getter) + ], + ("list", "recommend_spec", "query_qps_range", "filter_cluster_spec"): [], + } + + def get_default_permission_class(self): + return [ResourceActionPermission([ActionEnum.RESOURCE_MANAGE])] + @staticmethod def instance_getter(request, view): # 如果是单个操作 @@ -67,18 +85,6 @@ def instance_getter(request, view): return [convert(request.data["spec_cluster_type"])] return [] - def _get_custom_permissions(self): - if self.action in ["delete", "batch_delete"]: - return [ResourceActionPermission([ActionEnum.SPEC_DESTROY], ResourceEnum.DBTYPE, self.instance_getter)] - elif self.action == "create": - return [ResourceActionPermission([ActionEnum.SPEC_CREATE], ResourceEnum.DBTYPE, self.instance_getter)] - elif self.action == "update": - return [ResourceActionPermission([ActionEnum.SPEC_UPDATE], ResourceEnum.DBTYPE, self.instance_getter)] - elif self.action in ["list", "recommend_spec", "query_qps_range", "filter_cluster_spec"]: - return [] - - return [ResourceActionPermission([ActionEnum.RESOURCE_MANAGE])] - def _remove_spec_fields(self, machine_type, data): """移除无需的字段""" remove_fields = [] diff --git a/dbm-ui/backend/db_services/group/views.py b/dbm-ui/backend/db_services/group/views.py index de3c40689b..8928b02942 100644 --- a/dbm-ui/backend/db_services/group/views.py +++ b/dbm-ui/backend/db_services/group/views.py @@ -24,7 +24,10 @@ from backend.db_meta.models import Group from backend.db_services.group.handlers import GroupHandler from backend.db_services.group.serializers import GroupMoveInstancesSerializer, GroupSerializer -from backend.iam_app.handlers.drf_perm.base import DBManagePermission, get_request_key_id +from backend.iam_app.dataclass import ResourceEnum +from backend.iam_app.dataclass.actions import ActionEnum +from backend.iam_app.handlers.drf_perm.base import DBManagePermission, ResourceActionPermission, get_request_key_id +from backend.iam_app.handlers.permission import Permission SWAGGER_TAG = _("分组") @@ -38,12 +41,28 @@ class GroupViewSet(viewsets.AuditedModelViewSet): serializer_class = GroupSerializer pagination_class = AuditedLimitOffsetPagination - def _get_custom_permissions(self): - bk_biz_id = get_request_key_id(self.request, key="bk_biz_id", default=0) - if int(bk_biz_id): - return [DBManagePermission()] - - return [] + def get_action_permission_map(self): + return { + ( + "create", + "update", + "destroy", + "move_instances", + ): [ResourceActionPermission([ActionEnum.GROUP_MANAGE], ResourceEnum.BUSINESS, self.inst_getter)] + } + + def get_default_permission_class(self): + return [DBManagePermission()] + + @staticmethod + def inst_getter(request, view): + if view.action == "move_instances": + bk_biz_id = Group.objects.get(id=request.data["new_group_id"]).bk_biz_id + elif view.detail: + bk_biz_id = Group.objects.get(id=view.kwargs["pk"]).bk_biz_id + else: + bk_biz_id = get_request_key_id(request, "bk_biz_id") + return [bk_biz_id] def get_queryset(self): queryset = super().get_queryset() @@ -67,6 +86,11 @@ def retrieve(self, request, *args, **kwargs): auto_schema=PaginatedResponseSwaggerAutoSchema, tags=[SWAGGER_TAG], ) + @Permission.decorator_external_permission_field( + param_field=lambda d: d["bk_biz_id"], + actions=[ActionEnum.GROUP_MANAGE], + resource_meta=ResourceEnum.BUSINESS, + ) def list(self, request, *args, **kwargs): return super().list(request, *args, **kwargs) diff --git a/dbm-ui/backend/db_services/infras/views.py b/dbm-ui/backend/db_services/infras/views.py index 036e716a49..dc6b4ad598 100644 --- a/dbm-ui/backend/db_services/infras/views.py +++ b/dbm-ui/backend/db_services/infras/views.py @@ -27,8 +27,7 @@ class DBTypeViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [] + default_permission_class = [] @common_swagger_auto_schema( operation_summary=_("查询集群类型"), @@ -41,8 +40,7 @@ def list_db_types(self, requests, *args, **kwargs): class LogicalCityViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [] + default_permission_class = [] @common_swagger_auto_schema( operation_summary=_("查询服务器资源的城市信息"), @@ -57,9 +55,7 @@ def list_cities(self, requests, *args, **kwargs): class HostSpecViewSet(viewsets.SystemViewSet): serializer_class = None - - def _get_custom_permissions(self): - return [] + default_permission_class = [] @common_swagger_auto_schema( operation_summary=_("服务器规格列表"), @@ -74,9 +70,7 @@ def list_host_specs(self, request, *args, **kwargs): class CapSpecViewSet(viewsets.SystemViewSet): serializer_class = serializers.QueryCapSpecSLZ - - def _get_custom_permissions(self): - return [] + default_permission_class = [] @common_swagger_auto_schema( operation_summary=_("容量规格列表"), diff --git a/dbm-ui/backend/db_services/ipchooser/views.py b/dbm-ui/backend/db_services/ipchooser/views.py index 06a6b89120..256a7593b3 100644 --- a/dbm-ui/backend/db_services/ipchooser/views.py +++ b/dbm-ui/backend/db_services/ipchooser/views.py @@ -28,9 +28,7 @@ class IpChooserTopoViewSet(viewsets.SystemViewSet): URL_BASE_NAME = "ipchooser_topo" - - def _get_custom_permissions(self): - return [] + default_permission_class = [] @swagger_auto_schema( operation_summary=_("批量获取含各节点主机数量的拓扑树"), @@ -104,9 +102,7 @@ def query_host_topo_infos(self, request, *args, **kwargs): class IpChooserHostViewSet(viewsets.SystemViewSet): URL_BASE_NAME = "ipchooser_host" pagination_class = None - - def _get_custom_permissions(self): - return [] + default_permission_class = [] @swagger_auto_schema( operation_summary=_("根据用户手动输入的`IP`/`IPv6`/`主机名`/`host_id`等关键字信息获取真实存在的机器信息"), @@ -146,9 +142,7 @@ def details(self, request, *args, **kwargs): class IpChooserSettingsViewSet(viewsets.SystemViewSet): LABEL_BASE_NAME = "ipchooser" pagination_class = None - - def _get_custom_permissions(self): - return [] + default_permission_class = [] DEFAULT_SETTINGS = { "settings_map": { diff --git a/dbm-ui/backend/db_services/mongodb/cluster/views.py b/dbm-ui/backend/db_services/mongodb/cluster/views.py index 32af4a0d3d..ce05a966be 100644 --- a/dbm-ui/backend/db_services/mongodb/cluster/views.py +++ b/dbm-ui/backend/db_services/mongodb/cluster/views.py @@ -14,5 +14,5 @@ class ClusterViewSet(BaseClusterViewSet): - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] diff --git a/dbm-ui/backend/db_services/mongodb/permission/db_authorize/handlers.py b/dbm-ui/backend/db_services/mongodb/permission/db_authorize/handlers.py index acbd7c54ae..a11b2e487b 100644 --- a/dbm-ui/backend/db_services/mongodb/permission/db_authorize/handlers.py +++ b/dbm-ui/backend/db_services/mongodb/permission/db_authorize/handlers.py @@ -33,9 +33,14 @@ class MongoDBAuthorizeHandler(AuthorizeHandler): EXCEL_ERROR_TEMPLATE: str = AUTHORIZE_EXCEL_ERROR_TEMPLATE authorize_meta: AuthorizeMeta = MongoDBAuthorizeMeta excel_authorize_meta: ExcelAuthorizeMeta = MongoDBExcelAuthorizeMeta + account_type: AccountType = AccountType.MONGODB def _pre_check_rules( - self, authorize: MongoDBAuthorizeMeta, user_db__rules: Dict = None, user__password: Dict = None + self, + authorize: MongoDBAuthorizeMeta, + user_info_map: Dict = None, + user_db__rules: Dict = None, + user__password: Dict = None, ) -> Tuple[bool, str, Dict]: """前置校验""" @@ -45,6 +50,7 @@ def _pre_check_rules( "cluster_ids": authorize.cluster_ids, "target_instances": authorize.target_instances, "access_dbs": authorize.access_dbs, + "account_id": user_info_map.get(username, {}).get("id"), "username": username, "password": "", "auth_db": auth_db, @@ -82,18 +88,32 @@ def _get_user_rules_and_password_map(self, users: List[str]): def pre_check_excel_rules(self, excel_authorize: ExcelAuthorizeMeta, **kwargs) -> Dict: users = [d[AuthorizeExcelHeader.USER] for d in excel_authorize.authorize_excel_data] + # 获取授权用户相关数据,缓存成字典减少重复请求 user_db__rules, user_password_map = self._get_user_rules_and_password_map(users) + user_info_map = self._get_user_info_map(self.account_type, self.bk_biz_id) + # 获取授权检查数据 return super().pre_check_excel_rules( - excel_authorize, user_db__rules=user_db__rules, user__password=user_password_map, **kwargs + excel_authorize, + user_db__rules=user_db__rules, + user_info_map=user_info_map, + user__password=user_password_map, + **kwargs ) def multi_user_pre_check_rules(self, authorize: MongoDBAuthorizeMeta, **kwargs): """多个账号的前置校验,适合mongodb的授权""" users = [user["user"] for user in authorize.mongo_users] + # 获取授权用户相关数据,缓存成字典减少重复请求 user_db__rules, user_password_map = self._get_user_rules_and_password_map(users) + user_info_map = self._get_user_info_map(self.account_type, self.bk_biz_id) # 获取授权检查数据 authorize_check_result = self._multi_user_pre_check_rules( - authorize, users_key="mongo_users", user_db__rules=user_db__rules, user_password_map=user_password_map + authorize, + users_key="mongo_users", + user_db__rules=user_db__rules, + user_password_map=user_password_map, + user_info_map=user_info_map, + **kwargs ) return authorize_check_result diff --git a/dbm-ui/backend/db_services/mongodb/permission/db_authorize/views.py b/dbm-ui/backend/db_services/mongodb/permission/db_authorize/views.py index eb4298cf47..d207a7b451 100644 --- a/dbm-ui/backend/db_services/mongodb/permission/db_authorize/views.py +++ b/dbm-ui/backend/db_services/mongodb/permission/db_authorize/views.py @@ -35,8 +35,8 @@ class DBAuthorizeViewSet(BaseDBAuthorizeViewSet): authorize_meta = MongoDBAuthorizeMeta excel_authorize_meta = MongoDBExcelAuthorizeMeta - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("规则前置检查"), diff --git a/dbm-ui/backend/db_services/mongodb/resources/views.py b/dbm-ui/backend/db_services/mongodb/resources/views.py index 5db7fca38c..e77d40bd30 100644 --- a/dbm-ui/backend/db_services/mongodb/resources/views.py +++ b/dbm-ui/backend/db_services/mongodb/resources/views.py @@ -106,8 +106,8 @@ def get_instance_role(self, request, *args, **kwargs): class ResourceTreeViewSet(SystemViewSet): serializer_class = SearchResourceTreeSLZ - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("获取资源拓扑树"), diff --git a/dbm-ui/backend/db_services/mongodb/restore/views.py b/dbm-ui/backend/db_services/mongodb/restore/views.py index 882a7c0787..eaccd61ba0 100644 --- a/dbm-ui/backend/db_services/mongodb/restore/views.py +++ b/dbm-ui/backend/db_services/mongodb/restore/views.py @@ -32,8 +32,8 @@ class MongoDBRestoreViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("获取集群单据备份记录"), diff --git a/dbm-ui/backend/db_services/mysql/cluster/views.py b/dbm-ui/backend/db_services/mysql/cluster/views.py index f2bee66adc..642348c1d9 100644 --- a/dbm-ui/backend/db_services/mysql/cluster/views.py +++ b/dbm-ui/backend/db_services/mysql/cluster/views.py @@ -36,8 +36,8 @@ class ClusterViewSet(BaseClusterViewSet): - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("通过过滤条件批量查询集群"), diff --git a/dbm-ui/backend/db_services/mysql/dumper/views/dumper_config.py b/dbm-ui/backend/db_services/mysql/dumper/views/dumper_config.py index 1d1e9ab9bd..56282eb3e5 100644 --- a/dbm-ui/backend/db_services/mysql/dumper/views/dumper_config.py +++ b/dbm-ui/backend/db_services/mysql/dumper/views/dumper_config.py @@ -40,20 +40,31 @@ class DumperConfigViewSet(viewsets.AuditedModelViewSet): serializer_class = DumperSubscribeConfigSerializer filter_class = DumperConfigListFilter + def get_action_permission_map(self): + return { + ("retrieve",): [ + ResourceActionPermission( + [ActionEnum.DUMPER_CONFIG_VIEW], ResourceEnum.DUMPER_SUBSCRIBE_CONFIG, self.inst_getter + ) + ], + ("update", "partial_update",): [ + ResourceActionPermission( + [ActionEnum.DUMPER_CONFIG_UPDATE], ResourceEnum.DUMPER_SUBSCRIBE_CONFIG, self.inst_getter + ) + ], + ("destroy",): [ + ResourceActionPermission( + [ActionEnum.DUMPER_CONFIG_DESTROY], ResourceEnum.DUMPER_SUBSCRIBE_CONFIG, self.inst_getter + ) + ], + } + + def get_default_permission_class(self): + return [DBManagePermission()] + def inst_getter(self, request, view): return [view.kwargs["pk"]] - def _get_custom_permissions(self): - resource_meta = ResourceEnum.DUMPER_SUBSCRIBE_CONFIG - if self.action == "retrieve": - return [ResourceActionPermission([ActionEnum.DUMPER_CONFIG_VIEW], resource_meta, self.inst_getter)] - elif self.action in ["update", "partial_update"]: - return [ResourceActionPermission([ActionEnum.DUMPER_CONFIG_UPDATE], resource_meta, self.inst_getter)] - elif self.action in ["destroy"]: - return [ResourceActionPermission([ActionEnum.DUMPER_CONFIG_DESTROY], resource_meta, self.inst_getter)] - - return [DBManagePermission()] - def get_queryset(self): return self.queryset.filter(bk_biz_id=self.kwargs["bk_biz_id"]) diff --git a/dbm-ui/backend/db_services/mysql/dumper/views/dumper_instance.py b/dbm-ui/backend/db_services/mysql/dumper/views/dumper_instance.py index 77c209b014..8cd782e6fd 100644 --- a/dbm-ui/backend/db_services/mysql/dumper/views/dumper_instance.py +++ b/dbm-ui/backend/db_services/mysql/dumper/views/dumper_instance.py @@ -31,9 +31,7 @@ class DumperInstanceViewSet(viewsets.AuditedModelViewSet): queryset = ExtraProcessInstance.objects.filter(proc_type=ExtraProcessType.TBINLOGDUMPER) serializer_class = DumperInstanceConfigSerializer filter_class = DumperInstanceListFilter - - def _get_custom_permissions(self): - return [] + default_permission_class = [] def get_queryset(self): return self.queryset.filter(bk_biz_id=self.kwargs["bk_biz_id"]) diff --git a/dbm-ui/backend/db_services/mysql/fixpoint_rollback/views.py b/dbm-ui/backend/db_services/mysql/fixpoint_rollback/views.py index 8ff81270fb..561390206d 100644 --- a/dbm-ui/backend/db_services/mysql/fixpoint_rollback/views.py +++ b/dbm-ui/backend/db_services/mysql/fixpoint_rollback/views.py @@ -44,8 +44,8 @@ class FixPointRollbackViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("通过日志平台获取集群备份记录"), diff --git a/dbm-ui/backend/db_services/mysql/open_area/handlers.py b/dbm-ui/backend/db_services/mysql/open_area/handlers.py index dba8ba232b..1c36d03425 100644 --- a/dbm-ui/backend/db_services/mysql/open_area/handlers.py +++ b/dbm-ui/backend/db_services/mysql/open_area/handlers.py @@ -166,4 +166,4 @@ def openarea_result_preview( rules_set: List[Dict[str, Any]] = cls.__get_openarea_rules_set( config, config_data, operator, cluster_id__cluster ) - return {"config_data": openarea_results, "rules_set": rules_set} + return {"config_data": openarea_results, "rules_set": rules_set, "config_id": config_id} diff --git a/dbm-ui/backend/db_services/mysql/open_area/views.py b/dbm-ui/backend/db_services/mysql/open_area/views.py index 048d185fd5..4de264326b 100644 --- a/dbm-ui/backend/db_services/mysql/open_area/views.py +++ b/dbm-ui/backend/db_services/mysql/open_area/views.py @@ -50,9 +50,10 @@ class OpenAreaViewSet(viewsets.AuditedModelViewSet): pagination_class = AuditedLimitOffsetPagination filter_class = TendbOpenAreaConfigListFilter - def _get_custom_permissions(self): - if self.action in ["create", "update", "destroy"]: - return [OpenareaConfigPermission(self.action)] + def get_action_permission_map(self): + return {("create", "update", "destroy"): [OpenareaConfigPermission(self.action)]} + + def get_default_permission_class(self): return [DBManagePermission()] def get_queryset(self): diff --git a/dbm-ui/backend/db_services/mysql/permission/authorize/handlers.py b/dbm-ui/backend/db_services/mysql/permission/authorize/handlers.py index 3ba9e27a19..3807b8cb87 100644 --- a/dbm-ui/backend/db_services/mysql/permission/authorize/handlers.py +++ b/dbm-ui/backend/db_services/mysql/permission/authorize/handlers.py @@ -47,18 +47,34 @@ class MySQLAuthorizeHandler(AuthorizeHandler): authorize_meta: AuthorizeMeta = MySQLAuthorizeMeta excel_authorize_meta: ExcelAuthorizeMeta = MySQLExcelAuthorizeMeta + def pre_check_excel_rules(self, excel_authorize: ExcelAuthorizeMeta, **kwargs) -> Dict: + """sqlserver的excel导入授权""" + account_type = ClusterType.cluster_type_to_db_type(excel_authorize.cluster_type) + user_info_map = self._get_user_info_map(account_type, self.bk_biz_id) + return super().pre_check_excel_rules(excel_authorize, user_info_map=user_info_map, **kwargs) + def pre_check_rules(self, authorize: AuthorizeMeta, task_index: int = None, **kwargs) -> Dict: + # 如果没有user_info_map,则请求一次。 + account_type = ClusterType.cluster_type_to_db_type(authorize.cluster_type) + if not kwargs.get("user_info_map"): + kwargs["user_info_map"] = super()._get_user_info_map(account_type, self.bk_biz_id) + pre_check_data = super().pre_check_rules(authorize, task_index, **kwargs) - pre_check_data["authorize_data"] = authorize.to_dict() + account_id = pre_check_data["authorize_data"]["account_id"] + # mysql的授权数据和输入的authorize保持一致 + pre_check_data["authorize_data"] = {"account_id": account_id, **authorize.to_dict()} return pre_check_data - def _pre_check_rules(self, authorize: AuthorizeMeta, **kwargs) -> Tuple[bool, str, Dict]: + def _pre_check_rules( + self, authorize: AuthorizeMeta, user_info_map: Dict = None, **kwargs + ) -> Tuple[bool, str, Dict]: """前置校验的具体实现逻辑""" account_rules = [{"bk_biz_id": self.bk_biz_id, "dbname": dbname} for dbname in authorize.access_dbs] source_ips = [item["ip"] if isinstance(item, dict) else item for item in authorize.source_ips] authorize_data = { "bk_biz_id": self.bk_biz_id, "operator": self.operator, + "account_id": user_info_map.get(authorize.user, {}).get("id"), "user": authorize.user, "access_dbs": authorize.access_dbs, "account_rules": account_rules, @@ -66,7 +82,6 @@ def _pre_check_rules(self, authorize: AuthorizeMeta, **kwargs) -> Tuple[bool, st "target_instances": authorize.target_instances, "cluster_type": authorize.cluster_type, } - try: # 这里需要authorize_data副本去请求参数,否则会带上app_code/app_secret resp = DBPrivManagerApi.pre_check_authorize_rules(params=copy.deepcopy(authorize_data), raw=True) diff --git a/dbm-ui/backend/db_services/mysql/permission/authorize/views.py b/dbm-ui/backend/db_services/mysql/permission/authorize/views.py index 1df6b9b576..f5a42539d7 100644 --- a/dbm-ui/backend/db_services/mysql/permission/authorize/views.py +++ b/dbm-ui/backend/db_services/mysql/permission/authorize/views.py @@ -31,8 +31,8 @@ class DBAuthorizeViewSet(BaseDBAuthorizeViewSet): authorize_meta = MySQLAuthorizeMeta excel_authorize_meta = MySQLExcelAuthorizeMeta - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("查询授权主机的信息"), diff --git a/dbm-ui/backend/db_services/mysql/permission/clone/views.py b/dbm-ui/backend/db_services/mysql/permission/clone/views.py index 0677166c14..e5b4090725 100644 --- a/dbm-ui/backend/db_services/mysql/permission/clone/views.py +++ b/dbm-ui/backend/db_services/mysql/permission/clone/views.py @@ -33,8 +33,8 @@ class DBCloneViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] def _view_common_handler( self, request, bk_biz_id: int, meta: Type[CloneMeta], func: str diff --git a/dbm-ui/backend/db_services/mysql/remote_service/views.py b/dbm-ui/backend/db_services/mysql/remote_service/views.py index 511511112f..877009f67e 100644 --- a/dbm-ui/backend/db_services/mysql/remote_service/views.py +++ b/dbm-ui/backend/db_services/mysql/remote_service/views.py @@ -32,8 +32,8 @@ class RemoteServiceViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] def _get_cluster_id_and_role(self, validated_data): cluster_id__role_map = {} diff --git a/dbm-ui/backend/db_services/mysql/resources/tendbcluster/views.py b/dbm-ui/backend/db_services/mysql/resources/tendbcluster/views.py index b110fccb5f..599c563474 100644 --- a/dbm-ui/backend/db_services/mysql/resources/tendbcluster/views.py +++ b/dbm-ui/backend/db_services/mysql/resources/tendbcluster/views.py @@ -82,7 +82,6 @@ class SpiderViewSet(viewsets.ResourceViewSet): list_perm_actions = [ ActionEnum.TENDBCLUSTER_SPIDER_ADD_NODES, ActionEnum.TENDBCLUSTER_SPIDER_REDUCE_NODES, - ActionEnum.TENDBCLUSTER_AUTHORIZE_RULES, ActionEnum.TENDBCLUSTER_NODE_REBALANCE, ActionEnum.TENDBCLUSTER_VIEW, ActionEnum.TENDBCLUSTER_ENABLE_DISABLE, diff --git a/dbm-ui/backend/db_services/mysql/resources/tendbha/views.py b/dbm-ui/backend/db_services/mysql/resources/tendbha/views.py index 5d8fa6da84..a4802af692 100644 --- a/dbm-ui/backend/db_services/mysql/resources/tendbha/views.py +++ b/dbm-ui/backend/db_services/mysql/resources/tendbha/views.py @@ -80,7 +80,6 @@ class DBHAViewSet(viewsets.ResourceViewSet): query_serializer_class = serializers.ListMySQLResourceSLZ list_perm_actions = [ - ActionEnum.MYSQL_AUTHORIZE_RULES, ActionEnum.MYSQL_ENABLE_DISABLE, ActionEnum.MYSQL_DESTROY, ActionEnum.MYSQL_VIEW, diff --git a/dbm-ui/backend/db_services/mysql/resources/tendbsingle/views.py b/dbm-ui/backend/db_services/mysql/resources/tendbsingle/views.py index fb60a08bee..e9a1e64fb7 100644 --- a/dbm-ui/backend/db_services/mysql/resources/tendbsingle/views.py +++ b/dbm-ui/backend/db_services/mysql/resources/tendbsingle/views.py @@ -81,7 +81,6 @@ class DBSingleViewSet(viewsets.ResourceViewSet): query_serializer_class = serializers.ListMySQLResourceSLZ list_perm_actions = [ - ActionEnum.MYSQL_AUTHORIZE_RULES, ActionEnum.MYSQL_ENABLE_DISABLE, ActionEnum.MYSQL_DESTROY, ActionEnum.MYSQL_VIEW, diff --git a/dbm-ui/backend/db_services/mysql/resources/views.py b/dbm-ui/backend/db_services/mysql/resources/views.py index 0dfc23b402..cef3294c12 100644 --- a/dbm-ui/backend/db_services/mysql/resources/views.py +++ b/dbm-ui/backend/db_services/mysql/resources/views.py @@ -38,8 +38,8 @@ class ListResourceViewSet(BaseListResourceViewSet): class ResourceTreeViewSet(SystemViewSet): pagination_class = None - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("获取资源拓扑树"), diff --git a/dbm-ui/backend/db_services/mysql/sql_import/views.py b/dbm-ui/backend/db_services/mysql/sql_import/views.py index 90868c8d85..2d3cfd6fb7 100644 --- a/dbm-ui/backend/db_services/mysql/sql_import/views.py +++ b/dbm-ui/backend/db_services/mysql/sql_import/views.py @@ -39,7 +39,7 @@ from backend.iam_app.dataclass.resources import ResourceEnum from backend.iam_app.handlers.drf_perm.base import DBManagePermission from backend.iam_app.handlers.drf_perm.taskflow import TaskFlowPermission -from backend.iam_app.handlers.drf_perm.ticket import CreateTicketPermission +from backend.iam_app.handlers.drf_perm.ticket import CreateTicketOneResourcePermission from backend.ticket.constants import TicketType SWAGGER_TAG = "db_services/mysql/sql_import" @@ -50,12 +50,11 @@ def _get_custom_permissions(self): if self.action == "semantic_check": cluster_type = self.request.data["cluster_type"] ticket_type = getattr(TicketType, f"{cluster_type.upper()}_IMPORT_SQLFILE") - return [CreateTicketPermission(ticket_type)] + return [CreateTicketOneResourcePermission(ticket_type)] elif self.action == "revoke_semantic_check": return [TaskFlowPermission([ActionEnum.FLOW_DETAIL], ResourceEnum.TASKFLOW)] elif self.action == "get_user_semantic_tasks": return [] - return [DBManagePermission()] def _view_common_handler( diff --git a/dbm-ui/backend/db_services/partition/serializers.py b/dbm-ui/backend/db_services/partition/serializers.py index 3930f90380..9a4ca05087 100644 --- a/dbm-ui/backend/db_services/partition/serializers.py +++ b/dbm-ui/backend/db_services/partition/serializers.py @@ -45,12 +45,6 @@ class Meta: swagger_schema_fields = {"example": mock.PARTITION_LIST_DATA} -class PartitionDeleteSerializer(serializers.Serializer): - bk_biz_id = serializers.IntegerField(help_text=_("业务ID")) - cluster_type = serializers.ChoiceField(help_text=_("集群类型"), choices=ClusterType.get_choices()) - ids = serializers.ListField(help_text=_("分区策略ID"), child=serializers.IntegerField()) - - class PartitionCreateSerializer(serializers.Serializer): cluster_id = serializers.IntegerField(help_text=_("集群ID")) dblikes = serializers.ListField(help_text=_("匹配库列表(支持通配)"), child=DBTableField(db_field=True)) @@ -93,6 +87,7 @@ class PartitionUpdateSerializer(PartitionCreateSerializer): class PartitionDisableSerializer(serializers.Serializer): cluster_type = serializers.ChoiceField(help_text=_("集群类型"), choices=ClusterType.get_choices()) + bk_biz_id = serializers.IntegerField(help_text=_("业务ID")) operator = serializers.SerializerMethodField(help_text=_("操作者")) ids = serializers.ListField(help_text=_("分区策略ID"), child=serializers.IntegerField()) @@ -104,6 +99,10 @@ class PartitionEnableSerializer(PartitionDisableSerializer): pass +class PartitionDeleteSerializer(PartitionDisableSerializer): + pass + + class PartitionLogSerializer(serializers.Serializer): cluster_type = serializers.ChoiceField(help_text=_("集群类型"), choices=ClusterType.get_choices()) config_id = serializers.IntegerField(help_text=_("分区策略ID")) diff --git a/dbm-ui/backend/db_services/partition/views.py b/dbm-ui/backend/db_services/partition/views.py index 8a8c3dd9e4..ce9a87a0f6 100644 --- a/dbm-ui/backend/db_services/partition/views.py +++ b/dbm-ui/backend/db_services/partition/views.py @@ -9,8 +9,6 @@ specific language governing permissions and limitations under the License. """ -from functools import wraps - from django.utils.translation import ugettext_lazy as _ from rest_framework import status from rest_framework.decorators import action @@ -36,10 +34,9 @@ PartitionRunSerializer, PartitionUpdateSerializer, ) -from backend.iam_app.handlers.drf_perm.base import DBManagePermission, get_request_key_id +from backend.iam_app.handlers.drf_perm.base import DBManagePermission from ...db_meta.models import Cluster -from ...iam_app.dataclass import ResourceEnum from ...iam_app.dataclass.actions import ActionEnum from ...iam_app.handlers.drf_perm.cluster import PartitionManagePermission from ...iam_app.handlers.permission import Permission @@ -49,46 +46,12 @@ from .handlers import PartitionHandler -def decorator_permission_field(): - def wrapper(view_func): - @wraps(view_func) - def wrapped_view(*args, **kwargs): - request = args[1] - response = view_func(*args, **kwargs) - # 嵌入是否有执行分区的权限 - cluster_type = get_request_key_id(request, "cluster_type") - bk_biz_id = get_request_key_id(request, "bk_biz_id") - if cluster_type == ClusterType.TenDBHA: - actions, resource_meta = [ActionEnum.MYSQL_PARTITION], ResourceEnum.MYSQL - else: - actions, resource_meta = [ActionEnum.TENDBCLUSTER_PARTITION], ResourceEnum.TENDBCLUSTER - - Permission.insert_permission_field( - response, actions, resource_meta, id_field=lambda x: x["cluster_id"], data_field=lambda x: x["results"] - ) - - # 嵌入是否有分区管理策略的管理权限 - resource_meta = ResourceEnum.BUSINESS - actions = ActionEnum.get_match_actions( - name="partition", exclude=[ActionEnum.MYSQL_PARTITION, ActionEnum.TENDBCLUSTER_PARTITION] - ) - Permission.insert_external_permission_field(response, actions, resource_meta, bk_biz_id) - - return response - - return wrapped_view - - return wrapper - - class DBPartitionViewSet(viewsets.AuditedModelViewSet): pagination_class = None - def _get_custom_permissions(self): - if self.action in ["list", "query_log", "verify_partition_field"]: - return [DBManagePermission()] - return [PartitionManagePermission()] + action_permission_map = {("list", "verify_partition_field"): [DBManagePermission()]} + default_permission_class = [PartitionManagePermission()] @staticmethod def _update_log_status(log_list): @@ -107,7 +70,14 @@ def _update_log_status(log_list): responses={status.HTTP_200_OK: PartitionListResponseSerializer()}, tags=[SWAGGER_TAG], ) - @decorator_permission_field() + @Permission.decorator_permission_field( + id_field=lambda d: d["cluster_id"], + data_field=lambda d: d["results"], + action_filed=lambda d: ActionEnum.get_match_actions( + name=f"{ClusterType.cluster_type_to_db_type(d['cluster_type'])}_partition", + exclude=[ActionEnum.MYSQL_PARTITION_CREATE, ActionEnum.TENDBCLUSTER_PARTITION_CREATE], + ), + ) def list(self, request, *args, **kwargs): validated_data = self.params_validate(PartitionListSerializer) partition_data = DBPartitionApi.query_conf(params=validated_data) diff --git a/dbm-ui/backend/db_services/plugin/mysql/authorize/views.py b/dbm-ui/backend/db_services/plugin/mysql/authorize/views.py index 690e92118a..1a3c55b696 100644 --- a/dbm-ui/backend/db_services/plugin/mysql/authorize/views.py +++ b/dbm-ui/backend/db_services/plugin/mysql/authorize/views.py @@ -25,8 +25,7 @@ class AuthorizePluginViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [DBManagePermission()] + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("第三方权限申请(兼容GCS)"), diff --git a/dbm-ui/backend/db_services/quick_search/views.py b/dbm-ui/backend/db_services/quick_search/views.py index 25e89fd4f4..62c8adc2ff 100644 --- a/dbm-ui/backend/db_services/quick_search/views.py +++ b/dbm-ui/backend/db_services/quick_search/views.py @@ -24,8 +24,7 @@ class QuickSearchViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [] + default_permission_class = [] @common_swagger_auto_schema( operation_summary=_("[quick_search] 快速查询"), diff --git a/dbm-ui/backend/db_services/redis/redis_dts/views.py b/dbm-ui/backend/db_services/redis/redis_dts/views.py index b162ad6c2f..8de4faca78 100644 --- a/dbm-ui/backend/db_services/redis/redis_dts/views.py +++ b/dbm-ui/backend/db_services/redis/redis_dts/views.py @@ -41,8 +41,8 @@ class TendisDtsJobViewSet(viewsets.AuditedModelViewSet): serializer_class = TbTendisDTSJobSerializer - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("获取DTS历史任务以及其对应task cnt"), diff --git a/dbm-ui/backend/db_services/redis/resources/views.py b/dbm-ui/backend/db_services/redis/resources/views.py index 790f72e586..df8611a8ff 100644 --- a/dbm-ui/backend/db_services/redis/resources/views.py +++ b/dbm-ui/backend/db_services/redis/resources/views.py @@ -36,8 +36,8 @@ class ListResourceViewSet(BaseListResourceViewSet): class ResourceTreeViewSet(SystemViewSet): pagination_class = None - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("获取资源拓扑树"), diff --git a/dbm-ui/backend/db_services/redis/rollback/views.py b/dbm-ui/backend/db_services/redis/rollback/views.py index 1aefa84756..0594d9d441 100644 --- a/dbm-ui/backend/db_services/redis/rollback/views.py +++ b/dbm-ui/backend/db_services/redis/rollback/views.py @@ -58,6 +58,7 @@ class RollbackViewSet(ReadOnlyAuditedModelViewSet): ) serializer_class = RollbackSerializer filter_class = RollbackListFilter + default_permission_class = [] def get_queryset(self): queryset = super().get_queryset() @@ -67,9 +68,6 @@ def get_queryset(self): return queryset - def _get_custom_permissions(self): - return [] - @common_swagger_auto_schema( operation_summary=_("构造时间合法性检查"), request_body=CheckTimeSerializer(), diff --git a/dbm-ui/backend/db_services/redis/toolbox/views.py b/dbm-ui/backend/db_services/redis/toolbox/views.py index 23e73606f8..cd820ee788 100644 --- a/dbm-ui/backend/db_services/redis/toolbox/views.py +++ b/dbm-ui/backend/db_services/redis/toolbox/views.py @@ -31,8 +31,8 @@ class ToolboxViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("根据IP查询集群、角色和规格"), diff --git a/dbm-ui/backend/db_services/sqlserver/cluster/views.py b/dbm-ui/backend/db_services/sqlserver/cluster/views.py index b82ed55c8b..30674fcfcb 100644 --- a/dbm-ui/backend/db_services/sqlserver/cluster/views.py +++ b/dbm-ui/backend/db_services/sqlserver/cluster/views.py @@ -32,8 +32,8 @@ class ClusterViewSet(BaseClusterViewSet): - def _get_custom_permissions(self): - return [DBManagePermission()] + action_permission_map = {} + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("通过库表匹配查询db"), diff --git a/dbm-ui/backend/db_services/sqlserver/data_migrate/views.py b/dbm-ui/backend/db_services/sqlserver/data_migrate/views.py index 6754a8d6b4..a94d8a5de8 100644 --- a/dbm-ui/backend/db_services/sqlserver/data_migrate/views.py +++ b/dbm-ui/backend/db_services/sqlserver/data_migrate/views.py @@ -35,8 +35,7 @@ class SQLServerDataMigrateViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [DBManagePermission()] + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("手动断开同步"), diff --git a/dbm-ui/backend/db_services/sqlserver/permission/db_authorize/handlers.py b/dbm-ui/backend/db_services/sqlserver/permission/db_authorize/handlers.py index eccace1e5a..60ab11c3cc 100644 --- a/dbm-ui/backend/db_services/sqlserver/permission/db_authorize/handlers.py +++ b/dbm-ui/backend/db_services/sqlserver/permission/db_authorize/handlers.py @@ -32,9 +32,10 @@ class SQLServerAuthorizeHandler(AuthorizeHandler): EXCEL_ERROR_TEMPLATE: str = AUTHORIZE_EXCEL_ERROR_TEMPLATE authorize_meta: AuthorizeMeta = SQLServerDBAuthorizeMeta excel_authorize_meta: ExcelAuthorizeMeta = SQLServerExcelAuthorizeMeta + account_type: AccountType = AccountType.SQLServer def _pre_check_rules( - self, authorize: AuthorizeMeta, user_db__rules: Dict = None, **kwargs + self, authorize: AuthorizeMeta, user_info_map: Dict = None, user_db__rules: Dict = None, **kwargs ) -> Tuple[bool, str, Dict]: """sqlserver前置检查""" @@ -43,6 +44,7 @@ def _pre_check_rules( authorize_data = { "bk_biz_id": self.bk_biz_id, "operator": self.operator, + "account_id": user_info_map.get(authorize.user, {}).get("id"), "user": authorize.user, "access_dbs": authorize.access_dbs, "account_rules": account_rules, @@ -65,17 +67,19 @@ def _pre_check_rules( return True, _("前置校验成功"), authorize_data def pre_check_excel_rules(self, excel_authorize: ExcelAuthorizeMeta, **kwargs) -> Dict: - user_db__rules = AccountHandler.aggregate_user_db_privileges(self.bk_biz_id, AccountType.SQLServer) - return super().pre_check_excel_rules(excel_authorize, user_db__rules=user_db__rules, **kwargs) + """sqlserver的excel导入授权""" + user_db__rules = AccountHandler.aggregate_user_db_privileges(self.bk_biz_id, self.account_type) + user_info_map = self._get_user_info_map(self.account_type, self.bk_biz_id) + return super().pre_check_excel_rules( + excel_authorize, user_info_map=user_info_map, user_db__rules=user_db__rules, **kwargs + ) def multi_user_pre_check_rules(self, authorize: SQLServerDBAuthorizeMeta, **kwargs): """多个账号的前置检查,适合sqlserver的授权""" - user_db__rules = AccountHandler.aggregate_user_db_privileges(self.bk_biz_id, AccountType.SQLServer) - # 获取授权检查数据 + user_db__rules = AccountHandler.aggregate_user_db_privileges(self.bk_biz_id, self.account_type) + user_info_map = self._get_user_info_map(self.account_type, self.bk_biz_id) authorize_check_result = self._multi_user_pre_check_rules( - authorize, - users_key="sqlserver_users", - user_db__rules=user_db__rules, + authorize, users_key="sqlserver_users", user_db__rules=user_db__rules, user_info_map=user_info_map ) return authorize_check_result diff --git a/dbm-ui/backend/db_services/sqlserver/permission/db_authorize/views.py b/dbm-ui/backend/db_services/sqlserver/permission/db_authorize/views.py index a71b784444..47ed9b95e1 100644 --- a/dbm-ui/backend/db_services/sqlserver/permission/db_authorize/views.py +++ b/dbm-ui/backend/db_services/sqlserver/permission/db_authorize/views.py @@ -35,8 +35,7 @@ class DBAuthorizeViewSet(BaseDBAuthorizeViewSet): authorize_meta = SQLServerDBAuthorizeMeta excel_authorize_meta = SQLServerExcelAuthorizeMeta - def _get_custom_permissions(self): - return [DBManagePermission()] + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("规则前置检查"), diff --git a/dbm-ui/backend/db_services/sqlserver/resources/views.py b/dbm-ui/backend/db_services/sqlserver/resources/views.py index b0be6dda76..b9b3fa694f 100644 --- a/dbm-ui/backend/db_services/sqlserver/resources/views.py +++ b/dbm-ui/backend/db_services/sqlserver/resources/views.py @@ -38,8 +38,7 @@ class ListResourceViewSet(BaseListResourceViewSet): class ResourceTreeViewSet(SystemViewSet): pagination_class = None - def _get_custom_permissions(self): - return [DBManagePermission()] + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("获取资源拓扑树"), diff --git a/dbm-ui/backend/db_services/sqlserver/rollback/views.py b/dbm-ui/backend/db_services/sqlserver/rollback/views.py index 03d09e3cc5..290d8b5066 100644 --- a/dbm-ui/backend/db_services/sqlserver/rollback/views.py +++ b/dbm-ui/backend/db_services/sqlserver/rollback/views.py @@ -37,8 +37,7 @@ class SQLServerRollbackViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [DBManagePermission()] + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("查询集群备份记录"), diff --git a/dbm-ui/backend/db_services/sqlserver/sql_import/views.py b/dbm-ui/backend/db_services/sqlserver/sql_import/views.py index 7e6f046b65..80779e8bfd 100644 --- a/dbm-ui/backend/db_services/sqlserver/sql_import/views.py +++ b/dbm-ui/backend/db_services/sqlserver/sql_import/views.py @@ -23,8 +23,7 @@ class SQLImportViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [DBManagePermission()] + default_permission_class = [DBManagePermission()] @common_swagger_auto_schema( operation_summary=_("sql文件导入"), diff --git a/dbm-ui/backend/db_services/taskflow/views/flow.py b/dbm-ui/backend/db_services/taskflow/views/flow.py index 0e261dd949..6de29bb7f8 100644 --- a/dbm-ui/backend/db_services/taskflow/views/flow.py +++ b/dbm-ui/backend/db_services/taskflow/views/flow.py @@ -54,10 +54,8 @@ class TaskFlowViewSet(viewsets.AuditedModelViewSet): "created_by": ["exact", "in"], } - def _get_custom_permissions(self): - if self.action == "list": - return [DBManagePermission()] - return [TaskFlowPermission([ActionEnum.FLOW_DETAIL], ResourceEnum.TASKFLOW)] + action_permission_map = {("list",): [DBManagePermission()]} + default_permission_class = [TaskFlowPermission([ActionEnum.FLOW_DETAIL], ResourceEnum.TASKFLOW)] def get_queryset(self): if self.action != self.list.__name__: diff --git a/dbm-ui/backend/db_services/taskflow/views/redis.py b/dbm-ui/backend/db_services/taskflow/views/redis.py index c6027394b3..f199bb37da 100644 --- a/dbm-ui/backend/db_services/taskflow/views/redis.py +++ b/dbm-ui/backend/db_services/taskflow/views/redis.py @@ -49,10 +49,8 @@ class KeyOpsViewSet(viewsets.ReadOnlyAuditedModelViewSet): serializer_class = FlowTaskSerializer queryset = FlowTree.objects.all() - def _get_custom_permissions(self): - if self.action in ["key_files"]: - return [TaskFlowPermission([ActionEnum.FLOW_DETAIL], ResourceEnum.TASKFLOW)] - return [] + action_permission_map = {("key_files",): [TaskFlowPermission([ActionEnum.FLOW_DETAIL], ResourceEnum.TASKFLOW)]} + default_permission_class = [] @common_swagger_auto_schema( operation_summary=_("结果文件列表"), diff --git a/dbm-ui/backend/db_services/user/views.py b/dbm-ui/backend/db_services/user/views.py index f5ae8dc6e1..d8fe313d66 100644 --- a/dbm-ui/backend/db_services/user/views.py +++ b/dbm-ui/backend/db_services/user/views.py @@ -22,8 +22,7 @@ class UserViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [] + default_permission_class = [] @common_swagger_auto_schema( operation_summary=_("人员列表"), diff --git a/dbm-ui/backend/db_services/version/views.py b/dbm-ui/backend/db_services/version/views.py index dd0657482b..1c8fe5bc53 100644 --- a/dbm-ui/backend/db_services/version/views.py +++ b/dbm-ui/backend/db_services/version/views.py @@ -23,8 +23,7 @@ class VersionViewSet(viewsets.SystemViewSet): - def _get_custom_permissions(self): - return [] + default_permission_class = [] @common_swagger_auto_schema( operation_summary=_("查询所有数据库的版本列表"), diff --git a/dbm-ui/backend/iam_app/dataclass/__init__.py b/dbm-ui/backend/iam_app/dataclass/__init__.py index bc7742427a..baeed69c22 100644 --- a/dbm-ui/backend/iam_app/dataclass/__init__.py +++ b/dbm-ui/backend/iam_app/dataclass/__init__.py @@ -78,14 +78,16 @@ def generate_iam_migration_json(): action_group_content[action.group][action.subgroup].append(action.id) elif action.group: action_group_content[action.group]["actions"].append(action.id) - # 顺便获取资源关联操作,用于创建自动授权 - for related_resource in action.related_resource_types: - # 不关联跨系统和特殊资源(dbtype) - if related_resource.system_id != BK_IAM_SYSTEM_ID: - continue - if related_resource in [ResourceEnum.DBTYPE]: - continue - resource_creator_actions[related_resource.id].append(action.id) + # 顺便获取资源关联操作,用于创建自动授权。目前仅支持动作关联一种资源 + if len(action.related_resource_types) != 1: + continue + related_resource = action.related_resource_types[0] + # 不关联跨系统和特殊资源(dbtype) + if related_resource.system_id != BK_IAM_SYSTEM_ID: + continue + if related_resource in [ResourceEnum.DBTYPE]: + continue + resource_creator_actions[related_resource.id].append(action.id) # 获取动作操作组的json内容 iam_action_group = [] diff --git a/dbm-ui/backend/iam_app/dataclass/actions.py b/dbm-ui/backend/iam_app/dataclass/actions.py index c6bc9c5086..f77d37b6cb 100644 --- a/dbm-ui/backend/iam_app/dataclass/actions.py +++ b/dbm-ui/backend/iam_app/dataclass/actions.py @@ -237,6 +237,17 @@ class ActionEnum: subgroup=_("集群管理"), ) + MYSQL_ADMIN_PWD_MODIFY = ActionMeta( + id="mysql_admin_pwd_modify", + name=_("MySQL 临时密码修改"), + name_en="mysql_admin_pwd_modify", + type="execute", + related_actions=[DB_MANAGE.id], + related_resource_types=[ResourceEnum.MYSQL], + group=_("MySQL"), + subgroup=_("集群管理"), + ) + MYSQL_ENABLE_DISABLE = ActionMeta( id="mysql_enable_disable", name=_("MySQL 集群禁用和启用"), @@ -287,22 +298,44 @@ class ActionEnum: name_en="MySQL Account Delete", type="delete", related_actions=[DB_MANAGE.id], - related_resource_types=[ResourceEnum.BUSINESS], + related_resource_types=[ResourceEnum.MYSQL_ACCOUNT], group=_("MySQL"), subgroup=_("权限管理"), ) MYSQL_ADD_ACCOUNT_RULE = ActionMeta( - id="mysql_account_rule_create", - name=_("MySQL 账号规则创建"), - name_en="MySQL Account Rule Create", + id="mysql_add_account_rule", + name=_("MySQL账号规则创建"), + name_en="mysql_add_account_rule", type="create", related_actions=[DB_MANAGE.id], + related_resource_types=[ResourceEnum.MYSQL_ACCOUNT], + group=_("MySQL"), + subgroup=_("权限管理"), + ) + + MYSQL_ACCOUNT_RULES_VIEW = ActionMeta( + id="mysql_account_rules_view", + name=_("MySQL 账号规则查看"), + name_en="mysql_account_rules_view", + type="view", + related_actions=[DB_MANAGE.id], related_resource_types=[ResourceEnum.BUSINESS], group=_("MySQL"), subgroup=_("权限管理"), ) + MYSQL_AUTHORIZE_RULES = ActionMeta( + id="mysql_authorize_rules", + name=_("MySQL授权"), + name_en="mysql authorize rules", + type="execute", + related_actions=[DB_MANAGE.id], + related_resource_types=[ResourceEnum.MYSQL_ACCOUNT, ResourceEnum.MYSQL], + group=_("MySQL"), + subgroup=_("权限管理"), + ) + MYSQL_EXCEL_AUTHORIZE_RULES = ActionMeta( id=TicketType.MYSQL_EXCEL_AUTHORIZE_RULES.lower(), related_resource_types=[ResourceEnum.BUSINESS], @@ -327,7 +360,7 @@ class ActionEnum: name_en="mysql_partition_update", type="manage", related_actions=[DB_MANAGE.id], - related_resource_types=[ResourceEnum.BUSINESS], + related_resource_types=[ResourceEnum.MYSQL], group=_("MySQL"), subgroup=_("分区管理"), ) @@ -338,7 +371,7 @@ class ActionEnum: name_en="mysql_partition_delete", type="delete", related_actions=[DB_MANAGE.id], - related_resource_types=[ResourceEnum.BUSINESS], + related_resource_types=[ResourceEnum.MYSQL], group=_("MySQL"), subgroup=_("分区管理"), ) @@ -349,11 +382,22 @@ class ActionEnum: name_en="mysql_partition_enable_disable", type="manage", related_actions=[DB_MANAGE.id], - related_resource_types=[ResourceEnum.BUSINESS], + related_resource_types=[ResourceEnum.MYSQL], group=_("MySQL"), subgroup=_("分区管理"), ) + MYSQL_OPEN_AREA = ActionMeta( + id="mysql_open_area", + name=_("MySQL 开区执行"), + name_en="mysql_open_area", + type="execute", + related_actions=[DB_MANAGE.id], + related_resource_types=[ResourceEnum.OPENAREA_CONFIG, ResourceEnum.MYSQL], + group=_("MySQL"), + subgroup=_("克隆开区"), + ) + MYSQL_OPENAREA_CONFIG_CREATE = ActionMeta( id="mysql_openarea_config_create", name=_("MySQL 开区模板创建"), @@ -475,6 +519,17 @@ class ActionEnum: subgroup=_("集群管理"), ) + TENDBCLUSTER_ADMIN_PWD_MODIFY = ActionMeta( + id="tendbcluster_admin_pwd_modify", + name=_("TendbCluster 临时密码修改"), + name_en="tendbcluster_admin_pwd_modify", + type="execute", + related_actions=[DB_MANAGE.id], + related_resource_types=[ResourceEnum.TENDBCLUSTER], + group=_("TendbCluster"), + subgroup=_("集群管理"), + ) + TENDBCLUSTER_CREATE_ACCOUNT = ActionMeta( id="tendbcluster_account_create", name=_("TendbCluster 账号创建"), @@ -492,7 +547,7 @@ class ActionEnum: name_en="tendbcluster_account_delete", type="delete", related_actions=[DB_MANAGE.id], - related_resource_types=[ResourceEnum.BUSINESS], + related_resource_types=[ResourceEnum.TENDBCLUSTER_ACCOUNT], group=_("TendbCluster"), subgroup=_("权限管理"), ) @@ -503,11 +558,33 @@ class ActionEnum: name_en="tendbcluster_add_account_rule", type="create", related_actions=[DB_MANAGE.id], + related_resource_types=[ResourceEnum.TENDBCLUSTER_ACCOUNT], + group=_("TendbCluster"), + subgroup=_("权限管理"), + ) + + TENDBCLUSTER_ACCOUNT_RULES_VIEW = ActionMeta( + id="tendbcluster_account_rules_view", + name=_("TendbCluster 账号规则查看"), + name_en="tendbcluster_account_rules_view", + type="view", + related_actions=[DB_MANAGE.id], related_resource_types=[ResourceEnum.BUSINESS], group=_("TendbCluster"), subgroup=_("权限管理"), ) + TENDBCLUSTER_AUTHORIZE_RULES = ActionMeta( + id="tendbcluster_authorize_rules", + name=_("TendbCluster 授权"), + name_en="tendbcluster_authorize_rules", + type="execute", + related_actions=[DB_MANAGE.id], + related_resource_types=[ResourceEnum.TENDBCLUSTER_ACCOUNT, ResourceEnum.TENDBCLUSTER], + group=_("TendbCluster"), + subgroup=_("权限管理"), + ) + TENDBCLUSTER_EXCEL_AUTHORIZE_RULES = ActionMeta( id="tendb_excel_authorize_rules", name=_("TendbCluster Excel授权"), @@ -537,6 +614,17 @@ class ActionEnum: subgroup=_("权限克隆"), ) + TENDBCLUSTER_OPEN_AREA = ActionMeta( + id="tendbcluster_open_area", + name=_("TendbCluster 开区执行"), + name_en="tendbcluster_open_area", + type="execute", + related_actions=[DB_MANAGE.id], + related_resource_types=[ResourceEnum.OPENAREA_CONFIG, ResourceEnum.TENDBCLUSTER], + group=_("TendbCluster"), + subgroup=_("克隆开区"), + ) + TENDBCLUSTER_OPENAREA_CONFIG_CREATE = ActionMeta( id="tendb_openarea_config_create", name=_("TendbCluster 开区模板创建"), @@ -587,7 +675,7 @@ class ActionEnum: name_en="tendbcluster_partition_update", type="manage", related_actions=[DB_MANAGE.id], - related_resource_types=[ResourceEnum.BUSINESS], + related_resource_types=[ResourceEnum.TENDBCLUSTER], group=_("TendbCluster"), subgroup=_("分区管理"), ) @@ -598,7 +686,7 @@ class ActionEnum: name_en="tendbcluster_partition_delete", type="delete", related_actions=[DB_MANAGE.id], - related_resource_types=[ResourceEnum.BUSINESS], + related_resource_types=[ResourceEnum.TENDBCLUSTER], group=_("TendbCluster"), subgroup=_("分区管理"), ) @@ -609,7 +697,7 @@ class ActionEnum: name_en="tendb_partition_enable_disable", type="manage", related_actions=[DB_MANAGE.id], - related_resource_types=[ResourceEnum.BUSINESS], + related_resource_types=[ResourceEnum.TENDBCLUSTER], group=_("TendbCluster"), subgroup=_("分区管理"), ) @@ -693,6 +781,19 @@ class ActionEnum: subgroup=_("实例管理"), ) + # TODO: 这里的分组管理设计不仅仅针对influxdb使用。 + # 不过目前只有influxdb使用了分组的概念,所以暂归属到InfluxDB中 + GROUP_MANAGE = ActionMeta( + id="group_manage", + name=_("InfluxDB 分组管理"), + name_en="group_manage", + type="manage", + related_actions=[DB_MANAGE.id], + related_resource_types=[ResourceEnum.BUSINESS], + group=_("InfluxDB"), + subgroup=_("实例管理"), + ) + INFLUXDB_ENABLE_DISABLE = ActionMeta( id="influxdb_enable_disable", name=_("InfluxDB 实例禁用启用"), @@ -898,11 +999,11 @@ class ActionEnum: ) MONGODB_DELETE_ACCOUNT = ActionMeta( - id="mongodb_delete_create", - name=_("MongoDB 账号删除"), - name_en="mongodb_delete_create", + id="mongodb_account_delete", + name=_("MongoDB 删除账号"), + name_en="mongodb_account_delete", type="delete", - related_resource_types=[ResourceEnum.BUSINESS], + related_resource_types=[ResourceEnum.MONGODB_ACCOUNT], group=_("MongoDB"), subgroup=_("权限管理"), ) @@ -913,6 +1014,39 @@ class ActionEnum: name_en="mongodb_add_account_rule", type="create", related_actions=[DB_MANAGE.id], + related_resource_types=[ResourceEnum.MONGODB_ACCOUNT], + group=_("MongoDB"), + subgroup=_("权限管理"), + ) + + MONGODB_ACCOUNT_RULES_VIEW = ActionMeta( + id="mongodb_account_rules_view", + name=_("MongoDB 账号规则查看"), + name_en="mongodb_account_rules_view", + type="view", + related_actions=[DB_MANAGE.id], + related_resource_types=[ResourceEnum.BUSINESS], + group=_("MongoDB"), + subgroup=_("权限管理"), + ) + + MONGODB_AUTHORIZE_RULES = ActionMeta( + id="mongodb_authorize_rules", + name=_("MongoDB 集群授权"), + name_en="mongodb_authorize_rules", + type="execute", + related_actions=[DB_MANAGE.id], + related_resource_types=[ResourceEnum.MONGODB_ACCOUNT, ResourceEnum.MONGODB], + group=_("MongoDB"), + subgroup=_("权限管理"), + ) + + MONGODB_EXCEL_AUTHORIZE_RULES = ActionMeta( + id="mongo_excel_authorize_rules", + name=_("MongoDB Excel集群授权"), + name_en="mongo_excel_authorize_rules", + type="execute", + related_actions=[DB_MANAGE.id], related_resource_types=[ResourceEnum.BUSINESS], group=_("MongoDB"), subgroup=_("权限管理"), @@ -928,6 +1062,17 @@ class ActionEnum: subgroup=_("集群管理"), ) + SQLSERVER_ADMIN_PWD_MODIFY = ActionMeta( + id="sqlserver_admin_pwd_modify", + name=_("SQLServer 临时密码修改"), + name_en="sqlserver_admin_pwd_modify", + type="execute", + related_actions=[DB_MANAGE.id], + related_resource_types=[ResourceEnum.SQLSERVER], + group=_("SQLServer"), + subgroup=_("集群管理"), + ) + SQLSERVER_APPLY = ActionMeta( id="sqlserver_apply", name=_("SQLServer 部署"), @@ -950,11 +1095,11 @@ class ActionEnum: ) SQLSERVER_DELETE_ACCOUNT = ActionMeta( - id="sqlserver_delete_create", - name=_("SQLServer 账号删除"), - name_en="sqlserver_delete_create", + id="sqlserver_account_delete", + name=_("SQLServer 删除账号"), + name_en="sqlserver_account_delete", type="delete", - related_resource_types=[ResourceEnum.BUSINESS], + related_resource_types=[ResourceEnum.SQLSERVER_ACCOUNT], group=_("SQLServer"), subgroup=_("权限管理"), ) @@ -965,6 +1110,39 @@ class ActionEnum: name_en="sqlserver_add_account_rule", type="create", related_actions=[DB_MANAGE.id], + related_resource_types=[ResourceEnum.SQLSERVER_ACCOUNT], + group=_("SQLServer"), + subgroup=_("权限管理"), + ) + + SQLSERVER_ACCOUNT_RULES_VIEW = ActionMeta( + id="sqlserver_account_rules_view", + name=_("SQLServer 账号规则查看"), + name_en="sqlserver_account_rules_view", + type="view", + related_actions=[DB_MANAGE.id], + related_resource_types=[ResourceEnum.BUSINESS], + group=_("SQLServer"), + subgroup=_("权限管理"), + ) + + SQLSERVER_AUTHORIZE_RULES = ActionMeta( + id="sqlserver_authorize_rules", + name=_("SQLServer 集群授权"), + name_en="sqlserver_authorize_rules", + type="execute", + related_actions=[DB_MANAGE.id], + related_resource_types=[ResourceEnum.SQLSERVER_ACCOUNT, ResourceEnum.SQLSERVER], + group=_("SQLServer"), + subgroup=_("权限管理"), + ) + + SQLSERVER_EXCEL_AUTHORIZE_RULES = ActionMeta( + id="sqlserver_excel_authorize_rules", + name=_("SQLServer Excel授权"), + name_en="sqlserver_excel_authorize_rules", + type="execute", + related_actions=[DB_MANAGE.id], related_resource_types=[ResourceEnum.BUSINESS], group=_("SQLServer"), subgroup=_("权限管理"), @@ -992,6 +1170,17 @@ class ActionEnum: subgroup=_("资源池"), ) + RESOURCE_OPERATION_VIEW = ActionMeta( + id="resource_operation_view", + name=_("资源池操作记录查看"), + name_en="resource_operation_view", + type="view", + related_actions=[RESOURCE_MANAGE.id], + related_resource_types=[], + group=_("资源管理"), + subgroup=_("资源池"), + ) + DIRTY_POLL_MANAGE = ActionMeta( id="dirty_pool_manage", name=_("污点池管理"), @@ -1034,17 +1223,6 @@ class ActionEnum: subgroup=_("告警组"), ) - GLOBAL_NOTIFY_GROUP_LIST = ActionMeta( - id="global_notify_group_list", - name=_("全局告警组查看"), - name_en="global_notify_group_view", - type="view", - related_actions=[GLOBAL_MANAGE.id], - related_resource_types=[], - group=_("平台管理"), - subgroup=_("告警组"), - ) - NOTIFY_GROUP_CREATE = ActionMeta( id="notify_group_create", name=_("告警组新建"), @@ -1056,57 +1234,35 @@ class ActionEnum: subgroup=_("告警组"), ) - GLOBAL_NOTIFY_GROUP_CREATE = ActionMeta( - id="global_notify_group_create", - name=_("全局告警组新建"), - name_en="global_notify_group_create", - type="create", - related_actions=[GLOBAL_MANAGE.id], - related_resource_types=[], - group=_("平台管理"), - subgroup=_("告警组"), - ) - NOTIFY_GROUP_UPDATE = ActionMeta( id="notify_group_update", name=_("告警组编辑"), name_en="notify_group_edit", type="manage", related_actions=[DB_MANAGE.id], - related_resource_types=[ResourceEnum.BUSINESS], + related_resource_types=[ResourceEnum.NOTIFY_GROUP], group=_("配置管理"), subgroup=_("告警组"), ) - GLOBAL_NOTIFY_GROUP_UPDATE = ActionMeta( - id="global_notify_group_update", - name=_("全局告警组编辑"), - name_en="global_notify_group_edit", - type="manage", - related_actions=[GLOBAL_MANAGE.id], - related_resource_types=[], - group=_("平台管理"), - subgroup=_("告警组"), - ) - NOTIFY_GROUP_DESTROY = ActionMeta( id="notify_group_delete", name=_("告警组删除"), name_en="notify_group_delete", type="delete", related_actions=[DB_MANAGE.id], - related_resource_types=[ResourceEnum.BUSINESS], + related_resource_types=[ResourceEnum.NOTIFY_GROUP], group=_("配置管理"), subgroup=_("告警组"), ) - GLOBAL_NOTIFY_GROUP_DESTROY = ActionMeta( - id="global_notify_group_delete", - name=_("全局告警组删除"), - name_en="global_notify_group_delete", - type="delete", + GLOBAL_NOTIFY_GROUP_UPDATE = ActionMeta( + id="global_notify_group_update", + name=_("全局告警组编辑"), + name_en="global_notify_group_edit", + type="manage", related_actions=[GLOBAL_MANAGE.id], - related_resource_types=[], + related_resource_types=[ResourceEnum.GLOBAL_NOTIFY_GROUP], group=_("平台管理"), subgroup=_("告警组"), ) @@ -1348,7 +1504,7 @@ class ActionEnum: name_en="duty_rule_update", type="manage", related_actions=[GLOBAL_MANAGE.id, DUTY_RULE_LIST.id], - related_resource_types=[ResourceEnum.DUTY_RULE], + related_resource_types=[ResourceEnum.DBTYPE], group=_("平台管理"), subgroup=_("轮值策略"), ) @@ -1359,7 +1515,7 @@ class ActionEnum: name_en="duty_rule_destroy", type="delete", related_actions=[GLOBAL_MANAGE.id, DUTY_RULE_LIST.id], - related_resource_types=[ResourceEnum.DUTY_RULE], + related_resource_types=[ResourceEnum.DBTYPE], group=_("平台管理"), subgroup=_("轮值策略"), ) @@ -1450,7 +1606,7 @@ def get_match_actions(cls, name, exclude=None): actions = [ action for action in cls.__dict__.values() - if isinstance(action, ActionMeta) and name in action.id and action not in exclude + if isinstance(action, ActionMeta) and name.lower() in action.id and action not in exclude ] return actions diff --git a/dbm-ui/backend/iam_app/dataclass/resources.py b/dbm-ui/backend/iam_app/dataclass/resources.py index b24a115030..f72b02eda1 100644 --- a/dbm-ui/backend/iam_app/dataclass/resources.py +++ b/dbm-ui/backend/iam_app/dataclass/resources.py @@ -16,6 +16,7 @@ from django.utils.translation import ugettext as _ from iam import Resource +from backend.components import DBPrivManagerApi from backend.configuration.constants import DBType from backend.db_meta.enums import ClusterType, InstanceRole from backend.db_meta.models import AppCache @@ -318,6 +319,71 @@ class InfluxDBResourceMeta(InstanceResourceMeta): name: str = _("InfluxDB实例") +@dataclass +class AccountResourceMeta(ResourceMeta): + """账号实例resource 属性定义,其他集群的账号资源应该继承此类""" + + id: str = "" + name: str = "" + system_id: str = BK_IAM_SYSTEM_ID + selection_mode: str = "all" + + lookup_field: str = "id" + display_fields: list = ResourceMeta.Field(["user"]) + attribute: str = "creator" + attribute_display: str = _("创建者") + parent: ResourceMeta = BusinessResourceMeta() + + @classmethod + def create_instance(cls, instance_id: str, attr=None) -> Resource: + resource = cls._create_simple_instance(instance_id, attr) + # 根据账号ID查询单个账号 + instance = DBPrivManagerApi.get_account(params={"ids": [int(instance_id)]})["results"][0] + # 更新resource的attribute,id和name + _bk_iam_path_ = "/{},{}/".format(cls.parent.id, instance[cls.parent.lookup_field]) + resource.attribute.update( + { + cls.attribute: instance["creator"], + "id": instance["id"], + "name": instance["user"], + "_bk_iam_path_": _bk_iam_path_, + } + ) + return resource + + +@dataclass +class MySQLAccountResourceMeta(AccountResourceMeta): + """MySQL账号实例resource 属性定义""" + + id: str = "mysql_account" + name: str = _("MySQL 账号") + + +@dataclass +class SQLServerAccountResourceMeta(AccountResourceMeta): + """MySQL账号实例resource 属性定义""" + + id: str = "sqlserver_account" + name: str = _("SQLServer 账号") + + +@dataclass +class MongoDBAccountResourceMeta(AccountResourceMeta): + """MySQL账号实例resource 属性定义""" + + id: str = "mongodb_account" + name: str = _("MongoDB 账号") + + +@dataclass +class TendbClusterAccountResourceMeta(AccountResourceMeta): + """MySQL账号实例resource 属性定义""" + + id: str = "tendbcluster_account" + name: str = _("TendbCluster 账号") + + @dataclass class MonitorPolicyResourceMeta(ResourceMeta): """监控策略实例resource 属性定义""" @@ -384,28 +450,59 @@ def instance_selection(cls): @dataclass -class DutyRuleResourceMeta(ResourceMeta): - """监控策略实例resource 属性定义""" +class NotifyGroupResourceMeta(ResourceMeta): + """告警组实例resource 属性定义""" system_id: str = BK_IAM_SYSTEM_ID - id: str = "duty_rule" - name: str = _("轮值策略") + id: str = "notify_group" + name: str = _("告警组") selection_mode: str = "all" attribute: str = "creator" attribute_display: str = _("创建者") lookup_field: str = "id" display_fields: list = ResourceMeta.Field(["name"]) - parent: ResourceMeta = DBTypeResourceMeta() + parent: ResourceMeta = BusinessResourceMeta() + + @classmethod + def get_bk_iam_path(cls, instance): + biz_topo = "/{},{}/".format(BusinessResourceMeta.id, instance.bk_biz_id) + dbtype_topo = "/{},{}/".format(DBTypeResourceMeta.id, instance.db_type) + if not instance.bk_biz_id: + return dbtype_topo + else: + return biz_topo @classmethod def create_instance(cls, instance_id: str, attr=None) -> Resource: - from backend.db_monitor.models.alarm import DutyRule + from backend.db_monitor.models.alarm import NoticeGroup - resource, __ = cls.create_model_instance(DutyRule, instance_id, attr) + resource, instance = cls.create_model_instance(NoticeGroup, instance_id, attr) + resource.attribute.update(_bk_iam_path_=cls.get_bk_iam_path(instance)) return resource +@dataclass +class GlobalNotifyGroupResourceMeta(NotifyGroupResourceMeta): + """标记为全局告警组视图资源""" + + for_select: bool = True + select_id: str = "global_notify_group" + name: str = _("全局告警组") + + @classmethod + def instance_selection(cls): + return { + "id": f"{cls.select_id}_list", + "name": _("{} 列表".format(cls.name)), + "name_en": f"{cls.select_id} list", + "resource_type_chain": [ + {"system_id": DBTypeResourceMeta.system_id, "id": DBTypeResourceMeta.id}, + {"system_id": cls.system_id, "id": cls.id}, + ], + } + + @dataclass class OpenareaConfigResourceMeta(ResourceMeta): """开区模板实例resource 属性定义""" @@ -474,9 +571,14 @@ class ResourceEnum: DBTYPE = DBTypeResourceMeta() MONITOR_POLICY = MonitorPolicyResourceMeta() GLOBAL_MONITOR_POLICY = GlobalMonitorPolicyResourceMeta() - DUTY_RULE = DutyRuleResourceMeta() + NOTIFY_GROUP = NotifyGroupResourceMeta() + GLOBAL_NOTIFY_GROUP = GlobalNotifyGroupResourceMeta() OPENAREA_CONFIG = OpenareaConfigResourceMeta() DUMPER_SUBSCRIBE_CONFIG = DumperSubscribeConfigResourceMeta() + MYSQL_ACCOUNT = MySQLAccountResourceMeta() + SQLSERVER_ACCOUNT = SQLServerAccountResourceMeta() + MONGODB_ACCOUNT = MongoDBAccountResourceMeta() + TENDBCLUSTER_ACCOUNT = TendbClusterAccountResourceMeta() @classmethod def get_resource_by_id(cls, resource_id: Union[ResourceMeta, str]): diff --git a/dbm-ui/backend/iam_app/handlers/converter.py b/dbm-ui/backend/iam_app/handlers/converter.py new file mode 100644 index 0000000000..7bbea26522 --- /dev/null +++ b/dbm-ui/backend/iam_app/handlers/converter.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" +import logging +import operator +from collections import ChainMap +from functools import reduce + +from django.db.models import Q +from iam import Converter, DjangoQuerySetConverter +from iam.eval.constants import KEYWORD_BK_IAM_PATH_FIELD_SUFFIX, OP +from iam.eval.expression import field_value_convert + +logger = logging.getLogger("root") + + +class MonitorDjangoQuerySetConverter(DjangoQuerySetConverter): + """监控策略的策略转换器""" + + def _iam_path_(self, left, right): + return reduce(operator.and_, [Q(**{field: right[field]}) for field in left.split(",")]) + + def operator_map(self, operator, field, value): + if field.endswith(KEYWORD_BK_IAM_PATH_FIELD_SUFFIX) and operator == OP.STARTS_WITH: + return self._iam_path_ + + +class NotifyGroupDjangoQuerySetConverter(MonitorDjangoQuerySetConverter): + """告警组的策略转换器""" + + def _iam_path_(self, left, right): + return reduce(operator.and_, [Q(**{field: right[field]}) for field in left.split(",") if field in right]) + + +class InterfaceConverter(Converter): + """接口转换器,接口转换只考虑支持and查询""" + + def __init__(self, key_mapping=None, value_hooks=None): + super(InterfaceConverter, self).__init__(key_mapping) + + self.value_hooks = value_hooks or {} + + def _positive(self, fmt, left, right): + kwargs = {fmt.format(left): right} + return kwargs + + def _not_eq(self, left, right): + pass + + def _not_in(self, left, right): + pass + + def _not_contains(self, left, right): + pass + + def _starts_with(self, left, right): + pass + + def _not_starts_with(self, left, right): + pass + + def _ends_with(self, left, right): + pass + + def _not_ends_with(self, left, right): + pass + + def _lt(self, left, right): + pass + + def _lte(self, left, right): + pass + + def _gt(self, left, right): + pass + + def _gte(self, left, right): + pass + + def _any(self, left, right): + pass + + def _or(self, content): + pass + + def _in(self, left, right): + return self._positive("{}s", left, right) + + def _eq(self, left, right): + return self._positive("{}", left, right) + + def _contains(self, left, right): + return self._positive("{}__contains", left, right) + + def _string_contains(self, left, right): + return self._positive("{}__contains", left, right) + + def _and(self, content): + return dict(ChainMap(*[self.convert(c) for c in content])) + + def operator_map(self, operator, field, value): + return None + + def convert(self, data): + op = data["op"] + + if op == OP.AND: + return self._and(data["content"]) + + # 只支持and的策略过滤 + value, field = data["value"], data["field"] + op_func = self.operator_map(op, field, value) + + if not op_func: + op_func = {OP.EQ: self._eq, OP.IN: self._in}.get(op) + + if op_func is None: + raise ValueError("invalid op %s" % op) + + # 权限中心保留字预处理 + field, value = field_value_convert(op, field, value) + + # key mapping + if self.key_mapping and field in self.key_mapping: + field = self.key_mapping.get(field) + # value hooks + if (field in self.value_hooks) and callable(self.value_hooks[field]): + value = self.value_hooks[field](value) + + return op_func(field, value) diff --git a/dbm-ui/backend/iam_app/handlers/drf_perm/account.py b/dbm-ui/backend/iam_app/handlers/drf_perm/account.py new file mode 100644 index 0000000000..8c088cc490 --- /dev/null +++ b/dbm-ui/backend/iam_app/handlers/drf_perm/account.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging +from typing import List + +from django.utils.translation import ugettext as _ + +from backend.iam_app.dataclass.actions import ActionEnum, ActionMeta +from backend.iam_app.dataclass.resources import ResourceEnum, ResourceMeta +from backend.iam_app.exceptions import ActionNotExistError +from backend.iam_app.handlers.drf_perm.base import ResourceActionPermission, get_request_key_id + +logger = logging.getLogger("root") + + +class AccountPermission(ResourceActionPermission): + """ + 账号相关动作鉴权 + """ + + def __init__( + self, account_type: str, view_action: str, actions: List[ActionMeta] = None, resource_meta: ResourceMeta = None + ) -> None: + self.account_type = account_type + self.view_action = view_action + super().__init__(actions, resource_meta, self.instance_id_getter) + + def instance_id_getter(self, request, view): + try: + self.actions = [getattr(ActionEnum, f"{self.account_type}_{self.view_action}".upper())] + except AttributeError: + ActionNotExistError(_("账号动作:{} 不存在/未实现").format(self.view_action)) + # 只有创建账号的动作是业务的,剩下对账号的操作都是以账号为维度的 + if self.view_action == "create_account": + self.resource_meta = ResourceEnum.BUSINESS + return [get_request_key_id(request, "bk_biz_id")] + else: + self.resource_meta = getattr(ResourceEnum, f"{self.account_type.upper()}_ACCOUNT") + return [get_request_key_id(request, "account_id")] diff --git a/dbm-ui/backend/iam_app/handlers/drf_perm/base.py b/dbm-ui/backend/iam_app/handlers/drf_perm/base.py index 1240c92647..58a437be09 100644 --- a/dbm-ui/backend/iam_app/handlers/drf_perm/base.py +++ b/dbm-ui/backend/iam_app/handlers/drf_perm/base.py @@ -10,7 +10,7 @@ """ import itertools import logging -from typing import Callable, Dict, List +from typing import Callable, List from bk_audit.constants.log import DEFAULT_EMPTY_VALUE, DEFAULT_SENSITIVITY from bk_audit.contrib.bk_audit.client import bk_audit_client @@ -28,10 +28,12 @@ def get_request_key_id(request, key, default=None): + # 优先以body为准,在以路由参数为准 + context_key = request.parser_context["kwargs"].get(key, default) if request.method == "GET": - return request.query_params.get(key, default) + return request.query_params.get(key, context_key) else: - return request.data.get(key, default) + return request.data.get(key, context_key) class CommonInstance(object): @@ -180,7 +182,8 @@ def has_permission(self, request, view): return super(ResourceActionPermission, self).has_permission(request, view) def has_object_permission(self, request, view, obj): - return self.has_permission(request, view) + """has_permission在实现上已经包含了对象鉴权""" + return True class MoreResourceActionPermission(IAMPermission): @@ -191,24 +194,26 @@ class MoreResourceActionPermission(IAMPermission): def __init__( self, actions: List[ActionMeta], - resource_metas: List[ResourceMeta], - instance_ids_getters: Dict[str, Callable] = None, + resource_metes: List[ResourceMeta], + instance_ids_getters: Callable = None, ): - self.resource_metas = resource_metas + self.resource_metes = resource_metes self.instance_ids_getters = instance_ids_getters super().__init__(actions) - def get_instance_ids(self, request, view, resource): - return self.instance_ids_getters[resource.id](request, view) - def has_permission(self, request, view): - more_resource_list = [] - # 先按照资源类型获取资源实例 - for resource_meta in self.resource_metas: - instance_ids = self.get_instance_ids(request, view, resource_meta) - more_resource_list.append([resource_meta.create_instance(instance_id) for instance_id in instance_ids]) - # 然后将多个资源实例列表分别组合 - self.resources = [list(resource_tuple) for resource_tuple in zip(*more_resource_list)] + # 先获取资源实例ID,结构为[(a1, b1, c1), (a2, b2, c2), ....] + # 这里资源元组中个体顺序和动作定义的关联资源顺序一致 + resources_map_list = self.instance_ids_getters(request, view) + # 根据类型和resource id创建资源实例 + more_resource_list = [ + [ + resource_meta.create_instance(resources[index]) + for index, resource_meta in enumerate(self.resource_metes) + ] + for resources in resources_map_list + ] + self.resources = more_resource_list # 如果无关联资源/动作,则无需鉴权 if not self.resources or not self.actions: @@ -217,7 +222,8 @@ def has_permission(self, request, view): return super().has_permission(request, view) def has_object_permission(self, request, view, obj): - return super().has_permission(request, view) + """has_permission在实现上已经包含了对象鉴权""" + return True class DBManagePermission(ResourceActionPermission): @@ -243,12 +249,11 @@ class BizDBTypeResourceActionPermission(MoreResourceActionPermission): """ def __init__(self, actions: List[ActionMeta], instance_biz_getter: Callable, instance_dbtype_getter: Callable): - resource_metas = [ResourceEnum.BUSINESS, ResourceEnum.DBTYPE] - instance_ids_getters = { - ResourceEnum.BUSINESS.id: instance_biz_getter, - ResourceEnum.DBTYPE.id: instance_dbtype_getter, - } - super().__init__(actions, resource_metas, instance_ids_getters) + resource_metes = [ResourceEnum.BUSINESS, ResourceEnum.DBTYPE] + instance_ids_getters = lambda request, view: [ # noqa + (instance_biz_getter(request, view)[0], instance_dbtype_getter(request, view)[0]) + ] + super().__init__(actions, resource_metes, instance_ids_getters) class IsAuthenticatedPermission(permissions.BasePermission): diff --git a/dbm-ui/backend/iam_app/handlers/drf_perm/cluster.py b/dbm-ui/backend/iam_app/handlers/drf_perm/cluster.py index 3098bb4c0c..90cb884e68 100644 --- a/dbm-ui/backend/iam_app/handlers/drf_perm/cluster.py +++ b/dbm-ui/backend/iam_app/handlers/drf_perm/cluster.py @@ -8,10 +8,15 @@ 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. """ +import functools +import operator from typing import List +from django.db.models import Q + +from backend.components.mysql_partition.client import DBPartitionApi from backend.db_meta.enums import ClusterType -from backend.db_meta.models import Cluster, StorageInstance +from backend.db_meta.models import Cluster, Machine, StorageInstance from backend.iam_app.dataclass.actions import ActionEnum, ActionMeta from backend.iam_app.dataclass.resources import ResourceEnum, ResourceMeta from backend.iam_app.handlers.drf_perm.base import ResourceActionPermission @@ -61,8 +66,7 @@ class PartitionManagePermission(ResourceActionPermission): """ def __init__(self): - resource_meta = ResourceEnum.BUSINESS - super().__init__(actions=None, resource_meta=resource_meta, instance_ids_getter=self.instance_ids_getter) + super().__init__(actions=None, resource_meta=None, instance_ids_getter=self.instance_ids_getter) def instance_ids_getter(self, request, view): # 从获取到业务ID和集群类型后,决定动作和资源类型 @@ -70,20 +74,62 @@ def instance_ids_getter(self, request, view): if view.action in ["create", "update"]: cluster = Cluster.objects.get(id=request.data["cluster_id"]) bk_biz_id, db_type = cluster.bk_biz_id, convert(cluster.cluster_type) + self.resource_meta = ResourceEnum.BUSINESS self.actions = [getattr(ActionEnum, f"{db_type.upper()}_PARTITION_{view.action.upper()}")] return [bk_biz_id] elif view.action in ["enable", "disable", "batch_delete"]: - db_type, bk_biz_id = convert(request.data["cluster_type"]), request.data["bk_biz_id"] + db_type = convert(request.data["cluster_type"]) + params = {"limit": len(request.data["ids"]), "offset": 0, **request.data} + partition_data = DBPartitionApi.query_conf(params=params)["items"] + cluster_ids = [data["cluster_id"] for data in partition_data] if view.action == "batch_delete": self.actions = [getattr(ActionEnum, f"{db_type.upper()}_PARTITION_DELETE")] else: self.actions = [getattr(ActionEnum, f"{db_type.upper()}_PARTITION_ENABLE_DISABLE")] - return [bk_biz_id] + self.resource_meta = getattr(ResourceEnum, f"{db_type.upper()}") + return list(set(cluster_ids)) - elif view.action in ["dry_run", "execute_partition"]: - cluster = Cluster.objects.get(id=request.data["cluster_id"]) + elif view.action in ["dry_run", "execute_partition", "query_log"]: + if view.action == "query_log": + config_id, cluster_type = int(request.query_params["config_id"]), request.query_params["cluster_type"] + params = {"limit": 1, "offset": 0, "ids": [config_id], "cluster_type": cluster_type} + cluster_id = DBPartitionApi.query_conf(params=params)["items"][0]["cluster_id"] + else: + cluster_id = request.data["cluster_id"] + cluster = Cluster.objects.get(id=cluster_id) db_type = convert(cluster.cluster_type) self.actions = [getattr(ActionEnum, f"{db_type.upper()}_PARTITION")] self.resource_meta = getattr(ResourceEnum, f"{db_type.upper()}") return [cluster.id] + + +class ModifyActionPermission(ResourceActionPermission): + """ + 集群admin密码修改相关动作鉴权 + """ + + def inst_ids_getter(self, request, view): + data = request.data or request.query_params + if view.action == "query_mysql_admin_password": + instances = data["instances"].split(",") + instance_list = [{"bk_cloud_id": inst.split(":")[0], "ip": inst.split(":")[1]} for inst in instances] + else: + instance_list = data["instance_list"] + + # 获取实例关联的machine(这里不查询实例是因为存在spider角色) + machine_ip_filters = functools.reduce( + operator.or_, [Q(bk_cloud_id=inst["bk_cloud_id"], ip=inst["ip"]) for inst in instance_list] + ) + machines = Machine.objects.filter(machine_ip_filters) + # 根据集群类型获得关联实例和动作 + db_type = ClusterType.cluster_type_to_db_type(machines.first().cluster_type) + self.actions = [getattr(ActionEnum, f"{db_type}_admin_pwd_modify".upper())] + self.resource_meta = getattr(ResourceEnum, db_type.upper()) + # 通过machine获取关联集群,用于鉴权 + cluster_id_tuples = list(machines.values("storageinstance__cluster", "proxyinstance__cluster")) + cluster_ids = set([v for item in cluster_id_tuples for v in item.values() if isinstance(v, int)]) + return cluster_ids + + def __init__(self): + super().__init__(actions=None, resource_meta=None, instance_ids_getter=self.inst_ids_getter) diff --git a/dbm-ui/backend/iam_app/handlers/drf_perm/dbconfig.py b/dbm-ui/backend/iam_app/handlers/drf_perm/dbconfig.py index b540c118f6..fbb6e62d1a 100644 --- a/dbm-ui/backend/iam_app/handlers/drf_perm/dbconfig.py +++ b/dbm-ui/backend/iam_app/handlers/drf_perm/dbconfig.py @@ -9,44 +9,16 @@ specific language governing permissions and limitations under the License. """ -from functools import wraps from typing import List from backend.db_meta.enums import ClusterType from backend.iam_app.dataclass import ResourceEnum -from backend.iam_app.dataclass.actions import ActionEnum, ActionMeta +from backend.iam_app.dataclass.actions import ActionMeta from backend.iam_app.handlers.drf_perm.base import ( BizDBTypeResourceActionPermission, ResourceActionPermission, get_request_key_id, ) -from backend.iam_app.handlers.permission import Permission - - -def decorator_biz_config_permission_field(): - def wrapper(view_func): - @wraps(view_func) - def wrapped_view(*args, **kwargs): - response = view_func(*args, **kwargs) - - bk_biz_id = get_request_key_id(args[1], key="bk_biz_id") - cluster_type = get_request_key_id(args[1], key="meta_cluster_type") - db_type = ClusterType.cluster_type_to_db_type(cluster_type) - - resources = [ - ResourceEnum.BUSINESS.create_instance(bk_biz_id), - ResourceEnum.DBTYPE.create_instance(db_type), - ] - permission_result = Permission().is_allowed(action=ActionEnum.DBCONFIG_EDIT, resources=resources) - - response.data = [ - {"permission": {ActionEnum.DBCONFIG_EDIT.id: permission_result}, **d} for d in response.data - ] - return response - - return wrapped_view - - return wrapper class BizDBConfigPermission(BizDBTypeResourceActionPermission): diff --git a/dbm-ui/backend/iam_app/handlers/drf_perm/monitor.py b/dbm-ui/backend/iam_app/handlers/drf_perm/monitor.py index 78ead8eedf..489552f8d4 100644 --- a/dbm-ui/backend/iam_app/handlers/drf_perm/monitor.py +++ b/dbm-ui/backend/iam_app/handlers/drf_perm/monitor.py @@ -16,7 +16,7 @@ from backend.db_monitor.models import MonitorPolicy, NoticeGroup from backend.iam_app.dataclass.actions import ActionEnum, ActionMeta from backend.iam_app.dataclass.resources import ResourceEnum, ResourceMeta -from backend.iam_app.exceptions import ResourceNotExistError +from backend.iam_app.exceptions import ActionNotExistError, ResourceNotExistError from backend.iam_app.handlers.drf_perm.base import ResourceActionPermission, get_request_key_id @@ -31,22 +31,22 @@ def __init__(self, view_action, actions: List[ActionMeta] = None, resource_meta: def instance_ids_getter(self, request, view): # 从业务或告警组后,决定动作和资源类型 - bk_biz_id = get_request_key_id(request, key="bk_biz_id") - - if bk_biz_id is None and "pk" in view.kwargs: - bk_biz_id = str(NoticeGroup.objects.get(id=view.kwargs["pk"]).bk_biz_id) - - if bk_biz_id is None: - raise ResourceNotExistError(_("未找到业务ID,无法决定告警组相关动作鉴权。请保证参数含义业务ID或者告警组ID")) - - if not int(bk_biz_id): - self.actions = [getattr(ActionEnum, f"GLOBAL_NOTIFY_GROUP_{self.view_action.upper()}")] - self.resource_meta = None - else: + if view.action in ["list", "create"]: self.actions = [getattr(ActionEnum, f"NOTIFY_GROUP_{self.view_action.upper()}")] self.resource_meta = ResourceEnum.BUSINESS - - return [bk_biz_id] + return [get_request_key_id(request, key="bk_biz_id")] + elif view.action in ["partial_update", "update", "destroy"]: + notify_group = NoticeGroup.objects.get(id=view.kwargs.get("pk")) + bk_biz_id = notify_group.bk_biz_id + if view.action == "destroy": + action = ActionEnum.NOTIFY_GROUP_DESTROY + else: + action = ActionEnum.NOTIFY_GROUP_UPDATE if bk_biz_id else ActionEnum.GLOBAL_NOTIFY_GROUP_UPDATE + self.actions = [action] + self.resource_meta = ResourceEnum.NOTIFY_GROUP + return [notify_group.id] + else: + raise ActionNotExistError(_("不合法的告警组任务ID:{}").format(view.action)) def has_object_permission(self, request, view, obj): """告警组粒度是业务级别,无需obj鉴权""" diff --git a/dbm-ui/backend/iam_app/handlers/drf_perm/ticket.py b/dbm-ui/backend/iam_app/handlers/drf_perm/ticket.py index efee9a59a6..9ba61cf848 100644 --- a/dbm-ui/backend/iam_app/handlers/drf_perm/ticket.py +++ b/dbm-ui/backend/iam_app/handlers/drf_perm/ticket.py @@ -8,13 +8,21 @@ 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. """ - +import itertools import logging +from typing import List + +from django.utils.translation import ugettext as _ from backend.db_meta.models import ExtraProcessInstance from backend.iam_app.dataclass.actions import ActionEnum from backend.iam_app.dataclass.resources import ResourceEnum -from backend.iam_app.handlers.drf_perm.base import ResourceActionPermission +from backend.iam_app.exceptions import ActionNotExistError +from backend.iam_app.handlers.drf_perm.base import ( + IAMPermission, + MoreResourceActionPermission, + ResourceActionPermission, +) from backend.ticket.builders import BuilderFactory from backend.ticket.builders.common.base import fetch_cluster_ids from backend.ticket.constants import TicketType @@ -23,16 +31,16 @@ logger = logging.getLogger("root") -class CreateTicketPermission(ResourceActionPermission): +class CreateTicketOneResourcePermission(ResourceActionPermission): """ - 创建单据相关动作鉴权 + 创建单据相关动作鉴权 -- 关联一个动作 """ def __init__(self, ticket_type: TicketType) -> None: self.ticket_type = ticket_type action = BuilderFactory.ticket_type__iam_action.get(ticket_type) actions = [action] if action else [] - # 默认只考虑关联一种资源 + # 只考虑关联一种资源 resource_meta = action.related_resource_types[0] if action else None if resource_meta == ResourceEnum.INFLUXDB: @@ -48,23 +56,26 @@ def __init__(self, ticket_type: TicketType) -> None: super().__init__(actions, resource_meta, instance_ids_getter=instance_ids_getter) - def instance_biz_ids_getter(self, request, view): + @staticmethod + def instance_biz_ids_getter(request, view): return [request.data["bk_biz_id"]] - def instance_cluster_ids_getter(self, request, view): + @staticmethod + def instance_cluster_ids_getter(request, view): # 集群ID从details解析,如果没有detail(比如sql模拟执行),则直接取request.data details = request.data.get("details") or request.data - cluster_key_fields = ["cluster_id", "cluster_ids", "src_cluster", "dst_cluster"] - cluster_ids = get_target_items_from_details(details, match_keys=cluster_key_fields) + cluster_ids = fetch_cluster_ids(details) # 排除非int型的cluster id(比如redis的构造实例恢复集群使用ip表示的) cluster_ids = [int(id) for id in cluster_ids if isinstance(id, int) or (isinstance(id, str) and id.isdigit())] return cluster_ids - def instance_influxdb_ids_getter(self, request, view): + @staticmethod + def instance_influxdb_ids_getter(request, view): details = request.data.get("details") or request.data return get_target_items_from_details(details, match_keys=["instance_id", "instance_ids"]) - def instance_dumper_cluster_ids_getter(self, request, view): + @staticmethod + def instance_dumper_cluster_ids_getter(request, view): details = request.data.get("details") or request.data # 如果是dumper部署,则从detail获取集群ID,否则从ExtraProcessInstance根据dumper获取集群ID if request.data["ticket_type"] == TicketType.TBINLOGDUMPER_INSTALL: @@ -75,3 +86,57 @@ def instance_dumper_cluster_ids_getter(self, request, view): ExtraProcessInstance.objects.filter(id__in=dumper_instance_ids).values_list("cluster_id", flat=True) ) return cluster_ids + + +class CreateTicketMoreResourcePermission(MoreResourceActionPermission): + """ + 创建单据相关动作鉴权 -- 关联多个动作 + 由于这种相关的单据类型很少,且资源独立,所以请根据单据类型来分别写instance_ids_getter函数 + """ + + def __init__(self, ticket_type: TicketType) -> None: + self.ticket_type = ticket_type + action = BuilderFactory.ticket_type__iam_action.get(ticket_type) + resource_metes = action.related_resource_types + # 根据单据类型来决定资源获取方式 + instance_ids_getters = None + + # 授权 - 关联:账号 + 集群 + if ticket_type in [ + TicketType.MYSQL_AUTHORIZE_RULES, + TicketType.TENDBCLUSTER_AUTHORIZE_RULES, + TicketType.SQLSERVER_AUTHORIZE_RULES, + TicketType.MONGODB_AUTHORIZE_RULES, + ]: + instance_ids_getters = self.authorize_instance_ids_getters + # 授权 - 关联:开区模板 + 集群 + elif ticket_type in [TicketType.MYSQL_OPEN_AREA, TicketType.TENDBCLUSTER_OPEN_AREA]: + instance_ids_getters = self.openarea_instance_ids_getters + + super().__init__(actions=[action], resource_metes=resource_metes, instance_ids_getters=instance_ids_getters) + + @staticmethod + def authorize_instance_ids_getters(request, view): + authorize_resource_tuples = [] + if "authorize_data" in request.data["details"]: + authorize_data_list = [request.data["details"]["authorize_data"]] + else: + authorize_data_list = request.data["details"]["authorize_data_list"] + for data in authorize_data_list: + authorize_resource_tuples.extend(list(itertools.product([data["account_id"]], data["cluster_ids"]))) + return authorize_resource_tuples + + @staticmethod + def openarea_instance_ids_getters(request, view): + details = request.data["details"] + return [(details["config_id"], details["cluster_id"])] + + +def create_ticket_permission(ticket_type: TicketType) -> List[IAMPermission]: + action = BuilderFactory.ticket_type__iam_action.get(ticket_type) + if not action: + raise ActionNotExistError(_("单据动作ID:{} 不存在").format(action)) + if len(action.related_resource_types) <= 1: + return [CreateTicketOneResourcePermission(ticket_type=ticket_type)] + else: + return [CreateTicketMoreResourcePermission(ticket_type=ticket_type)] diff --git a/dbm-ui/backend/iam_app/handlers/permission.py b/dbm-ui/backend/iam_app/handlers/permission.py index aff03bc723..71ec8c24f0 100644 --- a/dbm-ui/backend/iam_app/handlers/permission.py +++ b/dbm-ui/backend/iam_app/handlers/permission.py @@ -365,9 +365,12 @@ def _get_topo_resource(self, resource: Resource): bk_iam_path = f"{resource.attribute.get('_bk_iam_path_', '/')}{resource.type},{resource.id}/" topo_resources = [] - for topo in bk_iam_path.split("/")[1:-1]: + # 获取祖先的拓扑结构 + for topo in bk_iam_path.split("/")[1:-1][:-1]: rtype, rid = topo.split(",") topo_resources.append(ResourceEnum.get_resource_by_id(rtype).create_instance(rid)) + # 最后一级拓扑是自身 + topo_resources.append(resource) return topo_resources def get_apply_data( diff --git a/dbm-ui/backend/iam_app/handlers/signal.py b/dbm-ui/backend/iam_app/handlers/signal.py index 34e4a84d7f..1528802881 100644 --- a/dbm-ui/backend/iam_app/handlers/signal.py +++ b/dbm-ui/backend/iam_app/handlers/signal.py @@ -17,7 +17,8 @@ from backend import env from backend.db_meta.enums import MachineType from backend.db_meta.models import Cluster, StorageInstance -from backend.db_monitor.models import DutyRule, MonitorPolicy +from backend.db_monitor.models import MonitorPolicy, NoticeGroup +from backend.db_services.dbpermission.db_account.signals import create_account_signal from backend.db_services.mysql.dumper.models import DumperSubscribeConfig from backend.db_services.mysql.open_area.models import TendbOpenAreaConfig from backend.flow.models import FlowTree @@ -80,10 +81,10 @@ def post_save_monitor_policy(sender, instance, created, **kwargs): post_save_grant_iam(resource_meta, MonitorPolicy, instance, instance.creator, created) -@receiver(post_save, sender=DutyRule) +@receiver(post_save, sender=NoticeGroup) def post_save_duty_rule(sender, instance, created, **kwargs): - resource_meta = ResourceEnum.DUTY_RULE - post_save_grant_iam(resource_meta, DutyRule, instance, instance.creator, created) + resource_meta = ResourceEnum.NOTIFY_GROUP + post_save_grant_iam(resource_meta, NoticeGroup, instance, instance.creator, created) @receiver(post_save, sender=TendbOpenAreaConfig) @@ -96,3 +97,10 @@ def post_save_openarea_config(sender, instance, created, **kwargs): def post_save_dumper_subscribe_config(sender, instance, created, **kwargs): resource_meta = ResourceEnum.DUMPER_SUBSCRIBE_CONFIG post_save_grant_iam(resource_meta, DumperSubscribeConfig, instance, instance.creator, created) + + +# TODO: 新建账号需要自定义信号 +@receiver(create_account_signal, sender=None) +def post_save_account(sender, account, **kwargs): + resource_meta = getattr(ResourceEnum, f"{account.account_type.upper()}_ACCOUNT") + post_save_grant_iam(resource_meta, DumperSubscribeConfig, account, account.creator, True) diff --git a/dbm-ui/backend/iam_app/migration_json_files/initial.json b/dbm-ui/backend/iam_app/migration_json_files/initial.json index 3a8173b1e3..615c5a5438 100644 --- a/dbm-ui/backend/iam_app/migration_json_files/initial.json +++ b/dbm-ui/backend/iam_app/migration_json_files/initial.json @@ -299,18 +299,18 @@ { "operation": "upsert_resource_type", "data": { - "id": "duty_rule", - "name": "轮值策略", - "name_en": "duty_rule", - "description": "轮值策略", + "id": "notify_group", + "name": "告警组", + "name_en": "notify_group", + "description": "告警组", "provider_config": { "path": "/apis/iam/resource/" }, "version": 1, "parents": [ { - "system_id": "bk_dbm", - "id": "dbtype" + "system_id": "bk_cmdb", + "id": "biz" } ] } @@ -353,6 +353,82 @@ ] } }, + { + "operation": "upsert_resource_type", + "data": { + "id": "mysql_account", + "name": "MySQL 账号", + "name_en": "mysql_account", + "description": "MySQL 账号", + "provider_config": { + "path": "/apis/iam/resource/" + }, + "version": 1, + "parents": [ + { + "system_id": "bk_cmdb", + "id": "biz" + } + ] + } + }, + { + "operation": "upsert_resource_type", + "data": { + "id": "sqlserver_account", + "name": "SQLServer 账号", + "name_en": "sqlserver_account", + "description": "SQLServer 账号", + "provider_config": { + "path": "/apis/iam/resource/" + }, + "version": 1, + "parents": [ + { + "system_id": "bk_cmdb", + "id": "biz" + } + ] + } + }, + { + "operation": "upsert_resource_type", + "data": { + "id": "mongodb_account", + "name": "MongoDB 账号", + "name_en": "mongodb_account", + "description": "MongoDB 账号", + "provider_config": { + "path": "/apis/iam/resource/" + }, + "version": 1, + "parents": [ + { + "system_id": "bk_cmdb", + "id": "biz" + } + ] + } + }, + { + "operation": "upsert_resource_type", + "data": { + "id": "tendbcluster_account", + "name": "TendbCluster 账号", + "name_en": "tendbcluster_account", + "description": "TendbCluster 账号", + "provider_config": { + "path": "/apis/iam/resource/" + }, + "version": 1, + "parents": [ + { + "system_id": "bk_cmdb", + "id": "biz" + } + ] + } + }, { "operation": "upsert_instance_selection", "data": { @@ -626,17 +702,17 @@ { "operation": "upsert_instance_selection", "data": { - "id": "duty_rule_list", - "name": "轮值策略 列表", - "name_en": "duty_rule list", + "id": "notify_group_list", + "name": "告警组 列表", + "name_en": "notify_group list", "resource_type_chain": [ { - "system_id": "bk_dbm", - "id": "dbtype" + "system_id": "bk_cmdb", + "id": "biz" }, { "system_id": "bk_dbm", - "id": "duty_rule" + "id": "notify_group" } ] } @@ -677,6 +753,78 @@ ] } }, + { + "operation": "upsert_instance_selection", + "data": { + "id": "mysql_account_list", + "name": "MySQL 账号 列表", + "name_en": "mysql_account list", + "resource_type_chain": [ + { + "system_id": "bk_cmdb", + "id": "biz" + }, + { + "system_id": "bk_dbm", + "id": "mysql_account" + } + ] + } + }, + { + "operation": "upsert_instance_selection", + "data": { + "id": "sqlserver_account_list", + "name": "SQLServer 账号 列表", + "name_en": "sqlserver_account list", + "resource_type_chain": [ + { + "system_id": "bk_cmdb", + "id": "biz" + }, + { + "system_id": "bk_dbm", + "id": "sqlserver_account" + } + ] + } + }, + { + "operation": "upsert_instance_selection", + "data": { + "id": "mongodb_account_list", + "name": "MongoDB 账号 列表", + "name_en": "mongodb_account list", + "resource_type_chain": [ + { + "system_id": "bk_cmdb", + "id": "biz" + }, + { + "system_id": "bk_dbm", + "id": "mongodb_account" + } + ] + } + }, + { + "operation": "upsert_instance_selection", + "data": { + "id": "tendbcluster_account_list", + "name": "TendbCluster 账号 列表", + "name_en": "tendbcluster_account list", + "resource_type_chain": [ + { + "system_id": "bk_cmdb", + "id": "biz" + }, + { + "system_id": "bk_dbm", + "id": "tendbcluster_account" + } + ] + } + }, { "operation": "upsert_instance_selection", "data": { @@ -695,6 +843,24 @@ ] } }, + { + "operation": "upsert_instance_selection", + "data": { + "id": "global_notify_group_list", + "name": "全局告警组 列表", + "name_en": "global_notify_group list", + "resource_type_chain": [ + { + "system_id": "bk_dbm", + "id": "dbtype" + }, + { + "system_id": "bk_dbm", + "id": "notify_group" + } + ] + } + }, { "operation": "upsert_action", "data": { @@ -1074,6 +1240,36 @@ "description_en": "MySQL View" } }, + { + "operation": "upsert_action", + "data": { + "id": "mysql_admin_pwd_modify", + "name": "MySQL 临时密码修改", + "name_en": "mysql_admin_pwd_modify", + "type": "execute", + "related_resource_types": [ + { + "system_id": "bk_dbm", + "id": "mysql", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "mysql_list" + } + ] + } + ], + "related_actions": [ + "db_manage" + ], + "version": 1, + "group": "MySQL", + "subgroup": "集群管理", + "description": "MySQL 临时密码修改", + "description_en": "mysql_admin_pwd_modify" + } + }, { "operation": "upsert_action", "data": { @@ -1203,13 +1399,13 @@ "type": "delete", "related_resource_types": [ { - "system_id": "bk_cmdb", - "id": "biz", - "selection_mode": "instance", + "system_id": "bk_dbm", + "id": "mysql_account", + "selection_mode": "all", "related_instance_selections": [ { - "system_id": "bk_cmdb", - "id": "business" + "system_id": "bk_dbm", + "id": "mysql_account_list" } ] } @@ -1227,10 +1423,40 @@ { "operation": "upsert_action", "data": { - "id": "mysql_account_rule_create", - "name": "MySQL 账号规则创建", - "name_en": "MySQL Account Rule Create", + "id": "mysql_add_account_rule", + "name": "MySQL账号规则创建", + "name_en": "mysql_add_account_rule", "type": "create", + "related_resource_types": [ + { + "system_id": "bk_dbm", + "id": "mysql_account", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "mysql_account_list" + } + ] + } + ], + "related_actions": [ + "db_manage" + ], + "version": 1, + "group": "MySQL", + "subgroup": "权限管理", + "description": "MySQL账号规则创建", + "description_en": "mysql_add_account_rule" + } + }, + { + "operation": "upsert_action", + "data": { + "id": "mysql_account_rules_view", + "name": "MySQL 账号规则查看", + "name_en": "mysql_account_rules_view", + "type": "view", "related_resource_types": [ { "system_id": "bk_cmdb", @@ -1250,8 +1476,49 @@ "version": 1, "group": "MySQL", "subgroup": "权限管理", - "description": "MySQL 账号规则创建", - "description_en": "MySQL Account Rule Create" + "description": "MySQL 账号规则查看", + "description_en": "mysql_account_rules_view" + } + }, + { + "operation": "upsert_action", + "data": { + "id": "mysql_authorize_rules", + "name": "MySQL授权", + "name_en": "mysql authorize rules", + "type": "execute", + "related_resource_types": [ + { + "system_id": "bk_dbm", + "id": "mysql_account", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "mysql_account_list" + } + ] + }, + { + "system_id": "bk_dbm", + "id": "mysql", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "mysql_list" + } + ] + } + ], + "related_actions": [ + "db_manage" + ], + "version": 1, + "group": "MySQL", + "subgroup": "权限管理", + "description": "MySQL授权", + "description_en": "mysql authorize rules" } }, { @@ -1321,13 +1588,13 @@ "type": "manage", "related_resource_types": [ { - "system_id": "bk_cmdb", - "id": "biz", - "selection_mode": "instance", + "system_id": "bk_dbm", + "id": "mysql", + "selection_mode": "all", "related_instance_selections": [ { - "system_id": "bk_cmdb", - "id": "business" + "system_id": "bk_dbm", + "id": "mysql_list" } ] } @@ -1351,13 +1618,13 @@ "type": "delete", "related_resource_types": [ { - "system_id": "bk_cmdb", - "id": "biz", - "selection_mode": "instance", + "system_id": "bk_dbm", + "id": "mysql", + "selection_mode": "all", "related_instance_selections": [ { - "system_id": "bk_cmdb", - "id": "business" + "system_id": "bk_dbm", + "id": "mysql_list" } ] } @@ -1381,13 +1648,13 @@ "type": "manage", "related_resource_types": [ { - "system_id": "bk_cmdb", - "id": "biz", - "selection_mode": "instance", + "system_id": "bk_dbm", + "id": "mysql", + "selection_mode": "all", "related_instance_selections": [ { - "system_id": "bk_cmdb", - "id": "business" + "system_id": "bk_dbm", + "id": "mysql_list" } ] } @@ -1402,6 +1669,47 @@ "description_en": "mysql_partition_enable_disable" } }, + { + "operation": "upsert_action", + "data": { + "id": "mysql_open_area", + "name": "MySQL 开区执行", + "name_en": "mysql_open_area", + "type": "execute", + "related_resource_types": [ + { + "system_id": "bk_dbm", + "id": "openarea_config", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "openarea_config_list" + } + ] + }, + { + "system_id": "bk_dbm", + "id": "mysql", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "mysql_list" + } + ] + } + ], + "related_actions": [ + "db_manage" + ], + "version": 1, + "group": "MySQL", + "subgroup": "克隆开区", + "description": "MySQL 开区执行", + "description_en": "mysql_open_area" + } + }, { "operation": "upsert_action", "data": { @@ -1726,6 +2034,36 @@ "description_en": "tendbcluster_view" } }, + { + "operation": "upsert_action", + "data": { + "id": "tendbcluster_admin_pwd_modify", + "name": "TendbCluster 临时密码修改", + "name_en": "tendbcluster_admin_pwd_modify", + "type": "execute", + "related_resource_types": [ + { + "system_id": "bk_dbm", + "id": "tendbcluster", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "tendbcluster_list" + } + ] + } + ], + "related_actions": [ + "db_manage" + ], + "version": 1, + "group": "TendbCluster", + "subgroup": "集群管理", + "description": "TendbCluster 临时密码修改", + "description_en": "tendbcluster_admin_pwd_modify" + } + }, { "operation": "upsert_action", "data": { @@ -1765,13 +2103,13 @@ "type": "delete", "related_resource_types": [ { - "system_id": "bk_cmdb", - "id": "biz", - "selection_mode": "instance", + "system_id": "bk_dbm", + "id": "tendbcluster_account", + "selection_mode": "all", "related_instance_selections": [ { - "system_id": "bk_cmdb", - "id": "business" + "system_id": "bk_dbm", + "id": "tendbcluster_account_list" } ] } @@ -1793,6 +2131,36 @@ "name": "TendbCluster 账号规则创建", "name_en": "tendbcluster_add_account_rule", "type": "create", + "related_resource_types": [ + { + "system_id": "bk_dbm", + "id": "tendbcluster_account", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "tendbcluster_account_list" + } + ] + } + ], + "related_actions": [ + "db_manage" + ], + "version": 1, + "group": "TendbCluster", + "subgroup": "权限管理", + "description": "TendbCluster 账号规则创建", + "description_en": "tendbcluster_add_account_rule" + } + }, + { + "operation": "upsert_action", + "data": { + "id": "tendbcluster_account_rules_view", + "name": "TendbCluster 账号规则查看", + "name_en": "tendbcluster_account_rules_view", + "type": "view", "related_resource_types": [ { "system_id": "bk_cmdb", @@ -1812,8 +2180,49 @@ "version": 1, "group": "TendbCluster", "subgroup": "权限管理", - "description": "TendbCluster 账号规则创建", - "description_en": "tendbcluster_add_account_rule" + "description": "TendbCluster 账号规则查看", + "description_en": "tendbcluster_account_rules_view" + } + }, + { + "operation": "upsert_action", + "data": { + "id": "tendbcluster_authorize_rules", + "name": "TendbCluster 授权", + "name_en": "tendbcluster_authorize_rules", + "type": "execute", + "related_resource_types": [ + { + "system_id": "bk_dbm", + "id": "tendbcluster_account", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "tendbcluster_account_list" + } + ] + }, + { + "system_id": "bk_dbm", + "id": "tendbcluster", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "tendbcluster_list" + } + ] + } + ], + "related_actions": [ + "db_manage" + ], + "version": 1, + "group": "TendbCluster", + "subgroup": "权限管理", + "description": "TendbCluster 授权", + "description_en": "tendbcluster_authorize_rules" } }, { @@ -1897,9 +2306,50 @@ ], "version": 1, "group": "TendbCluster", - "subgroup": "权限克隆", - "description": "TendbCluster DB实例权限克隆", - "description_en": "tendb_instance_clone_rules" + "subgroup": "权限克隆", + "description": "TendbCluster DB实例权限克隆", + "description_en": "tendb_instance_clone_rules" + } + }, + { + "operation": "upsert_action", + "data": { + "id": "tendbcluster_open_area", + "name": "TendbCluster 开区执行", + "name_en": "tendbcluster_open_area", + "type": "execute", + "related_resource_types": [ + { + "system_id": "bk_dbm", + "id": "openarea_config", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "openarea_config_list" + } + ] + }, + { + "system_id": "bk_dbm", + "id": "tendbcluster", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "tendbcluster_list" + } + ] + } + ], + "related_actions": [ + "db_manage" + ], + "version": 1, + "group": "TendbCluster", + "subgroup": "克隆开区", + "description": "TendbCluster 开区执行", + "description_en": "tendbcluster_open_area" } }, { @@ -2027,13 +2477,13 @@ "type": "manage", "related_resource_types": [ { - "system_id": "bk_cmdb", - "id": "biz", - "selection_mode": "instance", + "system_id": "bk_dbm", + "id": "tendbcluster", + "selection_mode": "all", "related_instance_selections": [ { - "system_id": "bk_cmdb", - "id": "business" + "system_id": "bk_dbm", + "id": "tendbcluster_list" } ] } @@ -2057,13 +2507,13 @@ "type": "delete", "related_resource_types": [ { - "system_id": "bk_cmdb", - "id": "biz", - "selection_mode": "instance", + "system_id": "bk_dbm", + "id": "tendbcluster", + "selection_mode": "all", "related_instance_selections": [ { - "system_id": "bk_cmdb", - "id": "business" + "system_id": "bk_dbm", + "id": "tendbcluster_list" } ] } @@ -2087,13 +2537,13 @@ "type": "manage", "related_resource_types": [ { - "system_id": "bk_cmdb", - "id": "biz", - "selection_mode": "instance", + "system_id": "bk_dbm", + "id": "tendbcluster", + "selection_mode": "all", "related_instance_selections": [ { - "system_id": "bk_cmdb", - "id": "business" + "system_id": "bk_dbm", + "id": "tendbcluster_list" } ] } @@ -2350,6 +2800,36 @@ "description_en": "influxdb_view" } }, + { + "operation": "upsert_action", + "data": { + "id": "group_manage", + "name": "InfluxDB 分组管理", + "name_en": "group_manage", + "type": "manage", + "related_resource_types": [ + { + "system_id": "bk_cmdb", + "id": "biz", + "selection_mode": "instance", + "related_instance_selections": [ + { + "system_id": "bk_cmdb", + "id": "business" + } + ] + } + ], + "related_actions": [ + "db_manage" + ], + "version": 1, + "group": "InfluxDB", + "subgroup": "实例管理", + "description": "InfluxDB 分组管理", + "description_en": "group_manage" + } + }, { "operation": "upsert_action", "data": { @@ -2946,10 +3426,68 @@ { "operation": "upsert_action", "data": { - "id": "mongodb_delete_create", - "name": "MongoDB 账号删除", - "name_en": "mongodb_delete_create", + "id": "mongodb_account_delete", + "name": "MongoDB 删除账号", + "name_en": "mongodb_account_delete", "type": "delete", + "related_resource_types": [ + { + "system_id": "bk_dbm", + "id": "mongodb_account", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "mongodb_account_list" + } + ] + } + ], + "related_actions": [], + "version": 1, + "group": "MongoDB", + "subgroup": "权限管理", + "description": "MongoDB 删除账号", + "description_en": "mongodb_account_delete" + } + }, + { + "operation": "upsert_action", + "data": { + "id": "mongodb_add_account_rule", + "name": "MongoDB 账号规则创建", + "name_en": "mongodb_add_account_rule", + "type": "create", + "related_resource_types": [ + { + "system_id": "bk_dbm", + "id": "mongodb_account", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "mongodb_account_list" + } + ] + } + ], + "related_actions": [ + "db_manage" + ], + "version": 1, + "group": "MongoDB", + "subgroup": "权限管理", + "description": "MongoDB 账号规则创建", + "description_en": "mongodb_add_account_rule" + } + }, + { + "operation": "upsert_action", + "data": { + "id": "mongodb_account_rules_view", + "name": "MongoDB 账号规则查看", + "name_en": "mongodb_account_rules_view", + "type": "view", "related_resource_types": [ { "system_id": "bk_cmdb", @@ -2963,20 +3501,182 @@ ] } ], - "related_actions": [], + "related_actions": [ + "db_manage" + ], "version": 1, "group": "MongoDB", "subgroup": "权限管理", - "description": "MongoDB 账号删除", - "description_en": "mongodb_delete_create" + "description": "MongoDB 账号规则查看", + "description_en": "mongodb_account_rules_view" } }, { "operation": "upsert_action", "data": { - "id": "mongodb_add_account_rule", - "name": "MongoDB 账号规则创建", - "name_en": "mongodb_add_account_rule", + "id": "mongodb_authorize_rules", + "name": "MongoDB 集群授权", + "name_en": "mongodb_authorize_rules", + "type": "execute", + "related_resource_types": [ + { + "system_id": "bk_dbm", + "id": "mongodb_account", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "mongodb_account_list" + } + ] + }, + { + "system_id": "bk_dbm", + "id": "mongodb", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "mongodb_list" + } + ] + } + ], + "related_actions": [ + "db_manage" + ], + "version": 1, + "group": "MongoDB", + "subgroup": "权限管理", + "description": "MongoDB 集群授权", + "description_en": "mongodb_authorize_rules" + } + }, + { + "operation": "upsert_action", + "data": { + "id": "mongo_excel_authorize_rules", + "name": "MongoDB Excel集群授权", + "name_en": "mongo_excel_authorize_rules", + "type": "execute", + "related_resource_types": [ + { + "system_id": "bk_cmdb", + "id": "biz", + "selection_mode": "instance", + "related_instance_selections": [ + { + "system_id": "bk_cmdb", + "id": "business" + } + ] + } + ], + "related_actions": [ + "db_manage" + ], + "version": 1, + "group": "MongoDB", + "subgroup": "权限管理", + "description": "MongoDB Excel集群授权", + "description_en": "mongo_excel_authorize_rules" + } + }, + { + "operation": "upsert_action", + "data": { + "id": "sqlserver_view", + "name": "SQLServer 集群详情查看", + "name_en": "sqlserver_view", + "type": "view", + "related_resource_types": [ + { + "system_id": "bk_dbm", + "id": "sqlserver", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "sqlserver_list" + } + ] + } + ], + "related_actions": [], + "version": 1, + "group": "SQLServer", + "subgroup": "集群管理", + "description": "SQLServer 集群详情查看", + "description_en": "sqlserver_view" + } + }, + { + "operation": "upsert_action", + "data": { + "id": "sqlserver_admin_pwd_modify", + "name": "SQLServer 临时密码修改", + "name_en": "sqlserver_admin_pwd_modify", + "type": "execute", + "related_resource_types": [ + { + "system_id": "bk_dbm", + "id": "sqlserver", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "sqlserver_list" + } + ] + } + ], + "related_actions": [ + "db_manage" + ], + "version": 1, + "group": "SQLServer", + "subgroup": "集群管理", + "description": "SQLServer 临时密码修改", + "description_en": "sqlserver_admin_pwd_modify" + } + }, + { + "operation": "upsert_action", + "data": { + "id": "sqlserver_apply", + "name": "SQLServer 部署", + "name_en": "sqlserver_apply", + "type": "execute", + "related_resource_types": [ + { + "system_id": "bk_cmdb", + "id": "biz", + "selection_mode": "instance", + "related_instance_selections": [ + { + "system_id": "bk_cmdb", + "id": "business" + } + ] + } + ], + "related_actions": [ + "db_manage", + "dbconfig_view" + ], + "version": 1, + "group": "SQLServer", + "subgroup": "集群管理", + "description": "SQLServer 部署", + "description_en": "sqlserver_apply" + } + }, + { + "operation": "upsert_action", + "data": { + "id": "sqlserver_account_create", + "name": "SQLServer 账号创建", + "name_en": "sqlserver_account_create", "type": "create", "related_resource_types": [ { @@ -2991,32 +3691,30 @@ ] } ], - "related_actions": [ - "db_manage" - ], + "related_actions": [], "version": 1, - "group": "MongoDB", + "group": "SQLServer", "subgroup": "权限管理", - "description": "MongoDB 账号规则创建", - "description_en": "mongodb_add_account_rule" + "description": "SQLServer 账号创建", + "description_en": "sqlserver_account_create" } }, { "operation": "upsert_action", "data": { - "id": "sqlserver_view", - "name": "SQLServer 集群详情查看", - "name_en": "sqlserver_view", - "type": "view", + "id": "sqlserver_account_delete", + "name": "SQLServer 删除账号", + "name_en": "sqlserver_account_delete", + "type": "delete", "related_resource_types": [ { "system_id": "bk_dbm", - "id": "sqlserver", + "id": "sqlserver_account", "selection_mode": "all", "related_instance_selections": [ { "system_id": "bk_dbm", - "id": "sqlserver_list" + "id": "sqlserver_account_list" } ] } @@ -3024,49 +3722,48 @@ "related_actions": [], "version": 1, "group": "SQLServer", - "subgroup": "集群管理", - "description": "SQLServer 集群详情查看", - "description_en": "sqlserver_view" + "subgroup": "权限管理", + "description": "SQLServer 删除账号", + "description_en": "sqlserver_account_delete" } }, { "operation": "upsert_action", "data": { - "id": "sqlserver_apply", - "name": "SQLServer 部署", - "name_en": "sqlserver_apply", - "type": "execute", + "id": "sqlserver_add_account_rule", + "name": "SQLServer 账号规则创建", + "name_en": "sqlserver_add_account_rule", + "type": "create", "related_resource_types": [ { - "system_id": "bk_cmdb", - "id": "biz", - "selection_mode": "instance", + "system_id": "bk_dbm", + "id": "sqlserver_account", + "selection_mode": "all", "related_instance_selections": [ { - "system_id": "bk_cmdb", - "id": "business" + "system_id": "bk_dbm", + "id": "sqlserver_account_list" } ] } ], "related_actions": [ - "db_manage", - "dbconfig_view" + "db_manage" ], "version": 1, "group": "SQLServer", - "subgroup": "集群管理", - "description": "SQLServer 部署", - "description_en": "sqlserver_apply" + "subgroup": "权限管理", + "description": "SQLServer 账号规则创建", + "description_en": "sqlserver_add_account_rule" } }, { "operation": "upsert_action", "data": { - "id": "sqlserver_account_create", - "name": "SQLServer 账号创建", - "name_en": "sqlserver_account_create", - "type": "create", + "id": "sqlserver_account_rules_view", + "name": "SQLServer 账号规则查看", + "name_en": "sqlserver_account_rules_view", + "type": "view", "related_resource_types": [ { "system_id": "bk_cmdb", @@ -3080,49 +3777,64 @@ ] } ], - "related_actions": [], + "related_actions": [ + "db_manage" + ], "version": 1, "group": "SQLServer", "subgroup": "权限管理", - "description": "SQLServer 账号创建", - "description_en": "sqlserver_account_create" + "description": "SQLServer 账号规则查看", + "description_en": "sqlserver_account_rules_view" } }, { "operation": "upsert_action", "data": { - "id": "sqlserver_delete_create", - "name": "SQLServer 账号删除", - "name_en": "sqlserver_delete_create", - "type": "delete", + "id": "sqlserver_authorize_rules", + "name": "SQLServer 集群授权", + "name_en": "sqlserver_authorize_rules", + "type": "execute", "related_resource_types": [ { - "system_id": "bk_cmdb", - "id": "biz", - "selection_mode": "instance", + "system_id": "bk_dbm", + "id": "sqlserver_account", + "selection_mode": "all", "related_instance_selections": [ { - "system_id": "bk_cmdb", - "id": "business" + "system_id": "bk_dbm", + "id": "sqlserver_account_list" + } + ] + }, + { + "system_id": "bk_dbm", + "id": "sqlserver", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "sqlserver_list" } ] } ], - "related_actions": [], + "related_actions": [ + "db_manage" + ], "version": 1, "group": "SQLServer", "subgroup": "权限管理", - "description": "SQLServer 账号删除", - "description_en": "sqlserver_delete_create" + "description": "SQLServer 集群授权", + "description_en": "sqlserver_authorize_rules" } }, { "operation": "upsert_action", "data": { - "id": "sqlserver_add_account_rule", - "name": "SQLServer 账号规则创建", - "name_en": "sqlserver_add_account_rule", - "type": "create", + "id": "sqlserver_excel_authorize_rules", + "name": "SQLServer Excel授权", + "name_en": "sqlserver_excel_authorize_rules", + "type": "execute", "related_resource_types": [ { "system_id": "bk_cmdb", @@ -3142,8 +3854,8 @@ "version": 1, "group": "SQLServer", "subgroup": "权限管理", - "description": "SQLServer 账号规则创建", - "description_en": "sqlserver_add_account_rule" + "description": "SQLServer Excel授权", + "description_en": "sqlserver_excel_authorize_rules" } }, { @@ -3194,6 +3906,24 @@ "description_en": "resource_pool_manage" } }, + { + "operation": "upsert_action", + "data": { + "id": "resource_operation_view", + "name": "资源池操作记录查看", + "name_en": "resource_operation_view", + "type": "view", + "related_resource_types": [], + "related_actions": [ + "resource_manage" + ], + "version": 1, + "group": "资源管理", + "subgroup": "资源池", + "description": "资源池操作记录查看", + "description_en": "resource_operation_view" + } + }, { "operation": "upsert_action", "data": { @@ -3302,24 +4032,6 @@ "description_en": "notify_group_view" } }, - { - "operation": "upsert_action", - "data": { - "id": "global_notify_group_list", - "name": "全局告警组查看", - "name_en": "global_notify_group_view", - "type": "view", - "related_resource_types": [], - "related_actions": [ - "global_manage" - ], - "version": 1, - "group": "平台管理", - "subgroup": "告警组", - "description": "全局告警组查看", - "description_en": "global_notify_group_view" - } - }, { "operation": "upsert_action", "data": { @@ -3350,24 +4062,6 @@ "description_en": "notify_group_create" } }, - { - "operation": "upsert_action", - "data": { - "id": "global_notify_group_create", - "name": "全局告警组新建", - "name_en": "global_notify_group_create", - "type": "create", - "related_resource_types": [], - "related_actions": [ - "global_manage" - ], - "version": 1, - "group": "平台管理", - "subgroup": "告警组", - "description": "全局告警组新建", - "description_en": "global_notify_group_create" - } - }, { "operation": "upsert_action", "data": { @@ -3377,13 +4071,13 @@ "type": "manage", "related_resource_types": [ { - "system_id": "bk_cmdb", - "id": "biz", - "selection_mode": "instance", + "system_id": "bk_dbm", + "id": "notify_group", + "selection_mode": "all", "related_instance_selections": [ { - "system_id": "bk_cmdb", - "id": "business" + "system_id": "bk_dbm", + "id": "notify_group_list" } ] } @@ -3398,24 +4092,6 @@ "description_en": "notify_group_edit" } }, - { - "operation": "upsert_action", - "data": { - "id": "global_notify_group_update", - "name": "全局告警组编辑", - "name_en": "global_notify_group_edit", - "type": "manage", - "related_resource_types": [], - "related_actions": [ - "global_manage" - ], - "version": 1, - "group": "平台管理", - "subgroup": "告警组", - "description": "全局告警组编辑", - "description_en": "global_notify_group_edit" - } - }, { "operation": "upsert_action", "data": { @@ -3425,13 +4101,13 @@ "type": "delete", "related_resource_types": [ { - "system_id": "bk_cmdb", - "id": "biz", - "selection_mode": "instance", + "system_id": "bk_dbm", + "id": "notify_group", + "selection_mode": "all", "related_instance_selections": [ { - "system_id": "bk_cmdb", - "id": "business" + "system_id": "bk_dbm", + "id": "notify_group_list" } ] } @@ -3449,19 +4125,31 @@ { "operation": "upsert_action", "data": { - "id": "global_notify_group_delete", - "name": "全局告警组删除", - "name_en": "global_notify_group_delete", - "type": "delete", - "related_resource_types": [], + "id": "global_notify_group_update", + "name": "全局告警组编辑", + "name_en": "global_notify_group_edit", + "type": "manage", + "related_resource_types": [ + { + "system_id": "bk_dbm", + "id": "notify_group", + "selection_mode": "all", + "related_instance_selections": [ + { + "system_id": "bk_dbm", + "id": "global_notify_group_list" + } + ] + } + ], "related_actions": [ "global_manage" ], "version": 1, "group": "平台管理", "subgroup": "告警组", - "description": "全局告警组删除", - "description_en": "global_notify_group_delete" + "description": "全局告警组编辑", + "description_en": "global_notify_group_edit" } }, { @@ -4090,12 +4778,12 @@ "related_resource_types": [ { "system_id": "bk_dbm", - "id": "duty_rule", - "selection_mode": "all", + "id": "dbtype", + "selection_mode": "instance", "related_instance_selections": [ { "system_id": "bk_dbm", - "id": "duty_rule_list" + "id": "dbtype_list" } ] } @@ -4121,12 +4809,12 @@ "related_resource_types": [ { "system_id": "bk_dbm", - "id": "duty_rule", - "selection_mode": "all", + "id": "dbtype", + "selection_mode": "instance", "related_instance_selections": [ { "system_id": "bk_dbm", - "id": "duty_rule_list" + "id": "dbtype_list" } ] } @@ -4488,34 +5176,6 @@ "description_en": "MYSQL_PROXY_SWITCH" } }, - { - "operation": "upsert_action", - "data": { - "id": "mysql_authorize_rules", - "name": "MySQL 集群授权", - "name_en": "MYSQL_AUTHORIZE_RULES", - "type": "execute", - "related_resource_types": [ - { - "system_id": "bk_dbm", - "id": "mysql", - "selection_mode": "all", - "related_instance_selections": [ - { - "system_id": "bk_dbm", - "id": "mysql_list" - } - ] - } - ], - "related_actions": [], - "version": 1, - "group": "MySQL", - "subgroup": "权限管理", - "description": "MySQL 集群授权", - "description_en": "MYSQL_AUTHORIZE_RULES" - } - }, { "operation": "upsert_action", "data": { @@ -4733,47 +5393,19 @@ } ], "related_actions": [], - "version": 1, - "group": "MySQL", - "subgroup": "回档", - "description": "MySQL 定点构造", - "description_en": "MYSQL_ROLLBACK_CLUSTER" - } - }, - { - "operation": "upsert_action", - "data": { - "id": "mysql_ha_full_backup", - "name": "MySQL 高可用全库备份", - "name_en": "MYSQL_HA_FULL_BACKUP", - "type": "execute", - "related_resource_types": [ - { - "system_id": "bk_dbm", - "id": "mysql", - "selection_mode": "all", - "related_instance_selections": [ - { - "system_id": "bk_dbm", - "id": "mysql_list" - } - ] - } - ], - "related_actions": [], - "version": 1, - "group": "MySQL", - "subgroup": "备份", - "description": "MySQL 高可用全库备份", - "description_en": "MYSQL_HA_FULL_BACKUP" + "version": 1, + "group": "MySQL", + "subgroup": "回档", + "description": "MySQL 定点构造", + "description_en": "MYSQL_ROLLBACK_CLUSTER" } }, { "operation": "upsert_action", "data": { - "id": "mysql_open_area", - "name": "MySQL 开区", - "name_en": "MYSQL_OPEN_AREA", + "id": "mysql_ha_full_backup", + "name": "MySQL 高可用全库备份", + "name_en": "MYSQL_HA_FULL_BACKUP", "type": "execute", "related_resource_types": [ { @@ -4791,9 +5423,9 @@ "related_actions": [], "version": 1, "group": "MySQL", - "subgroup": "克隆开区", - "description": "MySQL 开区", - "description_en": "MYSQL_OPEN_AREA" + "subgroup": "备份", + "description": "MySQL 高可用全库备份", + "description_en": "MYSQL_HA_FULL_BACKUP" } }, { @@ -5328,34 +5960,6 @@ "description_en": "TENDBCLUSTER_FLASHBACK" } }, - { - "operation": "upsert_action", - "data": { - "id": "tendbcluster_authorize_rules", - "name": "TenDB Cluster 授权", - "name_en": "TENDBCLUSTER_AUTHORIZE_RULES", - "type": "execute", - "related_resource_types": [ - { - "system_id": "bk_dbm", - "id": "tendbcluster", - "selection_mode": "all", - "related_instance_selections": [ - { - "system_id": "bk_dbm", - "id": "tendbcluster_list" - } - ] - } - ], - "related_actions": [], - "version": 1, - "group": "TendbCluster", - "subgroup": "权限管理", - "description": "TenDB Cluster 授权", - "description_en": "TENDBCLUSTER_AUTHORIZE_RULES" - } - }, { "operation": "upsert_action", "data": { @@ -5720,62 +6324,6 @@ "description_en": "SQLSERVER_ROLLBACK" } }, - { - "operation": "upsert_action", - "data": { - "id": "sqlserver_authorize_rules", - "name": "SQLServer 集群授权", - "name_en": "SQLSERVER_AUTHORIZE_RULES", - "type": "execute", - "related_resource_types": [ - { - "system_id": "bk_dbm", - "id": "sqlserver", - "selection_mode": "all", - "related_instance_selections": [ - { - "system_id": "bk_dbm", - "id": "sqlserver_list" - } - ] - } - ], - "related_actions": [], - "version": 1, - "group": "SQLServer", - "subgroup": "权限管理", - "description": "SQLServer 集群授权", - "description_en": "SQLSERVER_AUTHORIZE_RULES" - } - }, - { - "operation": "upsert_action", - "data": { - "id": "sqlserver_excel_authorize_rules", - "name": "SQLServer EXCEL授权", - "name_en": "SQLSERVER_EXCEL_AUTHORIZE_RULES", - "type": "execute", - "related_resource_types": [ - { - "system_id": "bk_dbm", - "id": "sqlserver", - "selection_mode": "all", - "related_instance_selections": [ - { - "system_id": "bk_dbm", - "id": "sqlserver_list" - } - ] - } - ], - "related_actions": [], - "version": 1, - "group": "SQLServer", - "subgroup": "权限管理", - "description": "SQLServer EXCEL授权", - "description_en": "SQLSERVER_EXCEL_AUTHORIZE_RULES" - } - }, { "operation": "upsert_action", "data": { @@ -7624,62 +8172,6 @@ "description_en": "MONGODB_CUTOFF" } }, - { - "operation": "upsert_action", - "data": { - "id": "mongodb_authorize", - "name": "MongoDB 授权", - "name_en": "MONGODB_AUTHORIZE", - "type": "execute", - "related_resource_types": [ - { - "system_id": "bk_dbm", - "id": "mongodb", - "selection_mode": "all", - "related_instance_selections": [ - { - "system_id": "bk_dbm", - "id": "mongodb_list" - } - ] - } - ], - "related_actions": [], - "version": 1, - "group": "MongoDB", - "subgroup": "权限管理", - "description": "MongoDB 授权", - "description_en": "MONGODB_AUTHORIZE" - } - }, - { - "operation": "upsert_action", - "data": { - "id": "mongodb_excel_authorize", - "name": "MongoDB Excel授权", - "name_en": "MONGODB_EXCEL_AUTHORIZE", - "type": "execute", - "related_resource_types": [ - { - "system_id": "bk_dbm", - "id": "mongodb", - "selection_mode": "all", - "related_instance_selections": [ - { - "system_id": "bk_dbm", - "id": "mongodb_list" - } - ] - } - ], - "related_actions": [], - "version": 1, - "group": "MongoDB", - "subgroup": "权限管理", - "description": "MongoDB Excel授权", - "description_en": "MONGODB_EXCEL_AUTHORIZE" - } - }, { "operation": "upsert_action", "data": { @@ -7839,17 +8331,8 @@ "name": "告警组", "name_en": "告警组", "actions": [ - { - "id": "global_notify_group_list" - }, - { - "id": "global_notify_group_create" - }, { "id": "global_notify_group_update" - }, - { - "id": "global_notify_group_delete" } ] }, @@ -7992,6 +8475,9 @@ "actions": [ { "id": "resource_pool_manage" + }, + { + "id": "resource_operation_view" } ] }, @@ -8036,6 +8522,9 @@ { "id": "mysql_view" }, + { + "id": "mysql_admin_pwd_modify" + }, { "id": "mysql_enable_disable" }, @@ -8058,14 +8547,17 @@ "id": "mysql_account_delete" }, { - "id": "mysql_account_rule_create" + "id": "mysql_add_account_rule" }, { - "id": "mysql_excel_authorize_rules" + "id": "mysql_account_rules_view" }, { "id": "mysql_authorize_rules" }, + { + "id": "mysql_excel_authorize_rules" + }, { "id": "mysql_instance_clone_rules" } @@ -8096,6 +8588,9 @@ "name": "克隆开区", "name_en": "克隆开区", "actions": [ + { + "id": "mysql_open_area" + }, { "id": "mysql_openarea_config_create" }, @@ -8104,9 +8599,6 @@ }, { "id": "mysql_openarea_config_destroy" - }, - { - "id": "mysql_open_area" } ] }, @@ -8235,6 +8727,9 @@ { "id": "tendbcluster_view" }, + { + "id": "tendbcluster_admin_pwd_modify" + }, { "id": "tendbcluster_apply" }, @@ -8263,13 +8758,16 @@ "id": "tendbcluster_add_account_rule" }, { - "id": "tendb_excel_authorize_rules" + "id": "tendbcluster_account_rules_view" }, { - "id": "tendb_cluster_client_clone_rules" + "id": "tendbcluster_authorize_rules" }, { - "id": "tendbcluster_authorize_rules" + "id": "tendb_excel_authorize_rules" + }, + { + "id": "tendb_cluster_client_clone_rules" } ] }, @@ -8286,6 +8784,9 @@ "name": "克隆开区", "name_en": "克隆开区", "actions": [ + { + "id": "tendbcluster_open_area" + }, { "id": "tendb_openarea_config_create" }, @@ -8552,6 +9053,9 @@ { "id": "influxdb_view" }, + { + "id": "group_manage" + }, { "id": "influxdb_enable_disable" }, @@ -8772,16 +9276,19 @@ "id": "mongodb_account_create" }, { - "id": "mongodb_delete_create" + "id": "mongodb_account_delete" }, { "id": "mongodb_add_account_rule" }, { - "id": "mongodb_authorize" + "id": "mongodb_account_rules_view" + }, + { + "id": "mongodb_authorize_rules" }, { - "id": "mongodb_excel_authorize" + "id": "mongo_excel_authorize_rules" } ] }, @@ -8862,6 +9369,9 @@ { "id": "sqlserver_view" }, + { + "id": "sqlserver_admin_pwd_modify" + }, { "id": "sqlserver_apply" }, @@ -8881,11 +9391,14 @@ "id": "sqlserver_account_create" }, { - "id": "sqlserver_delete_create" + "id": "sqlserver_account_delete" }, { "id": "sqlserver_add_account_rule" }, + { + "id": "sqlserver_account_rules_view" + }, { "id": "sqlserver_authorize_rules" }, @@ -9015,6 +9528,10 @@ "id": "mysql_view", "required": true }, + { + "id": "mysql_admin_pwd_modify", + "required": true + }, { "id": "mysql_enable_disable", "required": true @@ -9023,6 +9540,18 @@ "id": "mysql_destroy", "required": true }, + { + "id": "mysql_partition_update", + "required": true + }, + { + "id": "mysql_partition_delete", + "required": true + }, + { + "id": "mysql_partition_enable_disable", + "required": true + }, { "id": "tbinlogdumper_install", "required": true @@ -9075,10 +9604,6 @@ "id": "mysql_proxy_switch", "required": true }, - { - "id": "mysql_authorize_rules", - "required": true - }, { "id": "mysql_instance_clone_rules", "required": true @@ -9114,9 +9639,18 @@ { "id": "mysql_ha_full_backup", "required": true + } + ] + }, + { + "id": "mysql_account", + "actions": [ + { + "id": "mysql_account_delete", + "required": true }, { - "id": "mysql_open_area", + "id": "mysql_add_account_rule", "required": true } ] @@ -9166,10 +9700,26 @@ "id": "tendbcluster_view", "required": true }, + { + "id": "tendbcluster_admin_pwd_modify", + "required": true + }, { "id": "tendb_instance_clone_rules", "required": true }, + { + "id": "tendbcluster_partition_update", + "required": true + }, + { + "id": "tendbcluster_partition_delete", + "required": true + }, + { + "id": "tendb_partition_enable_disable", + "required": true + }, { "id": "tendbcluster_enable_disable", "required": true @@ -9253,9 +9803,18 @@ { "id": "tendbcluster_flashback", "required": true + } + ] + }, + { + "id": "tendbcluster_account", + "actions": [ + { + "id": "tendbcluster_account_delete", + "required": true }, { - "id": "tendbcluster_authorize_rules", + "id": "tendbcluster_add_account_rule", "required": true } ] @@ -9627,23 +10186,28 @@ "required": true }, { - "id": "mongodb_authorize", + "id": "mongodb_restore", "required": true }, { - "id": "mongodb_excel_authorize", + "id": "mongodb_temporary_destroy", "required": true }, { - "id": "mongodb_restore", + "id": "mongodb_install_dbmon", "required": true - }, + } + ] + }, + { + "id": "mongodb_account", + "actions": [ { - "id": "mongodb_temporary_destroy", + "id": "mongodb_account_delete", "required": true }, { - "id": "mongodb_install_dbmon", + "id": "mongodb_add_account_rule", "required": true } ] @@ -9655,6 +10219,10 @@ "id": "sqlserver_view", "required": true }, + { + "id": "sqlserver_admin_pwd_modify", + "required": true + }, { "id": "sqlserver_enable_disable", "required": true @@ -9710,13 +10278,35 @@ { "id": "sqlserver_rollback", "required": true + } + ] + }, + { + "id": "sqlserver_account", + "actions": [ + { + "id": "sqlserver_account_delete", + "required": true + }, + { + "id": "sqlserver_add_account_rule", + "required": true + } + ] + }, + { + "id": "notify_group", + "actions": [ + { + "id": "notify_group_update", + "required": true }, { - "id": "sqlserver_authorize_rules", + "id": "notify_group_delete", "required": true }, { - "id": "sqlserver_excel_authorize_rules", + "id": "global_notify_group_update", "required": true } ] @@ -9757,19 +10347,6 @@ "required": true } ] - }, - { - "id": "duty_rule", - "actions": [ - { - "id": "duty_rule_update", - "required": true - }, - { - "id": "duty_rule_destroy", - "required": true - } - ] } ] } diff --git a/dbm-ui/backend/iam_app/migrations/0006_bk_dbm_202403192052.py b/dbm-ui/backend/iam_app/migrations/0006_bk_dbm_202404031731.py similarity index 100% rename from dbm-ui/backend/iam_app/migrations/0006_bk_dbm_202403192052.py rename to dbm-ui/backend/iam_app/migrations/0006_bk_dbm_202404031731.py diff --git a/dbm-ui/backend/iam_app/urls.py b/dbm-ui/backend/iam_app/urls.py index b600a5c6f6..07aa08a8ef 100644 --- a/dbm-ui/backend/iam_app/urls.py +++ b/dbm-ui/backend/iam_app/urls.py @@ -16,6 +16,12 @@ from backend import env from backend.iam_app.handlers.permission import Permission +from backend.iam_app.views.account_provider import ( + MongoDBAccountResourceProvider, + MySQLAccountResourceProvider, + SQLServerAccountResourceProvider, + TendbClusterAccountResourceProvider, +) from backend.iam_app.views.cluster_provider import ( EsClusterResourceProvider, HdfsClusterResourceProvider, @@ -29,10 +35,10 @@ ) from backend.iam_app.views.dbtype_provider import DBTypeResourceProvider from backend.iam_app.views.dumper_config_provider import DumperSubscribeConfigResourceProvider -from backend.iam_app.views.duty_rule_provider import DutyRuleResourceProvider from backend.iam_app.views.flow_provider import FlowResourceProvider from backend.iam_app.views.instance_provider import InfluxDBInstanceResourceProvider from backend.iam_app.views.monitor_policy_provider import MonitorPolicyResourceProvider +from backend.iam_app.views.notify_group_provider import NotifyGroupResourceProvider from backend.iam_app.views.openarea_config_provider import OpenareaConfigResourceProvider from backend.iam_app.views.ticket_provider import TicketResourceProvider from backend.iam_app.views.views import IAMViewSet @@ -41,8 +47,12 @@ router.register(r"", IAMViewSet, basename="iam") dispatcher = DjangoBasicResourceApiDispatcher(Permission.get_iam_client(), env.BK_IAM_SYSTEM_ID) + dispatcher.register(r"flow", FlowResourceProvider()) dispatcher.register(r"ticket", TicketResourceProvider()) +dispatcher.register(r"openarea_config", OpenareaConfigResourceProvider()) +dispatcher.register(r"dumper_subscribe_config", DumperSubscribeConfigResourceProvider()) + dispatcher.register(r"mysql", MySQLResourceProvider()) dispatcher.register(r"tendbcluster", TendbClusterResourceProvider()) dispatcher.register(r"redis", RedisClusterResourceProvider()) @@ -54,9 +64,14 @@ dispatcher.register(r"dbtype", DBTypeResourceProvider()) dispatcher.register(r"mongodb", MongoDBClusterResourceProvider()) dispatcher.register(r"sqlserver", SQLServerClusterResourceProvider()) + +dispatcher.register(r"mysql_account", MySQLAccountResourceProvider()) +dispatcher.register(r"tendbcluster_account", TendbClusterAccountResourceProvider()) +dispatcher.register(r"sqlserver_account", SQLServerAccountResourceProvider()) +dispatcher.register(r"mongodb_account", MongoDBAccountResourceProvider()) + dispatcher.register(r"monitor_policy", MonitorPolicyResourceProvider()) -dispatcher.register(r"duty_rule", DutyRuleResourceProvider()) -dispatcher.register(r"openarea_config", OpenareaConfigResourceProvider()) -dispatcher.register(r"dumper_subscribe_config", DumperSubscribeConfigResourceProvider()) +dispatcher.register(r"notify_group", NotifyGroupResourceProvider()) + urlpatterns = [url(r"^", include(router.urls)), url(r"^resource/$", dispatcher.as_view([login_exempt]))] diff --git a/dbm-ui/backend/iam_app/views/account_provider.py b/dbm-ui/backend/iam_app/views/account_provider.py new file mode 100644 index 0000000000..d2b2565cc8 --- /dev/null +++ b/dbm-ui/backend/iam_app/views/account_provider.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging + +from backend.components import DBPrivManagerApi +from backend.components.base import DataAPI +from backend.db_services.dbpermission.constants import AccountType +from backend.iam_app.dataclass.resources import ResourceEnum, ResourceMeta +from backend.iam_app.views.iam_provider import BaseInterfaceResourceProvider + +logger = logging.getLogger("root") + + +class AccountResourceProvider(BaseInterfaceResourceProvider): + """ + 账号资源的反向拉取类 + """ + + api: DataAPI = DBPrivManagerApi.get_account + resource_meta: ResourceMeta = None + account_type: AccountType = None + + @staticmethod + def convert_condition_field(condition): + # 账号的业务参数要是整型 + condition["bk_biz_id"] = int(condition["bk_biz_id"]) + return condition + + def list_instance(self, filter, page, **options): + filter.data_source = self.api + filter.value_list = [self.resource_meta.lookup_field, *self.resource_meta.display_fields] + filter.keyword_field = "user_like" + filter.conditions = {"cluster_type": self.account_type} + return super().list_instance(filter, page, **options) + + def search_instance(self, filter, page, **options): + return self.list_instance(filter, page, **options) + + def fetch_instance_info(self, filter, **options): + filter.data_source = self.api + return super().fetch_instance_info(filter, **options) + + def list_instance_by_policy(self, filter, page, **options): + key_mapping = { + f"{self.resource_meta.id}.ids": "ids", + f"{self.resource_meta.id}.id": "id", + f"{self.resource_meta.id}._bk_iam_path_": "bk_biz_id", + } + values_hook = {"bk_biz_id": lambda value: value[1:-1].split(",")[1]} + return self._list_instance_by_policy( + data_source=self.api, + value_list=["id", "user"], + key_mapping=key_mapping, + value_hooks=values_hook, + filter=filter, + page=page, + ) + + +class MySQLAccountResourceProvider(AccountResourceProvider): + """ + mysql账号资源的反向拉取类 + """ + + account_type = AccountType.MYSQL + resource_meta = ResourceEnum.MYSQL_ACCOUNT + + +class SQLServerAccountResourceProvider(AccountResourceProvider): + """ + mysql账号资源的反向拉取类 + """ + + account_type = AccountType.SQLServer + resource_meta = ResourceEnum.SQLSERVER_ACCOUNT + + +class MongoDBAccountResourceProvider(AccountResourceProvider): + """ + mysql账号资源的反向拉取类 + """ + + account_type = AccountType.MONGODB + resource_meta = ResourceEnum.MONGODB_ACCOUNT + + +class TendbClusterAccountResourceProvider(AccountResourceProvider): + """ + mysql账号资源的反向拉取类 + """ + + account_type = AccountType.TENDBCLUSTER + resource_meta = ResourceEnum.TENDBCLUSTER_ACCOUNT diff --git a/dbm-ui/backend/iam_app/views/cluster_provider.py b/dbm-ui/backend/iam_app/views/cluster_provider.py index 48b91693b7..38d883f9db 100644 --- a/dbm-ui/backend/iam_app/views/cluster_provider.py +++ b/dbm-ui/backend/iam_app/views/cluster_provider.py @@ -20,12 +20,12 @@ from backend.db_meta.enums import ClusterType from backend.db_meta.models import Cluster from backend.iam_app.dataclass.resources import ResourceEnum, ResourceMeta -from backend.iam_app.views.iam_provider import BaseResourceProvider, CommonProviderMixin +from backend.iam_app.views.iam_provider import BaseModelResourceProvider logger = logging.getLogger("root") -class ClusterResourceProvider(BaseResourceProvider, CommonProviderMixin): +class ClusterResourceProvider(BaseModelResourceProvider): """ 集群资源的反向拉取基类 """ @@ -34,25 +34,13 @@ class ClusterResourceProvider(BaseResourceProvider, CommonProviderMixin): resource_meta: ResourceMeta = None cluster_types: List[ClusterType] = [] - def get_bk_iam_path(self, instance_ids, *args, **kwargs) -> Dict: - return self.get_model_bk_iam_path(self.model, instance_ids, *args, **kwargs) - - def list_attr(self, **options): - """查询某个资源类型可用于配置权限的属性列表""" - return self._list_attr(id=self.resource_meta.attribute, display_name=self.resource_meta.attribute_display) - - def list_attr_value(self, filter, page, **options): - """获取一个资源类型某个属性的值列表""" - user_resource = self.list_user_resource() - return self._list_attr_value(self.resource_meta.attribute, user_resource, filter, page, **options) - def list_instance(self, filter, page, **options): # 资源的模型 - filter.model = self.model + filter.data_source = self.model # 资源模型提取的字段,通常第一个字段是主键,剩下的字段是展示字段 filter.value_list = [self.resource_meta.lookup_field, *self.resource_meta.display_fields] # 资源过滤的字段 - filter.keyword_field = "immute_domain" + filter.keyword_field = "immute_domain__icontains" # 资源其他过滤条件,会同keyword_field一起组成Q查询 filter.conditions = {"cluster_type__in": self.cluster_types} return super().list_instance(filter, page, **options) @@ -60,6 +48,10 @@ def list_instance(self, filter, page, **options): def search_instance(self, filter, page, **options): return self.list_instance(filter, page, **options) + def fetch_instance_info(self, filter, **options): + filter.data_source = self.model + return super().fetch_instance_info(filter, **options) + def list_instance_by_policy(self, filter, page, **options): # 策略字段与模型字段之间的映射 key_mapping = { @@ -70,7 +62,7 @@ def list_instance_by_policy(self, filter, page, **options): # 模型字段对应数据提取方法 values_hook = {"bk_biz_id": lambda value: value[1:-1].split(",")[1]} return self._list_instance_by_policy( - obj_model=self.model, + data_source=self.model, value_list=[self.resource_meta.lookup_field, *self.resource_meta.display_fields], key_mapping=key_mapping, value_hooks=values_hook, @@ -80,14 +72,14 @@ def list_instance_by_policy(self, filter, page, **options): def _list_instance_with_cluster_type( self, - obj_model: models.Model, + data_source: models.Model, condition: Dict, value_list: List[str], page: Page, cluster_type__label: Dict[str, str], ): """集群资源展示的时候加上类型标识""" - queryset = obj_model.objects.filter(**condition)[page.slice_from : page.slice_to] + queryset = data_source.objects.filter(**condition)[page.slice_from : page.slice_to] results = [] for cluster in queryset: cluster_type_label = cluster_type__label.get(cluster.cluster_type, cluster.cluster_type) @@ -99,9 +91,9 @@ class MySQLResourceProvider(ClusterResourceProvider): resource_meta: ResourceMeta = ResourceEnum.MYSQL cluster_types: ClusterType = [ClusterType.TenDBSingle, ClusterType.TenDBHA] - def _list_instance(self, obj_model: models.Model, condition: Dict, value_list: List[str], page): + def _list_instance(self, data_source: models.Model, condition: Dict, value_list: List[str], page): cluster_type__label = {ClusterType.TenDBSingle: _("单节点"), ClusterType.TenDBHA: _("高可用")} - return super()._list_instance_with_cluster_type(obj_model, condition, value_list, page, cluster_type__label) + return super()._list_instance_with_cluster_type(data_source, condition, value_list, page, cluster_type__label) class TendbClusterResourceProvider(ClusterResourceProvider): @@ -143,15 +135,15 @@ class MongoDBClusterResourceProvider(ClusterResourceProvider): resource_meta: ResourceMeta = ResourceEnum.MONGODB cluster_types: ClusterType = [ClusterType.MongoShardedCluster, ClusterType.MongoReplicaSet] - def _list_instance(self, obj_model: models.Model, condition: Dict, value_list: List[str], page): + def _list_instance(self, data_source: models.Model, condition: Dict, value_list: List[str], page): cluster_type__label = {ClusterType.MongoReplicaSet: _("副本集"), ClusterType.MongoShardedCluster: _("分片集")} - return super()._list_instance_with_cluster_type(obj_model, condition, value_list, page, cluster_type__label) + return super()._list_instance_with_cluster_type(data_source, condition, value_list, page, cluster_type__label) class SQLServerClusterResourceProvider(MySQLResourceProvider): resource_meta: ResourceMeta = ResourceEnum.SQLSERVER cluster_types: ClusterType = [ClusterType.SqlserverHA, ClusterType.SqlserverSingle] - def _list_instance(self, obj_model: models.Model, condition: Dict, value_list: List[str], page): + def _list_instance(self, data_source: models.Model, condition: Dict, value_list: List[str], page): cluster_type__label = {ClusterType.SqlserverSingle: _("单节点"), ClusterType.SqlserverHA: _("高可用")} - return super()._list_instance_with_cluster_type(obj_model, condition, value_list, page, cluster_type__label) + return super()._list_instance_with_cluster_type(data_source, condition, value_list, page, cluster_type__label) diff --git a/dbm-ui/backend/iam_app/views/dumper_config_provider.py b/dbm-ui/backend/iam_app/views/dumper_config_provider.py index 7305235f1a..e0b90c7c50 100644 --- a/dbm-ui/backend/iam_app/views/dumper_config_provider.py +++ b/dbm-ui/backend/iam_app/views/dumper_config_provider.py @@ -10,18 +10,17 @@ """ import logging -from typing import Dict from django.db import models from backend.db_services.mysql.dumper.models import DumperSubscribeConfig from backend.iam_app.dataclass.resources import ResourceEnum, ResourceMeta -from backend.iam_app.views.iam_provider import BaseResourceProvider, CommonProviderMixin +from backend.iam_app.views.iam_provider import BaseModelResourceProvider logger = logging.getLogger("root") -class DumperSubscribeConfigResourceProvider(BaseResourceProvider, CommonProviderMixin): +class DumperSubscribeConfigResourceProvider(BaseModelResourceProvider): """ flow资源的反向拉取类 """ @@ -29,34 +28,28 @@ class DumperSubscribeConfigResourceProvider(BaseResourceProvider, CommonProvider model: models.Model = DumperSubscribeConfig resource_meta: ResourceMeta = ResourceEnum.DUMPER_SUBSCRIBE_CONFIG - def get_bk_iam_path(self, instance_ids, *args, **kwargs) -> Dict: - return super().get_model_bk_iam_path(self.model, instance_ids, *args, **kwargs) - - def list_attr(self, **options): - return self._list_attr(id=self.resource_meta.attribute, display_name=self.resource_meta.attribute_display) - - def list_attr_value(self, filter, page, **options): - user_resource = self.list_user_resource() - return self._list_attr_value(self.resource_meta.attribute, user_resource, filter, page, **options) - def list_instance(self, filter, page, **options): - filter.model = self.model + filter.data_source = self.model filter.value_list = [self.resource_meta.lookup_field, *self.resource_meta.display_fields] - filter.keyword_field = "name" + filter.keyword_field = "name__contains" return super().list_instance(filter, page, **options) def search_instance(self, filter, page, **options): return self.list_instance(filter, page, **options) + def fetch_instance_info(self, filter, **options): + filter.data_source = self.model + return super().fetch_instance_info(filter, **options) + def list_instance_by_policy(self, filter, page, **options): key_mapping = { f"{self.resource_meta.id}.id": "id", - f"{self.resource_meta.id}.created_by": "created_by", + f"{self.resource_meta.id}.creator": "creator", f"{self.resource_meta.id}._bk_iam_path_": "bk_biz_id", } values_hook = {"bk_biz_id": lambda value: value[1:-1].split(",")[1]} return self._list_instance_by_policy( - obj_model=self.model, + data_source=self.model, value_list=["id", "name"], key_mapping=key_mapping, value_hooks=values_hook, diff --git a/dbm-ui/backend/iam_app/views/duty_rule_provider.py b/dbm-ui/backend/iam_app/views/duty_rule_provider.py deleted file mode 100644 index 4ef438e3c0..0000000000 --- a/dbm-ui/backend/iam_app/views/duty_rule_provider.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 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 https://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. -""" - -import logging -from typing import Dict - -from django.db import models - -from backend.db_monitor.models import DutyRule -from backend.iam_app.dataclass.resources import ResourceEnum, ResourceMeta -from backend.iam_app.views.iam_provider import BaseResourceProvider, CommonProviderMixin - -logger = logging.getLogger("root") - - -class DutyRuleResourceProvider(BaseResourceProvider, CommonProviderMixin): - """ - 轮值策略资源的反向拉取类 - """ - - model: models.Model = DutyRule - resource_meta: ResourceMeta = ResourceEnum.DUTY_RULE - - def get_bk_iam_path(self, instance_ids, *args, **kwargs) -> Dict: - return super().get_model_bk_iam_path(self.model, instance_ids, *args, **kwargs) - - def list_attr(self, **options): - return self._list_attr(id=self.resource_meta.attribute, display_name=self.resource_meta.attribute_display) - - def list_attr_value(self, filter, page, **options): - user_resource = self.list_user_resource() - return self._list_attr_value(self.resource_meta.attribute, user_resource, filter, page, **options) - - def list_instance(self, filter, page, **options): - filter.model = self.model - filter.value_list = [self.resource_meta.lookup_field, *self.resource_meta.display_fields] - filter.keyword_field = "name" - return super().list_instance(filter, page, **options) - - def search_instance(self, filter, page, **options): - return self.list_instance(filter, page, **options) - - def list_instance_by_policy(self, filter, page, **options): - key_mapping = { - f"{self.resource_meta.id}.id": "id", - f"{self.resource_meta.id}.creator": "creator", - f"{self.resource_meta.id}._bk_iam_path_": "db_type", - } - values_hook = {"db_type": lambda value: value[1:-1].split(",")[1]} - return self._list_instance_by_policy( - obj_model=self.model, - # root_id 同时作为id和display name - value_list=["id", "name"], - key_mapping=key_mapping, - value_hooks=values_hook, - filter=filter, - page=page, - ) diff --git a/dbm-ui/backend/iam_app/views/flow_provider.py b/dbm-ui/backend/iam_app/views/flow_provider.py index 9a268ea5ad..aba02a731a 100644 --- a/dbm-ui/backend/iam_app/views/flow_provider.py +++ b/dbm-ui/backend/iam_app/views/flow_provider.py @@ -10,18 +10,17 @@ """ import logging -from typing import Dict from django.db import models from backend.flow.models import FlowTree from backend.iam_app.dataclass.resources import ResourceEnum, ResourceMeta -from backend.iam_app.views.iam_provider import BaseResourceProvider, CommonProviderMixin +from backend.iam_app.views.iam_provider import BaseModelResourceProvider logger = logging.getLogger("root") -class FlowResourceProvider(BaseResourceProvider, CommonProviderMixin): +class FlowResourceProvider(BaseModelResourceProvider): """ flow资源的反向拉取类 """ @@ -29,25 +28,19 @@ class FlowResourceProvider(BaseResourceProvider, CommonProviderMixin): model: models.Model = FlowTree resource_meta: ResourceMeta = ResourceEnum.TASKFLOW - def get_bk_iam_path(self, instance_ids, *args, **kwargs) -> Dict: - return super().get_model_bk_iam_path(self.model, instance_ids, *args, **kwargs) - - def list_attr(self, **options): - return self._list_attr(id=self.resource_meta.attribute, display_name=self.resource_meta.attribute_display) - - def list_attr_value(self, filter, page, **options): - user_resource = self.list_user_resource() - return self._list_attr_value(self.resource_meta.attribute, user_resource, filter, page, **options) - def list_instance(self, filter, page, **options): - filter.model = self.model + filter.data_source = self.model filter.value_list = [self.resource_meta.lookup_field, *self.resource_meta.display_fields] - filter.keyword_field = "root_id" + filter.keyword_field = "root_id__icontains" return super().list_instance(filter, page, **options) def search_instance(self, filter, page, **options): return self.list_instance(filter, page, **options) + def fetch_instance_info(self, filter, **options): + filter.data_source = self.model + return super().fetch_instance_info(filter, **options) + def list_instance_by_policy(self, filter, page, **options): key_mapping = { f"{self.resource_meta.id}.id": "root_id", @@ -56,7 +49,7 @@ def list_instance_by_policy(self, filter, page, **options): } values_hook = {"bk_biz_id": lambda value: value[1:-1].split(",")[1]} return self._list_instance_by_policy( - obj_model=self.model, + data_source=self.model, # root_id 同时作为id和display name value_list=["root_id", "root_id"], key_mapping=key_mapping, diff --git a/dbm-ui/backend/iam_app/views/iam_provider.py b/dbm-ui/backend/iam_app/views/iam_provider.py index b3636ea28d..a75f37fa1d 100644 --- a/dbm-ui/backend/iam_app/views/iam_provider.py +++ b/dbm-ui/backend/iam_app/views/iam_provider.py @@ -10,37 +10,26 @@ """ import abc -from typing import Dict, List +from typing import Any, Dict, List from blueapps.account.models import User from django.db import models from iam import PathEqDjangoQuerySetConverter from iam.resource.provider import ListResult, ResourceProvider +from iam.resource.utils import FancyDict, Page +from backend.components.base import DataAPI from backend.iam_app.dataclass.resources import ResourceEnum, ResourceMeta +from backend.iam_app.handlers.converter import InterfaceConverter class CommonProviderMixin(object): """ - 封装了Provider可能用到的方法 + 封装了Provider通用的,非ResourceProvider框架的方法 """ resource_meta: ResourceMeta = None - def get_model_bk_iam_path(self, model, instance_ids, *args, **kwargs) -> Dict: - """获取带有模型实例的bk_iam_path""" - instances = model.objects.filter(pk__in=instance_ids) - # 默认考虑一层父类 - id__bk_iam_path = { - # TODO: 拓扑结构目前是/{resource_type},{resource_id}/ - instance.id: "/{},{}/".format( - self.resource_meta.parent.id, - getattr(instance, self.resource_meta.parent.lookup_field), - ) - for instance in instances - } - return id__bk_iam_path - @staticmethod def list_user_resource(): """获取以user为属性的value""" @@ -48,115 +37,107 @@ def list_user_resource(): user_resource = [{"id": user.username, "display_name": user.username} for user in users] return user_resource - -class BaseResourceProvider(ResourceProvider, metaclass=abc.ABCMeta): - """ - 基类Provider, 提供通用处理函数 - """ - - resource_meta: ResourceMeta = None - - def resources_filter(self, filter, resources) -> List[Dict]: + @staticmethod + def resources_filter(filter, resources) -> List[Dict]: + """根据filter字段过滤resource""" if not filter.get("keyword") and not filter.get("ids"): return resources - results = [] for resource in resources: if filter.get("ids") and resource["id"] in filter.get("ids"): results.append(resource) if filter.get("keyword") and filter.get("keyword") in resource["display_name"]: results.append(resource) + return results + + def fetch_instance_info_with_resource(self, id__bk_iam_path, resource_meta, resources, value_list) -> List[Dict]: + """根据资源列表和字段,返回iam的资源字段信息""" + results = [] + for item in resources: + item["id"] = item.pop(resource_meta.lookup_field) + if "display_name" in value_list: + display_name_list = [str(item.pop(field, "")) for field in resource_meta.display_fields] + item["display_name"] = ":".join(display_name_list) or item["id"] + if "_bk_iam_path_" in value_list: + item["_bk_iam_path_"] = id__bk_iam_path[item["id"]] + results.append(item) return results + +class BaseResourceProvider(ResourceProvider, CommonProviderMixin, metaclass=abc.ABCMeta): + """ + 基类Provider, 提供通用处理函数 + """ + + resource_meta: ResourceMeta = None + + @abc.abstractmethod def get_bk_iam_path(self, instance_ids, *args, **kwargs) -> Dict: """获取实例的bk_iam_path,由各个视图独立实现""" - id__bk_iam_path = {inst: "" for inst in instance_ids} - return id__bk_iam_path + raise NotImplementedError def _list_attr(self, id: str, display_name: str, **options) -> ListResult: """考虑只支持一种属性""" results = [{"id": id, "display_name": display_name}] return ListResult(results=results, count=len(results)) - def _list_attr_value(self, attr: str, resources: List[Dict], filter, page, **options) -> ListResult: + def _list_attr_value( + self, attr: str, resources: List[Dict], filter: FancyDict, page: Page, **options + ) -> ListResult: + """根据属性value过滤资源""" if filter.attr == attr: - results = self.resources_filter(filter, resources) + results = super().resources_filter(filter, resources) return ListResult(results=results[page.slice_from : page.slice_to], count=len(results)) else: return ListResult(results=[], count=0) - def _search_instance(self, obj_model: models.Model, condition: Dict, value_list: List[str], page): - return self._list_instance(obj_model, condition, value_list, page) + @abc.abstractmethod + def _search_instance(self, data_source: Any, condition: Dict, value_list: List[str], page): + """根据过滤条件搜索资源,由子类独立实现""" + raise NotImplementedError - def _list_instance(self, obj_model: models.Model, condition: Dict, value_list: List[str], page): - queryset = obj_model.objects.filter(**condition).values(*value_list)[page.slice_from : page.slice_to] - results = [ - {"id": str(item[value_list[0]]), "display_name": ":".join([str(item[value]) for value in value_list[1:]])} - for item in list(queryset) - ] - return ListResult(results=results, count=len(results)) + @abc.abstractmethod + def _list_instance(self, data_source: Any, condition: Dict, value_list: List[str], page: Page): + """根据条件过滤资源列表,由子类独立实现""" + raise NotImplementedError + @abc.abstractmethod def _fetch_instance_info( - self, obj_model: models.Model, resource_meta: ResourceMeta, value_list: List[str], filter + self, data_source: Any, resource_meta: ResourceMeta, value_list: List[str], filter: FancyDict ): - # 查询关联字段的queryset - field_list = [field for field in value_list if field not in ["_bk_iam_path_", "display_name"]] - field_list.extend([resource_meta.lookup_field, *resource_meta.display_fields]) - queryset = obj_model.objects.filter(pk__in=filter.ids).values(*field_list) - # 获取资源实例与_bk_iam_path_的关联 - id__bk_iam_path = self.get_bk_iam_path(filter.ids) - - results = [] - for item in queryset: - item["id"] = item.pop(resource_meta.lookup_field) - if "display_name" in value_list: - display_name_list = [str(item.pop(field, "")) for field in resource_meta.display_fields] - item["display_name"] = ":".join(display_name_list) or item["id"] - if "_bk_iam_path_" in value_list: - item["_bk_iam_path_"] = id__bk_iam_path[item["id"]] - - results.append(item) - - return ListResult(results=results, count=len(results)) + """批量获取资源实例详情,由子类独立实现""" + raise NotImplementedError + @abc.abstractmethod def _list_instance_by_policy( self, - obj_model: models.Model, + data_source: Any, value_list: List[str], key_mapping: Dict, value_hooks: Dict, - filter, - page, + filter: FancyDict, + page: Page, **options, ): - expression = filter.expression - if not expression: - return ListResult(results=[], count=0) - - converter = options.get("converter_class", PathEqDjangoQuerySetConverter)(key_mapping, value_hooks) - filters = converter.convert(expression) - queryset = obj_model.objects.filter(filters).values(*value_list)[page.slice_from : page.slice_to] - results = [ - {"id": str(item[value_list[0]]), "display_name": ":".join([str(item[value]) for value in value_list[1:]])} - for item in queryset - ] - return ListResult(results=results, count=len(results)) + """根据策略获取资源,由子类独立实现""" + raise NotImplementedError def list_attr(self, **options): """ 通过属性配置权限会用到, 没有属性权限管控不需要实现 属性列表 """ - return ListResult(results=[], count=0) + return self._list_attr(id=self.resource_meta.attribute, display_name=self.resource_meta.attribute_display) def list_attr_value(self, filter, page, **options): """ - 通过属性配置权限会用到, 没有属性权限管控不需要实现 + 通过属性配置权限会用到, 没有属性权限管控不需要实现。默认实现是以"创建者"为属性 属性值列表 注意, 有翻页; 需要返回count """ - return ListResult(results=[], count=0) + user_resource = super().list_user_resource() + return self._list_attr_value(self.resource_meta.attribute, user_resource, filter, page, **options) def list_instance(self, filter, page, **options): """ @@ -174,10 +155,10 @@ def list_instance(self, filter, page, **options): # iam页面过滤搜索 keyword = filter.get("search") or filter.get("keyword") if keyword: - conditions.update({f"{filter.keyword_field}__icontains": keyword}) + conditions.update({f"{filter.keyword_field}": keyword}) return self._list_instance( - obj_model=filter.model, condition=conditions, value_list=filter.value_list, page=page + data_source=filter.data_source, condition=conditions, value_list=filter.value_list, page=page ) def search_instance(self, filter, page, **options): @@ -188,7 +169,7 @@ def search_instance(self, filter, page, **options): def list_instance_by_policy(self, filter, page, **options): """ - 动态查询资源实例进行预览 + 动态查询资源实例进行预览,默认无实现 """ return ListResult(results=[], count=0) @@ -198,5 +179,169 @@ def fetch_instance_info(self, filter, **options): """ value_list = filter.get("attrs") or [self.resource_meta.attribute, "display_name", "_bk_iam_path_"] return self._fetch_instance_info( - obj_model=self.model, resource_meta=self.resource_meta, value_list=value_list, filter=filter + data_source=filter.data_source, resource_meta=self.resource_meta, value_list=value_list, filter=filter ) + + +class BaseModelResourceProvider(BaseResourceProvider): + """ + 模型基类Provider, 提供通用处理函数。 + 适用于资源是在DBM服务内,可以通过ORM查询 + """ + + resource_meta: ResourceMeta = None + model: models.Model = None + + def get_model_bk_iam_path(self, model, instance_ids, *args, **kwargs) -> Dict: + """获取带有模型实例的bk_iam_path""" + instances = model.objects.filter(pk__in=instance_ids) + # 默认考虑一层父类 + id__bk_iam_path = { + instance.id: "/{},{}/".format( + self.resource_meta.parent.id, + getattr(instance, self.resource_meta.parent.lookup_field), + ) + for instance in instances + } + return id__bk_iam_path + + def get_bk_iam_path(self, instance_ids, *args, **kwargs) -> Dict: + return self.get_model_bk_iam_path(self.model, instance_ids, *args, **kwargs) + + def _search_instance(self, data_source: models.Model, condition: Dict, value_list: List[str], page): + return self._list_instance(data_source, condition, value_list, page) + + def _list_instance(self, data_source: models.Model, condition: Dict, value_list: List[str], page): + # 根据过滤条件查询关联的queryset + queryset = data_source.objects.filter(**condition).values(*value_list)[page.slice_from : page.slice_to] + results = [ + {"id": str(item[value_list[0]]), "display_name": ":".join([str(item[value]) for value in value_list[1:]])} + for item in list(queryset) + ] + return ListResult(results=results, count=len(results)) + + def _fetch_instance_info( + self, data_source: models.Model, resource_meta: ResourceMeta, value_list: List[str], filter + ): + # 查询关联字段的queryset + field_list = [field for field in value_list if field not in ["_bk_iam_path_", "display_name"]] + field_list.extend([resource_meta.lookup_field, *resource_meta.display_fields]) + queryset = data_source.objects.filter(pk__in=filter.ids).values(*field_list) + # 获取资源实例与_bk_iam_path_的关联 + id__bk_iam_path = self.get_bk_iam_path(filter.ids) + # 根据value_list,获取实例信息字段 + results = self.fetch_instance_info_with_resource(id__bk_iam_path, resource_meta, queryset, value_list) + return ListResult(results=results, count=len(results)) + + def _list_instance_by_policy( + self, + data_source: models.Model, + value_list: List[str], + key_mapping: Dict, + value_hooks: Dict, + filter: FancyDict, + page: Page, + **options, + ): + expression = filter.expression + # 无策略表达式,则直接返回 + if not expression: + return ListResult(results=[], count=0) + + # 根据转换钩子将表达式转换为orm的过滤条件 + converter = options.get("converter_class", PathEqDjangoQuerySetConverter)(key_mapping, value_hooks) + filters = converter.convert(expression) + + # 过滤资源实例 + queryset = data_source.objects.filter(filters).values(*value_list)[page.slice_from : page.slice_to] + results = [ + {"id": str(item[value_list[0]]), "display_name": ":".join([str(item[value]) for value in value_list[1:]])} + for item in queryset + ] + + return ListResult(results=results, count=len(results)) + + +class BaseInterfaceResourceProvider(BaseResourceProvider): + """ + 接口基类Provider, 提供通用处理函数。 + 适用于资源是在DBM服务外部,可以通过接口查询 + """ + + resource_meta: ResourceMeta = None + # 获取资源数据的api,默认需要支持分页 + api: DataAPI = None + + def get_bk_iam_path(self, instance_ids, *args, **kwargs) -> Dict: + instances = self.api(params={"ids": instance_ids})["results"] + # 默认考虑一层父类 + id__bk_iam_path = { + instance["id"]: "/{},{}/".format( + self.resource_meta.parent.id, instance[self.resource_meta.parent.lookup_field] + ) + for instance in instances + } + return id__bk_iam_path + + @staticmethod + def convert_condition_field(condition: Dict): + """转换过滤字段: 模糊字段(xx__contains),主键(id),主键数组(ids)等。子类可覆写""" + return condition + + def _search_instance(self, data_source: DataAPI, condition: Dict, value_list: List[str], page: Page): + return self._list_instance(data_source, condition, value_list, page) + + def _list_instance(self, data_source: DataAPI, condition: Dict, value_list: List[str], page: Page): + # 转换查询字段(需要api接口支持) + condition = self.convert_condition_field(condition) + # 填充分页参数 + condition.update(limit=page.limit, offset=page.offset) + # 查询数据 + data = data_source(params=condition)["results"] + results = [ + {"id": str(item[value_list[0]]), "display_name": ":".join([str(item[value]) for value in value_list[1:]])} + for item in list(data) + ] + return ListResult(results=results, count=len(results)) + + def _fetch_instance_info( + self, data_source: DataAPI, resource_meta: ResourceMeta, value_list: List[str], filter: FancyDict + ): + # 查询关联字段的queryset + field_list = [field for field in value_list if field not in ["_bk_iam_path_", "display_name"]] + field_list.extend([resource_meta.lookup_field, *resource_meta.display_fields]) + data = [{field: d[field] for field in field_list} for d in data_source(params={"ids": filter.ids})["results"]] + # 获取资源实例与_bk_iam_path_的关联 + id__bk_iam_path = self.get_bk_iam_path(filter.ids) + # 根据value_list,获取实例信息字段 + results = self.fetch_instance_info_with_resource(id__bk_iam_path, resource_meta, data, value_list) + return ListResult(results=results, count=len(results)) + + def _list_instance_by_policy( + self, + data_source: DataAPI, + value_list: List[str], + key_mapping: Dict, + value_hooks: Dict, + filter: FancyDict, + page: Page, + **options, + ): + expression = filter.expression + # 无策略表达式,则直接返回 + if not expression: + return ListResult(results=[], count=0) + + # 根据转换钩子将表达式转换为orm的过滤条件 + converter = options.get("converter_class", InterfaceConverter)(key_mapping, value_hooks) + filters = converter.convert(expression) + + # 过滤资源实例 + filters.update(limit=page.limit, offset=page.offset) + data = [{field: d[field] for field in value_list} for d in data_source(params=filters)["results"]] + results = [ + {"id": str(item[value_list[0]]), "display_name": ":".join([str(item[value]) for value in value_list[1:]])} + for item in data + ] + + return ListResult(results=results, count=len(results)) diff --git a/dbm-ui/backend/iam_app/views/instance_provider.py b/dbm-ui/backend/iam_app/views/instance_provider.py index 44e146527a..c1e5419efc 100644 --- a/dbm-ui/backend/iam_app/views/instance_provider.py +++ b/dbm-ui/backend/iam_app/views/instance_provider.py @@ -10,19 +10,19 @@ """ import logging -from typing import Dict, List +from typing import List from django.db import models from backend.db_meta.enums import InstanceRole from backend.db_meta.models import StorageInstance from backend.iam_app.dataclass.resources import ResourceEnum, ResourceMeta -from backend.iam_app.views.iam_provider import BaseResourceProvider, CommonProviderMixin +from backend.iam_app.views.iam_provider import BaseModelResourceProvider logger = logging.getLogger("root") -class InstanceResourceProvider(BaseResourceProvider, CommonProviderMixin): +class InstanceResourceProvider(BaseModelResourceProvider): """ 集群资源的反向拉取基类 """ @@ -31,26 +31,20 @@ class InstanceResourceProvider(BaseResourceProvider, CommonProviderMixin): resource_meta: ResourceMeta = None instance_roles: List[InstanceRole] = [] - def get_bk_iam_path(self, instance_ids, *args, **kwargs) -> Dict: - return self.get_model_bk_iam_path(self.model, instance_ids, *args, **kwargs) - - def list_attr(self, **options): - return self._list_attr(id=self.resource_meta.attribute, display_name=self.resource_meta.attribute_display) - - def list_attr_value(self, filter, page, **options): - user_resource = self.list_user_resource() - return self._list_attr_value(self.resource_meta.attribute, user_resource, filter, page, **options) - def list_instance(self, filter, page, **options): - filter.model = self.model + filter.data_source = self.model filter.value_list = [self.resource_meta.lookup_field, *self.resource_meta.display_fields] - filter.keyword_field = "machine__ip" + filter.keyword_field = "machine__ip__icontains" filter.conditions = {"instance_role__in": self.instance_roles} return super().list_instance(filter, page, **options) def search_instance(self, filter, page, **options): return self.list_instance(filter, page, **options) + def fetch_instance_info(self, filter, **options): + filter.data_source = self.model + return super().fetch_instance_info(filter, **options) + def list_instance_by_policy(self, filter, page, **options): key_mapping = { f"{self.resource_meta.id}.id": "id", @@ -59,7 +53,7 @@ def list_instance_by_policy(self, filter, page, **options): } values_hook = {"bk_biz_id": lambda value: value[1:-1].split(",")[1]} return self._list_instance_by_policy( - obj_model=self.model, + data_source=self.model, value_list=["id", "ip", "port"], key_mapping=key_mapping, value_hooks=values_hook, diff --git a/dbm-ui/backend/iam_app/views/monitor_policy_provider.py b/dbm-ui/backend/iam_app/views/monitor_policy_provider.py index 4c912dbb2c..002a94a371 100644 --- a/dbm-ui/backend/iam_app/views/monitor_policy_provider.py +++ b/dbm-ui/backend/iam_app/views/monitor_policy_provider.py @@ -10,25 +10,21 @@ """ import logging -import operator -from functools import reduce from typing import Dict from django.db import models -from django.db.models import Q -from iam import DjangoQuerySetConverter -from iam.eval.constants import KEYWORD_BK_IAM_PATH_FIELD_SUFFIX, OP from backend.db_monitor.models import MonitorPolicy from backend.iam_app.dataclass.resources import MonitorPolicyResourceMeta, ResourceEnum -from backend.iam_app.views.iam_provider import BaseResourceProvider, CommonProviderMixin +from backend.iam_app.handlers.converter import MonitorDjangoQuerySetConverter +from backend.iam_app.views.iam_provider import BaseModelResourceProvider logger = logging.getLogger("root") -class MonitorPolicyResourceProvider(BaseResourceProvider, CommonProviderMixin): +class MonitorPolicyResourceProvider(BaseModelResourceProvider): """ - 集群资源的反向拉取基类 + 监控策略资源的反向拉取基类 """ model: models.Model = MonitorPolicy @@ -54,18 +50,11 @@ def get_bk_iam_path(self, instance_ids, *args, **kwargs) -> Dict: id__bk_iam_path = {instance.id: self.resource_meta.get_bk_iam_path(instance) for instance in instances} return id__bk_iam_path - def list_attr(self, **options): - return self._list_attr(id=self.resource_meta.attribute, display_name=self.resource_meta.attribute_display) - - def list_attr_value(self, filter, page, **options): - user_resource = self.list_user_resource() - return self._list_attr_value(self.resource_meta.attribute, user_resource, filter, page, **options) - def list_instance(self, filter, page, **options): logger.info("list_instance params: %s, %s, %s", filter, page, options) - filter.model = self.model + filter.data_source = self.model filter.value_list = [self.resource_meta.lookup_field, *self.resource_meta.display_fields] - filter.keyword_field = "name" + filter.keyword_field = "name__icontains" # 默认给上bk_biz_id=0的过滤,如果监控有业务层级,则被覆盖不影响 filter.conditions = {"bk_biz_id": 0} return super().list_instance(filter, page, **options) @@ -82,21 +71,13 @@ def list_instance_by_policy(self, filter, page, **options): f"{self.resource_meta.id}._bk_iam_path_": "bk_biz_id,db_type", } value_hooks = {"bk_biz_id,db_type": self.parse_iam_path} + converter_class = options.get("converter_class", MonitorDjangoQuerySetConverter) return self._list_instance_by_policy( - obj_model=self.model, + data_source=self.model, value_list=["id", "name"], key_mapping=key_mapping, value_hooks=value_hooks, filter=filter, page=page, - converter_class=MonitorDjangoQuerySetConverter, + converter_class=converter_class, ) - - -class MonitorDjangoQuerySetConverter(DjangoQuerySetConverter): - def _iam_path_(self, left, right): - return reduce(operator.and_, [Q(**{field: right[field]}) for field in left.split(",")]) - - def operator_map(self, operator, field, value): - if field.endswith(KEYWORD_BK_IAM_PATH_FIELD_SUFFIX) and operator == OP.STARTS_WITH: - return self._iam_path_ diff --git a/dbm-ui/backend/iam_app/views/notify_group_provider.py b/dbm-ui/backend/iam_app/views/notify_group_provider.py new file mode 100644 index 0000000000..9122a10030 --- /dev/null +++ b/dbm-ui/backend/iam_app/views/notify_group_provider.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 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 https://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. +""" + +import logging + +from django.db import models + +from backend.db_monitor.models import NoticeGroup +from backend.iam_app.dataclass.resources import MonitorPolicyResourceMeta, ResourceEnum +from backend.iam_app.handlers.converter import NotifyGroupDjangoQuerySetConverter +from backend.iam_app.views.monitor_policy_provider import MonitorPolicyResourceProvider + +logger = logging.getLogger("root") + + +class NotifyGroupResourceProvider(MonitorPolicyResourceProvider): + """ + 告警组资源的反向拉取基类,和监控策略的provider类似,可直接继承使用 + """ + + model: models.Model = NoticeGroup + resource_meta: MonitorPolicyResourceMeta = ResourceEnum.NOTIFY_GROUP + + @staticmethod + def parse_iam_path(iam_path): + topo_type, topo_value = iam_path.strip("/").split(",") + topo_type = "bk_biz_id" if topo_type == "biz" else "db_type" + return {topo_type: topo_value} + + def list_instance_by_policy(self, filter, page, **options): + options.update(converter_class=NotifyGroupDjangoQuerySetConverter) + super().list_instance_by_policy(filter, page, **options) diff --git a/dbm-ui/backend/iam_app/views/openarea_config_provider.py b/dbm-ui/backend/iam_app/views/openarea_config_provider.py index a36b3c7ee2..fa3d992d30 100644 --- a/dbm-ui/backend/iam_app/views/openarea_config_provider.py +++ b/dbm-ui/backend/iam_app/views/openarea_config_provider.py @@ -10,18 +10,17 @@ """ import logging -from typing import Dict from django.db import models from backend.db_services.mysql.open_area.models import TendbOpenAreaConfig from backend.iam_app.dataclass.resources import ResourceEnum, ResourceMeta -from backend.iam_app.views.iam_provider import BaseResourceProvider, CommonProviderMixin +from backend.iam_app.views.iam_provider import BaseModelResourceProvider logger = logging.getLogger("root") -class OpenareaConfigResourceProvider(BaseResourceProvider, CommonProviderMixin): +class OpenareaConfigResourceProvider(BaseModelResourceProvider): """ flow资源的反向拉取类 """ @@ -29,25 +28,19 @@ class OpenareaConfigResourceProvider(BaseResourceProvider, CommonProviderMixin): model: models.Model = TendbOpenAreaConfig resource_meta: ResourceMeta = ResourceEnum.OPENAREA_CONFIG - def get_bk_iam_path(self, instance_ids, *args, **kwargs) -> Dict: - return super().get_model_bk_iam_path(self.model, instance_ids, *args, **kwargs) - - def list_attr(self, **options): - return self._list_attr(id=self.resource_meta.attribute, display_name=self.resource_meta.attribute_display) - - def list_attr_value(self, filter, page, **options): - user_resource = self.list_user_resource() - return self._list_attr_value(self.resource_meta.attribute, user_resource, filter, page, **options) - def list_instance(self, filter, page, **options): - filter.model = self.model + filter.data_source = self.model filter.value_list = [self.resource_meta.lookup_field, *self.resource_meta.display_fields] - filter.keyword_field = "config_name" + filter.keyword_field = "config_name__icontains" return super().list_instance(filter, page, **options) def search_instance(self, filter, page, **options): return self.list_instance(filter, page, **options) + def fetch_instance_info(self, filter, **options): + filter.data_source = self.model + return super().fetch_instance_info(filter, **options) + def list_instance_by_policy(self, filter, page, **options): key_mapping = { f"{self.resource_meta.id}.id": "id", @@ -56,7 +49,7 @@ def list_instance_by_policy(self, filter, page, **options): } values_hook = {"bk_biz_id": lambda value: value[1:-1].split(",")[1]} return self._list_instance_by_policy( - obj_model=self.model, + data_source=self.model, # id 同时作为查询id,cluster_type, config_name作为展示 value_list=["id", "cluster_type", "config_name"], key_mapping=key_mapping, diff --git a/dbm-ui/backend/iam_app/views/ticket_provider.py b/dbm-ui/backend/iam_app/views/ticket_provider.py index 793930729c..869e353d8e 100644 --- a/dbm-ui/backend/iam_app/views/ticket_provider.py +++ b/dbm-ui/backend/iam_app/views/ticket_provider.py @@ -10,18 +10,17 @@ """ import logging -from typing import Dict from django.db import models from backend.iam_app.dataclass.resources import ResourceEnum, ResourceMeta -from backend.iam_app.views.iam_provider import BaseResourceProvider, CommonProviderMixin +from backend.iam_app.views.iam_provider import BaseModelResourceProvider from backend.ticket.models import Ticket logger = logging.getLogger("root") -class TicketResourceProvider(BaseResourceProvider, CommonProviderMixin): +class TicketResourceProvider(BaseModelResourceProvider): """ flow资源的反向拉取类 """ @@ -29,25 +28,19 @@ class TicketResourceProvider(BaseResourceProvider, CommonProviderMixin): model: models.Model = Ticket resource_meta: ResourceMeta = ResourceEnum.TICKET - def get_bk_iam_path(self, instance_ids, *args, **kwargs) -> Dict: - return super().get_model_bk_iam_path(self.model, instance_ids, *args, **kwargs) - - def list_attr(self, **options): - return self._list_attr(id=self.resource_meta.attribute, display_name=self.resource_meta.attribute_display) - - def list_attr_value(self, filter, page, **options): - user_resource = self.list_user_resource() - return self._list_attr_value(self.resource_meta.attribute, user_resource, filter, page, **options) - def list_instance(self, filter, page, **options): - filter.model = self.model + filter.data_source = self.model filter.value_list = [self.resource_meta.lookup_field, *self.resource_meta.display_fields] - filter.keyword_field = "id" + filter.keyword_field = "id__icontains" return super().list_instance(filter, page, **options) def search_instance(self, filter, page, **options): return self.list_instance(filter, page, **options) + def fetch_instance_info(self, filter, **options): + filter.data_source = self.model + return super().fetch_instance_info(filter, **options) + def list_instance_by_policy(self, filter, page, **options): key_mapping = { f"{self.resource_meta.id}.id": "id", @@ -56,7 +49,7 @@ def list_instance_by_policy(self, filter, page, **options): } values_hook = {"bk_biz_id": lambda value: value[1:-1].split(",")[1]} return self._list_instance_by_policy( - obj_model=self.model, + data_source=self.model, # id作为id,id+ticket_type作为display name value_list=["id", "id"], key_mapping=key_mapping, diff --git a/dbm-ui/backend/iam_app/views/views.py b/dbm-ui/backend/iam_app/views/views.py index ddad798d08..84fa1b9586 100644 --- a/dbm-ui/backend/iam_app/views/views.py +++ b/dbm-ui/backend/iam_app/views/views.py @@ -30,8 +30,7 @@ class IAMViewSet(viewsets.SystemViewSet): serializer_class = None permission_classes = () - def _get_custom_permissions(self): - return [] + default_permission_class = [] @common_swagger_auto_schema(operation_summary=_("获取系统权限中心信息"), tags=[SWAGGER_TAG]) @action(methods=["GET"], detail=False) diff --git a/dbm-ui/backend/ticket/builders/mongodb/mongo_authorize.py b/dbm-ui/backend/ticket/builders/mongodb/mongo_authorize.py index c12056c06e..7a45660084 100644 --- a/dbm-ui/backend/ticket/builders/mongodb/mongo_authorize.py +++ b/dbm-ui/backend/ticket/builders/mongodb/mongo_authorize.py @@ -45,7 +45,7 @@ class MongoDBAuthorizeRulesFlowParamBuilder(builders.FlowParamBuilder): controller = MongoDBController.create_user -@builders.BuilderFactory.register(TicketType.MONGODB_AUTHORIZE) +@builders.BuilderFactory.register(TicketType.MONGODB_AUTHORIZE_RULES) class MongoDBAuthorizeRulesFlowBuilder(BaseMongoDBTicketFlowBuilder): serializer = MongoDBAuthorizeRulesSerializer inner_flow_builder = MongoDBAuthorizeRulesFlowParamBuilder @@ -60,7 +60,7 @@ def patch_ticket_detail(self): self.ticket.update_details(infos=data) -@builders.BuilderFactory.register(TicketType.MONGODB_EXCEL_AUTHORIZE) +@builders.BuilderFactory.register(TicketType.MONGODB_EXCEL_AUTHORIZE_RULES) class MySQLExcelAuthorizeRulesFlowBuilder(MongoDBAuthorizeRulesFlowBuilder): serializer = MongodbExcelAuthorizeRulesSerializer inner_flow_name = _("MongoDB Excel授权执行") diff --git a/dbm-ui/backend/ticket/constants.py b/dbm-ui/backend/ticket/constants.py index 30eb458e99..f0cc8948c0 100644 --- a/dbm-ui/backend/ticket/constants.py +++ b/dbm-ui/backend/ticket/constants.py @@ -187,7 +187,7 @@ def get_db_type_by_ticket(cls, ticket_type): MYSQL_SINGLE_RENAME_DATABASE = TicketEnumField("MYSQL_SINGLE_RENAME_DATABASE", _("MySQL 单节点DB重命名"), register_iam=False) MYSQL_HA_STANDARDIZE = TicketEnumField("MYSQL_HA_STANDARDIZE", _("TendbHA 标准化"), register_iam=False) MYSQL_HA_METADATA_IMPORT = TicketEnumField("MYSQL_HA_METADATA_IMPORT", _("TendbHA 元数据导入"), register_iam=False) - MYSQL_OPEN_AREA = TicketEnumField("MYSQL_OPEN_AREA", _("MySQL 开区"), _("克隆开区")) + MYSQL_OPEN_AREA = TicketEnumField("MYSQL_OPEN_AREA", _("MySQL 开区"), _("克隆开区"), register_iam=False) # SPIDER(TenDB Cluster) TENDBCLUSTER_OPEN_AREA = TicketEnumField("TENDBCLUSTER_OPEN_AREA", _("TenDB Cluster 开区"), _("克隆开区"), register_iam=False) @@ -375,8 +375,8 @@ def get_db_type_by_ticket(cls, ticket_type): MONGODB_DISABLE = TicketEnumField("MONGODB_DISABLE", _("MongoDB 集群禁用"), register_iam=False) MONGODB_DESTROY = TicketEnumField("MONGODB_DESTROY", _("MongoDB 集群删除"), _("集群管理")) MONGODB_CUTOFF = TicketEnumField("MONGODB_CUTOFF", _("MongoDB 整机替换"), _("集群维护")) - MONGODB_AUTHORIZE = TicketEnumField("MONGODB_AUTHORIZE", _("MongoDB 授权"), _("权限管理")) - MONGODB_EXCEL_AUTHORIZE = TicketEnumField("MONGODB_EXCEL_AUTHORIZE", _("MongoDB Excel授权"), _("权限管理")) + MONGODB_AUTHORIZE_RULES = TicketEnumField("MONGODB_AUTHORIZE_RULES", _("MongoDB 授权"), _("权限管理")) + MONGODB_EXCEL_AUTHORIZE_RULES = TicketEnumField("MONGODB_EXCEL_AUTHORIZE_RULES", _("MongoDB Excel授权"), _("权限管理")) MONGODB_RESTORE = TicketEnumField("MONGODB_RESTORE", _("MongoDB 定点回档"), _("集群维护")) MONGODB_TEMPORARY_DESTROY = TicketEnumField("MONGODB_TEMPORARY_DESTROY", _("MongoDB 临时集群销毁"), _("集群维护")) MONGODB_INSTALL_DBMON = TicketEnumField("MONGODB_INSTALL_DBMON", _("MongoDB 安装DBMon"), _("集群维护")) diff --git a/dbm-ui/backend/ticket/views.py b/dbm-ui/backend/ticket/views.py index ec3634b01a..c72bec9375 100644 --- a/dbm-ui/backend/ticket/views.py +++ b/dbm-ui/backend/ticket/views.py @@ -31,7 +31,7 @@ from backend.iam_app.dataclass.actions import ActionEnum from backend.iam_app.handlers.drf_perm.base import RejectPermission, ResourceActionPermission from backend.iam_app.handlers.drf_perm.cluster import ClusterDetailPermission, InstanceDetailPermission -from backend.iam_app.handlers.drf_perm.ticket import CreateTicketPermission +from backend.iam_app.handlers.drf_perm.ticket import create_ticket_permission from backend.iam_app.handlers.permission import Permission from backend.ticket.builders import BuilderFactory from backend.ticket.builders.common.base import InfluxdbTicketFlowBuilderPatchMixin, fetch_cluster_ids @@ -84,7 +84,7 @@ class TicketViewSet(viewsets.AuditedModelViewSet): def _get_custom_permissions(self): # 创建单据,关联单据类型的动作 if self.action == "create": - return [CreateTicketPermission(self.request.data["ticket_type"])] + return create_ticket_permission(self.request.data["ticket_type"]) # 查看集群/实例变更条件,关联集群详情 elif self.action == "get_cluster_operate_records": return [ClusterDetailPermission()]