mirror of
				https://github.com/veops/cmdb.git
				synced 2025-11-04 13:46:17 +08:00 
			
		
		
		
	Partially completed backend development of permissions management
This commit is contained in:
		@@ -0,0 +1 @@
 | 
			
		||||
# -*- coding:utf-8 -*-
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										91
									
								
								api/lib/perm/acl/cache.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								api/lib/perm/acl/cache.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
			
		||||
# -*- coding:utf-8 -*-
 | 
			
		||||
 | 
			
		||||
from api.extensions import cache
 | 
			
		||||
from api.models.acl import Role
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RoleCache(object):
 | 
			
		||||
    PREFIX_ID = "Role::id::{0}"
 | 
			
		||||
    PREFIX_NAME = "Role::app_id::{0}::name::{1}"
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_by_name(cls, app_id, name):
 | 
			
		||||
        role = cache.get(cls.PREFIX_NAME.format(app_id, name))
 | 
			
		||||
        if role is None:
 | 
			
		||||
            role = Role.get_by(app_id=app_id, name=name, first=True, to_dict=False)
 | 
			
		||||
            if role is not None:
 | 
			
		||||
                cache.set(cls.PREFIX_NAME.format(app_id, name), role)
 | 
			
		||||
 | 
			
		||||
        return role
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get(cls, rid):
 | 
			
		||||
        role = cache.get(cls.PREFIX_ID.format(rid))
 | 
			
		||||
        if role is None:
 | 
			
		||||
            role = Role.get_by_id(rid)
 | 
			
		||||
            if role is not None:
 | 
			
		||||
                cache.set(cls.PREFIX_ID.format(rid), role)
 | 
			
		||||
 | 
			
		||||
        return role
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def clean(cls, rid):
 | 
			
		||||
        cache.delete(cls.PREFIX_ID.format(rid))
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def clean_by_name(cls, app_id, name):
 | 
			
		||||
        cache.delete(cls.PREFIX_NAME.format(app_id, name))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RoleRelationCache(object):
 | 
			
		||||
    PREFIX_PARENT = "RoleRelationParent::id::{0}"
 | 
			
		||||
    PREFIX_CHILDREN = "RoleRelationChildren::id::{0}"
 | 
			
		||||
    PREFIX_RESOURCES = "RoleRelationResources::id::{0}"
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_parent_ids(cls, rid):
 | 
			
		||||
        parent_ids = cache.get(cls.PREFIX_PARENT.format(rid))
 | 
			
		||||
        if not parent_ids:
 | 
			
		||||
            from api.lib.perm.acl.role import RoleCRUD
 | 
			
		||||
            parent_ids = RoleCRUD.get_parent_ids(rid)
 | 
			
		||||
            cache.set(cls.PREFIX_PARENT.format(rid), parent_ids, timeout=0)
 | 
			
		||||
 | 
			
		||||
        return parent_ids
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_child_ids(cls, rid):
 | 
			
		||||
        child_ids = cache.get(cls.PREFIX_CHILDREN.format(rid))
 | 
			
		||||
        if not child_ids:
 | 
			
		||||
            from api.lib.perm.acl.role import RoleCRUD
 | 
			
		||||
            child_ids = RoleCRUD.get_child_ids(rid)
 | 
			
		||||
            cache.set(cls.PREFIX_CHILDREN.format(rid), child_ids, timeout=0)
 | 
			
		||||
 | 
			
		||||
        return child_ids
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_resources(cls, rid):
 | 
			
		||||
        """
 | 
			
		||||
        :param rid: 
 | 
			
		||||
        :return: {id2perms: {resource_id: [perm,]}, group2perms: {group_id: [perm, ]}}
 | 
			
		||||
        """
 | 
			
		||||
        resources = cache.get(cls.PREFIX_RESOURCES.format(rid))
 | 
			
		||||
        if not resources:
 | 
			
		||||
            from api.lib.perm.acl.role import RoleCRUD
 | 
			
		||||
            resources = RoleCRUD.get_resources(rid)
 | 
			
		||||
            cache.set(cls.PREFIX_RESOURCES.format(rid), resources, timeout=0)
 | 
			
		||||
 | 
			
		||||
        return resources or {}
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def rebuild(cls, rid):
 | 
			
		||||
        cls.clean(rid)
 | 
			
		||||
 | 
			
		||||
        cls.get_parent_ids(rid)
 | 
			
		||||
        cls.get_child_ids(rid)
 | 
			
		||||
        cls.get_resources(rid)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def clean(cls, rid):
 | 
			
		||||
        cache.delete(cls.PREFIX_PARENT.format(rid))
 | 
			
		||||
        cache.delete(cls.PREFIX_CHILDREN.format(rid))
 | 
			
		||||
        cache.delete(cls.PREFIX_RESOURCES.format(rid))
 | 
			
		||||
							
								
								
									
										4
									
								
								api/lib/perm/acl/const.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								api/lib/perm/acl/const.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
