# -*- coding:utf-8 -*- import functools import six from flask import current_app, g, request from flask import session, abort from api.lib.cmdb.const import ResourceTypeEnum as CmdbResourceType from api.lib.cmdb.const import RoleEnum from api.lib.perm.acl.cache import AppCache from api.lib.perm.acl.cache import RoleCache from api.lib.perm.acl.cache import UserCache from api.lib.perm.acl.permission import PermissionCRUD from api.lib.perm.acl.resource import ResourceCRUD from api.lib.perm.acl.role import RoleCRUD from api.models.acl import Resource from api.models.acl import ResourceGroup from api.models.acl import ResourceType from api.models.acl import Role CMDB_RESOURCE_TYPES = CmdbResourceType.all() class ACLManager(object): def __init__(self): self.app_id = AppCache.get('cmdb') if not self.app_id: raise Exception("cmdb not in acl apps") self.app_id = self.app_id.id def _get_resource(self, name, resource_type_name): resource_type = ResourceType.get_by(name=resource_type_name, first=True, to_dict=False) resource_type or abort(404, "ResourceType <{0}> cannot be found".format(resource_type_name)) return Resource.get_by(resource_type_id=resource_type.id, app_id=self.app_id, name=name, first=True, to_dict=False) def _get_resource_group(self, name): return ResourceGroup.get_by( app_id=self.app_id, name=name, first=True, to_dict=False ) def _get_role(self, name): user = UserCache.get(name) if user: return Role.get_by(name=name, uid=user.uid, first=True, to_dict=False) return Role.get_by(name=name, app_id=self.app_id, first=True, to_dict=False) def add_resource(self, name, resource_type_name=None): resource_type = ResourceType.get_by(name=resource_type_name, first=True, to_dict=False) resource_type or abort(404, "ResourceType <{0}> cannot be found".format(resource_type_name)) ResourceCRUD.add(name, resource_type.id, self.app_id) def grant_resource_to_role(self, name, role, resource_type_name=None, permissions=None): resource = self._get_resource(name, resource_type_name) role = self._get_role(role) if resource: PermissionCRUD.grant(role.id, permissions, resource_id=resource.id) else: group = self._get_resource_group(name) if group: PermissionCRUD.grant(role.id, permissions, group_id=group.id) def del_resource(self, name, resource_type_name=None): resource = self._get_resource(name, resource_type_name) if resource: ResourceCRUD.delete(resource.id) def has_permission(self, resource_name, resource_type, perm): role = self._get_role(g.user.username) role or abort(404, "Role <{0}> is not found".format(g.user.username)) return RoleCRUD.has_permission(role.id, resource_name, resource_type, self.app_id, perm) def validate_permission(resources, resource_type, perm): if not resources: return if current_app.config.get("USE_ACL"): if g.user.username == "worker": return resources = [resources] if isinstance(resources, six.string_types) else resources for resource in resources: if not ACLManager().has_permission(resource, resource_type, perm): return abort(403, "has no permission") def has_perm(resources, resource_type, perm): def decorator_has_perm(func): @functools.wraps(func) def wrapper_has_perm(*args, **kwargs): if not resources: return if current_app.config.get("USE_ACL"): if is_app_admin(): return func(*args, **kwargs) validate_permission(resources, resource_type, perm) return func(*args, **kwargs) return wrapper_has_perm return decorator_has_perm def is_app_admin(app=None): if RoleEnum.CONFIG in session.get("acl", {}).get("parentRoles", []): return True app = app or 'cmdb' app_id = AppCache.get(app).id for role_name in session.get("acl", {}).get("parentRoles", []): role = RoleCache.get_by_name(app_id, role_name) if role and role.is_app_admin: return True return False def has_perm_from_args(arg_name, resource_type, perm, callback=None): def decorator_has_perm(func): @functools.wraps(func) def wrapper_has_perm(*args, **kwargs): if not arg_name: return resource = request.view_args.get(arg_name) or request.values.get(arg_name) if callback is not None and resource: resource = callback(resource) if current_app.config.get("USE_ACL") and resource: if is_app_admin(): return func(*args, **kwargs) validate_permission(resource, resource_type, perm) return func(*args, **kwargs) return wrapper_has_perm return decorator_has_perm def role_required(role_name): def decorator_role_required(func): @functools.wraps(func) def wrapper_role_required(*args, **kwargs): if not role_name: return if current_app.config.get("USE_ACL"): if role_name not in session.get("acl", {}).get("parentRoles", []) and not is_app_admin(): return abort(403, "Role {0} is required".format(role_name)) return func(*args, **kwargs) return wrapper_role_required return decorator_role_required