From 6324b3579f185a017739db2bbe2f56b6eb80eddf Mon Sep 17 00:00:00 2001 From: jamesgetx Date: Fri, 8 Oct 2021 19:35:21 +0800 Subject: [PATCH 1/5] feat: project, cluster, namespace based and templateset on iam v3 --- .../bcs_web/iam/open_apis/authentication.py | 2 +- bcs-app/backend/iam/__init__.py | 14 ++ bcs-app/backend/iam/permissions/__init__.py | 14 ++ bcs-app/backend/iam/permissions/apply_url.py | 45 ++++ bcs-app/backend/iam/permissions/client.py | 100 ++++++++ bcs-app/backend/iam/permissions/decorators.py | 141 ++++++++++++ bcs-app/backend/iam/permissions/exceptions.py | 58 +++++ bcs-app/backend/iam/permissions/perm.py | 180 +++++++++++++++ bcs-app/backend/iam/permissions/request.py | 97 ++++++++ .../iam/permissions/resources/__init__.py | 14 ++ .../iam/permissions/resources/cluster.py | 117 ++++++++++ .../iam/permissions/resources/constants.py | 20 ++ .../iam/permissions/resources/namespace.py | 142 ++++++++++++ .../iam/permissions/resources/project.py | 82 +++++++ .../iam/permissions/resources/templateset.py | 124 ++++++++++ bcs-app/backend/tests/iam/__init__.py | 1 + bcs-app/backend/tests/iam/conftest.py | 65 ++++++ bcs-app/backend/tests/iam/fake_iam.py | 93 ++++++++ .../backend/tests/iam/permissions/__init__.py | 12 + .../backend/tests/iam/permissions/conftest.py | 32 +++ .../backend/tests/iam/permissions/roles.py | 40 ++++ .../tests/iam/permissions/test_cluster.py | 216 ++++++++++++++++++ .../tests/iam/permissions/test_namespace.py | 204 +++++++++++++++++ .../tests/iam/permissions/test_project.py | 96 ++++++++ .../tests/iam/permissions/test_templateset.py | 134 +++++++++++ bcs-app/backend/utils/error_codes.py | 4 - .../support-files/iam/0002_project_extra.json | 44 ++++ bcs-app/support-files/iam/0003_cluster.json | 156 +++++++++++++ bcs-app/support-files/iam/0004_namespace.json | 157 +++++++++++++ .../support-files/iam/0005_templateset.json | 0 .../support-files/iam/0006_action_group.json | 96 ++++++++ .../iam/0007_resource_creator_actions.json | 80 +++++++ 32 files changed, 2575 insertions(+), 5 deletions(-) create mode 100644 bcs-app/backend/iam/__init__.py create mode 100644 bcs-app/backend/iam/permissions/__init__.py create mode 100644 bcs-app/backend/iam/permissions/apply_url.py create mode 100644 bcs-app/backend/iam/permissions/client.py create mode 100644 bcs-app/backend/iam/permissions/decorators.py create mode 100644 bcs-app/backend/iam/permissions/exceptions.py create mode 100644 bcs-app/backend/iam/permissions/perm.py create mode 100644 bcs-app/backend/iam/permissions/request.py create mode 100644 bcs-app/backend/iam/permissions/resources/__init__.py create mode 100644 bcs-app/backend/iam/permissions/resources/cluster.py create mode 100644 bcs-app/backend/iam/permissions/resources/constants.py create mode 100644 bcs-app/backend/iam/permissions/resources/namespace.py create mode 100644 bcs-app/backend/iam/permissions/resources/project.py create mode 100644 bcs-app/backend/iam/permissions/resources/templateset.py create mode 100644 bcs-app/backend/tests/iam/__init__.py create mode 100644 bcs-app/backend/tests/iam/conftest.py create mode 100644 bcs-app/backend/tests/iam/fake_iam.py create mode 100644 bcs-app/backend/tests/iam/permissions/__init__.py create mode 100644 bcs-app/backend/tests/iam/permissions/conftest.py create mode 100644 bcs-app/backend/tests/iam/permissions/roles.py create mode 100644 bcs-app/backend/tests/iam/permissions/test_cluster.py create mode 100644 bcs-app/backend/tests/iam/permissions/test_namespace.py create mode 100644 bcs-app/backend/tests/iam/permissions/test_project.py create mode 100644 bcs-app/backend/tests/iam/permissions/test_templateset.py create mode 100644 bcs-app/support-files/iam/0002_project_extra.json create mode 100644 bcs-app/support-files/iam/0003_cluster.json create mode 100644 bcs-app/support-files/iam/0004_namespace.json create mode 100644 bcs-app/support-files/iam/0005_templateset.json create mode 100644 bcs-app/support-files/iam/0006_action_group.json create mode 100644 bcs-app/support-files/iam/0007_resource_creator_actions.json diff --git a/bcs-app/backend/bcs_web/iam/open_apis/authentication.py b/bcs-app/backend/bcs_web/iam/open_apis/authentication.py index 363bbcc51..c4812b51e 100644 --- a/bcs-app/backend/bcs_web/iam/open_apis/authentication.py +++ b/bcs-app/backend/bcs_web/iam/open_apis/authentication.py @@ -35,7 +35,7 @@ def authenticate(self, request): raise AuthenticationFailed(str(e)) return result - def authenticate_credentials(self, userid, password): + def authenticate_credentials(self, userid, password, request=None): if userid != "bk_iam": raise AuthenticationFailed("username is not bk_iam") diff --git a/bcs-app/backend/iam/__init__.py b/bcs-app/backend/iam/__init__.py new file mode 100644 index 000000000..08f8f6ab8 --- /dev/null +++ b/bcs-app/backend/iam/__init__.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" diff --git a/bcs-app/backend/iam/permissions/__init__.py b/bcs-app/backend/iam/permissions/__init__.py new file mode 100644 index 000000000..08f8f6ab8 --- /dev/null +++ b/bcs-app/backend/iam/permissions/__init__.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" diff --git a/bcs-app/backend/iam/permissions/apply_url.py b/bcs-app/backend/iam/permissions/apply_url.py new file mode 100644 index 000000000..44d63f6e7 --- /dev/null +++ b/bcs-app/backend/iam/permissions/apply_url.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import logging +from typing import List + +from django.conf import settings +from iam import IAM +from iam.apply import models + +from .request import ActionResourcesRequest + +logger = logging.getLogger(__name__) + + +class ApplyURLGenerator: + iam = IAM(settings.APP_ID, settings.APP_TOKEN, settings.BK_IAM_HOST, settings.BK_PAAS_INNER_HOST) + + @classmethod + def generate_apply_url(cls, username: str, action_request_list: List[ActionResourcesRequest]) -> str: + """ + 生成权限申请跳转 url + 参考 https://github.com/TencentBlueKing/iam-python-sdk/blob/master/docs/usage.md#14-获取无权限申请跳转url + """ + app = cls._make_application(action_request_list) + ok, _, url = cls.iam.get_apply_url(app, bk_username=username) + if not ok: + return settings.BK_IAM_APP_URL + return url + + @staticmethod + def _make_application(action_request_list: List[ActionResourcesRequest]) -> models.Application: + """为 generate_apply_url 方法生成 models.Application""" + return models.Application(settings.APP_ID, actions=[req.to_action() for req in action_request_list]) diff --git a/bcs-app/backend/iam/permissions/client.py b/bcs-app/backend/iam/permissions/client.py new file mode 100644 index 000000000..436fc342f --- /dev/null +++ b/bcs-app/backend/iam/permissions/client.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from typing import Dict, List, Optional + +from django.conf import settings +from iam import IAM, Action, MultiActionRequest, Request, Resource, Subject + +from .request import ResourceRequest + + +class IAMClient: + """提供基础的 iam client 方法封装""" + + iam = IAM(settings.APP_ID, settings.APP_TOKEN, settings.BK_IAM_HOST, settings.BK_PAAS_INNER_HOST) + + def resource_type_allowed(self, username: str, action_id: str, use_cache: bool = False) -> bool: + """ + 判断用户是否具备某个操作的权限 + note: 权限判断与资源实例无关,如创建某资源 + """ + request = self._make_request(username, action_id) + if not use_cache: + return self.iam.is_allowed(request) + return self.iam.is_allowed_with_cache(request) + + def resource_inst_allowed( + self, username: str, action_id: str, res_request: ResourceRequest, use_cache: bool = False + ) -> bool: + """ + 判断用户对某个资源实例是否具有指定操作的权限 + note: 权限判断与资源实例有关,如更新某个具体资源 + """ + request = self._make_request(username, action_id, resources=res_request.make_resources()) + if not use_cache: + return self.iam.is_allowed(request) + return self.iam.is_allowed_with_cache(request) + + def resource_type_multi_actions_allowed(self, username: str, action_ids: List[str]) -> Dict[str, bool]: + """ + 判断用户是否具备多个操作的权限 + note: 权限判断与资源实例无关,如创建某资源 + + :returns 示例 {'project_create': True} + """ + return {action_id: self.resource_type_allowed(username, action_id) for action_id in action_ids} + + def resource_inst_multi_actions_allowed( + self, username: str, action_ids: List[str], res_request: ResourceRequest + ) -> Dict[str, bool]: + """ + 判断用户对某个资源实例是否具有多个操作的权限. + note: 权限判断与资源实例有关,如更新某个具体资源 + + :returns 示例 {'project_view': True, 'project_edit': False} + """ + actions = [Action(action_id) for action_id in action_ids] + request = MultiActionRequest( + settings.APP_ID, Subject("user", username), actions, res_request.make_resources(), None + ) + return self.iam.resource_multi_actions_allowed(request) + + def batch_resource_multi_actions_allowed( + self, username: str, action_ids: List[str], res_request: ResourceRequest + ) -> Dict[str, Dict[str, bool]]: + """ + 判断用户对某些资源是否具有多个指定操作的权限 + note: 当前sdk仅支持同类型的资源 + + :returns 示例 {'0ad86c25363f4ef8adcb7ac67a483837': {'project_view': True, 'project_edit': False}} + """ + actions = [Action(action_id) for action_id in action_ids] + request = MultiActionRequest(settings.APP_ID, Subject("user", username), actions, [], None) + resources_list = [[res] for res in res_request.make_resources()] + return self.iam.batch_resource_multi_actions_allowed(request, resources_list) + + def _make_request(self, username: str, action_id: str, resources: Optional[List[Resource]] = None) -> Request: + return Request( + settings.APP_ID, + Subject("user", username), + Action(action_id), + resources, + None, + ) + + def _grant_resource_creator_actions(self, username: str, data: Dict): + """ + 用于创建资源时,注册用户对该资源的关联操作权限. + note: 具体的关联操作见权限模型的 resource_creator_actions 字段 + """ + return self.iam._client.grant_resource_creator_actions(None, username, data) diff --git a/bcs-app/backend/iam/permissions/decorators.py b/bcs-app/backend/iam/permissions/decorators.py new file mode 100644 index 000000000..4d0d34361 --- /dev/null +++ b/bcs-app/backend/iam/permissions/decorators.py @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import importlib +import logging +from abc import ABCMeta, abstractmethod + +import wrapt + +from .exceptions import PermissionDeniedError +from .perm import PermCtx +from .perm import Permission as PermPermission + +logger = logging.getLogger(__name__) + + +class RelatedPermission(metaclass=ABCMeta): + """ + 用于资源 Permission 类的方法装饰, 目的是支持 related_actions. + + 如 related_project_perm 和 related_cluster_perm 装饰器的用法: + + class ClusterPermission(Permission): + + resource_type: str = 'cluster' + resource_request_cls: Type[ResourceRequest] = ClusterRequest + + @related_project_perm(method_name='can_view') + def can_view(self, perm_ctx: ClusterPermCtx, raise_exception: bool = True, just_raise: bool = False) -> bool: + return self.can_action(perm_ctx, ClusterAction.VIEW, raise_exception, just_raise) + + @related_cluster_perm(method_name='can_view') + def can_manage(self, perm_ctx: ClusterPermCtx, raise_exception: bool = True, just_raise: bool = False) -> bool: + return self.can_action(perm_ctx, ClusterAction.MANAGE, raise_exception, just_raise) + + """ + + module_name: str # 资源模块名 如 cluster, project + + def __init__(self, method_name: str): + """ + :param method_name: 权限类的 can_{action} 方法名,用于校验用户是否具有对应的操作权限 + """ + self.method_name = method_name + + def _gen_perm_obj(self) -> PermPermission: + """获取权限类实例,如 project.ProjectPermission""" + p_module_name = __name__[: __name__.rfind(".")] + try: + return getattr( + importlib.import_module(f'{p_module_name}.resources.{self.module_name}'), + f'{self.module_name.capitalize()}Permission', + )() + except (ModuleNotFoundError, AttributeError) as e: + logger.error('_gen_perm_obj error: %s', e) + + @wrapt.decorator + def __call__(self, wrapped, instance, args, kwargs): + self.perm_obj = self._gen_perm_obj() + + perm_ctx = self._convert_perm_ctx(instance, args, kwargs) + + try: + is_allowed = wrapped(*args, **kwargs) + except PermissionDeniedError as e: + # 按照权限中心的建议,无论关联资源操作是否有权限,统一按照无权限返回,目的是生成最终的 apply_url + perm_ctx.force_raise = True + try: + getattr(self.perm_obj, self.method_name)(perm_ctx) + except PermissionDeniedError as err: + raise PermissionDeniedError( + f'{e.message}; {err.message}', + username=perm_ctx.username, + action_request_list=e.action_request_list + err.action_request_list, + ) + else: + # 无权限,并且没有抛出 PermissionDeniedError, 说明 raise_exception = False + if not is_allowed: + return is_allowed + + logger.debug(f'continue to verify {self.method_name} {self.module_name} permission...') + + # 有权限时,继续校验关联操作的权限 + raise_exception = kwargs.get('raise_exception', True) + return getattr(self.perm_obj, self.method_name)(perm_ctx, raise_exception=raise_exception) + + @abstractmethod + def _convert_perm_ctx(self, instance, args, kwargs) -> PermCtx: + """将被装饰的方法中的 perm_ctx 转换成 perm_obj.method_name 需要的 perm_ctx""" + + @property + def action_id(self) -> str: + return f'{self.perm_obj.resource_type}_{self.method_name[4:]}' + + +class Permission: + """鉴权装饰器基类,用于装饰函数或者方法""" + + module_name: str # 资源模块名 如 cluster, project + + def __init__(self, method_name: str): + """ + :param method_name: 权限类的 can_{action} 方法名,用于校验用户是否具有对应的操作权限 + """ + self.method_name = method_name + + def _gen_perm_obj(self) -> PermPermission: + """获取权限类实例,如 project.ProjectPermission""" + p_module_name = __name__[: __name__.rfind(".")] + try: + return getattr( + importlib.import_module(f'{p_module_name}.resources.{self.module_name}'), + f'{self.module_name.capitalize()}Permission', + )() + except (ModuleNotFoundError, AttributeError) as e: + logger.error('_gen_perm_obj error: %s', e) + + @wrapt.decorator + def __call__(self, wrapped, instance, args, kwargs): + + self.perm_obj = self._gen_perm_obj() + + if len(args) <= 0: + raise TypeError('missing PermCtx instance argument') + if not isinstance(args[0], PermCtx): + raise TypeError('missing ProjectPermCtx instance argument') + + getattr(self.perm_obj, self.method_name)(args[0]) + + return wrapped(*args, **kwargs) diff --git a/bcs-app/backend/iam/permissions/exceptions.py b/bcs-app/backend/iam/permissions/exceptions.py new file mode 100644 index 000000000..bafe684a5 --- /dev/null +++ b/bcs-app/backend/iam/permissions/exceptions.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from typing import Dict, List, Optional + +from .apply_url import ApplyURLGenerator +from .request import ActionResourcesRequest + + +class PermissionDeniedError(Exception): + message = 'Permission denied' + code = 40300 + + def __init__( + self, + message: str = '', + username: str = '', + action_request_list: Optional[List[ActionResourcesRequest]] = None, + ): + """ + :param message: 异常信息 + :param username: 无权限的用户名, 用于生成 apply_url + :param action_request_list: 用于生成 apply_url + """ + + if message: + self.message = message + + self.username = username + self.action_request_list = action_request_list + + @property + def data(self) -> Dict: + return { + 'apply_url': ApplyURLGenerator.generate_apply_url(self.username, self.action_request_list), + 'action_list': [ + {'resource_type': action_request.resource_type, 'action_id': action_request.action_id} + for action_request in self.action_request_list + ], + } + + def __str__(self): + return f'{self.code}: {self.message}' + + +class AttrValidationError(Exception): + """属性字段校验异常""" diff --git a/bcs-app/backend/iam/permissions/perm.py b/bcs-app/backend/iam/permissions/perm.py new file mode 100644 index 000000000..16150838a --- /dev/null +++ b/bcs-app/backend/iam/permissions/perm.py @@ -0,0 +1,180 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import logging +from abc import ABC, abstractmethod +from typing import List, Optional, Type + +import attr +from django.conf import settings + +from .client import IAMClient +from .exceptions import AttrValidationError, PermissionDeniedError +from .request import ActionResourcesRequest, IAMResource, ResourceRequest + +logger = logging.getLogger(__name__) + + +@attr.dataclass +class PermCtx: + """ + 权限参数上下文 + note: 由于 force_raise 默认值的原因,其子类属性必须设置默认值 + """ + + username: str + force_raise: bool = False # 如果为 True, 表示不做权限校验,直接以无权限方式抛出异常 + + def validate_resource_id(self): + """校验资源实例 ID. 如果校验不过,抛出 AttrValidationError 异常""" + if not self.resource_id: + raise AttrValidationError(f'missing valid resource_id') + + @property + def resource_id(self) -> str: + return '' + + +class Permission(ABC, IAMClient): + """ + 对接 IAM 的权限基类 + """ + + resource_type: str = '' + resource_request_cls: Type[ResourceRequest] = ResourceRequest + parent_res_perm: Optional['Permission'] = None # 父级资源的权限类对象 + + def can_action_with_view( + self, perm_ctx: PermCtx, action_id: str, view_action_id: str, raise_exception: bool, use_cache: bool = False + ) -> bool: + """ + 校验用户的 action_id 权限时,级联校验对资源的查看(view_action_id)权限 + + :param perm_ctx: 权限校验的上下文 + :param action_id: 资源操作 ID + :param raise_exception: 无权限时,是否抛出异常 + :param use_cache: 是否使用本地缓存 (缓存时间 1 min) 校验权限。用于非敏感操作鉴权,比如 view 操作 + """ + if action_id == view_action_id: + raise ValueError('parameter action_id and view_action_id are equal') + + if not view_action_id.endswith('_view'): + raise ValueError("parameter view_action_id must ends with '_view'") + + try: + is_allowed = self.can_action(perm_ctx, action_id, raise_exception, use_cache) + except PermissionDeniedError as e: + # 按照权限中心的建议,无论关联资源操作是否有权限,统一按照无权限返回,目的是生成最终的 apply_url + perm_ctx.force_raise = True + try: + self.can_action(perm_ctx, view_action_id, raise_exception, use_cache) + except PermissionDeniedError as err: + raise PermissionDeniedError( + f'{e.message}; {err.message}', + username=perm_ctx.username, + action_request_list=e.action_request_list + err.action_request_list, + ) + else: + # action_id 无权限,并且没有抛出 PermissionDeniedError, 说明 raise_exception = False + if not is_allowed: + return is_allowed + # action_id 有权限时,继续校验 view_action_id 权限 + logger.debug(f'continue to verify {view_action_id} permission...') + return self.can_action(perm_ctx, view_action_id, raise_exception, use_cache) + + def can_action(self, perm_ctx: PermCtx, action_id: str, raise_exception: bool, use_cache: bool = False) -> bool: + """ + 校验用户的 action_id 权限 + + :param perm_ctx: 权限校验的上下文 + :param action_id: 资源操作 ID + :param raise_exception: 无权限时,是否抛出异常 + :param use_cache: 是否使用本地缓存 (缓存时间 1 min) 校验权限。用于非敏感操作鉴权,比如 view 操作 + """ + if perm_ctx.force_raise: + self._raise_permission_denied_error(perm_ctx, action_id) + + is_allowed = self._can_action(perm_ctx, action_id, use_cache) + + if raise_exception and not is_allowed: + self._raise_permission_denied_error(perm_ctx, action_id) + + return is_allowed + + def grant_resource_creator_actions(self, username: str, resource_id: str, resource_name: str): + """ + 用于创建资源时,注册用户对该资源的关联操作权限. + note: 具体的关联操作见权限模型的 resource_creator_actions 字段 + TODO 需要针对层级资源重构 + """ + data = { + "type": self.resource_type, + "id": resource_id, + "name": resource_name, + "system": settings.APP_ID, + "creator": username, + } + return self.iam._client.grant_resource_creator_actions(None, username, data) + + def make_res_request(self, res_id: str, perm_ctx: PermCtx) -> ResourceRequest: + """创建当前资源 request""" + return self.resource_request_cls(res_id) + + def has_parent_resource(self) -> bool: + return self.parent_res_perm is not None + + @abstractmethod + def get_parent_chain(self, perm_ctx: PermCtx) -> List[IAMResource]: + """从 ctx 中获取 parent_chain""" + + def _can_action(self, perm_ctx: PermCtx, action_id: str, use_cache: bool = False) -> bool: + res_id = self.get_resource_id(perm_ctx) + + if res_id: # 与当前资源实例相关 + res_request = self.make_res_request(res_id, perm_ctx) + return self.resource_inst_allowed(perm_ctx.username, action_id, res_request, use_cache) + + # 与当前资源实例无关, 并且无关联上级资源, 按资源实例无关处理 + if not self.has_parent_resource(): + return self.resource_type_allowed(perm_ctx.username, action_id, use_cache) + + # 有关联上级资源 + res_request = self.parent_res_perm.make_res_request( + res_id=self.parent_res_perm.get_resource_id(perm_ctx), perm_ctx=perm_ctx + ) + return self.resource_inst_allowed(perm_ctx.username, action_id, res_request, use_cache) + + def _raise_permission_denied_error(self, perm_ctx: PermCtx, action_id: str): + res_id = self.get_resource_id(perm_ctx) + resources = None + resource_type = self.resource_type + parent_chain = None + + if res_id: + resources = [res_id] + parent_chain = self.get_parent_chain(perm_ctx) + elif self.has_parent_resource(): + resource_type = self.parent_res_perm.resource_type + resources = [self.parent_res_perm.get_resource_id(perm_ctx)] + parent_chain = self.parent_res_perm.get_parent_chain(perm_ctx) + + raise PermissionDeniedError( + f"no {action_id} permission", + username=perm_ctx.username, + action_request_list=[ActionResourcesRequest(action_id, resource_type, resources, parent_chain)], + ) + + @abstractmethod + def get_resource_id(self, perm_ctx: PermCtx) -> Optional[str]: + """从 ctx 中获取当前资源对应的 id""" diff --git a/bcs-app/backend/iam/permissions/request.py b/bcs-app/backend/iam/permissions/request.py new file mode 100644 index 000000000..44bcf7fdf --- /dev/null +++ b/bcs-app/backend/iam/permissions/request.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from collections import namedtuple +from typing import Dict, List, Optional, Union + +from django.conf import settings +from iam import Resource +from iam.apply import models + + +class ResourceRequest: + resource_type: str = '' + attr: Optional[Dict] = None + + def __init__(self, res: Union[List[str], str], **attr_kwargs): + """ + :param res: 单个资源 ID 或资源 ID 列表 + :param attr_kwargs: 用于替换 attr 中可能需要 format 的值 + """ + self.res = [str(res_id) for res_id in res] if isinstance(res, list) else str(res) + self.attr_kwargs = dict(**attr_kwargs) + self._validate_attr_kwargs() + + def make_resources(self) -> List[Resource]: + if isinstance(self.res, str): + return [Resource(settings.APP_ID, self.resource_type, self.res, self._make_attribute(self.res))] + + return [ + Resource(settings.APP_ID, self.resource_type, res_id, self._make_attribute(res_id)) for res_id in self.res + ] + + def _validate_attr_kwargs(self): + """如果校验不通过,抛出 AttrValidateError 异常""" + return + + def _make_attribute(self, res_id: str) -> Dict: + return {} + + +IAMResource = namedtuple('IAMResource', 'resource_type resource_id') + + +class ActionResourcesRequest: + """ + 操作资源请求. + note: resources 是由资源 ID 构成的列表. 为 None 时,表示资源无关. + 资源实例相关时,resources 表示的资源必须具有相同的父实例。以命名空间为例,它们必须是同项目同集群下 + """ + + def __init__( + self, + action_id: str, + resource_type: Optional[str] = None, + resources: Optional[List[str]] = None, + parent_chain: List[IAMResource] = None, + ): + """ + :param action_id: 操作 ID + :param resource_type: 资源类型 + :param resources: 资源 ID 列表 + :param parent_chain: 按照父类层级排序(父->子) [(resource_type, resource_id), ] + """ + self.action_id = action_id + self.resource_type = resource_type + self.resources = resources + self.parent_chain = parent_chain + + def to_action(self) -> Union[models.ActionWithResources, models.ActionWithoutResources]: + # 资源实例相关 + if self.resources: + parent_chain_node = self._to_parent_chain_node() + instances = [ + models.ResourceInstance(parent_chain_node + [models.ResourceNode(self.resource_type, res_id, res_id)]) + for res_id in self.resources + ] + related_resource_type = models.RelatedResourceType(settings.APP_ID, self.resource_type, instances) + return models.ActionWithResources(self.action_id, [related_resource_type]) + + # 资源实例无关 + return models.ActionWithoutResources(self.action_id) + + def _to_parent_chain_node(self) -> List[models.ResourceNode]: + if self.parent_chain: + return [models.ResourceNode(p.resource_type, p.resource_id, p.resource_id) for p in self.parent_chain] + return [] diff --git a/bcs-app/backend/iam/permissions/resources/__init__.py b/bcs-app/backend/iam/permissions/resources/__init__.py new file mode 100644 index 000000000..08f8f6ab8 --- /dev/null +++ b/bcs-app/backend/iam/permissions/resources/__init__.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" diff --git a/bcs-app/backend/iam/permissions/resources/cluster.py b/bcs-app/backend/iam/permissions/resources/cluster.py new file mode 100644 index 000000000..5df347c4f --- /dev/null +++ b/bcs-app/backend/iam/permissions/resources/cluster.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from typing import Dict, List, Optional, Type + +import attr + +from backend.iam.permissions import decorators +from backend.iam.permissions.exceptions import AttrValidationError +from backend.iam.permissions.perm import PermCtx, Permission, ResourceRequest +from backend.iam.permissions.request import IAMResource +from backend.packages.blue_krill.data_types.enum import EnumField, StructuredEnum + +from .constants import ResourceType +from .project import ProjectPermission, related_project_perm + + +class ClusterAction(str, StructuredEnum): + CREATE = EnumField('cluster_create', label='cluster_create') + VIEW = EnumField('cluster_view', label='cluster_view') + MANAGE = EnumField('cluster_manage', label='cluster_manage') + DELETE = EnumField('cluster_delete', label='cluster_delete') + USE = EnumField('cluster_use', label='cluster_use') + + +@attr.dataclass +class ClusterPermCtx(PermCtx): + project_id: str = '' + cluster_id: Optional[str] = None + + @property + def resource_id(self) -> str: + return self.cluster_id + + +class ClusterRequest(ResourceRequest): + resource_type: str = ResourceType.Cluster + attr = {'_bk_iam_path_': f'/project,{{project_id}}/'} + + def _make_attribute(self, res_id: str) -> Dict: + return {'_bk_iam_path_': self.attr['_bk_iam_path_'].format(project_id=self.attr_kwargs['project_id'])} + + def _validate_attr_kwargs(self): + if not self.attr_kwargs.get('project_id'): + raise AttrValidationError('missing project_id or project_id is invalid') + + +class related_cluster_perm(decorators.RelatedPermission): + + module_name: str = ResourceType.Cluster + + def _convert_perm_ctx(self, instance, args, kwargs) -> PermCtx: + """仅支持第一个参数是 PermCtx 子类实例""" + if len(args) <= 0: + raise TypeError('missing ClusterPermCtx instance argument') + if isinstance(args[0], PermCtx): + return ClusterPermCtx( + username=args[0].username, project_id=args[0].project_id, cluster_id=args[0].cluster_id + ) + else: + raise TypeError('missing ClusterPermCtx instance argument') + + +class cluster_perm(decorators.Permission): + module_name: str = ResourceType.Cluster + + +class ClusterPermission(Permission): + """集群权限""" + + resource_type: str = ResourceType.Cluster + resource_request_cls: Type[ResourceRequest] = ClusterRequest + parent_res_perm = ProjectPermission() + + @related_project_perm(method_name='can_view') + def can_create(self, perm_ctx: ClusterPermCtx, raise_exception: bool = True) -> bool: + return self.can_action(perm_ctx, ClusterAction.CREATE, raise_exception) + + @related_project_perm(method_name='can_view') + def can_view(self, perm_ctx: ClusterPermCtx, raise_exception: bool = True) -> bool: + perm_ctx.validate_resource_id() + return self.can_action(perm_ctx, ClusterAction.VIEW, raise_exception) + + @related_cluster_perm(method_name='can_view') + def can_manage(self, perm_ctx: ClusterPermCtx, raise_exception: bool = True) -> bool: + perm_ctx.validate_resource_id() + return self.can_action(perm_ctx, ClusterAction.MANAGE, raise_exception) + + @related_cluster_perm(method_name='can_view') + def can_delete(self, perm_ctx: ClusterPermCtx, raise_exception: bool = True) -> bool: + perm_ctx.validate_resource_id() + return self.can_action(perm_ctx, ClusterAction.DELETE, raise_exception) + + @related_cluster_perm(method_name='can_view') + def can_use(self, perm_ctx: ClusterPermCtx, raise_exception: bool = True) -> bool: + perm_ctx.validate_resource_id() + return self.can_action(perm_ctx, ClusterAction.USE, raise_exception) + + def make_res_request(self, res_id: str, perm_ctx: ClusterPermCtx) -> ResourceRequest: + return self.resource_request_cls(res_id, project_id=perm_ctx.project_id) + + def get_parent_chain(self, perm_ctx: ClusterPermCtx) -> List[IAMResource]: + return [IAMResource(ResourceType.Project, perm_ctx.project_id)] + + def get_resource_id(self, perm_ctx: ClusterPermCtx) -> Optional[str]: + return perm_ctx.cluster_id diff --git a/bcs-app/backend/iam/permissions/resources/constants.py b/bcs-app/backend/iam/permissions/resources/constants.py new file mode 100644 index 000000000..086f02617 --- /dev/null +++ b/bcs-app/backend/iam/permissions/resources/constants.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from backend.packages.blue_krill.data_types.enum import StructuredEnum + + +class ResourceType(str, StructuredEnum): + Project = 'project' + Cluster = 'cluster' + Namespace = 'namespace' + Templateset = "templateset" diff --git a/bcs-app/backend/iam/permissions/resources/namespace.py b/bcs-app/backend/iam/permissions/resources/namespace.py new file mode 100644 index 000000000..5b31d4294 --- /dev/null +++ b/bcs-app/backend/iam/permissions/resources/namespace.py @@ -0,0 +1,142 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from typing import Dict, List, Optional, Type + +import attr + +from backend.iam.permissions import decorators +from backend.iam.permissions.exceptions import AttrValidationError +from backend.iam.permissions.perm import PermCtx, Permission +from backend.iam.permissions.request import IAMResource, ResourceRequest +from backend.packages.blue_krill.data_types.enum import EnumField, StructuredEnum + +from .cluster import ClusterPermission, related_cluster_perm +from .constants import ResourceType + + +def calc_iam_ns_id(cluster_id: str, name: Optional[str] = None, max_length: int = 32) -> Optional[str]: + """ + 计算出注册到权限中心的命名空间ID,具备唯一性. + TODO 下一个 PR 中实现,方便 review + """ + return "default" + + +class NamespaceAction(str, StructuredEnum): + CREATE = EnumField('namespace_create', label='namespace_create') + VIEW = EnumField('namespace_view', label='namespace_view') + UPDATE = EnumField('namespace_update', label='namespace_update') + DELETE = EnumField('namespace_delete', label='namespace_delete') + USE = EnumField('namespace_use', label='namespace_use') + + +@attr.dataclass +class NamespacePermCtx(PermCtx): + project_id: str = '' + cluster_id: str = '' + name: Optional[str] = None # 命名空间名 + iam_ns_id: Optional[str] = None # 注册到权限中心的命名空间ID + + def __attrs_post_init__(self): + """权限中心的 resource_id 长度限制为32位""" + self.iam_ns_id = calc_iam_ns_id(self.cluster_id, self.name) + + @property + def resource_id(self) -> str: + return self.iam_ns_id + + +class NamespaceRequest(ResourceRequest): + resource_type: str = ResourceType.Namespace + attr = {'_bk_iam_path_': f'/project,{{project_id}}/cluster,{{cluster_id}}/'} + + def _make_attribute(self, res_id: str) -> Dict: + return { + '_bk_iam_path_': self.attr['_bk_iam_path_'].format( + project_id=self.attr_kwargs['project_id'], cluster_id=self.attr_kwargs['cluster_id'] + ) + } + + def _validate_attr_kwargs(self): + if not self.attr_kwargs.get('project_id'): + raise AttrValidationError('missing project_id or project_id is invalid') + + if not self.attr_kwargs.get('cluster_id'): + raise AttrValidationError('missing cluster_id or cluster_id is invalid') + + +class related_namespace_perm(decorators.RelatedPermission): + + module_name: str = ResourceType.Namespace + + def _convert_perm_ctx(self, instance, args, kwargs) -> PermCtx: + """仅支持第一个参数是 PermCtx 子类实例""" + if len(args) <= 0: + raise TypeError('missing NamespacePermCtx instance argument') + if isinstance(args[0], PermCtx): + return NamespacePermCtx( + username=args[0].username, + project_id=args[0].project_id, + cluster_id=args[0].cluster_id, + name=args[0].name, + ) + else: + raise TypeError('missing NamespacePermCtx instance argument') + + +class namespace_perm(decorators.Permission): + module_name: str = ResourceType.Namespace + + +class NamespacePermission(Permission): + """命名空间权限""" + + resource_type: str = ResourceType.Namespace + resource_request_cls: Type[ResourceRequest] = NamespaceRequest + parent_res_perm = ClusterPermission() + + @related_cluster_perm(method_name='can_use') + def can_create(self, perm_ctx: NamespacePermCtx, raise_exception: bool = True) -> bool: + return self.can_action(perm_ctx, NamespaceAction.CREATE, raise_exception) + + @related_cluster_perm(method_name='can_view') + def can_view(self, perm_ctx: NamespacePermCtx, raise_exception: bool = True) -> bool: + perm_ctx.validate_resource_id() + return self.can_action(perm_ctx, NamespaceAction.VIEW, raise_exception) + + @related_cluster_perm(method_name='can_use') + def can_update(self, perm_ctx: NamespacePermCtx, raise_exception: bool = True) -> bool: + perm_ctx.validate_resource_id() + return self.can_action_with_view(perm_ctx, NamespaceAction.UPDATE, NamespaceAction.VIEW, raise_exception) + + @related_cluster_perm(method_name='can_use') + def can_delete(self, perm_ctx: NamespacePermCtx, raise_exception: bool = True) -> bool: + perm_ctx.validate_resource_id() + return self.can_action_with_view(perm_ctx, NamespaceAction.DELETE, NamespaceAction.VIEW, raise_exception) + + @related_cluster_perm(method_name='can_use') + def can_use(self, perm_ctx: NamespacePermCtx, raise_exception: bool = True) -> bool: + perm_ctx.validate_resource_id() + return self.can_action_with_view(perm_ctx, NamespaceAction.USE, NamespaceAction.VIEW, raise_exception) + + def make_res_request(self, res_id: str, perm_ctx: NamespacePermCtx) -> ResourceRequest: + return self.resource_request_cls(res_id, project_id=perm_ctx.project_id, cluster_id=perm_ctx.cluster_id) + + def get_parent_chain(self, perm_ctx: NamespacePermCtx) -> List[IAMResource]: + return [ + IAMResource(ResourceType.Project, perm_ctx.project_id), + IAMResource(ResourceType.Cluster, perm_ctx.cluster_id), + ] + + def get_resource_id(self, perm_ctx: NamespacePermCtx) -> Optional[str]: + return perm_ctx.iam_ns_id diff --git a/bcs-app/backend/iam/permissions/resources/project.py b/bcs-app/backend/iam/permissions/resources/project.py new file mode 100644 index 000000000..66a3d34b1 --- /dev/null +++ b/bcs-app/backend/iam/permissions/resources/project.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from typing import List, Optional, Type + +import attr + +from backend.iam.permissions import decorators +from backend.iam.permissions.perm import PermCtx, Permission, ResourceRequest +from backend.iam.permissions.request import IAMResource +from backend.packages.blue_krill.data_types.enum import EnumField, StructuredEnum + +from .constants import ResourceType + + +class ProjectAction(str, StructuredEnum): + CREATE = EnumField('project_create', label='project_create') + VIEW = EnumField('project_view', label='project_view') + EDIT = EnumField('project_edit', label='project_edit') + + +class ProjectRequest(ResourceRequest): + resource_type: str = ResourceType.Project + + +@attr.dataclass +class ProjectPermCtx(PermCtx): + project_id: Optional[str] = None + + @property + def resource_id(self) -> str: + return self.project_id + + +class related_project_perm(decorators.RelatedPermission): + + module_name: str = ResourceType.Project + + def _convert_perm_ctx(self, instance, args, kwargs) -> PermCtx: + """仅支持第一个参数是 PermCtx 子类实例""" + if len(args) <= 0: + raise TypeError('missing ProjectPermCtx instance argument') + if isinstance(args[0], PermCtx): + return ProjectPermCtx(username=args[0].username, project_id=args[0].project_id) + else: + raise TypeError('missing ProjectPermCtx instance argument') + + +class ProjectPermission(Permission): + """项目权限""" + + resource_type: str = ResourceType.Project + resource_request_cls: Type[ResourceRequest] = ProjectRequest + + def can_create(self, perm_ctx: ProjectPermCtx, raise_exception: bool = True) -> bool: + return self.can_action(perm_ctx, ProjectAction.CREATE, raise_exception) + + def can_view(self, perm_ctx: ProjectPermCtx, raise_exception: bool = True) -> bool: + perm_ctx.validate_resource_id() + return self.can_action(perm_ctx, ProjectAction.VIEW, raise_exception) + + @related_project_perm(method_name='can_view') + def can_edit(self, perm_ctx: ProjectPermCtx, raise_exception: bool = True) -> bool: + perm_ctx.validate_resource_id() + return self.can_action(perm_ctx, ProjectAction.EDIT, raise_exception) + + def get_parent_chain(self, perm_ctx: ProjectPermCtx) -> List[IAMResource]: + return [] + + def get_resource_id(self, perm_ctx: ProjectPermCtx) -> Optional[str]: + return perm_ctx.project_id diff --git a/bcs-app/backend/iam/permissions/resources/templateset.py b/bcs-app/backend/iam/permissions/resources/templateset.py new file mode 100644 index 000000000..3bf1e54b1 --- /dev/null +++ b/bcs-app/backend/iam/permissions/resources/templateset.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from typing import Dict, List, Optional, Type, Union + +import attr + +from backend.iam.permissions import decorators +from backend.iam.permissions.exceptions import AttrValidationError +from backend.iam.permissions.perm import PermCtx, Permission +from backend.iam.permissions.request import IAMResource, ResourceRequest +from backend.packages.blue_krill.data_types.enum import EnumField, StructuredEnum + +from .constants import ResourceType +from .project import ProjectPermission, related_project_perm + + +class TemplatesetAction(str, StructuredEnum): + CREATE = EnumField("templateset_create", label="templateset_create") + VIEW = EnumField("templateset_view", label="templateset_view") + UPDATE = EnumField("templateset_update", label="templateset_update") + DELETE = EnumField("templateset_delete", label="templateset_delete") + INSTANTIATE = EnumField("templateset_instantiate", label="templateset_instantiate") + COPY = EnumField("templateset_copy", label="templateset_copy") + + +@attr.dataclass +class TemplatesetPermCtx(PermCtx): + project_id: str = '' + template_id: Union[str, int, None] = None + + def __attrs_post_init__(self): + if self.template_id: + self.template_id = str(self.template_id) + + @property + def resource_id(self) -> str: + return self.template_id + + +class TemplatesetRequest(ResourceRequest): + resource_type: str = ResourceType.Templateset + attr = {'_bk_iam_path_': f'/project,{{project_id}}/'} + + def _make_attribute(self, res_id: str) -> Dict: + return {'_bk_iam_path_': self.attr['_bk_iam_path_'].format(project_id=self.attr_kwargs['project_id'])} + + def _validate_attr_kwargs(self): + if not self.attr_kwargs.get('project_id'): + raise AttrValidationError('missing project_id or project_id is invalid') + + +class related_templateset_perm(decorators.RelatedPermission): + module_name: str = ResourceType.Templateset + + def _convert_perm_ctx(self, instance, args, kwargs) -> PermCtx: + """仅支持第一个参数是 PermCtx 子类实例""" + if len(args) <= 0: + raise TypeError('missing TemplatesetPermCtx instance a rgument') + if isinstance(args[0], PermCtx): + return TemplatesetPermCtx( + username=args[0].username, project_id=args[0].project_id, template_id=args[0].template_id + ) + else: + raise TypeError('missing TemplatesetPermCtx instance argument') + + +class templateset_perm(decorators.Permission): + module_name: str = ResourceType.Templateset + + +class TemplatesetPermission(Permission): + """模板集权限""" + + resource_type: str = ResourceType.Templateset + resource_request_cls: Type[ResourceRequest] = TemplatesetRequest + parent_res_perm = ProjectPermission() + + @related_project_perm(method_name="can_view") + def can_create(self, perm_ctx: TemplatesetPermCtx, raise_exception: bool = True) -> bool: + return self.can_action(perm_ctx, TemplatesetAction.CREATE, raise_exception) + + @related_project_perm(method_name="can_view") + def can_view(self, perm_ctx: TemplatesetPermCtx, raise_exception: bool = True) -> bool: + perm_ctx.validate_resource_id() + return self.can_action(perm_ctx, TemplatesetAction.VIEW, raise_exception) + + @related_templateset_perm(method_name='can_view') + def can_copy(self, perm_ctx: TemplatesetPermCtx, raise_exception: bool = True) -> bool: + perm_ctx.validate_resource_id() + return self.can_action(perm_ctx, TemplatesetAction.COPY, raise_exception) + + @related_templateset_perm(method_name='can_view') + def can_update(self, perm_ctx: TemplatesetPermCtx, raise_exception: bool = True) -> bool: + perm_ctx.validate_resource_id() + return self.can_action(perm_ctx, TemplatesetAction.UPDATE, raise_exception) + + @related_templateset_perm(method_name='can_view') + def can_delete(self, perm_ctx: TemplatesetPermCtx, raise_exception: bool = True) -> bool: + perm_ctx.validate_resource_id() + return self.can_action(perm_ctx, TemplatesetAction.DELETE, raise_exception) + + @related_templateset_perm(method_name='can_view') + def can_instantiate(self, perm_ctx: TemplatesetPermCtx, raise_exception: bool = True) -> bool: + perm_ctx.validate_resource_id() + return self.can_action(perm_ctx, TemplatesetAction.INSTANTIATE, raise_exception) + + def make_res_request(self, res_id: str, perm_ctx: TemplatesetPermCtx) -> ResourceRequest: + return self.resource_request_cls(res_id, project_id=perm_ctx.project_id) + + def get_parent_chain(self, perm_ctx: TemplatesetPermCtx) -> List[IAMResource]: + return [IAMResource(ResourceType.Project, perm_ctx.project_id)] + + def get_resource_id(self, perm_ctx: TemplatesetPermCtx) -> Optional[str]: + return perm_ctx.template_id diff --git a/bcs-app/backend/tests/iam/__init__.py b/bcs-app/backend/tests/iam/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/bcs-app/backend/tests/iam/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/bcs-app/backend/tests/iam/conftest.py b/bcs-app/backend/tests/iam/conftest.py new file mode 100644 index 000000000..f411532e6 --- /dev/null +++ b/bcs-app/backend/tests/iam/conftest.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from typing import List +from unittest import mock + +import pytest + +from backend.iam.permissions.apply_url import ApplyURLGenerator +from backend.iam.permissions.request import ActionResourcesRequest +from backend.iam.permissions.resources.cluster import ClusterPermission +from backend.iam.permissions.resources.namespace import NamespacePermission +from backend.iam.permissions.resources.project import ProjectPermission + +from .fake_iam import FakeClusterPermission, FakeNamespacePermission, FakeProjectPermission + + +def generate_apply_url(username: str, action_request_list: List[ActionResourcesRequest]) -> List[str]: + expect = [] + for req in action_request_list: + resources = '' + if req.resources: + resources = ''.join(req.resources) + + parent_chain = '' + if req.parent_chain: + parent_chain = ''.join([f'{item.resource_type}/{item.resource_id}' for item in req.parent_chain]) + expect.append(f'{req.resource_type}:{req.action_id}:{resources}:{parent_chain}') + + return expect + + +@pytest.fixture(autouse=True) +def patch_generate_apply_url(): + with mock.patch.object(ApplyURLGenerator, 'generate_apply_url', new=generate_apply_url): + yield + + +@pytest.fixture +def project_permission_obj(): + patcher = mock.patch.object(ProjectPermission, '__bases__', (FakeProjectPermission,)) + with patcher: + patcher.is_local = True # 标注为本地属性,__exit__ 的时候恢复成 patcher.temp_original + yield ProjectPermission() + + +@pytest.fixture +def namespace_permission_obj(): + cluster_patcher = mock.patch.object(ClusterPermission, '__bases__', (FakeClusterPermission,)) + project_patcher = mock.patch.object(ProjectPermission, '__bases__', (FakeProjectPermission,)) + namespace_patcher = mock.patch.object(NamespacePermission, '__bases__', (FakeNamespacePermission,)) + with cluster_patcher, project_patcher, namespace_patcher: + cluster_patcher.is_local = True # 标注为本地属性,__exit__ 的时候恢复成 patcher.temp_original + project_patcher.is_local = True + namespace_patcher.is_local = True + yield NamespacePermission() diff --git a/bcs-app/backend/tests/iam/fake_iam.py b/bcs-app/backend/tests/iam/fake_iam.py new file mode 100644 index 000000000..f36e62dcf --- /dev/null +++ b/bcs-app/backend/tests/iam/fake_iam.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from iam import Request + +from backend.iam.permissions.perm import Permission +from backend.iam.permissions.resources.project import ProjectAction + +from .permissions import roles + + +class FakeProjectIAM: + def is_allowed(self, request: Request) -> bool: + if request.subject.id in [roles.ADMIN_USER, roles.PROJECT_CLUSTER_USER, roles.PROJECT_NO_CLUSTER_USER]: + return True + + if request.subject.id == roles.PROJECT_NO_VIEW_USER: + if request.action.id == ProjectAction.VIEW: + return False + return True + + return False + + def is_allowed_with_cache(self, request: Request) -> bool: + return self.is_allowed(request) + + +class FakeProjectPermission(Permission): + iam = FakeProjectIAM() + + +class FakeClusterIAM: + def is_allowed(self, request: Request) -> bool: + if request.subject.id in [ + roles.ADMIN_USER, + roles.CLUSTER_USER, + roles.PROJECT_CLUSTER_USER, + roles.CLUSTER_NO_PROJECT_USER, + ]: + return True + return False + + def is_allowed_with_cache(self, request: Request) -> bool: + return self.is_allowed(request) + + +class FakeClusterPermission(Permission): + iam = FakeClusterIAM() + + +class FakeNamespaceIAM: + def is_allowed(self, request: Request) -> bool: + if request.subject.id in [roles.ADMIN_USER, roles.NAMESPACE_NO_CLUSTER_PROJECT_USER]: + return True + return False + + def is_allowed_with_cache(self, request: Request) -> bool: + return self.is_allowed(request) + + +class FakeNamespacePermission(Permission): + iam = FakeNamespaceIAM() + + +class FakeTemplatesetIAM: + def __init__(self, *args, **kwargs): + """""" + + def is_allowed(self, request: Request) -> bool: + if request.subject.id in [ + roles.ADMIN_USER, + roles.TEMPLATESET_USER, + roles.PROJECT_TEMPLATESET_USER, + roles.TEMPLATESET_NO_PROJECT_USER, + ]: + return True + return False + + def is_allowed_with_cache(self, request: Request) -> bool: + return self.is_allowed(request) + + +class FakeTemplatesetPermission(Permission): + iam = FakeTemplatesetIAM() diff --git a/bcs-app/backend/tests/iam/permissions/__init__.py b/bcs-app/backend/tests/iam/permissions/__init__.py new file mode 100644 index 000000000..02a6c5cd3 --- /dev/null +++ b/bcs-app/backend/tests/iam/permissions/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" diff --git a/bcs-app/backend/tests/iam/permissions/conftest.py b/bcs-app/backend/tests/iam/permissions/conftest.py new file mode 100644 index 000000000..b326c790d --- /dev/null +++ b/bcs-app/backend/tests/iam/permissions/conftest.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import pytest + +from backend.tests.testing_utils.base import generate_random_string + + +@pytest.fixture +def namespace_name(): + return generate_random_string(16) + + +@pytest.fixture +def template_id(): + """生成一个随机模板集 ID""" + return generate_random_string(32) + + +@pytest.fixture +def template_name(): + """生成一个随机模板集 name""" + return generate_random_string(16) diff --git a/bcs-app/backend/tests/iam/permissions/roles.py b/bcs-app/backend/tests/iam/permissions/roles.py new file mode 100644 index 000000000..c87f072ee --- /dev/null +++ b/bcs-app/backend/tests/iam/permissions/roles.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" + +ADMIN_USER = 'admin' + +ANONYMOUS_USER = 'anonymous_user' + +PROJECT_NO_VIEW_USER = 'project_no_view_user' + +NO_PROJECT_USER = 'no_project_user' + +CLUSTER_USER = 'cluster_user' + +PROJECT_CLUSTER_USER = 'project_cluster_user' + +PROJECT_NO_CLUSTER_USER = 'project_no_cluster_user' + +CLUSTER_NO_PROJECT_USER = 'cluster_no_project_user' + +NAMESPACE_NO_CLUSTER_PROJECT_USER = 'namespace_no_cluster_project_user' + +TEMPLATESET_USER = "templateset_user" + +PROJECT_TEMPLATESET_USER = 'project_templateset_user' + +PROJECT_NO_TEMPLATESET_USER = 'project_no_templateset_user' + +TEMPLATESET_NO_PROJECT_USER = 'templateset_no_project_user' diff --git a/bcs-app/backend/tests/iam/permissions/test_cluster.py b/bcs-app/backend/tests/iam/permissions/test_cluster.py new file mode 100644 index 000000000..1deb7f210 --- /dev/null +++ b/bcs-app/backend/tests/iam/permissions/test_cluster.py @@ -0,0 +1,216 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from unittest import mock + +import pytest + +from backend.iam.permissions.exceptions import PermissionDeniedError +from backend.iam.permissions.request import ActionResourcesRequest, IAMResource +from backend.iam.permissions.resources.cluster import ClusterAction, ClusterPermCtx, ClusterPermission, cluster_perm +from backend.iam.permissions.resources.constants import ResourceType +from backend.iam.permissions.resources.project import ProjectAction, ProjectPermission +from backend.tests.iam.conftest import generate_apply_url + +from ..fake_iam import FakeClusterPermission, FakeProjectPermission +from . import roles + + +@pytest.fixture +def cluster_permission_obj(): + cluster_patcher = mock.patch.object(ClusterPermission, '__bases__', (FakeClusterPermission,)) + project_patcher = mock.patch.object(ProjectPermission, '__bases__', (FakeProjectPermission,)) + with cluster_patcher, project_patcher: + cluster_patcher.is_local = True # 标注为本地属性,__exit__ 的时候恢复成 patcher.temp_original + project_patcher.is_local = True + yield ClusterPermission() + + +class TestClusterPermission: + """ + 集群资源权限 + note: 仅测试 cluster_create 和 cluster_view 两种代表性的权限,其他操作权限逻辑重复 + """ + + def test_can_create(self, cluster_permission_obj, project_id, cluster_id): + """测试场景:有集群创建权限(同时有项目查看权限)""" + perm_ctx = ClusterPermCtx(username=roles.ADMIN_USER, project_id=project_id) + assert cluster_permission_obj.can_create(perm_ctx) + + def test_can_not_create(self, cluster_permission_obj, project_id, cluster_id): + """测试场景:无集群创建权限(同时无项目查看权限)""" + perm_ctx = ClusterPermCtx(username=roles.ANONYMOUS_USER, project_id=project_id) + with pytest.raises(PermissionDeniedError) as exec: + cluster_permission_obj.can_create(perm_ctx) + assert exec.value.data['apply_url'] == generate_apply_url( + roles.ANONYMOUS_USER, + [ + ActionResourcesRequest( + ClusterAction.CREATE, + resource_type=ProjectPermission.resource_type, + resources=[project_id], + ), + ActionResourcesRequest( + ProjectAction.VIEW, resource_type=ProjectPermission.resource_type, resources=[project_id] + ), + ], + ) + + def test_can_view(self, cluster_permission_obj, project_id, cluster_id): + """测试场景:有集群查看权限(同时有项目查看权限)""" + perm_ctx = ClusterPermCtx(username=roles.ADMIN_USER, project_id=project_id, cluster_id=cluster_id) + assert cluster_permission_obj.can_view(perm_ctx) + + def test_can_not_view_but_project(self, cluster_permission_obj, project_id, cluster_id): + """测试场景:无集群查看权限(同时有项目查看权限)""" + self._test_can_not_view( + roles.PROJECT_NO_CLUSTER_USER, + cluster_permission_obj, + project_id, + cluster_id, + expected_action_list=[ + ActionResourcesRequest( + ClusterAction.VIEW, + resource_type=cluster_permission_obj.resource_type, + resources=[cluster_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + ProjectAction.VIEW, resource_type=ProjectPermission.resource_type, resources=[project_id] + ), + ], + ) + + def test_can_view_but_no_project(self, cluster_permission_obj, project_id, cluster_id): + """测试场景:有集群查看权限(同时无项目查看权限)""" + self._test_can_not_view( + roles.CLUSTER_NO_PROJECT_USER, + cluster_permission_obj, + project_id, + cluster_id, + expected_action_list=[ + ActionResourcesRequest( + ProjectAction.VIEW, + resource_type=ProjectPermission.resource_type, + resources=[project_id], + ) + ], + ) + + def test_can_not_view(self, cluster_permission_obj, project_id, cluster_id): + """测试场景:无集群查看权限(同时无项目查看权限)""" + self._test_can_not_view( + roles.ANONYMOUS_USER, + cluster_permission_obj, + project_id, + cluster_id, + expected_action_list=[ + ActionResourcesRequest( + ClusterAction.VIEW, + resource_type=cluster_permission_obj.resource_type, + resources=[cluster_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + ProjectAction.VIEW, + resource_type=ProjectPermission.resource_type, + resources=[project_id], + ), + ], + ) + + def _test_can_not_view(self, username, cluster_permission_obj, project_id, cluster_id, expected_action_list): + perm_ctx = ClusterPermCtx(username=username, project_id=project_id, cluster_id=cluster_id) + with pytest.raises(PermissionDeniedError) as exec: + cluster_permission_obj.can_view(perm_ctx) + assert exec.value.data['apply_url'] == generate_apply_url(username, expected_action_list) + + def test_can_not_manage(self, cluster_permission_obj, project_id, cluster_id): + """测试场景:无集群管理权限(同时无项目查看权限)""" + username = roles.ANONYMOUS_USER + perm_ctx = ClusterPermCtx(username=username, project_id=project_id, cluster_id=cluster_id) + with pytest.raises(PermissionDeniedError) as exec: + cluster_permission_obj.can_manage(perm_ctx) + assert exec.value.data['apply_url'] == generate_apply_url( + username, + [ + ActionResourcesRequest( + ClusterAction.MANAGE, + resource_type=ClusterPermission.resource_type, + resources=[cluster_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + ClusterAction.VIEW, + resource_type=ClusterPermission.resource_type, + resources=[cluster_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + ProjectAction.VIEW, resource_type=ProjectPermission.resource_type, resources=[project_id] + ), + ], + ) + + def test_can_manage_but_no_project(self, cluster_permission_obj, project_id, cluster_id): + """测试场景:有集群管理权限(同时无项目权限)""" + username = roles.CLUSTER_NO_PROJECT_USER + perm_ctx = ClusterPermCtx(username=username, project_id=project_id, cluster_id=cluster_id) + with pytest.raises(PermissionDeniedError) as exec: + cluster_permission_obj.can_manage(perm_ctx) + assert exec.value.data['apply_url'] == generate_apply_url( + username, + [ + ActionResourcesRequest( + ProjectAction.VIEW, resource_type=ProjectPermission.resource_type, resources=[project_id] + ) + ], + ) + + +@cluster_perm(method_name='can_manage') +def manage_cluster(perm_ctx: ClusterPermCtx): + """""" + + +class TestClusterPermDecorator: + def test_can_manage(self, cluster_permission_obj, project_id, cluster_id): + """测试场景:有集群管理权限(同时有项目查看权限)""" + perm_ctx = ClusterPermCtx(username=roles.ADMIN_USER, project_id=project_id, cluster_id=cluster_id) + manage_cluster(perm_ctx) + + def test_can_not_manage(self, cluster_permission_obj, project_id, cluster_id): + """测试场景:无集群管理权限(同时无项目查看权限)""" + username = roles.ANONYMOUS_USER + perm_ctx = ClusterPermCtx(username=username, project_id=project_id, cluster_id=cluster_id) + with pytest.raises(PermissionDeniedError) as exec: + manage_cluster(perm_ctx) + assert exec.value.data['apply_url'] == generate_apply_url( + username, + [ + ActionResourcesRequest( + ClusterAction.MANAGE, + resource_type=ClusterPermission.resource_type, + resources=[cluster_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + ClusterAction.VIEW, + resource_type=ClusterPermission.resource_type, + resources=[cluster_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + ProjectAction.VIEW, resource_type=ProjectPermission.resource_type, resources=[project_id] + ), + ], + ) diff --git a/bcs-app/backend/tests/iam/permissions/test_namespace.py b/bcs-app/backend/tests/iam/permissions/test_namespace.py new file mode 100644 index 000000000..9fb9f5b6b --- /dev/null +++ b/bcs-app/backend/tests/iam/permissions/test_namespace.py @@ -0,0 +1,204 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import pytest + +from backend.iam.permissions.exceptions import PermissionDeniedError +from backend.iam.permissions.request import ActionResourcesRequest, IAMResource +from backend.iam.permissions.resources.cluster import ClusterAction, ClusterPermission +from backend.iam.permissions.resources.constants import ResourceType +from backend.iam.permissions.resources.namespace import ( + NamespaceAction, + NamespacePermCtx, + NamespacePermission, + namespace_perm, +) +from backend.iam.permissions.resources.project import ProjectAction, ProjectPermission +from backend.tests.iam.conftest import generate_apply_url + +from . import roles + + +class TestNamespacePermission: + """ + 命名空间资源权限 + note: 仅测试 namespace_use 这一代表性的权限,其他操作权限逻辑重复 + """ + + def test_can_create_but_no_cluster_project(self, namespace_permission_obj, project_id, cluster_id): + """测试场景:有命名空间创建权限(同时无集群使用/查看权限、无项目查看权限)""" + username = roles.NAMESPACE_NO_CLUSTER_PROJECT_USER + perm_ctx = NamespacePermCtx(username=username, project_id=project_id, cluster_id=cluster_id) + with pytest.raises(PermissionDeniedError) as exec: + namespace_permission_obj.can_create(perm_ctx) + assert exec.value.data['apply_url'] == generate_apply_url( + username, + [ + ActionResourcesRequest( + ClusterAction.USE, + resource_type=ClusterPermission.resource_type, + resources=[cluster_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + ClusterAction.VIEW, + resource_type=ClusterPermission.resource_type, + resources=[cluster_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + ProjectAction.VIEW, resource_type=ProjectPermission.resource_type, resources=[project_id] + ), + ], + ) + + def test_can_not_use(self, namespace_permission_obj, project_id, cluster_id, namespace_name): + """测试场景:无命名空间使用/查看权限(同时无集群和项目权限)""" + username = roles.ANONYMOUS_USER + perm_ctx = NamespacePermCtx( + username=username, project_id=project_id, cluster_id=cluster_id, name=namespace_name + ) + with pytest.raises(PermissionDeniedError) as exec: + namespace_permission_obj.can_use(perm_ctx) + assert exec.value.data['apply_url'] == generate_apply_url( + username, + [ + ActionResourcesRequest( + NamespaceAction.USE, + resource_type=NamespacePermission.resource_type, + resources=[perm_ctx.iam_ns_id], + parent_chain=[ + IAMResource(ResourceType.Project, project_id), + IAMResource(ResourceType.Cluster, cluster_id), + ], + ), + ActionResourcesRequest( + NamespaceAction.VIEW, + resource_type=NamespacePermission.resource_type, + resources=[perm_ctx.iam_ns_id], + parent_chain=[ + IAMResource(ResourceType.Project, project_id), + IAMResource(ResourceType.Cluster, cluster_id), + ], + ), + ActionResourcesRequest( + ClusterAction.USE, + resource_type=ClusterPermission.resource_type, + resources=[cluster_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + ClusterAction.VIEW, + resource_type=ClusterPermission.resource_type, + resources=[cluster_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + ProjectAction.VIEW, resource_type=ProjectPermission.resource_type, resources=[project_id] + ), + ], + ) + + def test_can_use_but_no_cluster_project(self, namespace_permission_obj, project_id, cluster_id, namespace_name): + """测试场景: 有命名空间使用权限(同时无集群和项目权限)""" + username = roles.NAMESPACE_NO_CLUSTER_PROJECT_USER + perm_ctx = NamespacePermCtx( + username=username, project_id=project_id, cluster_id=cluster_id, name=namespace_name + ) + + # 不抛出异常 + assert not namespace_permission_obj.can_use(perm_ctx, raise_exception=False) + + # 抛出异常 + with pytest.raises(PermissionDeniedError) as exec: + namespace_permission_obj.can_use(perm_ctx) + assert exec.value.data['apply_url'] == generate_apply_url( + username, + [ + ActionResourcesRequest( + ClusterAction.USE, + resource_type=ClusterPermission.resource_type, + resources=[cluster_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + ClusterAction.VIEW, + resource_type=ClusterPermission.resource_type, + resources=[cluster_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + ProjectAction.VIEW, resource_type=ProjectPermission.resource_type, resources=[project_id] + ), + ], + ) + + +@namespace_perm(method_name='can_use') +def helm_install(perm_ctx: NamespacePermCtx): + """helm install 到某个命名空间""" + + +class TestNamespacePermDecorator: + def test_can_use(self, namespace_permission_obj, project_id, cluster_id, namespace_name): + """测试场景:有命名空间使用权限(同时有集群和项目权限)""" + perm_ctx = NamespacePermCtx( + username=roles.ADMIN_USER, project_id=project_id, cluster_id=cluster_id, name=namespace_name + ) + helm_install(perm_ctx) + + def test_can_not_use(self, namespace_permission_obj, project_id, cluster_id, namespace_name): + """测试场景:无命名空间使用权限(同时无集群和项目权限)""" + username = roles.ANONYMOUS_USER + perm_ctx = NamespacePermCtx( + username=username, project_id=project_id, cluster_id=cluster_id, name=namespace_name + ) + with pytest.raises(PermissionDeniedError) as exec: + helm_install(perm_ctx) + assert exec.value.data['apply_url'] == generate_apply_url( + username, + [ + ActionResourcesRequest( + NamespaceAction.USE, + resource_type=NamespacePermission.resource_type, + resources=[perm_ctx.iam_ns_id], + parent_chain=[ + IAMResource(ResourceType.Project, project_id), + IAMResource(ResourceType.Cluster, cluster_id), + ], + ), + ActionResourcesRequest( + NamespaceAction.VIEW, + resource_type=NamespacePermission.resource_type, + resources=[perm_ctx.iam_ns_id], + parent_chain=[ + IAMResource(ResourceType.Project, project_id), + IAMResource(ResourceType.Cluster, cluster_id), + ], + ), + ActionResourcesRequest( + ClusterAction.USE, + resource_type=ClusterPermission.resource_type, + resources=[cluster_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + ClusterAction.VIEW, + resource_type=ClusterPermission.resource_type, + resources=[cluster_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + ProjectAction.VIEW, resource_type=ProjectPermission.resource_type, resources=[project_id] + ), + ], + ) diff --git a/bcs-app/backend/tests/iam/permissions/test_project.py b/bcs-app/backend/tests/iam/permissions/test_project.py new file mode 100644 index 000000000..a18387a84 --- /dev/null +++ b/bcs-app/backend/tests/iam/permissions/test_project.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import pytest + +from backend.iam.permissions.exceptions import PermissionDeniedError +from backend.iam.permissions.perm import ActionResourcesRequest +from backend.iam.permissions.resources.project import ProjectAction, ProjectPermCtx +from backend.tests.iam.conftest import generate_apply_url + +from . import roles + + +class TestProjectPermission: + """ + 测试项目资源权限 + note: 仅测试 project_create 和 project_view 两种代表性的权限,其他操作权限逻辑重复 + """ + + def test_can_create(self, project_permission_obj): + """测试场景:有项目创建权限""" + perm_ctx = ProjectPermCtx(username=roles.ADMIN_USER) + assert project_permission_obj.can_create(perm_ctx) + + def test_can_not_create(self, project_permission_obj): + """测试场景:无项目创建权限""" + + # 无权限不抛出异常 + username = roles.NO_PROJECT_USER + perm_ctx = ProjectPermCtx(username=username) + assert not project_permission_obj.can_create(perm_ctx, raise_exception=False) + + # 无权限抛出异常 + with pytest.raises(PermissionDeniedError) as exec: + project_permission_obj.can_create(perm_ctx) + assert exec.value.code == PermissionDeniedError.code + assert exec.value.data['apply_url'] == generate_apply_url( + username, + action_request_list=[ + ActionResourcesRequest(ProjectAction.CREATE, resource_type=project_permission_obj.resource_type) + ], + ) + + def test_can_view(self, project_permission_obj, project_id): + """测试场景:有项目查看权限""" + perm_ctx = ProjectPermCtx(username=roles.ADMIN_USER, project_id=project_id) + assert project_permission_obj.can_view(perm_ctx) + + def test_can_not_view(self, project_permission_obj, project_id): + """测试场景:无项目查看权限""" + + # 无权限不抛出异常 + username = roles.NO_PROJECT_USER + perm_ctx = ProjectPermCtx(username=username, project_id=project_id) + assert not project_permission_obj.can_view(perm_ctx, raise_exception=False) + + # 无权限抛出异常 + with pytest.raises(PermissionDeniedError) as exec: + project_permission_obj.can_view(perm_ctx) + assert exec.value.code == PermissionDeniedError.code + assert exec.value.data['apply_url'] == generate_apply_url( + username, + [ + ActionResourcesRequest( + ProjectAction.VIEW, + resource_type=project_permission_obj.resource_type, + resources=[project_id], + ) + ], + ) + + def test_can_edit_not_view(self, project_permission_obj, project_id): + """测试场景:有项目编辑权限(同时无项目查看权限)""" + username = roles.PROJECT_NO_VIEW_USER + perm_ctx = ProjectPermCtx(username=username, project_id=project_id) + with pytest.raises(PermissionDeniedError) as exec: + project_permission_obj.can_edit(perm_ctx) + assert exec.value.data['apply_url'] == generate_apply_url( + username, + [ + ActionResourcesRequest( + ProjectAction.VIEW, + resource_type=project_permission_obj.resource_type, + resources=[project_id], + ) + ], + ) diff --git a/bcs-app/backend/tests/iam/permissions/test_templateset.py b/bcs-app/backend/tests/iam/permissions/test_templateset.py new file mode 100644 index 000000000..d57fd4119 --- /dev/null +++ b/bcs-app/backend/tests/iam/permissions/test_templateset.py @@ -0,0 +1,134 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from unittest import mock + +import pytest + +from backend.iam.permissions.exceptions import PermissionDeniedError +from backend.iam.permissions.request import ActionResourcesRequest, IAMResource +from backend.iam.permissions.resources.constants import ResourceType +from backend.iam.permissions.resources.project import ProjectAction, ProjectPermission +from backend.iam.permissions.resources.templateset import ( + TemplatesetAction, + TemplatesetPermCtx, + TemplatesetPermission, + templateset_perm, +) +from backend.tests.iam.conftest import generate_apply_url + +from ..fake_iam import FakeProjectPermission, FakeTemplatesetPermission +from . import roles + + +@pytest.fixture +def templateset_permission_obj(): + templateset_patcher = mock.patch.object(TemplatesetPermission, '__bases__', (FakeTemplatesetPermission,)) + project_patcher = mock.patch.object(ProjectPermission, '__bases__', (FakeProjectPermission,)) + with templateset_patcher, project_patcher: + templateset_patcher.is_local = True # 标注为本地属性,__exit__ 的时候恢复成 patcher.temp_original + project_patcher.is_local = True + yield TemplatesetPermission() + + +class TestTemplatesetPermission: + """ + 模板集资源权限 + note: 仅测试 templateset_instantiate,其他操作权限逻辑重复(和TestClusterPermission类似) + """ + + def test_can_instantiate(self, templateset_permission_obj, project_id, template_id): + """测试场景:有模板集实例化权限""" + username = roles.ADMIN_USER + perm_ctx = TemplatesetPermCtx(username=username, project_id=project_id, template_id=template_id) + assert templateset_permission_obj.can_instantiate(perm_ctx) + + def test_can_instantiate_but_no_project(self, templateset_permission_obj, project_id, template_id): + """测试场景:有模板集实例化权限(同时无项目查看权限)""" + username = roles.TEMPLATESET_NO_PROJECT_USER + perm_ctx = TemplatesetPermCtx(username=username, project_id=project_id, template_id=template_id) + with pytest.raises(PermissionDeniedError) as exec: + templateset_permission_obj.can_instantiate(perm_ctx) + assert exec.value.data['apply_url'] == generate_apply_url( + username, + [ + ActionResourcesRequest( + ProjectAction.VIEW, resource_type=ProjectPermission.resource_type, resources=[project_id] + ) + ], + ) + + def test_can_not_instantiate(self, templateset_permission_obj, project_id, template_id): + """测试场景:无模板集实例化权限(同时无项目查看权限)""" + username = roles.ANONYMOUS_USER + perm_ctx = TemplatesetPermCtx(username=username, project_id=project_id, template_id=template_id) + with pytest.raises(PermissionDeniedError) as exec: + templateset_permission_obj.can_instantiate(perm_ctx) + assert exec.value.data['apply_url'] == generate_apply_url( + username, + [ + ActionResourcesRequest( + TemplatesetAction.INSTANTIATE, + resource_type=TemplatesetPermission.resource_type, + resources=[template_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + TemplatesetAction.VIEW, + resource_type=TemplatesetPermission.resource_type, + resources=[template_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + ProjectAction.VIEW, resource_type=ProjectPermission.resource_type, resources=[project_id] + ), + ], + ) + + +@templateset_perm(method_name='can_instantiate') +def instantiate_templateset(perm_ctx: TemplatesetPermCtx): + """""" + + +class TestTemplatesetPermDecorator: + def test_can_instantiate(self, templateset_permission_obj, project_id, template_id): + """测试场景:有模板集实例化权限""" + perm_ctx = TemplatesetPermCtx(username=roles.ADMIN_USER, project_id=project_id, template_id=template_id) + instantiate_templateset(perm_ctx) + + def test_can_not_instantiate(self, templateset_permission_obj, project_id, template_id): + """测试场景:无模板集实例化权限(同时无项目查看权限)""" + username = roles.ANONYMOUS_USER + perm_ctx = TemplatesetPermCtx(username=username, project_id=project_id, template_id=template_id) + with pytest.raises(PermissionDeniedError) as exec: + instantiate_templateset(perm_ctx) + assert exec.value.data['apply_url'] == generate_apply_url( + username, + [ + ActionResourcesRequest( + TemplatesetAction.INSTANTIATE, + resource_type=TemplatesetPermission.resource_type, + resources=[template_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + TemplatesetAction.VIEW, + resource_type=TemplatesetPermission.resource_type, + resources=[template_id], + parent_chain=[IAMResource(ResourceType.Project, project_id)], + ), + ActionResourcesRequest( + ProjectAction.VIEW, resource_type=ProjectPermission.resource_type, resources=[project_id] + ), + ], + ) diff --git a/bcs-app/backend/utils/error_codes.py b/bcs-app/backend/utils/error_codes.py index 90afc1069..76a600e57 100644 --- a/bcs-app/backend/utils/error_codes.py +++ b/bcs-app/backend/utils/error_codes.py @@ -70,10 +70,6 @@ class ErrorCodes: code_num=40101, status_code=status.HTTP_401_UNAUTHORIZED, ) - # 没有权限,最好使用drf permission class检查权限 - Forbidden = ErrorCode(_('没有使用权限'), code_num=40301, status_code=status.HTTP_403_FORBIDDEN) - # 权限中心错误码 - IAMCheckFailed = ErrorCode(_('权限校验失败'), code_num=40302, status_code=status.HTTP_403_FORBIDDEN) # 资源未找到 ResNotFoundError = ErrorCode(_('资源未找到'), code_num=40400, status_code=status.HTTP_404_NOT_FOUND) diff --git a/bcs-app/support-files/iam/0002_project_extra.json b/bcs-app/support-files/iam/0002_project_extra.json new file mode 100644 index 000000000..fa2c39535 --- /dev/null +++ b/bcs-app/support-files/iam/0002_project_extra.json @@ -0,0 +1,44 @@ +{ + "system_id": "bk_bcs_app", + "operations": [ + { + "operation": "upsert_action", + "data": { + "id": "project_create", + "name": "项目创建", + "name_en": "create project", + "description": "用户创建项目", + "description_en": "create project", + "type": "create", + "related_resource_types": [], + "related_actions": [], + "version": 1 + } + }, + { + "operation": "upsert_action", + "data": { + "id": "project_delete", + "name": "项目删除", + "name_en": "delete project", + "description": "用户删除项目", + "description_en": "user delete project", + "type": "delete", + "related_resource_types": [{ + "system_id": "bk_bcs_app", + "id": "project", + "name_alias": "", + "name_alias_en": "", + "selection_mode": "instance", + "related_instance_selections": [ + { + "system_id": "bk_bcs_app", + "id": "project_list" + } + ] + }], + "version": 1 + } + } + ] +} diff --git a/bcs-app/support-files/iam/0003_cluster.json b/bcs-app/support-files/iam/0003_cluster.json new file mode 100644 index 000000000..d3e996903 --- /dev/null +++ b/bcs-app/support-files/iam/0003_cluster.json @@ -0,0 +1,156 @@ +{ + "system_id": "bk_bcs_app", + "operations": [ + { + "operation": "upsert_resource_type", + "data": { + "id": "cluster", + "name": "集群", + "name_en": "cluster", + "description": "集群", + "description_en": "cluster", + "provider_config": { + "path": "/o/bk_bcs_app/apis/iam/v1/clusters/" + }, + "version": 1 + } + }, + { + "operation": "upsert_instance_selection", + "data": { + "id": "cluster_list", + "name": "集群", + "name_en": "cluster", + "resource_type_chain": [ + {"system_id": "bk_bcs_app", "id": "project"}, + {"system_id": "bk_bcs_app", "id": "cluster"} + ] + } + }, + { + "operation": "upsert_action", + "data": { + "id": "cluster_create", + "name": "集群创建", + "name_en": "create cluster", + "description": "用户创建集群", + "description_en": "user create cluster", + "type": "create", + "related_resource_types": [{ + "system_id": "bk_bcs_app", + "id": "project", + "related_instance_selections": [{ + "system_id": "bk_bcs_app", + "id": "project_list" + }] + }], + "related_actions": ["project_view"], + "version": 1 + } + }, + { + "operation": "upsert_action", + "data": { + "id": "cluster_view", + "name": "集群查看", + "name_en": "view cluster", + "description": "用户查看集群", + "description_en": "user view cluster", + "type": "view", + "related_resource_types": [{ + "system_id": "bk_bcs_app", + "id": "cluster", + "name_alias": "", + "name_alias_en": "", + "selection_mode": "instance", + "related_instance_selections": [ + { + "system_id": "bk_bcs_app", + "id": "cluster_list" + } + ] + }], + "related_actions": ["project_view"], + "version": 1 + } + }, + { + "operation": "upsert_action", + "data": { + "id": "cluster_manage", + "name": "集群管理", + "name_en": "manage cluster", + "description": "用户管理集群(包括添加/删除节点)", + "description_en": "user manage cluster(include add/delete node)", + "type": "manage", + "related_resource_types": [{ + "system_id": "bk_bcs_app", + "id": "cluster", + "name_alias": "", + "name_alias_en": "", + "selection_mode": "instance", + "related_instance_selections": [ + { + "system_id": "bk_bcs_app", + "id": "cluster_list" + } + ] + }], + "related_actions": ["project_view", "cluster_view"], + "version": 1 + } + }, + { + "operation": "upsert_action", + "data": { + "id": "cluster_delete", + "name": "集群删除", + "name_en": "delete cluster", + "description": "用户删除集群", + "description_en": "user delete cluster", + "type": "delete", + "related_resource_types": [{ + "system_id": "bk_bcs_app", + "id": "cluster", + "name_alias": "", + "name_alias_en": "", + "selection_mode": "instance", + "related_instance_selections": [ + { + "system_id": "bk_bcs_app", + "id": "cluster_list" + } + ] + }], + "related_actions": ["project_view", "cluster_view"], + "version": 1 + } + }, + { + "operation": "upsert_action", + "data": { + "id": "cluster_use", + "name": "集群使用", + "name_en": "use cluster", + "description": "用户使用集群(如创建命名空间等)", + "description_en": "user use cluster(e.g. create namespace)", + "type": "use", + "related_resource_types": [{ + "system_id": "bk_bcs_app", + "id": "cluster", + "name_alias": "", + "name_alias_en": "", + "selection_mode": "instance", + "related_instance_selections": [ + { + "system_id": "bk_bcs_app", + "id": "cluster_list" + } + ] + }], + "related_actions": ["project_view", "cluster_view"], + "version": 1 + } + } + ] +} diff --git a/bcs-app/support-files/iam/0004_namespace.json b/bcs-app/support-files/iam/0004_namespace.json new file mode 100644 index 000000000..22e9e1e88 --- /dev/null +++ b/bcs-app/support-files/iam/0004_namespace.json @@ -0,0 +1,157 @@ +{ + "system_id": "bk_bcs_app", + "operations": [ + { + "operation": "upsert_resource_type", + "data": { + "id": "namespace", + "name": "命名空间", + "name_en": "namespace", + "description": "命名空间", + "description_en": "namespace", + "provider_config": { + "path": "/o/bk_bcs_app/apis/iam/v1/namespaces/" + }, + "version": 1 + } + }, + { + "operation": "upsert_instance_selection", + "data": { + "id": "namespace_list", + "name": "命名空间", + "name_en": "namespace", + "resource_type_chain": [ + {"system_id": "bk_bcs_app", "id": "project"}, + {"system_id": "bk_bcs_app", "id": "cluster"}, + {"system_id": "bk_bcs_app", "id": "namespace"} + ] + } + }, + { + "operation": "upsert_action", + "data": { + "id": "namespace_create", + "name": "命名空间创建", + "name_en": "create namespace", + "description": "用户创建命名空间", + "description_en": "user create namespace", + "type": "create", + "related_resource_types": [{ + "system_id": "bk_bcs_app", + "id": "cluster", + "related_instance_selections": [{ + "system_id": "bk_bcs_app", + "id": "cluster_list" + }] + }], + "related_actions": ["project_view", "cluster_view", "cluster_use"], + "version": 1 + } + }, + { + "operation": "upsert_action", + "data": { + "id": "namespace_view", + "name": "命名空间查看", + "name_en": "view namespace", + "description": "用户查看命名空间", + "description_en": "user view namespace", + "type": "view", + "related_resource_types": [{ + "system_id": "bk_bcs_app", + "id": "namespace", + "name_alias": "", + "name_alias_en": "", + "selection_mode": "instance", + "related_instance_selections": [ + { + "system_id": "bk_bcs_app", + "id": "namespace_list" + } + ] + }], + "related_actions": ["project_view", "cluster_view"], + "version": 1 + } + }, + { + "operation": "upsert_action", + "data": { + "id": "namespace_update", + "name": "命名空间更新", + "name_en": "update namespace", + "description": "用户更新命名空间", + "description_en": "user update namespace", + "type": "edit", + "related_resource_types": [{ + "system_id": "bk_bcs_app", + "id": "namespace", + "name_alias": "", + "name_alias_en": "", + "selection_mode": "instance", + "related_instance_selections": [ + { + "system_id": "bk_bcs_app", + "id": "namespace_list" + } + ] + }], + "related_actions": ["project_view", "cluster_view", "cluster_use","namespace_view"], + "version": 1 + } + }, + { + "operation": "upsert_action", + "data": { + "id": "namespace_delete", + "name": "命名空间删除", + "name_en": "delete namespace", + "description": "用户删除命名空间", + "description_en": "user delete namespace", + "type": "delete", + "related_resource_types": [{ + "system_id": "bk_bcs_app", + "id": "namespace", + "name_alias": "", + "name_alias_en": "", + "selection_mode": "instance", + "related_instance_selections": [ + { + "system_id": "bk_bcs_app", + "id": "namespace_list" + } + ] + }], + "related_actions": ["project_view", "cluster_view", "cluster_use","namespace_view"], + "version": 1 + } + }, + { + "operation": "upsert_action", + "data": { + "id": "namespace_use", + "name": "命名空间使用", + "name_en": "use namespace", + "description": "用户使用命名空间", + "description_en": "user use namespace", + "type": "use", + "related_resource_types": [{ + "system_id": "bk_bcs_app", + "id": "namespace", + "name_alias": "", + "name_alias_en": "", + "selection_mode": "instance", + "related_instance_selections": [ + { + "system_id": "bk_bcs_app", + "id": "namespace_list" + } + ] + }], + "related_actions": ["project_view", "cluster_view", "cluster_use", "namespace_view"], + "version": 1 + } + } + ] +} diff --git a/bcs-app/support-files/iam/0005_templateset.json b/bcs-app/support-files/iam/0005_templateset.json new file mode 100644 index 000000000..e69de29bb diff --git a/bcs-app/support-files/iam/0006_action_group.json b/bcs-app/support-files/iam/0006_action_group.json new file mode 100644 index 000000000..ccd9cec7c --- /dev/null +++ b/bcs-app/support-files/iam/0006_action_group.json @@ -0,0 +1,96 @@ +{ + "system_id": "bk_bcs_app", + "operations": [ + { + "operation": "upsert_action_groups", + "data": [ + { + "name": "项目", + "name_en": "Project", + "actions": [ + { + "id": "project_create" + }, + { + "id": "project_view" + }, + { + "id": "project_edit" + }, + { + "id": "project_delete" + } + ], + "sub_groups": [ + { + "name": "集群", + "name_en": "Cluster", + "actions": [ + { + "id": "cluster_create" + }, + { + "id": "cluster_view" + }, + { + "id": "cluster_manage" + }, + { + "id": "cluster_delete" + }, + { + "id": "cluster_use" + } + ] + }, + { + "name": "命名空间", + "name_en": "Namespace", + "actions": [ + { + "id": "namespace_create" + }, + { + "id": "namespace_view" + }, + { + "id": "namespace_update" + }, + { + "id": "namespace_delete" + }, + { + "id": "namespace_use" + } + ] + }, + { + "name": "模板集", + "name_en": "Templateset", + "actions": [ + { + "id": "templateset_create" + }, + { + "id": "templateset_view" + }, + { + "id": "templateset_copy" + }, + { + "id": "templateset_update" + }, + { + "id": "templateset_delete" + }, + { + "id": "templateset_instantiate" + } + ] + } + ] + } + ] + } + ] +} diff --git a/bcs-app/support-files/iam/0007_resource_creator_actions.json b/bcs-app/support-files/iam/0007_resource_creator_actions.json new file mode 100644 index 000000000..a7ca53378 --- /dev/null +++ b/bcs-app/support-files/iam/0007_resource_creator_actions.json @@ -0,0 +1,80 @@ +{ + "system_id": "bk_bcs_app", + "operations": [ + { + "operation": "upsert_resource_creator_actions", + "data": { + "config":[ + { + "id":"project", + "actions":[ + { + "id":"project_edit", + "required":false + }, + { + "id":"project_view", + "required":true + } + ] + }, + { + "id":"cluster", + "actions":[ + { + "id": "cluster_use", + "required":false + }, + { + "id":"cluster_manage", + "required":false + }, + { + "id":"cluster_view", + "required":true + } + ] + }, + { + "id":"namespace", + "actions":[ + { + "id": "namespace_use", + "required":false + }, + { + "id":"namespace_update", + "required":false + }, + { + "id":"namespace_view", + "required":true + } + ] + }, + { + "id":"templateset", + "actions":[ + { + "id": "templateset_instantiate", + "required":false + }, + { + "id":"templateset_update", + "required":false + }, + { + "id":"templateset_view", + "required":true + }, + { + "id":"templateset_copy", + "required":false + } + ] + } + ] + } + } + ] +} From daeb10684403e9e64af15c99e4a0f233ab44ee8b Mon Sep 17 00:00:00 2001 From: jamesgetx Date: Sat, 9 Oct 2021 10:26:10 +0800 Subject: [PATCH 2/5] feat: add project & namespace iam api --- bcs-app/backend/api_urls.py | 2 +- bcs-app/backend/bcs_web/iam/__init__.py | 1 - .../bcs_web/iam/bcs_iam_migration/__init__.py | 1 - .../bcs_iam_migration/migrations/__init__.py | 1 - .../backend/bcs_web/iam/open_apis/__init__.py | 1 - .../iam/open_apis/resources/__init__.py | 1 - .../iam/open_apis/resources/project.py | 49 ------------ .../iam/open_apis/resources/provider.py | 64 ---------------- .../bcs_web/iam/open_apis/v1/__init__.py | 1 - bcs-app/backend/bcs_web/permissions.py | 4 +- bcs-app/backend/components/paas_auth.py | 2 +- bcs-app/backend/components/paas_cc.py | 9 ++- .../container_service/projects/base/utils.py | 2 +- .../projects/open_apis/views.py | 4 +- .../container_service/projects/serializers.py | 2 +- .../container_service/projects/views.py | 8 +- .../bcs_iam_migration/__init__.py} | 7 -- .../iam/bcs_iam_migration/apps.py | 3 +- .../migrations/0001_initial.py | 4 +- .../0002_bk_bcs_app_202108181450.py | 35 +++++++++ .../0003_bk_bcs_app_202108181523.py | 35 +++++++++ .../0004_bk_bcs_app_202108181524.py | 35 +++++++++ .../0005_bk_bcs_app_202108181525.py | 35 +++++++++ .../0006_bk_bcs_app_202108181525.py | 35 +++++++++ .../0007_bk_bcs_app_202108262047.py | 35 +++++++++ .../bcs_iam_migration/migrations/__init__.py} | 5 -- .../permissions.py => iam/legacy_perms.py} | 13 ++-- bcs-app/backend/iam/open_apis/__init__.py | 12 +++ .../iam/open_apis/authentication.py | 13 ++-- .../{bcs_web => }/iam/open_apis/constants.py | 30 ++++---- .../{bcs_web => }/iam/open_apis/exceptions.py | 5 -- .../iam/open_apis/provider/__init__.py | 12 +++ .../iam/open_apis/provider/namespace.py | 68 +++++++++++++++++ .../backend/iam/open_apis/provider/project.py | 45 +++++++++++ .../iam/open_apis/provider/resource.py | 74 +++++++++++++++++++ .../backend/iam/open_apis/provider/utils.py | 18 +++++ .../iam/open_apis/serializers.py | 12 ++- .../{bcs_web => }/iam/open_apis/urls.py | 4 +- bcs-app/backend/iam/open_apis/v1/__init__.py | 12 +++ bcs-app/backend/iam/open_apis/v1/urls.py | 20 +++++ .../{bcs_web => }/iam/open_apis/views.py | 10 +-- bcs-app/backend/settings/ce/base.py | 2 +- .../tests/bcs_mocks/data/paas_cc_json.py | 72 ++++++++++++++++++ bcs-app/backend/tests/bcs_mocks/misc.py | 13 +++- .../backend/tests/components/test_paas_cc.py | 8 ++ .../tests/components/test_permissions.py | 2 +- .../backend/tests/iam/open_apis/__init__.py | 12 +++ .../backend/tests/iam/open_apis/conftest.py | 24 ++++++ .../tests/iam/open_apis/test_namespace.py | 61 +++++++++++++++ .../tests/iam/open_apis/test_project.py | 53 +++++++++++++ .../tests/testing_utils/mocks/paas_cc.py | 38 ++++++++++ bcs-app/backend/utils/permissions.py | 2 +- 52 files changed, 812 insertions(+), 204 deletions(-) delete mode 100644 bcs-app/backend/bcs_web/iam/__init__.py delete mode 100644 bcs-app/backend/bcs_web/iam/bcs_iam_migration/__init__.py delete mode 100644 bcs-app/backend/bcs_web/iam/bcs_iam_migration/migrations/__init__.py delete mode 100644 bcs-app/backend/bcs_web/iam/open_apis/__init__.py delete mode 100644 bcs-app/backend/bcs_web/iam/open_apis/resources/__init__.py delete mode 100644 bcs-app/backend/bcs_web/iam/open_apis/resources/project.py delete mode 100644 bcs-app/backend/bcs_web/iam/open_apis/resources/provider.py delete mode 100644 bcs-app/backend/bcs_web/iam/open_apis/v1/__init__.py rename bcs-app/backend/{bcs_web/iam/open_apis/v1/urls.py => iam/bcs_iam_migration/__init__.py} (85%) rename bcs-app/backend/{bcs_web => }/iam/bcs_iam_migration/apps.py (92%) rename bcs-app/backend/{bcs_web => }/iam/bcs_iam_migration/migrations/0001_initial.py (94%) create mode 100644 bcs-app/backend/iam/bcs_iam_migration/migrations/0002_bk_bcs_app_202108181450.py create mode 100644 bcs-app/backend/iam/bcs_iam_migration/migrations/0003_bk_bcs_app_202108181523.py create mode 100644 bcs-app/backend/iam/bcs_iam_migration/migrations/0004_bk_bcs_app_202108181524.py create mode 100644 bcs-app/backend/iam/bcs_iam_migration/migrations/0005_bk_bcs_app_202108181525.py create mode 100644 bcs-app/backend/iam/bcs_iam_migration/migrations/0006_bk_bcs_app_202108181525.py create mode 100644 bcs-app/backend/iam/bcs_iam_migration/migrations/0007_bk_bcs_app_202108262047.py rename bcs-app/backend/{bcs_web/iam/open_apis/v1/views.py => iam/bcs_iam_migration/migrations/__init__.py} (89%) rename bcs-app/backend/{bcs_web/iam/permissions.py => iam/legacy_perms.py} (97%) create mode 100644 bcs-app/backend/iam/open_apis/__init__.py rename bcs-app/backend/{bcs_web => }/iam/open_apis/authentication.py (85%) rename bcs-app/backend/{bcs_web => }/iam/open_apis/constants.py (57%) rename bcs-app/backend/{bcs_web => }/iam/open_apis/exceptions.py (94%) create mode 100644 bcs-app/backend/iam/open_apis/provider/__init__.py create mode 100644 bcs-app/backend/iam/open_apis/provider/namespace.py create mode 100644 bcs-app/backend/iam/open_apis/provider/project.py create mode 100644 bcs-app/backend/iam/open_apis/provider/resource.py create mode 100644 bcs-app/backend/iam/open_apis/provider/utils.py rename bcs-app/backend/{bcs_web => }/iam/open_apis/serializers.py (74%) rename bcs-app/backend/{bcs_web => }/iam/open_apis/urls.py (90%) create mode 100644 bcs-app/backend/iam/open_apis/v1/__init__.py create mode 100644 bcs-app/backend/iam/open_apis/v1/urls.py rename bcs-app/backend/{bcs_web => }/iam/open_apis/views.py (84%) create mode 100644 bcs-app/backend/tests/iam/open_apis/__init__.py create mode 100644 bcs-app/backend/tests/iam/open_apis/conftest.py create mode 100644 bcs-app/backend/tests/iam/open_apis/test_namespace.py create mode 100644 bcs-app/backend/tests/iam/open_apis/test_project.py diff --git a/bcs-app/backend/api_urls.py b/bcs-app/backend/api_urls.py index 1755451d5..40e4281c0 100644 --- a/bcs-app/backend/api_urls.py +++ b/bcs-app/backend/api_urls.py @@ -40,7 +40,7 @@ include("backend.templatesets.open_apis.template_urls"), ), # 提供给iam拉取资源实例的url(已注册到iam后台) - url(r"^iam/", include("backend.bcs_web.iam.open_apis.urls")), + url(r"^iam/", include("backend.iam.open_apis.urls")), # web_console API url( r"^projects/(?P[\w\-]+)/clusters/(?P[\w\-]+)/web_console/sessions/", diff --git a/bcs-app/backend/bcs_web/iam/__init__.py b/bcs-app/backend/bcs_web/iam/__init__.py deleted file mode 100644 index 40a96afc6..000000000 --- a/bcs-app/backend/bcs_web/iam/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/bcs-app/backend/bcs_web/iam/bcs_iam_migration/__init__.py b/bcs-app/backend/bcs_web/iam/bcs_iam_migration/__init__.py deleted file mode 100644 index 40a96afc6..000000000 --- a/bcs-app/backend/bcs_web/iam/bcs_iam_migration/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/bcs-app/backend/bcs_web/iam/bcs_iam_migration/migrations/__init__.py b/bcs-app/backend/bcs_web/iam/bcs_iam_migration/migrations/__init__.py deleted file mode 100644 index 40a96afc6..000000000 --- a/bcs-app/backend/bcs_web/iam/bcs_iam_migration/migrations/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/bcs-app/backend/bcs_web/iam/open_apis/__init__.py b/bcs-app/backend/bcs_web/iam/open_apis/__init__.py deleted file mode 100644 index 40a96afc6..000000000 --- a/bcs-app/backend/bcs_web/iam/open_apis/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/bcs-app/backend/bcs_web/iam/open_apis/resources/__init__.py b/bcs-app/backend/bcs_web/iam/open_apis/resources/__init__.py deleted file mode 100644 index 40a96afc6..000000000 --- a/bcs-app/backend/bcs_web/iam/open_apis/resources/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/bcs-app/backend/bcs_web/iam/open_apis/resources/project.py b/bcs-app/backend/bcs_web/iam/open_apis/resources/project.py deleted file mode 100644 index b92b34cec..000000000 --- a/bcs-app/backend/bcs_web/iam/open_apis/resources/project.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community -Edition) available. -Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. -Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://opensource.org/licenses/MIT - -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on -an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. -""" -try: - from iam.resource.provider import ListResult, ResourceProvider -except Exception: - pass -from backend.components import ssm -from backend.container_service.projects.base import filter_projects - - -class ProjectProvider(ResourceProvider): - def list_attr(self, **options): - return ListResult(results=[], count=0) - - def list_attr_value(self, filter, page, **options): - return ListResult(results=[], count=0) - - def list_instance(self, filter, page, **options): - access_token = ssm.get_client_access_token()["access_token"] - projects = filter_projects(access_token) - count = len(projects) - projects = projects[page.slice_from : page.slice_to] # noqa - results = [{"id": p["project_id"], "display_name": p["project_name"]} for p in projects] - return ListResult(results=results, count=count) - - def fetch_instance_info(self, filter, **options): - access_token = ssm.get_client_access_token()["access_token"] - query_params = None - if filter.ids: - query_params = {"project_ids": ",".join(filter.ids)} - projects = filter_projects(access_token, query_params) - results = [{"id": p["project_id"], "display_name": p["project_name"]} for p in projects] - return ListResult(results=results, count=len(results)) - - def list_instance_by_policy(self, filter, page, **options): - # TODO 确认基于实例的查询是不是就是id的过滤查询 - return ListResult(results=[], count=0) diff --git a/bcs-app/backend/bcs_web/iam/open_apis/resources/provider.py b/bcs-app/backend/bcs_web/iam/open_apis/resources/provider.py deleted file mode 100644 index 7558e247f..000000000 --- a/bcs-app/backend/bcs_web/iam/open_apis/resources/provider.py +++ /dev/null @@ -1,64 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community -Edition) available. -Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. -Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://opensource.org/licenses/MIT - -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on -an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. -""" -try: - from iam.resource.utils import get_filter_obj, get_page_obj -except Exception: - pass - -from ..exceptions import ResNotFoundError -from .project import ProjectProvider - -PROVIDER_CLS_MAP = {"project": ProjectProvider} - - -class BCSResourceProvider: - def __init__(self, resource_type): - try: - self.resource_provider = PROVIDER_CLS_MAP.get(resource_type)() - except Exception: - raise ResNotFoundError(f"unsupported resource type: {resource_type}") - - def _parse_filter_and_page(self, data): - filter_obj = get_filter_obj(data["filter"], ["ids", "parent", "search", "resource_type_chain"]) - page_obj = get_page_obj(data.get("page")) - return filter_obj, page_obj - - def provide(self, data, **options): - handler = getattr(self, data["method"]) - return handler(data, **options) - - def list_attr(self, data, **options): - result = self.resource_provider.list_attr(**options) - return result.to_list() - - def list_attr_value(self, data, **options): - filter, page = self._parse_filter_and_page(data) - result = self.resource_provider.list_attr_value(filter, page, **options) - return result.to_dict() - - def list_instance(self, data, **options): - filter, page = self._parse_filter_and_page(data) - result = self.resource_provider.list_instance(filter, page, **options) - return result.to_dict() - - def fetch_instance_info(self, data, **options): - filter, _ = self._parse_filter_and_page(data) - result = self.resource_provider.fetch_instance_info(filter, **options) - return result.to_list() - - def list_instance_by_policy(self, data, **options): - filter, page = self._parse_filter_and_page(data) - result = self.resource_provider.list_instance_by_policy(filter, page, **options) - return result.to_list() diff --git a/bcs-app/backend/bcs_web/iam/open_apis/v1/__init__.py b/bcs-app/backend/bcs_web/iam/open_apis/v1/__init__.py deleted file mode 100644 index 40a96afc6..000000000 --- a/bcs-app/backend/bcs_web/iam/open_apis/v1/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/bcs-app/backend/bcs_web/permissions.py b/bcs-app/backend/bcs_web/permissions.py index acb6a66f4..607ff87c4 100644 --- a/bcs-app/backend/bcs_web/permissions.py +++ b/bcs-app/backend/bcs_web/permissions.py @@ -5,9 +5,7 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://opensource.org/licenses/MIT - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. @@ -19,11 +17,11 @@ from backend.accounts import bcs_perm from backend.bcs_web.audit_log.audit.context import AuditContext -from backend.bcs_web.iam import permissions from backend.components.base import ComponentAuth from backend.components.paas_cc import PaaSCCClient from backend.container_service.clusters.base.models import CtxCluster from backend.container_service.projects.base.models import CtxProject +from backend.iam import legacy_perms as permissions from backend.utils import FancyDict from backend.utils.cache import region diff --git a/bcs-app/backend/components/paas_auth.py b/bcs-app/backend/components/paas_auth.py index 29d7c3b16..7beeb0b64 100644 --- a/bcs-app/backend/components/paas_auth.py +++ b/bcs-app/backend/components/paas_auth.py @@ -14,7 +14,7 @@ """ import logging -from backend.bcs_web.iam import permissions +from backend.iam import legacy_perms as permissions from .ssm import get_client_access_token diff --git a/bcs-app/backend/components/paas_cc.py b/bcs-app/backend/components/paas_cc.py index 80997f625..2cd6ca5c2 100644 --- a/bcs-app/backend/components/paas_cc.py +++ b/bcs-app/backend/components/paas_cc.py @@ -20,10 +20,10 @@ from django.conf import settings from django.utils.translation import ugettext_lazy as _ -from backend.bcs_web.iam import permissions from backend.components.base import BaseHttpClient, BkApiClient, ComponentAuth, response_handler from backend.components.utils import http_delete, http_get, http_patch, http_post, http_put from backend.container_service.clusters.models import CommonStatus +from backend.iam import legacy_perms as permissions from backend.utils.basic import getitems from backend.utils.decorators import parse_response_data from backend.utils.errcodes import ErrorCode @@ -523,6 +523,7 @@ def __init__(self, host: str): # PaaSCC 系统接口地址 self.get_cluster_url = f"{host}/projects/{{project_id}}/clusters/{{cluster_id}}" + self.get_cluster_by_id_url = f"{host}/clusters/{{cluster_id}}/" self.get_project_url = f"{host}/projects/{{project_id}}/" self.update_cluster_url = f"{host}/projects/{{project_id}}/clusters/{{cluster_id}}/" self.delete_cluster_url = f"{host}/projects/{{project_id}}/clusters/{{cluster_id}}/" @@ -552,6 +553,12 @@ def get_cluster(self, project_id: str, cluster_id: str) -> Dict: url = self._config.get_cluster_url.format(project_id=project_id, cluster_id=cluster_id) return self._client.request_json('GET', url) + @response_handler() + def get_cluster_by_id(self, cluster_id: str) -> Dict: + """根据集群ID获取集群信息""" + url = self._config.get_cluster_by_id_url.format(cluster_id=cluster_id) + return self._client.request_json('GET', url) + @parse_response_data() def get_project(self, project_id: str) -> Dict: """获取项目信息""" diff --git a/bcs-app/backend/container_service/projects/base/utils.py b/bcs-app/backend/container_service/projects/base/utils.py index 2dac8ca69..e97611052 100644 --- a/bcs-app/backend/container_service/projects/base/utils.py +++ b/bcs-app/backend/container_service/projects/base/utils.py @@ -26,7 +26,7 @@ def query_projects(access_token, query_params=None): return paas_cc.get_projects(access_token, query_params) -def filter_projects(access_token, query_params=None): +def list_projects(access_token, query_params=None): data = query_projects(access_token, query_params) projects = data.get("results") or [] # 为了兼容导航的参数要求, 增加了project_code字段 diff --git a/bcs-app/backend/container_service/projects/open_apis/views.py b/bcs-app/backend/container_service/projects/open_apis/views.py index 7a92543cc..3f784afac 100644 --- a/bcs-app/backend/container_service/projects/open_apis/views.py +++ b/bcs-app/backend/container_service/projects/open_apis/views.py @@ -16,7 +16,7 @@ from backend.bcs_web.apis.authentication import JWTAuthentication from backend.bcs_web.apis.permissions import AccessTokenPermission -from backend.container_service.projects.base import filter_projects +from backend.container_service.projects.base import list_projects from backend.container_service.projects.views import NavProjectPermissionViewSet from backend.utils.renderers import BKAPIRenderer @@ -27,5 +27,5 @@ class ProjectsViewSet(NavProjectPermissionViewSet): permission_classes = (AccessTokenPermission,) def list_projects(self, request): - projects = filter_projects(request.user.token.access_token) + projects = list_projects(request.user.token.access_token) return Response(projects) diff --git a/bcs-app/backend/container_service/projects/serializers.py b/bcs-app/backend/container_service/projects/serializers.py index 5cf032238..f33b55026 100644 --- a/bcs-app/backend/container_service/projects/serializers.py +++ b/bcs-app/backend/container_service/projects/serializers.py @@ -14,8 +14,8 @@ """ from rest_framework import serializers -from backend.bcs_web.iam.permissions import ProjectActions from backend.container_service.projects.base.constants import ProjectKindID +from backend.iam.legacy_perms import ProjectActions class UpdateProjectNewSLZ(serializers.Serializer): diff --git a/bcs-app/backend/container_service/projects/views.py b/bcs-app/backend/container_service/projects/views.py index 4b8fde312..9d00e848c 100644 --- a/bcs-app/backend/container_service/projects/views.py +++ b/bcs-app/backend/container_service/projects/views.py @@ -24,11 +24,11 @@ from backend.bcs_web.audit_log import client from backend.bcs_web.constants import bcs_project_cache_key -from backend.bcs_web.iam.permissions import ProjectPermission from backend.bcs_web.viewsets import SystemViewSet from backend.components import cc, paas_cc from backend.container_service.projects import base as Project from backend.container_service.projects.utils import fetch_has_maintain_perm_apps, update_bcs_service_for_project +from backend.iam.legacy_perms import ProjectPermission from backend.utils.basic import normalize_datetime from backend.utils.cache import region from backend.utils.errcodes import ErrorCode @@ -229,11 +229,11 @@ def filter_projects(self, request): access_token = request.user.token.access_token if project_code: - projects = Project.filter_projects(access_token, {"english_names": project_code}) + projects = Project.list_projects(access_token, {"english_names": project_code}) elif project_name: - projects = Project.filter_projects(access_token, {"project_names": project_name}) + projects = Project.list_projects(access_token, {"project_names": project_name}) else: - projects = Project.filter_projects(access_token) + projects = Project.list_projects(access_token) if not projects: return Response(projects) diff --git a/bcs-app/backend/bcs_web/iam/open_apis/v1/urls.py b/bcs-app/backend/iam/bcs_iam_migration/__init__.py similarity index 85% rename from bcs-app/backend/bcs_web/iam/open_apis/v1/urls.py rename to bcs-app/backend/iam/bcs_iam_migration/__init__.py index f1bb3b727..02a6c5cd3 100644 --- a/bcs-app/backend/bcs_web/iam/open_apis/v1/urls.py +++ b/bcs-app/backend/iam/bcs_iam_migration/__init__.py @@ -5,15 +5,8 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://opensource.org/licenses/MIT - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ -from django.conf.urls import url - -from . import views - -urlpatterns = [url(r"^projects/", views.ProjectAPIView.as_view())] diff --git a/bcs-app/backend/bcs_web/iam/bcs_iam_migration/apps.py b/bcs-app/backend/iam/bcs_iam_migration/apps.py similarity index 92% rename from bcs-app/backend/bcs_web/iam/bcs_iam_migration/apps.py rename to bcs-app/backend/iam/bcs_iam_migration/apps.py index 4618ee720..e745c8562 100644 --- a/bcs-app/backend/bcs_web/iam/bcs_iam_migration/apps.py +++ b/bcs-app/backend/iam/bcs_iam_migration/apps.py @@ -18,4 +18,5 @@ class BcsIamMigrationConfig(AppConfig): - name = "backend.bcs_web.iam.bcs_iam_migration" + name = "backend.iam.bcs_iam_migration" + label = "bcs_iam_migration" diff --git a/bcs-app/backend/bcs_web/iam/bcs_iam_migration/migrations/0001_initial.py b/bcs-app/backend/iam/bcs_iam_migration/migrations/0001_initial.py similarity index 94% rename from bcs-app/backend/bcs_web/iam/bcs_iam_migration/migrations/0001_initial.py rename to bcs-app/backend/iam/bcs_iam_migration/migrations/0001_initial.py index df90f86df..128bc875f 100644 --- a/bcs-app/backend/bcs_web/iam/bcs_iam_migration/migrations/0001_initial.py +++ b/bcs-app/backend/iam/bcs_iam_migration/migrations/0001_initial.py @@ -32,6 +32,4 @@ class Migration(migrations.Migration): dependencies = [] - operations = [ - migrations.RunPython(forward_func) - ] + operations = [migrations.RunPython(forward_func)] diff --git a/bcs-app/backend/iam/bcs_iam_migration/migrations/0002_bk_bcs_app_202108181450.py b/bcs-app/backend/iam/bcs_iam_migration/migrations/0002_bk_bcs_app_202108181450.py new file mode 100644 index 000000000..b869146cd --- /dev/null +++ b/bcs-app/backend/iam/bcs_iam_migration/migrations/0002_bk_bcs_app_202108181450.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import codecs +import json +import os + +from django.conf import settings +from django.db import migrations +from iam.contrib.iam_migration.migrator import IAMMigrator + + +def forward_func(apps, schema_editor): + + migrator = IAMMigrator(Migration.migration_json) + migrator.migrate() + + +class Migration(migrations.Migration): + migration_json = "0002_project_extra.json" + + dependencies = [('bcs_iam_migration', '0001_initial')] + + operations = [migrations.RunPython(forward_func)] diff --git a/bcs-app/backend/iam/bcs_iam_migration/migrations/0003_bk_bcs_app_202108181523.py b/bcs-app/backend/iam/bcs_iam_migration/migrations/0003_bk_bcs_app_202108181523.py new file mode 100644 index 000000000..e8cbe960a --- /dev/null +++ b/bcs-app/backend/iam/bcs_iam_migration/migrations/0003_bk_bcs_app_202108181523.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import codecs +import json +import os + +from django.conf import settings +from django.db import migrations +from iam.contrib.iam_migration.migrator import IAMMigrator + + +def forward_func(apps, schema_editor): + + migrator = IAMMigrator(Migration.migration_json) + migrator.migrate() + + +class Migration(migrations.Migration): + migration_json = "0003_cluster.json" + + dependencies = [('bcs_iam_migration', '0002_bk_bcs_app_202108181450')] + + operations = [migrations.RunPython(forward_func)] diff --git a/bcs-app/backend/iam/bcs_iam_migration/migrations/0004_bk_bcs_app_202108181524.py b/bcs-app/backend/iam/bcs_iam_migration/migrations/0004_bk_bcs_app_202108181524.py new file mode 100644 index 000000000..765aa7f2c --- /dev/null +++ b/bcs-app/backend/iam/bcs_iam_migration/migrations/0004_bk_bcs_app_202108181524.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import codecs +import json +import os + +from django.conf import settings +from django.db import migrations +from iam.contrib.iam_migration.migrator import IAMMigrator + + +def forward_func(apps, schema_editor): + + migrator = IAMMigrator(Migration.migration_json) + migrator.migrate() + + +class Migration(migrations.Migration): + migration_json = "0004_namespace.json" + + dependencies = [('bcs_iam_migration', '0003_bk_bcs_app_202108181523')] + + operations = [migrations.RunPython(forward_func)] diff --git a/bcs-app/backend/iam/bcs_iam_migration/migrations/0005_bk_bcs_app_202108181525.py b/bcs-app/backend/iam/bcs_iam_migration/migrations/0005_bk_bcs_app_202108181525.py new file mode 100644 index 000000000..6887db145 --- /dev/null +++ b/bcs-app/backend/iam/bcs_iam_migration/migrations/0005_bk_bcs_app_202108181525.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import codecs +import json +import os + +from django.conf import settings +from django.db import migrations +from iam.contrib.iam_migration.migrator import IAMMigrator + + +def forward_func(apps, schema_editor): + + migrator = IAMMigrator(Migration.migration_json) + migrator.migrate() + + +class Migration(migrations.Migration): + migration_json = "0005_templateset.json" + + dependencies = [('bcs_iam_migration', '0004_bk_bcs_app_202108181524')] + + operations = [migrations.RunPython(forward_func)] diff --git a/bcs-app/backend/iam/bcs_iam_migration/migrations/0006_bk_bcs_app_202108181525.py b/bcs-app/backend/iam/bcs_iam_migration/migrations/0006_bk_bcs_app_202108181525.py new file mode 100644 index 000000000..6464edc72 --- /dev/null +++ b/bcs-app/backend/iam/bcs_iam_migration/migrations/0006_bk_bcs_app_202108181525.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import codecs +import json +import os + +from django.conf import settings +from django.db import migrations +from iam.contrib.iam_migration.migrator import IAMMigrator + + +def forward_func(apps, schema_editor): + + migrator = IAMMigrator(Migration.migration_json) + migrator.migrate() + + +class Migration(migrations.Migration): + migration_json = "0006_action_group.json" + + dependencies = [('bcs_iam_migration', '0005_bk_bcs_app_202108181525')] + + operations = [migrations.RunPython(forward_func)] diff --git a/bcs-app/backend/iam/bcs_iam_migration/migrations/0007_bk_bcs_app_202108262047.py b/bcs-app/backend/iam/bcs_iam_migration/migrations/0007_bk_bcs_app_202108262047.py new file mode 100644 index 000000000..23ad50b44 --- /dev/null +++ b/bcs-app/backend/iam/bcs_iam_migration/migrations/0007_bk_bcs_app_202108262047.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import codecs +import json +import os + +from django.conf import settings +from django.db import migrations +from iam.contrib.iam_migration.migrator import IAMMigrator + + +def forward_func(apps, schema_editor): + + migrator = IAMMigrator(Migration.migration_json) + migrator.migrate() + + +class Migration(migrations.Migration): + migration_json = "0007_resource_creator_actions.json" + + dependencies = [('bcs_iam_migration', '0006_bk_bcs_app_202108181525')] + + operations = [migrations.RunPython(forward_func)] diff --git a/bcs-app/backend/bcs_web/iam/open_apis/v1/views.py b/bcs-app/backend/iam/bcs_iam_migration/migrations/__init__.py similarity index 89% rename from bcs-app/backend/bcs_web/iam/open_apis/v1/views.py rename to bcs-app/backend/iam/bcs_iam_migration/migrations/__init__.py index 59a1e9c91..08f8f6ab8 100644 --- a/bcs-app/backend/bcs_web/iam/open_apis/v1/views.py +++ b/bcs-app/backend/iam/bcs_iam_migration/migrations/__init__.py @@ -12,8 +12,3 @@ 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 ..views import ResourceAPIView - - -class ProjectAPIView(ResourceAPIView): - pass diff --git a/bcs-app/backend/bcs_web/iam/permissions.py b/bcs-app/backend/iam/legacy_perms.py similarity index 97% rename from bcs-app/backend/bcs_web/iam/permissions.py rename to bcs-app/backend/iam/legacy_perms.py index 1d39bfb39..d5c9833c2 100644 --- a/bcs-app/backend/bcs_web/iam/permissions.py +++ b/bcs-app/backend/iam/legacy_perms.py @@ -15,15 +15,12 @@ from typing import Dict, Optional from django.conf import settings +from iam import IAM, OP, Action, MultiActionRequest, Request, Resource, Subject +from iam.api.client import Client +from iam.api.http import http_get, http_post +from iam.apply import models +from iam.exceptions import AuthAPIError, AuthInvalidRequest -try: - from iam import IAM, OP, Action, MultiActionRequest, Request, Resource, Subject - from iam.api.client import Client - from iam.api.http import http_get, http_post - from iam.apply import models - from iam.exceptions import AuthAPIError, AuthInvalidRequest -except Exception: - pass from backend.utils.basic import ChoicesEnum from backend.utils.exceptions import PermissionDeniedError diff --git a/bcs-app/backend/iam/open_apis/__init__.py b/bcs-app/backend/iam/open_apis/__init__.py new file mode 100644 index 000000000..02a6c5cd3 --- /dev/null +++ b/bcs-app/backend/iam/open_apis/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" diff --git a/bcs-app/backend/bcs_web/iam/open_apis/authentication.py b/bcs-app/backend/iam/open_apis/authentication.py similarity index 85% rename from bcs-app/backend/bcs_web/iam/open_apis/authentication.py rename to bcs-app/backend/iam/open_apis/authentication.py index c4812b51e..be7d78cdf 100644 --- a/bcs-app/backend/bcs_web/iam/open_apis/authentication.py +++ b/bcs-app/backend/iam/open_apis/authentication.py @@ -13,29 +13,28 @@ specific language governing permissions and limitations under the License. """ from django.conf import settings -from rest_framework import exceptions +from iam import IAM from rest_framework.authentication import BasicAuthentication +from rest_framework.exceptions import AuthenticationFailed as RESTAuthenticationFailed -try: - from iam import IAM -except Exception: - pass from backend.utils import FancyDict from .exceptions import AuthenticationFailed class IamBasicAuthentication(BasicAuthentication): + """自定义认证逻辑, 对权限中心请求认证""" + def authenticate(self, request): try: result = super().authenticate(request) if result is None: raise AuthenticationFailed("basic auth failed") - except exceptions.AuthenticationFailed as e: + except RESTAuthenticationFailed as e: raise AuthenticationFailed(str(e)) return result - def authenticate_credentials(self, userid, password, request=None): + def authenticate_credentials(self, userid: str, password: str, request=None): if userid != "bk_iam": raise AuthenticationFailed("username is not bk_iam") diff --git a/bcs-app/backend/bcs_web/iam/open_apis/constants.py b/bcs-app/backend/iam/open_apis/constants.py similarity index 57% rename from bcs-app/backend/bcs_web/iam/open_apis/constants.py rename to bcs-app/backend/iam/open_apis/constants.py index fc280e557..6124af3eb 100644 --- a/bcs-app/backend/bcs_web/iam/open_apis/constants.py +++ b/bcs-app/backend/iam/open_apis/constants.py @@ -5,27 +5,25 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://opensource.org/licenses/MIT - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ -from backend.utils.basic import ChoicesEnum +from backend.iam.permissions.resources import constants +from backend.packages.blue_krill.data_types.enum import StructuredEnum + +ResourceType = constants.ResourceType -class MethodChoices(ChoicesEnum): - LIST_ATTR = "list_attr" - LIST_ATTR_VALUE = "list_attr_value" - LIST_INSTANCE = "list_instance" - FETCH_INSTANCE_INFO = "fetch_instance_info" - LIST_INSTANCE_BY_POLICY = "list_instance_by_policy" +class MethodType(str, StructuredEnum): + """ + 权限中心拉取资源的 method 参数值 + 字段协议说明 https://bk.tencent.com/docs/document/6.0/160/8427?r=1 + """ - _choices_labels = ( - (LIST_ATTR, "list_attr"), - (LIST_ATTR_VALUE, "list_attr_value"), - (LIST_INSTANCE, "list_instance"), - (FETCH_INSTANCE_INFO, "fetch_instance_info"), - (LIST_INSTANCE_BY_POLICY, "list_instance_by_policy"), - ) + LIST_ATTR = 'list_attr' + LIST_ATTR_VALUE = 'list_attr_value' + LIST_INSTANCE = 'list_instance' + FETCH_INSTANCE_INFO = 'fetch_instance_info' + LIST_INSTANCE_BY_POLICY = 'list_instance_by_policy' diff --git a/bcs-app/backend/bcs_web/iam/open_apis/exceptions.py b/bcs-app/backend/iam/open_apis/exceptions.py similarity index 94% rename from bcs-app/backend/bcs_web/iam/open_apis/exceptions.py rename to bcs-app/backend/iam/open_apis/exceptions.py index 2b87587b1..641982e7b 100644 --- a/bcs-app/backend/bcs_web/iam/open_apis/exceptions.py +++ b/bcs-app/backend/iam/open_apis/exceptions.py @@ -5,9 +5,7 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://opensource.org/licenses/MIT - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. @@ -17,6 +15,3 @@ class AuthenticationFailed(exceptions.APIError): code = 401 - - -ResNotFoundError = exceptions.ResNotFoundError diff --git a/bcs-app/backend/iam/open_apis/provider/__init__.py b/bcs-app/backend/iam/open_apis/provider/__init__.py new file mode 100644 index 000000000..02a6c5cd3 --- /dev/null +++ b/bcs-app/backend/iam/open_apis/provider/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" diff --git a/bcs-app/backend/iam/open_apis/provider/namespace.py b/bcs-app/backend/iam/open_apis/provider/namespace.py new file mode 100644 index 000000000..09f725e4e --- /dev/null +++ b/bcs-app/backend/iam/open_apis/provider/namespace.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from typing import Dict, List + +from iam.collection import FancyDict +from iam.resource.provider import ListResult, ResourceProvider +from iam.resource.utils import Page + +from backend.components.base import ComponentAuth +from backend.components.paas_cc import PaaSCCClient + +from .utils import get_system_token + + +class NamespaceProvider(ResourceProvider): + """命名空间 Provider""" + + def list_instance(self, filter_obj: FancyDict, page_obj: Page, **options) -> ListResult: + cluster_id = filter_obj.parent['id'] + namespace_list = self._list_namespaces(cluster_id) + + namespace_slice = namespace_list[page_obj.slice_from : page_obj.slice_to] + results = [{'id': f"{cluster_id}:{ns['name']}", 'display_name': ns['name']} for ns in namespace_slice] + + return ListResult(results=results, count=len(namespace_list)) + + def fetch_instance_info(self, filter_obj: FancyDict, **options) -> ListResult: + cluster_id = filter_obj.parent['id'] + namespace_list = self._list_namespaces(cluster_id) + + if filter_obj.ids: + # cluster_ns_id 结构如 BCS-K8S-40000:test + filter_ns_list = [cluster_ns_id.split(':')[1] for cluster_ns_id in filter_obj.ids] + results = [ + {'id': f"{cluster_id}:{ns['name']}", 'display_name': ns['name']} + for ns in namespace_list + if ns['name'] in filter_ns_list + ] + else: + results = [{'id': f"{cluster_id}:{ns['name']}", 'display_name': ns['name']} for ns in namespace_list] + + return ListResult(results=results, count=len(results)) + + def list_instance_by_policy(self, filter_obj: FancyDict, page_obj: Page, **options) -> ListResult: + # TODO 确认基于实例的查询是不是就是id的过滤查询 + return ListResult(results=[], count=0) + + def list_attr(self, **options) -> ListResult: + return ListResult(results=[], count=0) + + def list_attr_value(self, filter_obj: FancyDict, page_obj: Page, **options) -> ListResult: + return ListResult(results=[], count=0) + + def _list_namespaces(self, cluster_id: str) -> List[Dict]: + paas_cc = PaaSCCClient(auth=ComponentAuth(get_system_token())) + cluster = paas_cc.get_cluster_by_id(cluster_id=cluster_id) + ns_data = paas_cc.get_cluster_namespace_list(project_id=cluster['project_id'], cluster_id=cluster_id) + return ns_data['results'] diff --git a/bcs-app/backend/iam/open_apis/provider/project.py b/bcs-app/backend/iam/open_apis/provider/project.py new file mode 100644 index 000000000..7b72c2a92 --- /dev/null +++ b/bcs-app/backend/iam/open_apis/provider/project.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from iam.collection import FancyDict +from iam.resource.provider import ListResult, ResourceProvider +from iam.resource.utils import Page + +from backend.container_service.projects.base import list_projects + +from .utils import get_system_token + + +class ProjectProvider(ResourceProvider): + """项目资源的 Provider""" + + def list_instance(self, filter_obj: FancyDict, page_obj: Page, **options) -> ListResult: + projects = list_projects(get_system_token()) + projects_slice = projects[page_obj.slice_from : page_obj.slice_to] + results = [{'id': p['project_id'], 'display_name': p['project_name']} for p in projects_slice] + return ListResult(results=results, count=len(projects)) + + def fetch_instance_info(self, filter_obj: FancyDict, **options) -> ListResult: + query_params = {'project_ids': ','.join(filter_obj.ids)} + projects = list_projects(get_system_token(), query_params) + results = [{'id': p['project_id'], 'display_name': p['project_name']} for p in projects] + return ListResult(results=results, count=len(results)) + + def list_instance_by_policy(self, filter_obj: FancyDict, page_obj: Page, **options) -> ListResult: + # TODO 确认基于实例的查询是不是就是id的过滤查询 + return ListResult(results=[], count=0) + + def list_attr(self, **options) -> ListResult: + return ListResult(results=[], count=0) + + def list_attr_value(self, filter_obj: FancyDict, page_obj: Page, **options) -> ListResult: + return ListResult(results=[], count=0) diff --git a/bcs-app/backend/iam/open_apis/provider/resource.py b/bcs-app/backend/iam/open_apis/provider/resource.py new file mode 100644 index 000000000..cff423152 --- /dev/null +++ b/bcs-app/backend/iam/open_apis/provider/resource.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from typing import Dict, List, Optional, Union + +from iam.collection import FancyDict +from iam.resource.utils import Page, get_filter_obj, get_page_obj + +from ..constants import ResourceType +from .namespace import NamespaceProvider +from .project import ProjectProvider + +PROVIDER_CLS_MAP = {ResourceType.Project: ProjectProvider, ResourceType.Namespace: NamespaceProvider} + + +class ResourceProvider: + def __init__(self, resource_type: str): + """:param resource_type: 资源类型 如 project, cluster 等""" + self.resource_provider = PROVIDER_CLS_MAP[resource_type]() + + def provide(self, method: str, data: Dict, **options) -> Union[List, Dict]: + """ + 根据 method 值, 调用对应的方法返回数据 + :param method: 值包括 list_attr, list_attr_value, list_instance 等 + :param data: 其他查询条件数据,如分页数据等 + """ + handler = getattr(self, method) + return handler(data, **options) + + def list_attr(self, data: Optional[Dict] = None, **options) -> List[Dict]: + """ + 查询某个资源类型可用于配置权限的属性列表 + :param data: 占位字段,为了上面provide方法的处理统一 + """ + result = self.resource_provider.list_attr(**options) + return result.to_list() + + def list_attr_value(self, data: Dict, **options) -> Dict: + """获取一个资源类型某个属性的值列表""" + filter_obj, page_obj = self._parse_filter_and_page(data) + result = self.resource_provider.list_attr_value(filter_obj, page_obj, **options) + return result.to_dict() + + def list_instance(self, data: Dict, **options) -> Dict: + """查询资源实例列表(支持分页)""" + filter_obj, page_obj = self._parse_filter_and_page(data) + result = self.resource_provider.list_instance(filter_obj, page_obj, **options) + return result.to_dict() + + def fetch_instance_info(self, data: Dict, **options) -> List[Dict]: + """查询资源实例列表""" + filter_obj, _ = self._parse_filter_and_page(data) + result = self.resource_provider.fetch_instance_info(filter_obj, **options) + return result.to_list() + + def list_instance_by_policy(self, data: Dict, **options) -> List[Dict]: + """根据策略表达式查询资源实例""" + filter_obj, page_obj = self._parse_filter_and_page(data) + result = self.resource_provider.list_instance_by_policy(filter_obj, page_obj, **options) + return result.to_list() + + def _parse_filter_and_page(self, data: Dict) -> (FancyDict, Page): + filter_obj = get_filter_obj(data["filter"], ["ids", "parent", "search", "resource_type_chain"]) + page_obj = get_page_obj(data.get("page")) + return filter_obj, page_obj diff --git a/bcs-app/backend/iam/open_apis/provider/utils.py b/bcs-app/backend/iam/open_apis/provider/utils.py new file mode 100644 index 000000000..f35b5590e --- /dev/null +++ b/bcs-app/backend/iam/open_apis/provider/utils.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from backend.components import ssm + + +def get_system_token(): + """获取非用户 access_token""" + return ssm.get_client_access_token()["access_token"] diff --git a/bcs-app/backend/bcs_web/iam/open_apis/serializers.py b/bcs-app/backend/iam/open_apis/serializers.py similarity index 74% rename from bcs-app/backend/bcs_web/iam/open_apis/serializers.py rename to bcs-app/backend/iam/open_apis/serializers.py index 33ef4cbb2..a57b11224 100644 --- a/bcs-app/backend/bcs_web/iam/open_apis/serializers.py +++ b/bcs-app/backend/iam/open_apis/serializers.py @@ -5,20 +5,18 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://opensource.org/licenses/MIT - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from rest_framework import serializers -from .constants import MethodChoices +from .constants import MethodType, ResourceType class QueryResourceSLZ(serializers.Serializer): - method = serializers.ChoiceField(choices=MethodChoices.get_choices()) - type = serializers.CharField() - filter = serializers.JSONField(default={}) - page = serializers.JSONField(default={}) + method = serializers.ChoiceField(choices=MethodType.get_choices()) + type = serializers.ChoiceField(choices=ResourceType.get_choices()) + filter = serializers.JSONField(default=dict) + page = serializers.JSONField(default=dict) diff --git a/bcs-app/backend/bcs_web/iam/open_apis/urls.py b/bcs-app/backend/iam/open_apis/urls.py similarity index 90% rename from bcs-app/backend/bcs_web/iam/open_apis/urls.py rename to bcs-app/backend/iam/open_apis/urls.py index 2dde8d822..e92874507 100644 --- a/bcs-app/backend/bcs_web/iam/open_apis/urls.py +++ b/bcs-app/backend/iam/open_apis/urls.py @@ -5,13 +5,11 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://opensource.org/licenses/MIT - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ from django.conf.urls import include, url -urlpatterns = [url(r"^v1/", include("backend.bcs_web.iam.open_apis.v1.urls"))] +urlpatterns = [url(r"^v1/", include("backend.iam.open_apis.v1.urls"))] diff --git a/bcs-app/backend/iam/open_apis/v1/__init__.py b/bcs-app/backend/iam/open_apis/v1/__init__.py new file mode 100644 index 000000000..02a6c5cd3 --- /dev/null +++ b/bcs-app/backend/iam/open_apis/v1/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" diff --git a/bcs-app/backend/iam/open_apis/v1/urls.py b/bcs-app/backend/iam/open_apis/v1/urls.py new file mode 100644 index 000000000..0b9aee694 --- /dev/null +++ b/bcs-app/backend/iam/open_apis/v1/urls.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from django.conf.urls import url + +from .. import views + +urlpatterns = [ + url(r'^projects/', views.ResourceAPIView.as_view()), + url(r'^namespaces/', views.ResourceAPIView.as_view()), +] diff --git a/bcs-app/backend/bcs_web/iam/open_apis/views.py b/bcs-app/backend/iam/open_apis/views.py similarity index 84% rename from bcs-app/backend/bcs_web/iam/open_apis/views.py rename to bcs-app/backend/iam/open_apis/views.py index 7fc1d38de..1b87f5a5b 100644 --- a/bcs-app/backend/bcs_web/iam/open_apis/views.py +++ b/bcs-app/backend/iam/open_apis/views.py @@ -5,9 +5,7 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://opensource.org/licenses/MIT - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. @@ -18,11 +16,13 @@ from backend.utils.renderers import BKAPIRenderer from .authentication import IamBasicAuthentication -from .resources.provider import BCSResourceProvider +from .provider.resource import ResourceProvider from .serializers import QueryResourceSLZ class ResourceAPIView(APIView): + """统一入口: 提供给权限中心拉取各类资源""" + renderer_classes = (BKAPIRenderer,) authentication_classes = (IamBasicAuthentication,) permission_classes = () @@ -40,6 +40,6 @@ def post(self, request): serializer.is_valid(raise_exception=True) validated_data = serializer.validated_data - provider = BCSResourceProvider(resource_type=validated_data["type"]) - resp = provider.provide(validated_data, **self._get_options(request)) + provider = ResourceProvider(resource_type=validated_data["type"]) + resp = provider.provide(method=validated_data['method'], data=validated_data, **self._get_options(request)) return Response(resp) diff --git a/bcs-app/backend/settings/ce/base.py b/bcs-app/backend/settings/ce/base.py index 72de8f3ea..10a92f7bc 100644 --- a/bcs-app/backend/settings/ce/base.py +++ b/bcs-app/backend/settings/ce/base.py @@ -36,7 +36,7 @@ "backend.uniapps.apis", "backend.bcs_web.apis.apps.APIConfig", "iam.contrib.iam_migration", - "backend.bcs_web.iam.bcs_iam_migration.apps.BcsIamMigrationConfig", + "backend.iam.bcs_iam_migration.apps.BcsIamMigrationConfig", ] # 统一登录页面 diff --git a/bcs-app/backend/tests/bcs_mocks/data/paas_cc_json.py b/bcs-app/backend/tests/bcs_mocks/data/paas_cc_json.py index 0090fa371..36a22ecd3 100644 --- a/bcs-app/backend/tests/bcs_mocks/data/paas_cc_json.py +++ b/bcs-app/backend/tests/bcs_mocks/data/paas_cc_json.py @@ -51,6 +51,78 @@ "request_id": uuid.uuid4().hex, } +resp_filter_projects_ok = { + "data": { + "results": [ + { + "approval_status": 2, + "approval_time": "2020-01-01T00:00:00+08:00", + "approver": "", + "bg_id": -1, + "bg_name": "", + "cc_app_id": 100, + "center_id": 100, + "center_name": "", + "created_at": "2020-01-01 00:00:00", + "creator": "unknown", + "data_id": 0, + "deploy_type": "null", + "dept_id": -1, + "dept_name": "", + "description": "", + "english_name": "unittest-cluster", + "extra": {}, + "is_offlined": False, + "is_secrecy": False, + "kind": 1, + "logo_addr": "", + "project_id": uuid.uuid4().hex, + "project_name": "unittest-cluster", + "project_type": 1, + "remark": "", + "updated_at": "2020-01-01 00:00:00", + "use_bk": False, + "cc_app_name": "demo-app", + "can_edit": False, + }, + { + "approval_status": 2, + "approval_time": "2020-01-01T00:00:00+08:00", + "approver": "", + "bg_id": -1, + "bg_name": "", + "cc_app_id": 100, + "center_id": 100, + "center_name": "", + "created_at": "2020-01-01 00:00:00", + "creator": "unknown", + "data_id": 0, + "deploy_type": "null", + "dept_id": -1, + "dept_name": "", + "description": "", + "english_name": "unittest-cluster-a", + "extra": {}, + "is_offlined": False, + "is_secrecy": False, + "kind": 1, + "logo_addr": "", + "project_id": uuid.uuid4().hex, + "project_name": "unittest-cluster-a", + "project_type": 1, + "remark": "", + "updated_at": "2020-01-01 00:00:00", + "use_bk": False, + "cc_app_name": "demo-app", + "can_edit": False, + }, + ] + }, + "code": 0, + "message": "OK", + "request_id": uuid.uuid4().hex, +} + resp_get_clusters_ok = { "code": 0, "data": { diff --git a/bcs-app/backend/tests/bcs_mocks/misc.py b/bcs-app/backend/tests/bcs_mocks/misc.py index b151c5ee0..caf5349d3 100644 --- a/bcs-app/backend/tests/bcs_mocks/misc.py +++ b/bcs-app/backend/tests/bcs_mocks/misc.py @@ -13,7 +13,7 @@ specific language governing permissions and limitations under the License. """ import copy -from typing import Dict +from typing import Dict, Optional from .data import paas_cc_json @@ -26,6 +26,17 @@ def get_project(self, access_token: str, project_id: str) -> Dict: resp_get_project_ok['data']['project_id'] = project_id return self._resp(resp_get_project_ok) + def get_projects(self, access_token: str, query_params: Optional[Dict]) -> Dict: + resp = self._resp(paas_cc_json.resp_filter_projects_ok) + + if not query_params: + return resp + + project_id_list = query_params['project_ids'].split(',') + resp['data']['results'][0]['project_id'] = project_id_list[0] + + return resp + def get_all_clusters(self, access_token, project_id, limit=None, offset=None, desire_all_data=0): resp = self._resp(paas_cc_json.resp_get_clusters_ok) for info in resp['data']['results']: diff --git a/bcs-app/backend/tests/components/test_paas_cc.py b/bcs-app/backend/tests/components/test_paas_cc.py index a73055a9b..10b6d24b3 100644 --- a/bcs-app/backend/tests/components/test_paas_cc.py +++ b/bcs-app/backend/tests/components/test_paas_cc.py @@ -27,6 +27,14 @@ def test_get_cluster_simple(self, project_id, cluster_id, requests_mock): assert resp == {'foo': 'bar'} assert requests_mock.called + def test_get_cluster_by_id(self, cluster_id, requests_mock): + requests_mock.get(ANY, json={'cluster_id': cluster_id}) + + client = PaaSCCClient(ComponentAuth('token')) + resp = client.get_cluster_by_id(cluster_id) + assert resp == {'cluster_id': cluster_id} + assert requests_mock.called + def test_update_cluster(self, project_id, cluster_id, requests_mock): requests_mock.put( ANY, json={"code": 0, "data": {"cluster_id": cluster_id, "project_id": project_id, "status": "normal"}} diff --git a/bcs-app/backend/tests/components/test_permissions.py b/bcs-app/backend/tests/components/test_permissions.py index 606786205..53ffb8a4a 100644 --- a/bcs-app/backend/tests/components/test_permissions.py +++ b/bcs-app/backend/tests/components/test_permissions.py @@ -15,7 +15,7 @@ import pytest from iam import OP -from backend.bcs_web.iam.permissions import ProjectPermission +from backend.iam.legacy_perms import ProjectPermission test_dict_filter_data = [ ({'op': OP.IN, 'value': [2, 1], 'field': 'project.id'}, {'project_id_list': [1, 2], 'op': OP.IN}), diff --git a/bcs-app/backend/tests/iam/open_apis/__init__.py b/bcs-app/backend/tests/iam/open_apis/__init__.py new file mode 100644 index 000000000..02a6c5cd3 --- /dev/null +++ b/bcs-app/backend/tests/iam/open_apis/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" diff --git a/bcs-app/backend/tests/iam/open_apis/conftest.py b/bcs-app/backend/tests/iam/open_apis/conftest.py new file mode 100644 index 000000000..800bac953 --- /dev/null +++ b/bcs-app/backend/tests/iam/open_apis/conftest.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import mock +import pytest + + +@pytest.fixture(autouse=True) +def patch4resource_api(): + with mock.patch( + 'backend.iam.open_apis.authentication.IamBasicAuthentication.authenticate', new=lambda *args, **kwargs: None + ), mock.patch( + 'backend.components.ssm.get_client_access_token', new=lambda *args, **kwargs: {"access_token": "test"} + ): + yield diff --git a/bcs-app/backend/tests/iam/open_apis/test_namespace.py b/bcs-app/backend/tests/iam/open_apis/test_namespace.py new file mode 100644 index 000000000..153a2355a --- /dev/null +++ b/bcs-app/backend/tests/iam/open_apis/test_namespace.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import mock +import pytest +from rest_framework.test import APIRequestFactory + +from backend.iam.open_apis.views import ResourceAPIView +from backend.tests.testing_utils.mocks.paas_cc import StubPaaSCCClient + +factory = APIRequestFactory() + + +@pytest.fixture(autouse=True) +def patch_paas_cc(): + with mock.patch('backend.iam.open_apis.provider.namespace.PaaSCCClient', new=StubPaaSCCClient): + yield + + +class TestNamespaceAPI: + def test_list_instance(self, project_id): + request = factory.post( + '/apis/iam/v1/namespaces/', + { + 'method': 'list_instance', + 'type': 'namespace', + 'page': {'offset': 0, 'limit': 1}, + 'filter': {'parent': {'id': project_id}}, + }, + ) + p_view = ResourceAPIView.as_view() + response = p_view(request) + data = response.data + assert data['count'] == 1 + assert data['results'][0]['display_name'] == 'default' + + def test_fetch_instance_info(self, cluster_id): + fetch_id = f'{cluster_id}:default' + request = factory.post( + '/apis/iam/v1/namespaces/', + { + 'method': 'fetch_instance_info', + 'type': 'namespace', + 'filter': {'ids': [fetch_id], 'parent': {'id': cluster_id}}, + }, + ) + p_view = ResourceAPIView.as_view() + response = p_view(request) + data = response.data + assert len(data) == 1 + assert data[0]['id'] == fetch_id + assert data[0]['display_name'] == 'default' diff --git a/bcs-app/backend/tests/iam/open_apis/test_project.py b/bcs-app/backend/tests/iam/open_apis/test_project.py new file mode 100644 index 000000000..a701754b7 --- /dev/null +++ b/bcs-app/backend/tests/iam/open_apis/test_project.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import mock +import pytest +from rest_framework.test import APIRequestFactory + +from backend.iam.open_apis.views import ResourceAPIView +from backend.tests.bcs_mocks.misc import FakePaaSCCMod + +factory = APIRequestFactory() + + +@pytest.fixture(autouse=True) +def patch_paas_cc(): + with mock.patch('backend.container_service.projects.base.utils.paas_cc', new=FakePaaSCCMod()): + yield + + +class TestProjectAPI: + def test_list_instance(self): + request = factory.post( + '/apis/iam/v1/projects/', {'method': 'list_instance', 'type': 'project', 'page': {'offset': 0, 'limit': 1}} + ) + p_view = ResourceAPIView.as_view() + response = p_view(request) + data = response.data + assert data['count'] == 2 + assert data['results'][0]['display_name'] == 'unittest-cluster' + + def test_fetch_instance_info(self, project_id): + request = factory.post( + '/apis/iam/v1/projects/', + { + 'method': 'fetch_instance_info', + 'type': 'project', + 'filter': {'ids': [project_id]}, + }, + ) + p_view = ResourceAPIView.as_view() + response = p_view(request) + data = response.data + assert data[0]['display_name'] == 'unittest-cluster' + assert data[0]['id'] == project_id diff --git a/bcs-app/backend/tests/testing_utils/mocks/paas_cc.py b/bcs-app/backend/tests/testing_utils/mocks/paas_cc.py index 3b908af02..1c7682df9 100644 --- a/bcs-app/backend/tests/testing_utils/mocks/paas_cc.py +++ b/bcs-app/backend/tests/testing_utils/mocks/paas_cc.py @@ -28,6 +28,10 @@ def __init__(self, *args, **kwargs): def get_cluster(self, project_id: str, cluster_id: str) -> Dict: return self.wrap_resp(self.make_cluster_data(project_id, cluster_id)) + @mockable_function + def get_cluster_by_id(self, cluster_id: str) -> Dict: + return self.make_cluster_data_by_id(cluster_id) + @mockable_function def get_project(self, project_id: str) -> Dict: return self.make_project_data(project_id) @@ -80,6 +84,40 @@ def make_cluster_data(project_id: str, cluster_id: str): 'updated_at': _stub_time, } + @staticmethod + def make_cluster_data_by_id(cluster_id: str): + _stub_time = '2021-01-01T00:00:00+08:00' + return { + 'area_id': 1, + 'artifactory': '', + 'capacity_updated_at': _stub_time, + 'cluster_id': cluster_id, + 'cluster_num': 1, + 'config_svr_count': 0, + 'created_at': _stub_time, + 'creator': 'unknown', + 'description': 'cluster description', + 'disabled': False, + 'environment': 'stag', + 'extra_cluster_id': '', + 'ip_resource_total': 0, + 'ip_resource_used': 0, + 'master_count': 0, + 'name': 'test-cluster', + 'need_nat': True, + 'node_count': 1, + 'project_id': uuid.uuid4().hex, + 'remain_cpu': 10, + 'remain_disk': 0, + 'remain_mem': 10, + 'status': 'normal', + 'total_cpu': 12, + 'total_disk': 0, + 'total_mem': 64, + 'type': 'k8s', + 'updated_at': _stub_time, + } + @staticmethod def make_project_data(project_id: str): _stub_time = '2021-01-01T00:00:00+08:00' diff --git a/bcs-app/backend/utils/permissions.py b/bcs-app/backend/utils/permissions.py index c780a608b..21f3fc1bd 100644 --- a/bcs-app/backend/utils/permissions.py +++ b/bcs-app/backend/utils/permissions.py @@ -16,8 +16,8 @@ from rest_framework.permissions import BasePermission from backend.accounts import bcs_perm -from backend.bcs_web.iam import permissions from backend.components import paas_auth, paas_cc +from backend.iam import legacy_perms as permissions from backend.utils import FancyDict from backend.utils.cache import region from backend.utils.error_codes import error_codes From 28bec4b9def4012fb700d73ab1de2de6b471dceb Mon Sep 17 00:00:00 2001 From: jamesgetx Date: Sat, 9 Oct 2021 10:58:06 +0800 Subject: [PATCH 3/5] minor: update license --- bcs-app/backend/bcs_web/permissions.py | 2 ++ bcs-app/backend/iam/bcs_iam_migration/__init__.py | 2 ++ bcs-app/backend/iam/open_apis/__init__.py | 2 ++ bcs-app/backend/iam/open_apis/constants.py | 2 ++ bcs-app/backend/iam/open_apis/exceptions.py | 2 ++ bcs-app/backend/iam/open_apis/provider/__init__.py | 2 ++ bcs-app/backend/iam/open_apis/provider/namespace.py | 2 ++ bcs-app/backend/iam/open_apis/provider/project.py | 2 ++ bcs-app/backend/iam/open_apis/provider/resource.py | 2 ++ bcs-app/backend/iam/open_apis/provider/utils.py | 2 ++ bcs-app/backend/iam/open_apis/serializers.py | 2 ++ bcs-app/backend/iam/open_apis/urls.py | 2 ++ bcs-app/backend/iam/open_apis/v1/__init__.py | 2 ++ bcs-app/backend/iam/open_apis/v1/urls.py | 2 ++ bcs-app/backend/iam/open_apis/views.py | 2 ++ bcs-app/backend/iam/permissions/client.py | 2 ++ bcs-app/backend/iam/permissions/resources/constants.py | 2 ++ bcs-app/backend/iam/permissions/resources/namespace.py | 2 ++ bcs-app/backend/iam/permissions/resources/templateset.py | 2 ++ bcs-app/backend/tests/iam/fake_iam.py | 2 ++ bcs-app/backend/tests/iam/open_apis/__init__.py | 2 ++ bcs-app/backend/tests/iam/open_apis/conftest.py | 2 ++ bcs-app/backend/tests/iam/open_apis/test_namespace.py | 2 ++ bcs-app/backend/tests/iam/open_apis/test_project.py | 2 ++ bcs-app/backend/tests/iam/permissions/__init__.py | 2 ++ bcs-app/backend/tests/iam/permissions/conftest.py | 2 ++ bcs-app/backend/tests/iam/permissions/test_cluster.py | 2 ++ bcs-app/backend/tests/iam/permissions/test_namespace.py | 2 ++ bcs-app/backend/tests/iam/permissions/test_project.py | 2 ++ bcs-app/backend/tests/iam/permissions/test_templateset.py | 2 ++ 30 files changed, 60 insertions(+) diff --git a/bcs-app/backend/bcs_web/permissions.py b/bcs-app/backend/bcs_web/permissions.py index 607ff87c4..a9baeb6e1 100644 --- a/bcs-app/backend/bcs_web/permissions.py +++ b/bcs-app/backend/bcs_web/permissions.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/bcs_iam_migration/__init__.py b/bcs-app/backend/iam/bcs_iam_migration/__init__.py index 02a6c5cd3..08f8f6ab8 100644 --- a/bcs-app/backend/iam/bcs_iam_migration/__init__.py +++ b/bcs-app/backend/iam/bcs_iam_migration/__init__.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/open_apis/__init__.py b/bcs-app/backend/iam/open_apis/__init__.py index 02a6c5cd3..08f8f6ab8 100644 --- a/bcs-app/backend/iam/open_apis/__init__.py +++ b/bcs-app/backend/iam/open_apis/__init__.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/open_apis/constants.py b/bcs-app/backend/iam/open_apis/constants.py index 6124af3eb..73e43586e 100644 --- a/bcs-app/backend/iam/open_apis/constants.py +++ b/bcs-app/backend/iam/open_apis/constants.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/open_apis/exceptions.py b/bcs-app/backend/iam/open_apis/exceptions.py index 641982e7b..027d9e902 100644 --- a/bcs-app/backend/iam/open_apis/exceptions.py +++ b/bcs-app/backend/iam/open_apis/exceptions.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/open_apis/provider/__init__.py b/bcs-app/backend/iam/open_apis/provider/__init__.py index 02a6c5cd3..08f8f6ab8 100644 --- a/bcs-app/backend/iam/open_apis/provider/__init__.py +++ b/bcs-app/backend/iam/open_apis/provider/__init__.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/open_apis/provider/namespace.py b/bcs-app/backend/iam/open_apis/provider/namespace.py index 09f725e4e..82d113602 100644 --- a/bcs-app/backend/iam/open_apis/provider/namespace.py +++ b/bcs-app/backend/iam/open_apis/provider/namespace.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/open_apis/provider/project.py b/bcs-app/backend/iam/open_apis/provider/project.py index 7b72c2a92..7951a7a00 100644 --- a/bcs-app/backend/iam/open_apis/provider/project.py +++ b/bcs-app/backend/iam/open_apis/provider/project.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/open_apis/provider/resource.py b/bcs-app/backend/iam/open_apis/provider/resource.py index cff423152..703f5785a 100644 --- a/bcs-app/backend/iam/open_apis/provider/resource.py +++ b/bcs-app/backend/iam/open_apis/provider/resource.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/open_apis/provider/utils.py b/bcs-app/backend/iam/open_apis/provider/utils.py index f35b5590e..3fd7664ce 100644 --- a/bcs-app/backend/iam/open_apis/provider/utils.py +++ b/bcs-app/backend/iam/open_apis/provider/utils.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/open_apis/serializers.py b/bcs-app/backend/iam/open_apis/serializers.py index a57b11224..82b12b1ba 100644 --- a/bcs-app/backend/iam/open_apis/serializers.py +++ b/bcs-app/backend/iam/open_apis/serializers.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/open_apis/urls.py b/bcs-app/backend/iam/open_apis/urls.py index e92874507..fc94015a6 100644 --- a/bcs-app/backend/iam/open_apis/urls.py +++ b/bcs-app/backend/iam/open_apis/urls.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/open_apis/v1/__init__.py b/bcs-app/backend/iam/open_apis/v1/__init__.py index 02a6c5cd3..08f8f6ab8 100644 --- a/bcs-app/backend/iam/open_apis/v1/__init__.py +++ b/bcs-app/backend/iam/open_apis/v1/__init__.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/open_apis/v1/urls.py b/bcs-app/backend/iam/open_apis/v1/urls.py index 0b9aee694..d970421c8 100644 --- a/bcs-app/backend/iam/open_apis/v1/urls.py +++ b/bcs-app/backend/iam/open_apis/v1/urls.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/open_apis/views.py b/bcs-app/backend/iam/open_apis/views.py index 1b87f5a5b..623962818 100644 --- a/bcs-app/backend/iam/open_apis/views.py +++ b/bcs-app/backend/iam/open_apis/views.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/permissions/client.py b/bcs-app/backend/iam/permissions/client.py index 436fc342f..ef6cf29a4 100644 --- a/bcs-app/backend/iam/permissions/client.py +++ b/bcs-app/backend/iam/permissions/client.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/permissions/resources/constants.py b/bcs-app/backend/iam/permissions/resources/constants.py index 086f02617..85b62c74b 100644 --- a/bcs-app/backend/iam/permissions/resources/constants.py +++ b/bcs-app/backend/iam/permissions/resources/constants.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/permissions/resources/namespace.py b/bcs-app/backend/iam/permissions/resources/namespace.py index 5b31d4294..e645373fb 100644 --- a/bcs-app/backend/iam/permissions/resources/namespace.py +++ b/bcs-app/backend/iam/permissions/resources/namespace.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/iam/permissions/resources/templateset.py b/bcs-app/backend/iam/permissions/resources/templateset.py index 3bf1e54b1..791440478 100644 --- a/bcs-app/backend/iam/permissions/resources/templateset.py +++ b/bcs-app/backend/iam/permissions/resources/templateset.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/tests/iam/fake_iam.py b/bcs-app/backend/tests/iam/fake_iam.py index f36e62dcf..c4c292f38 100644 --- a/bcs-app/backend/tests/iam/fake_iam.py +++ b/bcs-app/backend/tests/iam/fake_iam.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/tests/iam/open_apis/__init__.py b/bcs-app/backend/tests/iam/open_apis/__init__.py index 02a6c5cd3..08f8f6ab8 100644 --- a/bcs-app/backend/tests/iam/open_apis/__init__.py +++ b/bcs-app/backend/tests/iam/open_apis/__init__.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/tests/iam/open_apis/conftest.py b/bcs-app/backend/tests/iam/open_apis/conftest.py index 800bac953..bdef52054 100644 --- a/bcs-app/backend/tests/iam/open_apis/conftest.py +++ b/bcs-app/backend/tests/iam/open_apis/conftest.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/tests/iam/open_apis/test_namespace.py b/bcs-app/backend/tests/iam/open_apis/test_namespace.py index 153a2355a..5f9117580 100644 --- a/bcs-app/backend/tests/iam/open_apis/test_namespace.py +++ b/bcs-app/backend/tests/iam/open_apis/test_namespace.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/tests/iam/open_apis/test_project.py b/bcs-app/backend/tests/iam/open_apis/test_project.py index a701754b7..9594d0b04 100644 --- a/bcs-app/backend/tests/iam/open_apis/test_project.py +++ b/bcs-app/backend/tests/iam/open_apis/test_project.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/tests/iam/permissions/__init__.py b/bcs-app/backend/tests/iam/permissions/__init__.py index 02a6c5cd3..08f8f6ab8 100644 --- a/bcs-app/backend/tests/iam/permissions/__init__.py +++ b/bcs-app/backend/tests/iam/permissions/__init__.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/tests/iam/permissions/conftest.py b/bcs-app/backend/tests/iam/permissions/conftest.py index b326c790d..89034aad0 100644 --- a/bcs-app/backend/tests/iam/permissions/conftest.py +++ b/bcs-app/backend/tests/iam/permissions/conftest.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/tests/iam/permissions/test_cluster.py b/bcs-app/backend/tests/iam/permissions/test_cluster.py index 1deb7f210..2c07d7981 100644 --- a/bcs-app/backend/tests/iam/permissions/test_cluster.py +++ b/bcs-app/backend/tests/iam/permissions/test_cluster.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/tests/iam/permissions/test_namespace.py b/bcs-app/backend/tests/iam/permissions/test_namespace.py index 9fb9f5b6b..d7a6f2085 100644 --- a/bcs-app/backend/tests/iam/permissions/test_namespace.py +++ b/bcs-app/backend/tests/iam/permissions/test_namespace.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/tests/iam/permissions/test_project.py b/bcs-app/backend/tests/iam/permissions/test_project.py index a18387a84..c834be0d2 100644 --- a/bcs-app/backend/tests/iam/permissions/test_project.py +++ b/bcs-app/backend/tests/iam/permissions/test_project.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/tests/iam/permissions/test_templateset.py b/bcs-app/backend/tests/iam/permissions/test_templateset.py index d57fd4119..e3e86aeb0 100644 --- a/bcs-app/backend/tests/iam/permissions/test_templateset.py +++ b/bcs-app/backend/tests/iam/permissions/test_templateset.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. From 465a03272efd62ac442751da826b52e0db1315e4 Mon Sep 17 00:00:00 2001 From: jamesgetx Date: Sat, 9 Oct 2021 14:21:17 +0800 Subject: [PATCH 4/5] chore: add requests_mock --- bcs-app/backend/settings/ce/dev.py | 2 +- .../backend/tests/components/test_paas_cc.py | 2 +- bcs-app/poetry.lock | 56 ++++++++++++++++++- bcs-app/pyproject.toml | 1 + 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/bcs-app/backend/settings/ce/dev.py b/bcs-app/backend/settings/ce/dev.py index e90c49324..965c86d9a 100644 --- a/bcs-app/backend/settings/ce/dev.py +++ b/bcs-app/backend/settings/ce/dev.py @@ -37,7 +37,7 @@ ] # 本地开发先去除权限中心v3的数据初始逻辑 -INSTALLED_APPS.remove("backend.bcs_web.iam.bcs_iam_migration.apps.BcsIamMigrationConfig") +INSTALLED_APPS.remove("backend.iam.bcs_iam_migration.apps.BcsIamMigrationConfig") LOG_LEVEL = "DEBUG" LOGGING = get_logging_config(LOG_LEVEL) diff --git a/bcs-app/backend/tests/components/test_paas_cc.py b/bcs-app/backend/tests/components/test_paas_cc.py index 10b6d24b3..2d6ea694d 100644 --- a/bcs-app/backend/tests/components/test_paas_cc.py +++ b/bcs-app/backend/tests/components/test_paas_cc.py @@ -28,7 +28,7 @@ def test_get_cluster_simple(self, project_id, cluster_id, requests_mock): assert requests_mock.called def test_get_cluster_by_id(self, cluster_id, requests_mock): - requests_mock.get(ANY, json={'cluster_id': cluster_id}) + requests_mock.get(ANY, json={'code': 0, 'data': {'cluster_id': cluster_id}}) client = PaaSCCClient(ComponentAuth('token')) resp = client.get_cluster_by_id(cluster_id) diff --git a/bcs-app/poetry.lock b/bcs-app/poetry.lock index ec5e7f254..0f4aaa465 100644 --- a/bcs-app/poetry.lock +++ b/bcs-app/poetry.lock @@ -1302,6 +1302,22 @@ urllib3 = ">=1.21.1,<1.25" security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)"] socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +[[package]] +name = "requests-mock" +version = "1.9.3" +description = "Mock out responses from the requests package" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +requests = ">=2.3,<3" +six = "*" + +[package.extras] +fixture = ["fixtures"] +test = ["fixtures", "mock", "purl", "pytest", "sphinx", "testrepository (>=0.0.18)", "testtools"] + [[package]] name = "requests-oauthlib" version = "1.3.0" @@ -1698,7 +1714,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "1.1" python-versions = ">=3.6.1,<3.7" -content-hash = "6263a238def4da72cfb2d5d3a7617ebabbdcdeb1289935c2beef112c981b4382" +content-hash = "1c2b95cd5bb9ad43c2f1aee35cc395c1942e917b65552cee4e38d7249fdb8b7a" [metadata.files] aiohttp = [ @@ -1837,24 +1853,36 @@ cffi = [ {file = "cffi-1.14.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058"}, {file = "cffi-1.14.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5"}, {file = "cffi-1.14.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132"}, + {file = "cffi-1.14.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24ec4ff2c5c0c8f9c6b87d5bb53555bf267e1e6f70e52e5a9740d32861d36b6f"}, + {file = "cffi-1.14.5-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c3f39fa737542161d8b0d680df2ec249334cd70a8f420f71c9304bd83c3cbed"}, + {file = "cffi-1.14.5-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:681d07b0d1e3c462dd15585ef5e33cb021321588bebd910124ef4f4fb71aef55"}, {file = "cffi-1.14.5-cp36-cp36m-win32.whl", hash = "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53"}, {file = "cffi-1.14.5-cp36-cp36m-win_amd64.whl", hash = "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813"}, {file = "cffi-1.14.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73"}, {file = "cffi-1.14.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06"}, {file = "cffi-1.14.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1"}, {file = "cffi-1.14.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49"}, + {file = "cffi-1.14.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06d7cd1abac2ffd92e65c0609661866709b4b2d82dd15f611e602b9b188b0b69"}, + {file = "cffi-1.14.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f861a89e0043afec2a51fd177a567005847973be86f709bbb044d7f42fc4e05"}, + {file = "cffi-1.14.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc5a8e069b9ebfa22e26d0e6b97d6f9781302fe7f4f2b8776c3e1daea35f1adc"}, {file = "cffi-1.14.5-cp37-cp37m-win32.whl", hash = "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62"}, {file = "cffi-1.14.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4"}, {file = "cffi-1.14.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053"}, {file = "cffi-1.14.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0"}, {file = "cffi-1.14.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e"}, {file = "cffi-1.14.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827"}, + {file = "cffi-1.14.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04c468b622ed31d408fea2346bec5bbffba2cc44226302a0de1ade9f5ea3d373"}, + {file = "cffi-1.14.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:06db6321b7a68b2bd6df96d08a5adadc1fa0e8f419226e25b2a5fbf6ccc7350f"}, + {file = "cffi-1.14.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:293e7ea41280cb28c6fcaaa0b1aa1f533b8ce060b9e701d78511e1e6c4a1de76"}, {file = "cffi-1.14.5-cp38-cp38-win32.whl", hash = "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e"}, {file = "cffi-1.14.5-cp38-cp38-win_amd64.whl", hash = "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396"}, {file = "cffi-1.14.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea"}, {file = "cffi-1.14.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322"}, {file = "cffi-1.14.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c"}, {file = "cffi-1.14.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee"}, + {file = "cffi-1.14.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bf1ac1984eaa7675ca8d5745a8cb87ef7abecb5592178406e55858d411eadc0"}, + {file = "cffi-1.14.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:df5052c5d867c1ea0b311fb7c3cd28b19df469c056f7fdcfe88c7473aa63e333"}, + {file = "cffi-1.14.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:24a570cd11895b60829e941f2613a4f79df1a27344cbbb82164ef2e0116f09c7"}, {file = "cffi-1.14.5-cp39-cp39-win32.whl", hash = "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396"}, {file = "cffi-1.14.5-cp39-cp39-win_amd64.whl", hash = "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d"}, {file = "cffi-1.14.5.tar.gz", hash = "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"}, @@ -1935,8 +1963,10 @@ cryptography = [ {file = "cryptography-3.4.7-cp36-abi3-win_amd64.whl", hash = "sha256:de4e5f7f68220d92b7637fc99847475b59154b7a1b3868fb7385337af54ac9ca"}, {file = "cryptography-3.4.7-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:26965837447f9c82f1855e0bc8bc4fb910240b6e0d16a664bb722df3b5b06873"}, {file = "cryptography-3.4.7-pp36-pypy36_pp73-manylinux2014_x86_64.whl", hash = "sha256:eb8cc2afe8b05acbd84a43905832ec78e7b3873fb124ca190f574dca7389a87d"}, + {file = "cryptography-3.4.7-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b01fd6f2737816cb1e08ed4807ae194404790eac7ad030b34f2ce72b332f5586"}, {file = "cryptography-3.4.7-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:7ec5d3b029f5fa2b179325908b9cd93db28ab7b85bb6c1db56b10e0b54235177"}, {file = "cryptography-3.4.7-pp37-pypy37_pp73-manylinux2014_x86_64.whl", hash = "sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9"}, + {file = "cryptography-3.4.7-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:bf40af59ca2465b24e54f671b2de2c59257ddc4f7e5706dbd6930e26823668d3"}, {file = "cryptography-3.4.7.tar.gz", hash = "sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713"}, ] curlify = [ @@ -2187,12 +2217,22 @@ mako = [ {file = "Mako-1.0.7.tar.gz", hash = "sha256:4e02fde57bd4abb5ec400181e4c314f56ac3e49ba4fb8b0d50bba18cb27d25ae"}, ] markupsafe = [ + {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, @@ -2201,14 +2241,21 @@ markupsafe = [ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, @@ -2218,6 +2265,9 @@ markupsafe = [ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, @@ -2481,6 +2531,10 @@ requests = [ {file = "requests-2.21.0-py2.py3-none-any.whl", hash = "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"}, {file = "requests-2.21.0.tar.gz", hash = "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e"}, ] +requests-mock = [ + {file = "requests-mock-1.9.3.tar.gz", hash = "sha256:8d72abe54546c1fc9696fa1516672f1031d72a55a1d66c85184f972a24ba0eba"}, + {file = "requests_mock-1.9.3-py2.py3-none-any.whl", hash = "sha256:0a2d38a117c08bb78939ec163522976ad59a6b7fdd82b709e23bb98004a44970"}, +] requests-oauthlib = [ {file = "requests-oauthlib-1.3.0.tar.gz", hash = "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"}, {file = "requests_oauthlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d"}, diff --git a/bcs-app/pyproject.toml b/bcs-app/pyproject.toml index 144b2878e..e0ebf05fc 100644 --- a/bcs-app/pyproject.toml +++ b/bcs-app/pyproject.toml @@ -68,6 +68,7 @@ mock = "3.0.5" pytest = "6.2.4" pytest-django = "3.9.0" pytest-asyncio = "^0.15.1" +requests-mock = "1.9.3" [tool.black] exclude = ''' From 188ac4680c946221b6a90628b7c3d49082d31fbe Mon Sep 17 00:00:00 2001 From: jamesgetx Date: Mon, 11 Oct 2021 21:36:55 +0800 Subject: [PATCH 5/5] minor: fix review --- .../0006_bk_bcs_app_202108181525.py | 2 +- bcs-app/backend/iam/open_apis/v1/urls.py | 5 +- bcs-app/backend/iam/open_apis/views.py | 2 +- bcs-app/backend/iam/permissions/client.py | 4 +- bcs-app/backend/iam/permissions/request.py | 3 +- .../support-files/iam/0005_templateset.json | 182 ++++++++++++++++++ ...ion_group.json => 0006_action_groups.json} | 0 7 files changed, 191 insertions(+), 7 deletions(-) rename bcs-app/support-files/iam/{0006_action_group.json => 0006_action_groups.json} (100%) diff --git a/bcs-app/backend/iam/bcs_iam_migration/migrations/0006_bk_bcs_app_202108181525.py b/bcs-app/backend/iam/bcs_iam_migration/migrations/0006_bk_bcs_app_202108181525.py index 6464edc72..63b5d80b8 100644 --- a/bcs-app/backend/iam/bcs_iam_migration/migrations/0006_bk_bcs_app_202108181525.py +++ b/bcs-app/backend/iam/bcs_iam_migration/migrations/0006_bk_bcs_app_202108181525.py @@ -28,7 +28,7 @@ def forward_func(apps, schema_editor): class Migration(migrations.Migration): - migration_json = "0006_action_group.json" + migration_json = "0006_action_groups.json" dependencies = [('bcs_iam_migration', '0005_bk_bcs_app_202108181525')] diff --git a/bcs-app/backend/iam/open_apis/v1/urls.py b/bcs-app/backend/iam/open_apis/v1/urls.py index d970421c8..fe206cb59 100644 --- a/bcs-app/backend/iam/open_apis/v1/urls.py +++ b/bcs-app/backend/iam/open_apis/v1/urls.py @@ -16,7 +16,8 @@ from .. import views +resource_types = 'projects|namespaces' + urlpatterns = [ - url(r'^projects/', views.ResourceAPIView.as_view()), - url(r'^namespaces/', views.ResourceAPIView.as_view()), + url(r'^(?P{})/'.format(resource_types), views.ResourceAPIView.as_view()), ] diff --git a/bcs-app/backend/iam/open_apis/views.py b/bcs-app/backend/iam/open_apis/views.py index 623962818..59e84665f 100644 --- a/bcs-app/backend/iam/open_apis/views.py +++ b/bcs-app/backend/iam/open_apis/views.py @@ -37,7 +37,7 @@ def _get_options(self, request): request.LANGUAGE_CODE = "en" return {"language": language} - def post(self, request): + def post(self, request, *args, **kwargs): serializer = QueryResourceSLZ(data=request.data) serializer.is_valid(raise_exception=True) diff --git a/bcs-app/backend/iam/permissions/client.py b/bcs-app/backend/iam/permissions/client.py index ef6cf29a4..af2d29f44 100644 --- a/bcs-app/backend/iam/permissions/client.py +++ b/bcs-app/backend/iam/permissions/client.py @@ -63,7 +63,7 @@ def resource_inst_multi_actions_allowed( 判断用户对某个资源实例是否具有多个操作的权限. note: 权限判断与资源实例有关,如更新某个具体资源 - :returns 示例 {'project_view': True, 'project_edit': False} + :return 示例 {'project_view': True, 'project_edit': False} """ actions = [Action(action_id) for action_id in action_ids] request = MultiActionRequest( @@ -78,7 +78,7 @@ def batch_resource_multi_actions_allowed( 判断用户对某些资源是否具有多个指定操作的权限 note: 当前sdk仅支持同类型的资源 - :returns 示例 {'0ad86c25363f4ef8adcb7ac67a483837': {'project_view': True, 'project_edit': False}} + :return 示例 {'0ad86c25363f4ef8adcb7ac67a483837': {'project_view': True, 'project_edit': False}} """ actions = [Action(action_id) for action_id in action_ids] request = MultiActionRequest(settings.APP_ID, Subject("user", username), actions, [], None) diff --git a/bcs-app/backend/iam/permissions/request.py b/bcs-app/backend/iam/permissions/request.py index 44bcf7fdf..9a4382bbb 100644 --- a/bcs-app/backend/iam/permissions/request.py +++ b/bcs-app/backend/iam/permissions/request.py @@ -12,6 +12,7 @@ an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ +from abc import ABC from collections import namedtuple from typing import Dict, List, Optional, Union @@ -20,7 +21,7 @@ from iam.apply import models -class ResourceRequest: +class ResourceRequest(ABC): resource_type: str = '' attr: Optional[Dict] = None diff --git a/bcs-app/support-files/iam/0005_templateset.json b/bcs-app/support-files/iam/0005_templateset.json index e69de29bb..751b65ad2 100644 --- a/bcs-app/support-files/iam/0005_templateset.json +++ b/bcs-app/support-files/iam/0005_templateset.json @@ -0,0 +1,182 @@ +{ + "system_id": "bk_bcs_app", + "operations": [ + { + "operation": "upsert_resource_type", + "data": { + "id": "templateset", + "name": "模板集", + "name_en": "templateset", + "description": "模板集", + "description_en": "templateset", + "provider_config": { + "path": "/o/bk_bcs_app/apis/iam/v1/templatesets/" + }, + "version": 1 + } + }, + { + "operation": "upsert_instance_selection", + "data": { + "id": "templateset_list", + "name": "模板集", + "name_en": "templateset", + "resource_type_chain": [ + {"system_id": "bk_bcs_app", "id": "project"}, + {"system_id": "bk_bcs_app", "id": "templateset"} + ] + } + }, + { + "operation": "upsert_action", + "data": { + "id": "templateset_create", + "name": "模板集创建", + "name_en": "create templateset", + "description": "用户创建模板集", + "description_en": "user create templateset", + "type": "create", + "related_resource_types": [{ + "system_id": "bk_bcs_app", + "id": "project", + "related_instance_selections": [{ + "system_id": "bk_bcs_app", + "id": "project_list" + }] + }], + "related_actions": ["project_view"], + "version": 1 + } + }, + { + "operation": "upsert_action", + "data": { + "id": "templateset_view", + "name": "模板集查看", + "name_en": "view templateset", + "description": "用户查看模板集", + "description_en": "user view templateset", + "type": "view", + "related_resource_types": [{ + "system_id": "bk_bcs_app", + "id": "templateset", + "name_alias": "", + "name_alias_en": "", + "selection_mode": "instance", + "related_instance_selections": [ + { + "system_id": "bk_bcs_app", + "id": "templateset_list" + } + ] + }], + "related_actions": ["project_view"], + "version": 1 + } + }, + { + "operation": "upsert_action", + "data": { + "id": "templateset_copy", + "name": "模板集复制", + "name_en": "copy templateset", + "description": "用户复制模板集", + "description_en": "user copy templateset", + "type": "use", + "related_resource_types": [{ + "system_id": "bk_bcs_app", + "id": "templateset", + "name_alias": "", + "name_alias_en": "", + "selection_mode": "instance", + "related_instance_selections": [ + { + "system_id": "bk_bcs_app", + "id": "templateset_list" + } + ] + }], + "related_actions": ["project_view", "templateset_view"], + "version": 1 + } + }, + { + "operation": "upsert_action", + "data": { + "id": "templateset_update", + "name": "模板集更新", + "name_en": "update templateset", + "description": "用户更新模板集", + "description_en": "user update templateset", + "type": "edit", + "related_resource_types": [{ + "system_id": "bk_bcs_app", + "id": "templateset", + "name_alias": "", + "name_alias_en": "", + "selection_mode": "instance", + "related_instance_selections": [ + { + "system_id": "bk_bcs_app", + "id": "templateset_list" + } + ] + }], + "related_actions": ["project_view", "templateset_view"], + "version": 1 + } + }, + { + "operation": "upsert_action", + "data": { + "id": "templateset_delete", + "name": "模板集删除", + "name_en": "delete templateset", + "description": "用户删除模板集", + "description_en": "user delete templateset", + "type": "delete", + "related_resource_types": [{ + "system_id": "bk_bcs_app", + "id": "templateset", + "name_alias": "", + "name_alias_en": "", + "selection_mode": "instance", + "related_instance_selections": [ + { + "system_id": "bk_bcs_app", + "id": "templateset_list" + } + ] + }], + "related_actions": ["project_view", "templateset_view"], + "version": 1 + } + }, + { + "operation": "upsert_action", + "data": { + "id": "templateset_instantiate", + "name": "模板集实例化", + "name_en": "instantiate templateset", + "description": "用户实例化模板集", + "description_en": "user instantiate templateset", + "type": "use", + "related_resource_types": [{ + "system_id": "bk_bcs_app", + "id": "templateset", + "name_alias": "", + "name_alias_en": "", + "selection_mode": "instance", + "related_instance_selections": [ + { + "system_id": "bk_bcs_app", + "id": "templateset_list" + } + ] + }], + "related_actions": ["project_view", "templateset_view"], + "version": 1 + } + } + ] +} diff --git a/bcs-app/support-files/iam/0006_action_group.json b/bcs-app/support-files/iam/0006_action_groups.json similarity index 100% rename from bcs-app/support-files/iam/0006_action_group.json rename to bcs-app/support-files/iam/0006_action_groups.json