# -*- coding:utf-8 -*-
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ACL_QUEUE = "acl_async"
 | 
			
		||||
							
								
								
									
										158
									
								
								api/lib/perm/acl/resource.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								api/lib/perm/acl/resource.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,158 @@
 | 
			
		||||
# -*- coding:utf-8 -*-
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from flask import abort
 | 
			
		||||
 | 
			
		||||
from api.extensions import db
 | 
			
		||||
from api.models.acl import Permission
 | 
			
		||||
from api.models.acl import Resource
 | 
			
		||||
from api.models.acl import ResourceGroup
 | 
			
		||||
from api.models.acl import ResourceGroupItems
 | 
			
		||||
from api.models.acl import ResourceType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResourceTypeCRUD(object):
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def search(q, app_id, page=1, page_size=None):
 | 
			
		||||
        query = db.session.query(ResourceType).filter(
 | 
			
		||||
            ResourceType.deleted.is_(False)).filter(ResourceType.app_id == app_id)
 | 
			
		||||
        if q:
 | 
			
		||||
            query = query.filter(ResourceType.name.ilike('%{0}%'.format(q)))
 | 
			
		||||
 | 
			
		||||
        numfound = query.count()
 | 
			
		||||
 | 
			
		||||
        return numfound, query.offset((page - 1) * page_size).limit(page_size)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def add(cls, app_id, name, perms):
 | 
			
		||||
        ResourceType.get_by(name=name, app_id=app_id) and abort(
 | 
			
		||||
            400, "ResourceType <{0}> is already existed".format(name))
 | 
			
		||||
 | 
			
		||||
        rt = ResourceType.create(name=name, app_id=app_id)
 | 
			
		||||
 | 
			
		||||
        cls.update_perms(rt.id, perms, app_id)
 | 
			
		||||
 | 
			
		||||
        return rt
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def update(cls, rt_id, **kwargs):
 | 
			
		||||
        rt = ResourceType.get_by_id(rt_id) or abort(404, "ResourceType <{0}> is not found".format(rt_id))
 | 
			
		||||
        if 'name' in kwargs:
 | 
			
		||||
            other = ResourceType.get_by(name=kwargs['name'], app_id=rt.app_id, to_dict=False, first=True)
 | 
			
		||||
            if other.id != rt_id:
 | 
			
		||||
                return abort(400, "ResourceType <{0}> is duplicated".format(kwargs['name']))
 | 
			
		||||
 | 
			
		||||
        if 'perms' in kwargs:
 | 
			
		||||
            cls.update_perms(rt_id, kwargs['perms'], rt.app_id)
 | 
			
		||||
 | 
			
		||||
        return rt.update(**kwargs)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def delete(cls, rt_id):
 | 
			
		||||
        rt = ResourceType.get_by_id(rt_id) or abort(404, "ResourceType <{0}> is not found".format(rt_id))
 | 
			
		||||
 | 
			
		||||
        cls.update_perms(rt_id, [], rt.app_id)
 | 
			
		||||
 | 
			
		||||
        rt.soft_delete()
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def update_perms(cls, rt_id, perms, app_id):
 | 
			
		||||
        existed = Permission.get_by(resource_type_id=rt_id, to_dict=False)
 | 
			
		||||
        existed_names = [i.name for i in existed]
 | 
			
		||||
 | 
			
		||||
        for i in existed:
 | 
			
		||||
            if i.name not in perms:
 | 
			
		||||
                i.soft_delete()
 | 
			
		||||
 | 
			
		||||
        for i in perms:
 | 
			
		||||
            if i not in existed_names:
 | 
			
		||||
                Permission.create(resource_type_id=rt_id,
 | 
			
		||||
                                  name=i,
 | 
			
		||||
                                  app_id=app_id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResourceGroupCRUD(object):
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def search(q, app_id, page=1, page_size=None):
 | 
			
		||||
        query = db.session.query(ResourceGroup).filter(
 | 
			
		||||
            ResourceGroup.deleted.is_(False)).filter(ResourceGroup.app_id == app_id)
 | 
			
		||||
 | 
			
		||||
        if q:
 | 
			
		||||
            query = query.filter(ResourceGroup.name.ilike("%{0}%".format(q)))
 | 
			
		||||
 | 
			
		||||
        numfound = query.count()
 | 
			
		||||
 | 
			
		||||
        return numfound, query.offset((page - 1) * page_size).limit(page_size)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_items(rg_id):
 | 
			
		||||
        items = ResourceGroupItems.get_by(group_id=rg_id, to_dict=False)
 | 
			
		||||
 | 
			
		||||
        return [i.resource.to_dict() for i in items]
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def add(name, type_id, app_id):
 | 
			
		||||
        ResourceGroup.get(name=name, resource_type_id=type_id, app_id=app_id) and abort(
 | 
			
		||||
            400, "ResourceGroup <{0}> is already existed".format(name))
 | 
			
		||||
 | 
			
		||||
        return ResourceGroup.create(name=name, resource_type_id=type_id, app_id=app_id)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def update(rg_id, items):
 | 
			
		||||
        existed = ResourceGroupItems.get_by(group_id=rg_id, to_dict=False)
 | 
			
		||||
        existed_ids = [i.resource_id for i in existed]
 | 
			
		||||
 | 
			
		||||
        for i in existed:
 | 
			
		||||
            if i.resource_id not in items:
 | 
			
		||||
                i.soft_delete()
 | 
			
		||||
 | 
			
		||||
        for _id in items:
 | 
			
		||||
            if _id not in existed_ids:
 | 
			
		||||
                ResourceGroupItems.create(group_id=rg_id, resource_id=_id)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def delete(rg_id):
 | 
			
		||||
        rg = ResourceGroup.get_by_id(rg_id) or abort(404, "ResourceGroup <{0}> is not found".format(rg_id))
 | 
			
		||||
 | 
			
		||||
        rg.soft_delete()
 | 
			
		||||
 | 
			
		||||
        items = ResourceGroupItems.get_by(group_id=rg_id, to_dict=False)
 | 
			
		||||
        for item in items:
 | 
			
		||||
            item.soft_delete()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResourceCRUD(object):
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def search(q, app_id, page=1, page_size=None):
 | 
			
		||||
        query = db.session.query(Resource).filter(
 | 
			
		||||
            Resource.deleted.is_(False)).filter(Resource.app_id == app_id)
 | 
			
		||||
 | 
			
		||||
        if q:
 | 
			
		||||
            query = query.filter(Resource.name.ilike("%{0}%".format(q)))
 | 
			
		||||
 | 
			
		||||
        numfound = query.count()
 | 
			
		||||
 | 
			
		||||
        return numfound, query.offset((page - 1) * page_size).limit(page_size)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def add(name, type_id, app_id):
 | 
			
		||||
        Resource.get(name=name, resource_type_id=type_id, app_id=app_id) and abort(
 | 
			
		||||
            400, "Resource <{0}> is already existed".format(name))
 | 
			
		||||
 | 
			
		||||
        return Resource.create(name=name, resource_type_id=type_id, app_id=app_id)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def update(_id, name):
 | 
			
		||||
        resource = Resource.get_by_id(_id) or abort(404, "Resource <{0}> is not found".format(_id))
 | 
			
		||||
 | 
			
		||||
        other = Resource.get_by(name=name, resource_type_id=resource.resource_type_id, to_dict=False, first=True)
 | 
			
		||||
        if other.id != _id:
 | 
			
		||||
            return abort(400, "Resource <{0}> is duplicated".format(name))
 | 
			
		||||
 | 
			
		||||
        return resource.update(name=name)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def delete(_id):
 | 
			
		||||
        resource = Resource.get_by_id(_id) or abort(404, "Resource <{0}> is not found".format(_id))
 | 
			
		||||
 | 
			
		||||
        resource.soft_delete()
 | 
			
		||||
							
								
								
									
										168
									
								
								api/lib/perm/acl/role.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								api/lib/perm/acl/role.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,168 @@
 | 
			
		||||
# -*- coding:utf-8 -*-
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from flask import abort
 | 
			
		||||
 | 
			
		||||
from api.extensions import db
 | 
			
		||||
from api.lib.perm.acl.cache import RoleCache
 | 
			
		||||
from api.lib.perm.acl.cache import RoleRelationCache
 | 
			
		||||
from api.lib.perm.acl.const import ACL_QUEUE
 | 
			
		||||
from api.models.acl import Resource
 | 
			
		||||
from api.models.acl import ResourceGroupItems
 | 
			
		||||
from api.models.acl import Role
 | 
			
		||||
from api.models.acl import RolePermission
 | 
			
		||||
from api.models.acl import RoleRelation
 | 
			
		||||
from api.tasks.acl import role_rebuild
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RoleRelationCRUD(object):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RoleCRUD(object):
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def search(q, app_id, page=1, page_size=None):
 | 
			
		||||
        query = db.session.query(Role).filter(Role.deleted.is_(False)).filter(
 | 
			
		||||
            Role.app_id == app_id).filter(Role.uid.is_(None))
 | 
			
		||||
        if q:
 | 
			
		||||
            query = query.filter(Role.name.ilike('%{0}%'.format(q)))
 | 
			
		||||
 | 
			
		||||
        numfound = query.count()
 | 
			
		||||
 | 
			
		||||
        return numfound, query.offset((page - 1) * page_size).limit(page_size)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def add_role(name, app_id, is_app_admin=False, uid=None):
 | 
			
		||||
        Role.get_by(name=name, app_id=app_id) and abort(400, "Role <{0}> is already existed".format(name))
 | 
			
		||||
 | 
			
		||||
        return Role.create(name=name,
 | 
			
		||||
                           app_id=app_id,
 | 
			
		||||
                           is_app_admin=is_app_admin,
 | 
			
		||||
                           uid=uid)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def update_role(rid, **kwargs):
 | 
			
		||||
        role = Role.get_by_id(rid) or abort(404, "Role <{0}> does not exist".format(rid))
 | 
			
		||||
 | 
			
		||||
        RoleCache.clean(rid)
 | 
			
		||||
 | 
			
		||||
        return role.update(**kwargs)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def delete_role(cls, rid):
 | 
			
		||||
        role = Role.get_by_id(rid) or abort(404, "Role <{0}> does not exist".format(rid))
 | 
			
		||||
 | 
			
		||||
        parent_ids = cls.get_parent_ids(rid)
 | 
			
		||||
        child_ids = cls.get_child_ids(rid)
 | 
			
		||||
 | 
			
		||||
        for i in RoleRelation.get_by(parent_id=rid, to_dict=False):
 | 
			
		||||
            i.soft_delete()
 | 
			
		||||
        for i in RoleRelation.get_by(child_id=rid, to_dict=False):
 | 
			
		||||
            i.soft_delete()
 | 
			
		||||
 | 
			
		||||
        for i in RolePermission.get_by(rid=rid, to_dict=False):
 | 
			
		||||
            i.soft_delete()
 | 
			
		||||
 | 
			
		||||
        role_rebuild.apply_async(args=(parent_ids + child_ids,), queue=ACL_QUEUE)
 | 
			
		||||
 | 
			
		||||
        RoleCache.clean(rid)
 | 
			
		||||
        RoleRelationCache.clean(rid)
 | 
			
		||||
 | 
			
		||||
        role.soft_delete()
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_resources(rid):
 | 
			
		||||
        res = RolePermission.get_by(rid=rid, to_dict=False)
 | 
			
		||||
        id2perms = dict(id2perms={}, group2perms={})
 | 
			
		||||
        for i in res:
 | 
			
		||||
            if i.resource_id:
 | 
			
		||||
                id2perms['id2perms'].setdefault(i.resource_id, []).append(i.perm.name)
 | 
			
		||||
            elif i.group_id:
 | 
			
		||||
                id2perms['group2perms'].setdefault(i.group_id, []).append(i.perm.name)
 | 
			
		||||
 | 
			
		||||
        return id2perms
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_group_ids(resource_id):
 | 
			
		||||
        return [i.group_id for i in ResourceGroupItems.get_by(resource_id, to_dict=False)]
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_parent_ids(rid):
 | 
			
		||||
        res = RoleRelation.get_by(child_id=rid, to_dict=False)
 | 
			
		||||
 | 
			
		||||
        return [i.parent_id for i in res]
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_child_ids(rid):
 | 
			
		||||
        res = RoleRelation.get_by(child_id=rid, to_dict=False)
 | 
			
		||||
 | 
			
		||||
        return [i.parent_id for i in res]
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def recursive_parent_ids(cls, rid):
 | 
			
		||||
        all_parent_ids = set()
 | 
			
		||||
 | 
			
		||||
        def _get_parent(_id):
 | 
			
		||||
            all_parent_ids.add(_id)
 | 
			
		||||
            parent_ids = RoleRelationCache.get_parent_ids(_id)
 | 
			
		||||
            for parent_id in parent_ids:
 | 
			
		||||
                _get_parent(parent_id)
 | 
			
		||||
 | 
			
		||||
        _get_parent(rid)
 | 
			
		||||
 | 
			
		||||
        return all_parent_ids
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def recursive_child_ids(cls, rid):
 | 
			
		||||
        all_child_ids = set()
 | 
			
		||||
 | 
			
		||||
        def _get_children(_id):
 | 
			
		||||
            all_child_ids.add(_id)
 | 
			
		||||
            child_ids = RoleRelationCache.get_child_ids(_id)
 | 
			
		||||
            for child_id in child_ids:
 | 
			
		||||
                _get_children(child_id)
 | 
			
		||||
 | 
			
		||||
        _get_children(rid)
 | 
			
		||||
 | 
			
		||||
        return all_child_ids
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def has_permission(cls, rid, resource_name, perm):
 | 
			
		||||
        resource = Resource.get_by(name=resource_name, first=True, to_dict=False)
 | 
			
		||||
        resource = resource or abort(403, "Resource <{0}> is not in ACL".format(resource_name))
 | 
			
		||||
 | 
			
		||||
        parent_ids = cls.recursive_parent_ids(rid)
 | 
			
		||||
 | 
			
		||||
        group_ids = cls.get_group_ids(resource.id)
 | 
			
		||||
 | 
			
		||||
        for parent_id in parent_ids:
 | 
			
		||||
            id2perms = RoleRelationCache.get_resources(parent_id)
 | 
			
		||||
 | 
			
		||||
            perms = id2perms['id2perms'].get(resource.id, [])
 | 
			
		||||
            if perms and {perm}.issubset(set(perms)):
 | 
			
		||||
                return True
 | 
			
		||||
 | 
			
		||||
            for group_id in group_ids:
 | 
			
		||||
                perms = id2perms['group2perms'].get(group_id, [])
 | 
			
		||||
                if perms and {perm}.issubset(set(perms)):
 | 
			
		||||
                    return True
 | 
			
		||||
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_permissions(cls, rid, resource_name):
 | 
			
		||||
        resource = Resource.get_by(name=resource_name, first=True, to_dict=False)
 | 
			
		||||
        resource = resource or abort(403, "Resource <{0}> is not in ACL".format(resource_name))
 | 
			
		||||
 | 
			
		||||
        parent_ids = cls.recursive_parent_ids(rid)
 | 
			
		||||
        group_ids = cls.get_group_ids(resource.id)
 | 
			
		||||
 | 
			
		||||
        perms = []
 | 
			
		||||
        for parent_id in parent_ids:
 | 
			
		||||
            id2perms = RoleRelationCache.get_resources(parent_id)
 | 
			
		||||
            perms += id2perms['id2perms'].get(parent_id, [])
 | 
			
		||||
 | 
			
		||||
            for group_id in group_ids:
 | 
			
		||||
                perms += id2perms['group2perms'].get(group_id, [])
 | 
			
		||||
 | 
			
		||||
        return set(perms)
 | 
			
		||||
@@ -3,3 +3,4 @@
 | 
			
		||||
 | 
			
		||||
from .account import User
 | 
			
		||||
from .cmdb import *
 | 
			
		||||
from .acl import *
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										91
									
								
								api/models/acl.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								api/models/acl.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
			
		||||
# -*- coding:utf-8 -*-
 | 
			
		||||
 | 
			
		||||
from api.extensions import db
 | 
			
		||||
from api.lib.database import Model
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class App(Model):
 | 
			
		||||
    __tablename__ = "acl_apps"
 | 
			
		||||
 | 
			
		||||
    name = db.Column(db.String(64), index=True)
 | 
			
		||||
    description = db.Column(db.Text)
 | 
			
		||||
    app_id = db.Column(db.Text)
 | 
			
		||||
    secret_key = db.Column(db.Text)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Role(Model):
 | 
			
		||||
    __tablename__ = "acl_roles"
 | 
			
		||||
 | 
			
		||||
    name = db.Column(db.Text, nullable=False)
 | 
			
		||||
    is_app_admin = db.Column(db.Boolean, default=False)
 | 
			
		||||
    app_id = db.Column(db.Integer, db.ForeignKey("acl_apps.id"))
 | 
			
		||||
    uid = db.Column(db.Integer, db.ForeignKey("users.uid"))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RoleRelation(Model):
 | 
			
		||||
    __tablename__ = "acl_role_relations"
 | 
			
		||||
 | 
			
		||||
    parent_id = db.Column(db.Integer, db.ForeignKey('acl_roles.id'))
 | 
			
		||||
    child_id = db.Column(db.Integer, db.ForeignKey('acl_roles.id'))
 | 
			
		||||
 | 
			
		||||
    __table_args__ = (
 | 
			
		||||
        db.UniqueConstraint("parent_id", "child_id", name="role_relation_unique"),)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResourceType(Model):
 | 
			
		||||
    __tablename__ = "acl_resource_types"
 | 
			
		||||
 | 
			
		||||
    name = db.Column(db.String(64), index=True)
 | 
			
		||||
    description = db.Column(db.Text)
 | 
			
		||||
    app_id = db.Column(db.Integer, db.ForeignKey('acl_apps.id'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResourceGroup(Model):
 | 
			
		||||
    __tablename__ = "acl_resource_groups"
 | 
			
		||||
 | 
			
		||||
    name = db.Column(db.String(64), index=True, nullable=False)
 | 
			
		||||
    resource_type_id = db.Column(db.Integer, db.ForeignKey("acl_resource_types.id"))
 | 
			
		||||
 | 
			
		||||
    app_id = db.Column(db.Integer, db.ForeignKey('acl_apps.id'))
 | 
			
		||||
 | 
			
		||||
    __table_args__ = (db.UniqueConstraint("name", "resource_type_id", "app_id", name="resource_group_app_unique"),)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Resource(Model):
 | 
			
		||||
    __tablename__ = "acl_resources"
 | 
			
		||||
 | 
			
		||||
    name = db.Column(db.String(128), nullable=False)
 | 
			
		||||
    resource_type_id = db.Column(db.Integer, db.ForeignKey("acl_resource_types.id"))
 | 
			
		||||
 | 
			
		||||
    app_id = db.Column(db.Integer, db.ForeignKey("acl_apps.id"))
 | 
			
		||||
 | 
			
		||||
    __table_args__ = (db.UniqueConstraint("name", "resource_type_id", "app_id", name="resource_name_app_unique"),)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResourceGroupItems(Model):
 | 
			
		||||
    __tablename__ = "acl_resource_group_items"
 | 
			
		||||
 | 
			
		||||
    group_id = db.Column(db.Integer, db.ForeignKey('acl_resource_groups.id'), nullable=False)
 | 
			
		||||
    resource_id = db.Column(db.Integer, db.ForeignKey('acl_resources.id'), nullable=False)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Permission(Model):
 | 
			
		||||
    __tablename__ = "acl_permissions"
 | 
			
		||||
 | 
			
		||||
    name = db.Column(db.String(64), nullable=False)
 | 
			
		||||
    resource_type_id = db.Column(db.Integer, db.ForeignKey("acl_resource_types.id"))
 | 
			
		||||
 | 
			
		||||
    app_id = db.Column(db.Integer, db.ForeignKey("acl_apps.id"))
 | 
			
		||||
 | 
			
		||||
    __table_args__ = (db.UniqueConstraint("name", "resource_type_id", "app_id", name="perm_name_app_unique"),)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RolePermission(Model):
 | 
			
		||||
    __tablename__ = "acl_role_permissions"
 | 
			
		||||
 | 
			
		||||
    rid = db.Column(db.Integer, db.ForeignKey('acl_roles.id'))
 | 
			
		||||
    resource_id = db.Column(db.Integer, db.ForeignKey('acl_resources.id'))
 | 
			
		||||
    group_id = db.Column(db.Integer, db.ForeignKey('acl_resource_groups.id'))
 | 
			
		||||
    perm_id = db.Column(db.Integer, db.ForeignKey('acl_permissions.id'))
 | 
			
		||||
 | 
			
		||||
    perm = db.relationship("Permission", backref='acl_role_permissions.perm_id')
 | 
			
		||||
							
								
								
									
										16
									
								
								api/tasks/acl.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								api/tasks/acl.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
# -*- coding:utf-8 -*-
 | 
			
		||||
 | 
			
		||||
from flask import current_app
 | 
			
		||||
 | 
			
		||||
from api.extensions import celery
 | 
			
		||||
from api.lib.perm.acl.cache import RoleRelationCache
 | 
			
		||||
from api.lib.perm.acl.const import ACL_QUEUE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@celery.task(name="acl.role_rebuild", queue=ACL_QUEUE)
 | 
			
		||||
def role_rebuild(rids):
 | 
			
		||||
    rids = rids if isinstance(rids, list) else [rids]
 | 
			
		||||
    for rid in rids:
 | 
			
		||||
        RoleRelationCache.rebuild(rid)
 | 
			
		||||
 | 
			
		||||
    current_app.logger.info("%d rebuild.........." % rids)
 | 
			
		||||
@@ -29,3 +29,9 @@ perm_rest.add_resource(GetUserInfoView, GetUserInfoView.url_prefix)
 | 
			
		||||
blueprint_cmdb_v01 = Blueprint('cmdb_api_v01', __name__, url_prefix='/api/v0.1')
 | 
			
		||||
rest = Api(blueprint_cmdb_v01)
 | 
			
		||||
register_resources(os.path.join(HERE, "cmdb"), rest)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# acl
 | 
			
		||||
blueprint_acl_v1 = Blueprint('cmdb_acl_v1', __name__, url_prefix='/api/v1/acl')
 | 
			
		||||
rest = Api(blueprint_acl_v1)
 | 
			
		||||
register_resources(os.path.join(HERE, "acl"), rest)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								api/views/acl/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								api/views/acl/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
# -*- coding:utf-8 -*-
 | 
			
		||||
 | 
			
		||||
__author__ = 'pycook'
 | 
			
		||||
							
								
								
									
										107
									
								
								api/views/acl/resource.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								api/views/acl/resource.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
			
		||||
# -*- coding:utf-8 -*-
 | 
			
		||||
 | 
			
		||||
from flask import request
 | 
			
		||||
 | 
			
		||||
from api.lib.decorator import args_required
 | 
			
		||||
from api.lib.perm.acl.resource import ResourceCRUD
 | 
			
		||||
from api.lib.perm.acl.resource import ResourceGroupCRUD
 | 
			
		||||
from api.lib.utils import get_page
 | 
			
		||||
from api.lib.utils import get_page_size
 | 
			
		||||
from api.lib.utils import handle_arg_list
 | 
			
		||||
from api.resource import APIView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResourceView(APIView):
 | 
			
		||||
    url_prefix = ("/resources", "/resources/<int:resource_id>")
 | 
			
		||||
 | 
			
		||||
    @args_required('app_id')
 | 
			
		||||
    def get(self):
 | 
			
		||||
        page = get_page(request.values.get("page", 1))
 | 
			
		||||
        page_size = get_page_size(request.values.get("page_size"))
 | 
			
		||||
        q = request.values.get('q')
 | 
			
		||||
        app_id = request.values.get('app_id')
 | 
			
		||||
 | 
			
		||||
        numfound, res = ResourceCRUD.search(q, app_id, page, page_size)
 | 
			
		||||
 | 
			
		||||
        return self.jsonify(numfound=numfound,
 | 
			
		||||
                            page=page,
 | 
			
		||||
                            page_size=page_size,
 | 
			
		||||
                            resources=[i.to_dict() for i in res])
 | 
			
		||||
 | 
			
		||||
    @args_required('name')
 | 
			
		||||
    @args_required('type_id')
 | 
			
		||||
    @args_required('app_id')
 | 
			
		||||
    def post(self):
 | 
			
		||||
        name = request.values.get('name')
 | 
			
		||||
        type_id = request.values.get('type_id')
 | 
			
		||||
        app_id = request.values.get('app_id')
 | 
			
		||||
 | 
			
		||||
        resource = ResourceCRUD.add(name, type_id, app_id)
 | 
			
		||||
 | 
			
		||||
        return self.jsonify(resource.to_dict())
 | 
			
		||||
 | 
			
		||||
    @args_required('name')
 | 
			
		||||
    def put(self, resource_id):
 | 
			
		||||
        name = request.values.get('name')
 | 
			
		||||
 | 
			
		||||
        resource = ResourceCRUD.update(resource_id, name)
 | 
			
		||||
 | 
			
		||||
        return self.jsonify(resource.to_dict())
 | 
			
		||||
 | 
			
		||||
    def delete(self, resource_id):
 | 
			
		||||
        ResourceCRUD.delete(resource_id)
 | 
			
		||||
 | 
			
		||||
        return self.jsonify(resource_id=resource_id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResourceGroupView(APIView):
 | 
			
		||||
    url_prefix = ("/resource_groups", "/resource_groups/<int:group_id>")
 | 
			
		||||
 | 
			
		||||
    def get(self):
 | 
			
		||||
        page = get_page(request.values.get("page", 1))
 | 
			
		||||
        page_size = get_page_size(request.values.get("page_size"))
 | 
			
		||||
        q = request.values.get('q')
 | 
			
		||||
        app_id = request.values.get('app_id')
 | 
			
		||||
 | 
			
		||||
        numfound, res = ResourceGroupCRUD.search(q, app_id, page, page_size)
 | 
			
		||||
 | 
			
		||||
        return self.jsonify(numfound=numfound,
 | 
			
		||||
                            page=page,
 | 
			
		||||
                            page_size=page_size,
 | 
			
		||||
                            groups=[i.to_dict() for i in res])
 | 
			
		||||
 | 
			
		||||
    @args_required('name')
 | 
			
		||||
    @args_required('type_id')
 | 
			
		||||
    @args_required('app_id')
 | 
			
		||||
    def post(self):
 | 
			
		||||
        name = request.values.get('name')
 | 
			
		||||
        type_id = request.values.get('type_id')
 | 
			
		||||
        app_id = request.values.get('app_id')
 | 
			
		||||
 | 
			
		||||
        group = ResourceGroupCRUD.add(name, type_id, app_id)
 | 
			
		||||
 | 
			
		||||
        return self.jsonify(group.to_dict())
 | 
			
		||||
 | 
			
		||||
    @args_required('items')
 | 
			
		||||
    def put(self, group_id):
 | 
			
		||||
        items = handle_arg_list(request.values.get("items"))
 | 
			
		||||
 | 
			
		||||
        ResourceGroupCRUD.update(group_id, items)
 | 
			
		||||
 | 
			
		||||
        items = ResourceGroupCRUD.get_items(group_id)
 | 
			
		||||
 | 
			
		||||
        return self.jsonify(items)
 | 
			
		||||
 | 
			
		||||
    def delete(self, group_id):
 | 
			
		||||
        ResourceGroupCRUD.delete(group_id)
 | 
			
		||||
 | 
			
		||||
        return self.jsonify(group_id=group_id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResourceGroupItemsView(APIView):
 | 
			
		||||
    url_prefix = "/resource_groups/<int:group_id>/items"
 | 
			
		||||
 | 
			
		||||
    def get(self, group_id):
 | 
			
		||||
        items = ResourceGroupCRUD.get_items(group_id)
 | 
			
		||||
 | 
			
		||||
        return self.jsonify(items)
 | 
			
		||||
							
								
								
									
										48
									
								
								api/views/acl/role.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								api/views/acl/role.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
# -*- coding:utf-8 -*-
 | 
			
		||||
 | 
			
		||||
from flask import request
 | 
			
		||||
 | 
			
		||||
from api.lib.decorator import args_required
 | 
			
		||||
from api.lib.perm.acl.role import RoleCRUD
 | 
			
		||||
from api.lib.utils import get_page
 | 
			
		||||
from api.lib.utils import get_page_size
 | 
			
		||||
from api.resource import APIView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RoleView(APIView):
 | 
			
		||||
    url_prefix = ("/roles", "/roles/<int:rid>")
 | 
			
		||||
 | 
			
		||||
    @args_required('app_id')
 | 
			
		||||
    def get(self):
 | 
			
		||||
        page = get_page(request.values.get("page", 1))
 | 
			
		||||
        page_size = get_page_size(request.values.get("page_size"))
 | 
			
		||||
        q = request.values.get('q')
 | 
			
		||||
        app_id = request.values.get('app_id')
 | 
			
		||||
 | 
			
		||||
        numfound, roles = RoleCRUD.search(q, app_id, page, page_size)
 | 
			
		||||
 | 
			
		||||
        return self.jsonify(numfound=numfound,
 | 
			
		||||
                            page=page,
 | 
			
		||||
                            page_size=page_size,
 | 
			
		||||
                            roles=[i.to_dict() for i in roles])
 | 
			
		||||
 | 
			
		||||
    @args_required('name')
 | 
			
		||||
    @args_required('app_id')
 | 
			
		||||
    def post(self):
 | 
			
		||||
        name = request.values.get('name')
 | 
			
		||||
        app_id = request.values.get('app_id')
 | 
			
		||||
        is_app_admin = request.values.get('is_app_admin', False)
 | 
			
		||||
 | 
			
		||||
        role = RoleCRUD.add_role(name, app_id, is_app_admin=is_app_admin)
 | 
			
		||||
 | 
			
		||||
        return self.jsonify(role.to_dict())
 | 
			
		||||
 | 
			
		||||
    def put(self, rid):
 | 
			
		||||
        role = RoleCRUD.update_role(rid, **request.values)
 | 
			
		||||
 | 
			
		||||
        return self.jsonify(role.to_dict())
 | 
			
		||||
 | 
			
		||||
    def delete(self, rid):
 | 
			
		||||
        RoleCRUD.delete_role(rid)
 | 
			
		||||
 | 
			
		||||
        return self.jsonify(rid=rid)
 | 
			
		||||
		Reference in New Issue
	
	Block a user