def process(self, request, *args, **kwargs): if request.is_trust: return template_source = request.GET.get("template_source", PROJECT) template_id = kwargs["template_id"] subject = Subject("user", request.user.username) if template_source in NON_COMMON_TEMPLATE_TYPES: action = Action(IAMMeta.FLOW_VIEW_ACTION) resources = res_factory.resources_for_flow(template_id) allow_or_raise_auth_failed(iam, IAMMeta.SYSTEM_ID, subject, action, resources, cache=True) else: action = Action(IAMMeta.COMMON_FLOW_VIEW_ACTION) resources = res_factory.resources_for_common_flow(template_id) allow_or_raise_auth_failed(iam, IAMMeta.SYSTEM_ID, subject, action, resources, cache=True)
class TemplatePermissionMixin: """ 两种Template统一的鉴权逻辑,需要通过template_type区分 """ iam_mapping_config = { "project": { "delete_action": Action(IAMMeta.FLOW_DELETE_ACTION), "resources_list_func": res_factory.resources_list_for_flows, }, "common": { "delete_action": Action(IAMMeta.COMMON_FLOW_DELETE_ACTION), "resources_list_func": res_factory.resources_list_for_common_flows, }, } def has_permission(self, request, view): if view.action == "batch_delete": self.check_batch_delete_permission(request, view) return True def check_batch_delete_permission(self, request, view): template_ids = request.data.get("template_ids") or [] action = self.iam_mapping_config[self.template_type]["delete_action"] resources_list = self.iam_mapping_config[ self.template_type]["resources_list_func"](template_ids) allow_or_raise_immediate_response_for_resources_list( iam=iam, system=IAMMeta.SYSTEM_ID, subject=Subject("user", request.user.username), action=action, resources_list=resources_list, )
def has_permission(self, request, view): if view.action == "list": if "project_id" not in request.query_params: return False project_resources = res_factory.resources_for_project(request.query_params["project_id"]) allow_or_raise_auth_failed( iam=iam, system=IAMMeta.SYSTEM_ID, subject=Subject("user", request.user.username), action=Action(IAMMeta.PROJECT_VIEW_ACTION), resources=project_resources, ) elif view.action == "create": # let serializer to handle this if "project_id" not in request.data: return True project_resources = res_factory.resources_for_project(request.data["project_id"]) allow_or_raise_auth_failed( iam=iam, system=IAMMeta.SYSTEM_ID, subject=Subject("user", request.user.username), action=Action(IAMMeta.PROJECT_EDIT_ACTION), resources=project_resources, ) return True
def process(self, request, *args, **kwargs): if request.is_trust: return params = json.loads(request.body) template_source = params.get("template_source", PROJECT) template_id = kwargs["template_id"] subject = Subject("user", request.user.username) if template_source in NON_COMMON_TEMPLATE_TYPES: action = Action(IAMMeta.FLOW_CREATE_TASK_ACTION) resources = res_factory.resources_for_flow(template_id) allow_or_raise_auth_failed(iam, IAMMeta.SYSTEM_ID, subject, action, resources, cache=True) else: action = Action(IAMMeta.COMMON_FLOW_CREATE_TASK_ACTION) resources = [ res_factory.resources_for_common_flow(template_id)[0], res_factory.resources_for_project_obj(request.project)[0], ] allow_or_raise_auth_failed(iam, IAMMeta.SYSTEM_ID, subject, action, resources, cache=True)
def check_project_or_admin_view_action_for_user(project_id, username): iam = get_iam_client() action = Action(IAMMeta.PROJECT_VIEW_ACTION) if project_id else Action(IAMMeta.ADMIN_VIEW_ACTION) resources = res_factory.resources_for_project(project_id) if project_id else [] allow_or_raise_immediate_response( iam=iam, system=IAMMeta.SYSTEM_ID, subject=Subject("user", username), action=action, resources=resources, )
def create_detail(self, object_list, bundle): system = self.helper.system subject = self.helper.get_subject(bundle) action = Action(self.helper.create_action) resources = self.helper.get_create_detail_resources(bundle) request = Request( system, subject, action, resources, self.helper.get_create_detail_environment(bundle), ) allowed = self.iam.is_allowed(request) logger.debug( "tastypie create_detail is_allowed request({}) result: {}".format( request.to_dict(), allowed)) if not allowed: raise ImmediateHttpResponse( IAMAuthFailedResponse( AuthFailedException(system, subject, action, resources))) return allowed
def alter_detail_data_to_serialize(self, request, data): helper = getattr(self._meta, "iam_resource_helper", None) if not helper: return data bundle = data # 1. get resources resources = helper.get_resources(bundle) # 2. make request request = MultiActionRequest( helper.system, helper.get_subject_for_alter_detail(request, data), [Action(action) for action in helper.actions], resources, helper.get_environment_for_alter_detail(request, data), ) actions_allowed = helper.iam.resource_multi_actions_allowed(request) logger.debug( "tastypie alter_detail_data_to_serialize resource_multi_actions_allowed request({}) result: {}".format( request.to_dict(), actions_allowed ) ) # 3. assemble action allowed data bundle.data["auth_actions"] = [action for action, allowed in actions_allowed.items() if allowed] return data
def obj_get_list(self, bundle, **kwargs): allow_or_raise_immediate_response( iam=iam, system=IAMMeta.SYSTEM_ID, subject=Subject("user", bundle.request.user.username), action=Action(IAMMeta.ADMIN_VIEW_ACTION), resources=[], ) filters = {} if hasattr(bundle.request, "GET"): # Grab a mutable copy. filters = bundle.request.GET.copy() # Update with the provided kwargs. filters.update(kwargs) applicable_filters = self.build_filters(filters=filters) try: objects = self.apply_filters(bundle.request, applicable_filters) return self.authorized_read_list(objects, bundle) except ValueError: raise BadRequest("Invalid resource lookup params provided")
def _check_user_scheme_permission(username, template_id, project_id=None): if project_id is None: try: template = TaskTemplate.objects.get(pk=template_id) except TaskTemplate.DoesNotExist: message = "flow template[id={template_id}] does not exist".format(template_id=template_id) logger.error(message) raise BadRequest(message) else: try: template = TaskTemplate.objects.get(pk=template_id, project_id=project_id) except TaskTemplate.DoesNotExist: message = "flow template[id={template_id}] in project[id={project_id}] does not exist".format( template_id=template_id, project_id=project_id ) logger.error(message) raise BadRequest(message) allow_or_raise_immediate_response( iam=iam, system=IAMMeta.SYSTEM_ID, subject=Subject("user", username), action=Action(IAMMeta.FLOW_EDIT_ACTION), resources=res_factory.resources_for_flow_obj(template), ) return True, template
def process(self, request, *args, **kwargs): data = request.data template_id_list = data["template_id_list"] subject = Subject("user", request.user.username) action = Action(IAMMeta.FLOW_VIEW_ACTION) resources_list = res_factory.resources_list_for_flows(template_id_list) if not resources_list: return resources_map = {} for resources in resources_list: resources_map[resources[0].id] = resources request = Request(IAMMeta.SYSTEM_ID, subject, action, [], {}) result = iam.batch_is_allowed(request, resources_list) if not result: raise MultiAuthFailedException(IAMMeta.SYSTEM_ID, subject, action, resources_list) not_allowed_list = [] for tid, allow in result.items(): if not allow: not_allowed_list.append(resources_map[tid]) if not_allowed_list: raise MultiAuthFailedException(IAMMeta.SYSTEM_ID, subject, action, not_allowed_list)
def obj_create(self, bundle, **kwargs): try: project_id = bundle.data.pop("project__id") template_id = bundle.data.pop("template_id") common_template = CommonTemplate.objects.get(pk=template_id) except Exception: raise BadRequest("common template does not exist") allow_or_raise_immediate_response( iam=iam, system=IAMMeta.SYSTEM_ID, subject=Subject("user", bundle.request.user.username), action=Action(IAMMeta.COMMON_FLOW_EDIT_ACTION), resources=res_factory.resources_for_common_flow_obj( common_template), ) bundle.data["name"] = standardize_name(bundle.data["name"], TEMPLATE_NODE_NAME_MAX_LENGTH) kwargs["unique_id"] = "%s-%s-%s" % (project_id, template_id, bundle.data["name"]) if TemplateScheme.objects.filter( unique_id=kwargs["unique_id"]).exists(): raise BadRequest( "common template scheme name has existed, please change the name" ) kwargs["template"] = common_template.pipeline_template return super(CommonTemplateSchemeResource, self).obj_create(bundle, **kwargs)
def get_resources_allowed_actions_for_user(username, system_id, actions, resources_list): subject = Subject("user", username) actions = [Action(act) for act in actions] request = MultiActionRequest(system_id, subject, actions, [], {}) iam = get_iam_client() return iam.batch_resource_multi_actions_allowed(request, resources_list)
def scheme_allow_or_raise_auth_failed(request, template_id=None): data = request.query_params or request.data if template_id is None: template_id = data.get("template_id") # 项目流程方案的权限控制 if "project_id" in data or data.get("template_type") != "common": # 默认进行是否有流程查看权限校验 scheme_action = IAMMeta.FLOW_VIEW_ACTION scheme_resources = res_factory.resources_for_flow(template_id) # 公共流程方案的权限控制 else: # 默认进行是否有流程查看权限校验 scheme_action = IAMMeta.COMMON_FLOW_VIEW_ACTION scheme_resources = res_factory.resources_for_common_flow( template_id) allow_or_raise_auth_failed( iam=iam, system=IAMMeta.SYSTEM_ID, subject=Subject("user", request.user.username), action=Action(scheme_action), resources=scheme_resources, ) return True
def obj_create(self, bundle, **kwargs): allow_or_raise_immediate_response( iam=iam, system=IAMMeta.SYSTEM_ID, subject=Subject("user", bundle.request.user.username), action=Action(IAMMeta.ADMIN_EDIT_ACTION), resources=[], ) model = bundle.obj.__class__ if model.objects.filter(status=RUNNING).exists(): raise BadRequest( "There is already a running sync task, please wait for it to complete and try again" ) if not CachePackageSource.objects.all().exists(): raise BadRequest( "No cache package found, please add cache package in Backend Manage" ) if len(PackageSourceResource.get_all_source_objects()) <= 1: raise BadRequest( "No original packages found, please add original packages in Backend Manage" ) return super(SyncTaskResource, self).obj_create(bundle, **kwargs)
def iam_get_instance_auth_actions(self, request, instance): helper = getattr(self, "iam_resource_helper", None) if not helper: return None # 1. get resources resources = helper.get_resources(instance) # 2. make request request = MultiActionRequest( helper.system, helper.get_subject_for_alter_detail(request, instance), [Action(action) for action in helper.actions], resources, helper.get_environment_for_alter_detail(request, instance), ) actions_allowed = helper.iam.resource_multi_actions_allowed(request) iam_logger.debug( "[drf iam_get_instance_auth_actions] resource_multi_actions_allowed request({}) result: {}" .format(request.to_dict(), actions_allowed)) # 3. assemble action allowed data auth_actions = [ action for action, allowed in actions_allowed.items() if allowed ] return auth_actions
def obj_create(self, bundle, **kwargs): allow_or_raise_immediate_response( iam=iam, system=IAMMeta.SYSTEM_ID, subject=Subject("user", bundle.request.user.username), action=Action(IAMMeta.ADMIN_EDIT_ACTION), resources=[], ) try: origins = bundle.data.pop("origins") caches = bundle.data.pop("caches") except KeyError: raise BadRequest("Invalid params, please check origins and caches") with transaction.atomic(): # collect packages of all origin to cache cache_packages = {} for origin in origins: try: jsonschema.validate(origin, ADD_SOURCE_SCHEMA) except jsonschema.ValidationError as e: message = "Invalid origin source params: %s" % e logger.error(message) raise BadRequest(message) cache_packages.update(origin["packages"]) # create cache first if caches exist for cache in caches: try: jsonschema.validate(cache, ADD_SOURCE_SCHEMA) except jsonschema.ValidationError as e: message = "Invalid cache source params: %s" % e logger.error(message) raise BadRequest(message) try: CachePackageSource.objects.add_cache_source( cache["name"], cache["type"], cache_packages, cache.get("desc", ""), **cache["details"]) except exceptions.GcloudExternalPluginsError as e: message = "Create cache source raise error: %s" % e logger.error(message) raise BadRequest(message) # create origins after for origin in origins: source_type = origin["type"] details = origin["details"] # divide details into two parts,base_kwargs mains fields in base model(e.g. fields of # pipeline.contrib.external_plugins.models.GitRepoSource) # original_kwargs mains field in origin model but not in base model(e.g. repo_address、desc) source_model = source_cls_factory[source_type] original_kwargs, base_kwargs = source_model.objects.divide_details_parts( source_type, details) original_kwargs["desc"] = origin.get("desc", "") source_model.objects.add_original_source( origin["name"], source_type, origin["packages"], original_kwargs, **base_kwargs)
def iam_auth_check(request, action, resources): allow_or_raise_auth_failed( iam=iam, system=IAMMeta.SYSTEM_ID, subject=Subject("user", request.user.username), action=Action(action), resources=resources, )
def process(self, request, *args, **kwargs): if request.is_trust: return subject = Subject("user", request.user.username) action = Action(IAMMeta.FUNCTION_VIEW_ACTION) resources = [] allow_or_raise_auth_failed(iam, IAMMeta.SYSTEM_ID, subject, action, resources, cache=True)
def _make_request(self, username: str, action_id: str, resources: Optional[List[Resource]] = None) -> Request: return Request( settings.BK_IAM_SYSTEM_ID, Subject("user", username), Action(action_id), resources, None, )
def process(self, request, *args, **kwargs): if request.is_trust: return task_id = kwargs["task_id"] subject = Subject("user", request.user.username) action = Action(IAMMeta.TASK_CLAIM_ACTION) resources = res_factory.resources_for_task(task_id) allow_or_raise_auth_failed(iam, IAMMeta.SYSTEM_ID, subject, action, resources, cache=True)
def obj_get_list(self, bundle, **kwargs): allow_or_raise_immediate_response( iam=iam, system=IAMMeta.SYSTEM_ID, subject=Subject("user", bundle.request.user.username), action=Action(IAMMeta.ADMIN_VIEW_ACTION), resources=[], ) return super(SyncTaskResource, self).obj_get_list(bundle, **kwargs)
def iam_resource_auth_or_raise(username, action, resource_id=None, get_resource_func=None): iam = get_iam_client() action = Action(action) subject = Subject("user", username) resources = None if get_resource_func: resources = getattr(res_factory, get_resource_func)(resource_id) request = Request(IAMMeta.SYSTEM_ID, subject, action, resources or [], {}) if not iam.is_allowed(request): raise AuthFailedException(IAMMeta.SYSTEM_ID, subject, action, resources or [])
def process(self, request, *args, **kwargs): subject = Subject("user", request.user.username) action = Action(self.action) request = Request(IAMMeta.SYSTEM_ID, subject, action, [], {}) allowed = iam.is_allowed(request) if not allowed: raise AuthFailedException(IAMMeta.SYSTEM_ID, subject, action, [])
def resource_inst_multi_actions_allowed(self, username, actions_ids, resource_id): resource = Resource(settings.APP_ID, self.resource_type_id, resource_id, {}) actions = [Action(action_id) for action_id in actions_ids] request = MultiActionRequest(settings.APP_ID, Subject("user", username), actions, [resource], None) return self.iam.resource_multi_actions_allowed(request)
def has_object_permission(self, request, view, obj): project_resources = res_factory.resources_for_project(obj.project_id) allow_or_raise_auth_failed( iam=iam, system=IAMMeta.SYSTEM_ID, subject=Subject("user", request.user.username), action=Action(IAMMeta.PROJECT_EDIT_ACTION), resources=project_resources, ) return True
def batch_resource_multi_actions_allowed( self, username: str, action_ids: List[str], resources: List[Resource] ) -> Dict[str, Dict[str, bool]]: """ 判断用户对某些资源是否具有多个指定操作的权限. 当前sdk仅支持同类型的资源 :return 示例 {'0ad86c25363f4ef8adcb7ac67a483837': {'project_view': True, 'project_edit': False}} """ actions = [Action(action_id) for action_id in action_ids] request = MultiActionRequest(settings.BK_IAM_SYSTEM_ID, Subject("user", username), actions, [], None) resources_list = [[res] for res in resources] return self.iam.batch_resource_multi_actions_allowed(request, resources_list)
def _make_request_with_resources(self, username, action_id, resources=None): request = Request( settings.APP_ID, Subject("user", username), Action(action_id), resources, None, ) return request
def process(self, request, *args, **kwargs): task_id = self.get_task_id(request, *args, **kwargs) subject = Subject("user", request.user.username) action = Action(self.action) resources = res_factory.resources_for_task(task_id) request = Request(IAMMeta.SYSTEM_ID, subject, action, resources, {}) allowed = iam.is_allowed_with_cache(request) if not allowed: raise AuthFailedException(IAMMeta.SYSTEM_ID, subject, action, resources)
def list(self, request, *args, **kwargs): project_id = request.query_params.get("project_id") if not project_id: raise ValidationException("project_id should be provided.") allow_or_raise_auth_failed( iam=iam, system=IAMMeta.SYSTEM_ID, subject=Subject("user", request.user.username), action=Action(IAMMeta.PROJECT_VIEW_ACTION), resources=res_factory.resources_for_project(project_id), ) return super(NewLabelViewSet, self).list(request, *args, **kwargs)
def resource_inst_multi_actions_allowed( self, username: str, action_ids: List[str], resources: List[Resource] ) -> Dict[str, bool]: """ 判断用户对某个(单个)资源实例是否具有多个操作的权限. note: 权限判断与资源实例有关,如更新某个具体资源 :return 示例 {'project_view': True, 'project_edit': False} """ actions = [Action(action_id) for action_id in action_ids] request = MultiActionRequest(settings.BK_IAM_SYSTEM_ID, Subject("user", username), actions, resources, None) return self.iam.resource_multi_actions_allowed(request)