diff --git a/api/commands/click_cmdb.py b/api/commands/click_cmdb.py index c53bd04..e8a9a88 100644 --- a/api/commands/click_cmdb.py +++ b/api/commands/click_cmdb.py @@ -6,15 +6,27 @@ import json import click from flask import current_app from flask.cli import with_appcontext +from werkzeug.exceptions import BadRequest import api.lib.cmdb.ci from api.extensions import db from api.extensions import rd +from api.lib.cmdb.const import PermEnum from api.lib.cmdb.const import REDIS_PREFIX_CI from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION +from api.lib.cmdb.const import ResourceTypeEnum +from api.lib.cmdb.const import RoleEnum from api.lib.cmdb.const import ValueTypeEnum +from api.lib.perm.acl.acl import ACLManager +from api.lib.perm.acl.cache import AppCache +from api.lib.perm.acl.resource import ResourceCRUD +from api.lib.perm.acl.resource import ResourceTypeCRUD +from api.lib.perm.acl.role import RoleCRUD +from api.models.acl import ResourceType from api.models.cmdb import CI from api.models.cmdb import CIRelation +from api.models.cmdb import CIType +from api.models.cmdb import PreferenceRelationView @click.command() @@ -74,3 +86,52 @@ def init_cache(): rd.create_or_update(relations, REDIS_PREFIX_CI_RELATION) db.session.remove() + + +@click.command() +@with_appcontext +def init_acl(): + app_id = AppCache.get('cmdb').id + # 1. add resource type + for resource_type in ResourceTypeEnum.all(): + try: + ResourceTypeCRUD.add(app_id, resource_type, '', PermEnum.all()) + except BadRequest: + pass + + # 2. add role + try: + RoleCRUD.add_role(RoleEnum.CONFIG, app_id, True) + except BadRequest: + pass + try: + RoleCRUD.add_role(RoleEnum.CMDB_READ_ALL, app_id, False) + except BadRequest: + pass + + # 3. add resource and grant + ci_types = CIType.get_by(to_dict=False) + type_id = ResourceType.get_by(name=ResourceTypeEnum.CI, first=True, to_dict=False).id + for ci_type in ci_types: + try: + ResourceCRUD.add(ci_type.name, type_id, app_id) + except BadRequest: + pass + + ACLManager().grant_resource_to_role(ci_type.name, + RoleEnum.CMDB_READ_ALL, + ResourceTypeEnum.CI, + [PermEnum.READ]) + + relation_views = PreferenceRelationView.get_by(to_dict=False) + type_id = ResourceType.get_by(name=ResourceTypeEnum.RELATION_VIEW, first=True, to_dict=False).id + for view in relation_views: + try: + ResourceCRUD.add(view.name, type_id, app_id) + except BadRequest: + pass + + ACLManager().grant_resource_to_role(view.name, + RoleEnum.CMDB_READ_ALL, + ResourceTypeEnum.RELATION_VIEW, + [PermEnum.READ]) diff --git a/api/lib/cmdb/ci.py b/api/lib/cmdb/ci.py index ef51902..0c77bcc 100644 --- a/api/lib/cmdb/ci.py +++ b/api/lib/cmdb/ci.py @@ -12,7 +12,6 @@ from api.extensions import db from api.extensions import rd from api.lib.cmdb.cache import AttributeCache from api.lib.cmdb.cache import CITypeCache -from api.lib.cmdb.cache import RelationTypeCache from api.lib.cmdb.ci_type import CITypeAttributeManager from api.lib.cmdb.ci_type import CITypeManager from api.lib.cmdb.const import CMDB_QUEUE @@ -32,6 +31,7 @@ from api.lib.utils import handle_arg_list from api.models.cmdb import CI from api.models.cmdb import CIRelation from api.models.cmdb import CITypeAttribute +from api.models.cmdb import CITypeRelation from api.tasks.cmdb import ci_cache from api.tasks.cmdb import ci_delete from api.tasks.cmdb import ci_relation_cache @@ -52,7 +52,7 @@ class CIManager(object): @staticmethod def confirm_ci_existed(ci_id): - CI.get_by_id(ci_id) or abort(404, "CI <{0}> is not existed".format(ci_id)) + return CI.get_by_id(ci_id) or abort(404, "CI <{0}> is not existed".format(ci_id)) @classmethod def get_ci_by_id(cls, ci_id, ret_key=RetKey.NAME, fields=None, need_children=True): @@ -408,10 +408,6 @@ class CIRelationManager(object): def __init__(self): pass - @staticmethod - def _get_default_relation_type(): - return RelationTypeCache.get("contain").id # FIXME - @classmethod def get_children(cls, ci_id, ret_key=RetKey.NAME): second_cis = CIRelation.get_by(first_ci_id=ci_id, to_dict=False) @@ -428,7 +424,8 @@ class CIRelationManager(object): res[ci_type.name] = children return res - def get_second_cis(self, first_ci_id, relation_type_id=None, page=1, per_page=None, **kwargs): + @staticmethod + def get_second_cis(first_ci_id, relation_type_id=None, page=1, per_page=None): second_cis = db.session.query(CI.id).filter(CI.deleted.is_(False)).join( CIRelation, CIRelation.second_ci_id == CI.id).filter( CIRelation.first_ci_id == first_ci_id).filter(CIRelation.deleted.is_(False)) @@ -436,9 +433,6 @@ class CIRelationManager(object): if relation_type_id is not None: second_cis = second_cis.filter(CIRelation.relation_type_id == relation_type_id) - if kwargs: # TODO: special for devices - second_cis = self._query_wrap_for_device(second_cis, **kwargs) - numfound = second_cis.count() if per_page != "all": second_cis = second_cis.offset((page - 1) * per_page).limit(per_page).all() @@ -473,33 +467,6 @@ class CIRelationManager(object): return query_sql - def _query_wrap_for_device(self, query_sql, **kwargs): - _type = kwargs.pop("_type", False) or kwargs.pop("type", False) or kwargs.pop("ci_type", False) - if _type: - ci_type = CITypeCache.get(_type) - if ci_type is None: - return - query_sql = query_sql.filter(CI.type_id == ci_type.id) - - for k, v in kwargs.items(): - attr = AttributeCache.get(k) - if attr is None: - continue - - value_table = TableMap(attr_name=k).table - ci_table = query_sql.subquery() - query_sql = db.session.query(ci_table.c.id).join( - value_table, value_table.ci_id == ci_table.c.id).filter( - value_table.attr_id == attr.id).filter(ci_table.deleted.is_(False)).filter( - value_table.value.ilike(v.replace("*", "%"))) - - # current_app.logger.debug(query_sql) - sort_by = kwargs.pop("sort", "") - if sort_by: - query_sql = self._sort_handler(sort_by, query_sql) - - return query_sql - @classmethod def get_first_cis(cls, second_ci, relation_type_id=None, page=1, per_page=None): first_cis = db.session.query(CIRelation.first_ci_id).filter( @@ -519,10 +486,8 @@ class CIRelationManager(object): @classmethod def add(cls, first_ci_id, second_ci_id, more=None, relation_type_id=None): - relation_type_id = relation_type_id or cls._get_default_relation_type() - - CIManager.confirm_ci_existed(first_ci_id) - CIManager.confirm_ci_existed(second_ci_id) + first_ci = CIManager.confirm_ci_existed(first_ci_id) + second_ci = CIManager.confirm_ci_existed(second_ci_id) existed = CIRelation.get_by(first_ci_id=first_ci_id, second_ci_id=second_ci_id, @@ -531,11 +496,22 @@ class CIRelationManager(object): if existed is not None: if existed.relation_type_id != relation_type_id: existed.update(relation_type_id=relation_type_id) + CIRelationHistoryManager().add(existed, OperateType.UPDATE) else: + if relation_type_id is None: + type_relation = CITypeRelation.get_by(parent_id=first_ci.type_id, + child_id=second_ci.type_id, + first=True, + to_dict=False) + relation_type_id = type_relation and type_relation.relation_type_id + relation_type_id or abort(404, "Relation {0} <-> {1} is not found".format( + first_ci.ci_type.name, second_ci.ci_type.name)) + existed = CIRelation.create(first_ci_id=first_ci_id, second_ci_id=second_ci_id, relation_type_id=relation_type_id) + CIRelationHistoryManager().add(existed, OperateType.ADD) ci_relation_cache.apply_async(args=(first_ci_id, second_ci_id), queue=CMDB_QUEUE) diff --git a/api/lib/cmdb/ci_type.py b/api/lib/cmdb/ci_type.py index 158eece..8d9f634 100644 --- a/api/lib/cmdb/ci_type.py +++ b/api/lib/cmdb/ci_type.py @@ -69,6 +69,15 @@ class CITypeManager(object): CITypeCache.clean(ci_type.name) + if current_app.config.get("USE_ACL"): + from api.lib.perm.acl.acl import ACLManager + from api.lib.cmdb.const import ResourceTypeEnum, RoleEnum, PermEnum + ACLManager().add_resource(ci_type.name, ResourceTypeEnum.CI) + ACLManager().grant_resource_to_role(ci_type.name, + RoleEnum.CMDB_READ_ALL, + ResourceTypeEnum.CI, + permissions=[PermEnum.READ]) + return ci_type.id @classmethod diff --git a/api/lib/cmdb/const.py b/api/lib/cmdb/const.py index 98263b9..e5bb8a7 100644 --- a/api/lib/cmdb/const.py +++ b/api/lib/cmdb/const.py @@ -37,8 +37,9 @@ class RetKey(BaseEnum): ALIAS = "alias" -class ResourceType(BaseEnum): +class ResourceTypeEnum(BaseEnum): CI = "CIType" + RELATION_VIEW = "RelationView" class PermEnum(BaseEnum): @@ -50,6 +51,7 @@ class PermEnum(BaseEnum): class RoleEnum(BaseEnum): CONFIG = "admin" + CMDB_READ_ALL = "CMDB_READ_ALL" CMDB_QUEUE = "cmdb_async" diff --git a/api/lib/cmdb/preference.py b/api/lib/cmdb/preference.py index 6ac6d62..1da4b37 100644 --- a/api/lib/cmdb/preference.py +++ b/api/lib/cmdb/preference.py @@ -7,6 +7,7 @@ import json import six import toposort from flask import abort +from flask import current_app from flask import g from api.extensions import db @@ -19,6 +20,8 @@ from api.models.cmdb import CITypeRelation from api.models.cmdb import PreferenceRelationView from api.models.cmdb import PreferenceShowAttributes from api.models.cmdb import PreferenceTreeView +from api.lib.perm.acl.acl import ACLManager +from api.lib.cmdb.const import ResourceTypeEnum, RoleEnum, PermEnum class PreferenceManager(object): @@ -116,6 +119,11 @@ class PreferenceManager(object): @staticmethod def get_relation_view(): views = PreferenceRelationView.get_by(to_dict=True) + if current_app.config.get("USE_ACL"): + views = [i for i in views if ACLManager().has_permission(i.get('name'), + ResourceTypeEnum.RELATION_VIEW, + PermEnum.READ)] + view2cr_ids = dict() result = dict() name2id = list() @@ -170,6 +178,13 @@ class PreferenceManager(object): if existed is None: PreferenceRelationView.create(name=name, cr_ids=json.dumps(cr_ids)) + if current_app.config.get("USE_ACL"): + ACLManager().add_resource(name, ResourceTypeEnum.RELATION_VIEW) + ACLManager().grant_resource_to_role(name, + RoleEnum.CMDB_READ_ALL, + ResourceTypeEnum.RELATION_VIEW, + permissions=[PermEnum.READ]) + return cls.get_relation_view() @staticmethod diff --git a/api/lib/perm/acl/acl.py b/api/lib/perm/acl/acl.py index ea7c400..f8400fd 100644 --- a/api/lib/perm/acl/acl.py +++ b/api/lib/perm/acl/acl.py @@ -6,54 +6,83 @@ 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.models.acl import ResourceType -from api.models.acl import Resource +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.user_info = session["acl"] if "acl" in session else {} 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) - if resource_type: - return abort(400, "ResourceType <{0}> cannot be found".format(resource_type_name)) + 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): - resource_type = ResourceType.get_by(name=resource_type_name, first=True, to_dict=False) - if resource_type: - return abort(400, "ResourceType <{0}> cannot be found".format(resource_type_name)) + 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_type = ResourceType.get_by(name=resource_type_name, first=True, to_dict=False) - if resource_type: - return abort(400, "ResourceType <{0}> cannot be found".format(resource_type_name)) - - resource = Resource.get_by(resource_type_id=resource_type.id, - app_id=self.app_id, - name=name, - first=True, - to_dict=False) + resource = self._get_resource(name, resource_type_name) if resource: ResourceCRUD.delete(resource.id) - def get_resources(self, resource_type_name=None): - if "acl" not in session: - abort(405) - return [] - def has_permission(self, resource_name, resource_type, perm): - if "acl" not in session: - abort(405) - return True + + 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): @@ -70,24 +99,6 @@ def validate_permission(resources, resource_type, perm): return abort(403, "has no permission") -def can_access_resources(resource_type): - def decorator_can_access_resources(func): - @functools.wraps(func) - def wrapper_can_access_resources(*args, **kwargs): - if current_app.config.get("USE_ACL"): - res = ACLManager().get_resources(resource_type) - result = {i.get("name"): i.get("permissions") for i in res} - if hasattr(g, "resources"): - g.resources.update({resource_type: result}) - else: - g.resources = {resource_type: result} - return func(*args, **kwargs) - - return wrapper_can_access_resources - - return decorator_can_access_resources - - def has_perm(resources, resource_type, perm): def decorator_has_perm(func): @functools.wraps(func) @@ -96,6 +107,9 @@ def has_perm(resources, resource_type, perm): 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) @@ -105,6 +119,13 @@ def has_perm(resources, resource_type, perm): return decorator_has_perm +def is_app_admin(): + if RoleEnum.CONFIG in session.get("acl", {}).get("parentRoles", []): + return True + + return False + + def has_perm_from_args(arg_name, resource_type, perm, callback=None): def decorator_has_perm(func): @functools.wraps(func) @@ -116,6 +137,9 @@ def has_perm_from_args(arg_name, resource_type, perm, callback=None): 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) diff --git a/api/lib/perm/acl/const.py b/api/lib/perm/acl/const.py index 355ccbf..76cc8a2 100644 --- a/api/lib/perm/acl/const.py +++ b/api/lib/perm/acl/const.py @@ -1,4 +1,5 @@ # -*- coding:utf-8 -*- +from api.lib.cmdb.const import CMDB_QUEUE -ACL_QUEUE = "acl_async" +ACL_QUEUE = CMDB_QUEUE diff --git a/api/lib/perm/acl/permission.py b/api/lib/perm/acl/permission.py index 57abae3..3126b87 100644 --- a/api/lib/perm/acl/permission.py +++ b/api/lib/perm/acl/permission.py @@ -3,7 +3,9 @@ from api.lib.perm.acl.cache import PermissionCache from api.lib.perm.acl.cache import RoleCache +from api.lib.perm.acl.const import ACL_QUEUE from api.models.acl import RolePermission +from api.tasks.acl import role_rebuild class PermissionCRUD(object): @@ -29,6 +31,8 @@ class PermissionCRUD(object): existed = RolePermission.get_by(rid=rid, perm_id=perm.id, group_id=group_id, resource_id=resource_id) existed or RolePermission.create(rid=rid, perm_id=perm.id, group_id=group_id, resource_id=resource_id) + role_rebuild.apply_async(args=(rid,), queue=ACL_QUEUE) + @staticmethod def revoke(rid, perms, resource_id=None, group_id=None): for perm in perms: @@ -40,3 +44,5 @@ class PermissionCRUD(object): first=True, to_dict=False) existed and existed.soft_delete() + + role_rebuild.apply_async(args=(rid,), queue=ACL_QUEUE) diff --git a/api/lib/perm/acl/resource.py b/api/lib/perm/acl/resource.py index 295ffbe..977824b 100644 --- a/api/lib/perm/acl/resource.py +++ b/api/lib/perm/acl/resource.py @@ -4,11 +4,14 @@ from flask import abort from api.extensions import db +from api.lib.perm.acl.const import ACL_QUEUE 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 +from api.models.acl import RolePermission +from api.tasks.acl import role_rebuild class ResourceTypeCRUD(object): @@ -134,6 +137,10 @@ class ResourceGroupCRUD(object): for item in items: item.soft_delete() + for i in RolePermission.get_by(group_id=rg_id, to_dict=False): + i.soft_delete() + role_rebuild.apply_async(args=(i.rid,), queue=ACL_QUEUE) + class ResourceCRUD(object): @staticmethod @@ -173,3 +180,7 @@ class ResourceCRUD(object): resource = Resource.get_by_id(_id) or abort(404, "Resource <{0}> is not found".format(_id)) resource.soft_delete() + + for i in RolePermission.get_by(resource_id=_id, to_dict=False): + i.soft_delete() + role_rebuild.apply_async(args=(i.rid,), queue=ACL_QUEUE) diff --git a/api/lib/perm/acl/role.py b/api/lib/perm/acl/role.py index ab128ab..f536bdf 100644 --- a/api/lib/perm/acl/role.py +++ b/api/lib/perm/acl/role.py @@ -10,6 +10,7 @@ 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 ResourceType from api.models.acl import Role from api.models.acl import RolePermission from api.models.acl import RoleRelation @@ -44,7 +45,7 @@ class RoleRelationCRUD(object): @staticmethod def get_child_ids(rid): - res = RoleRelation.get_by(child_id=rid, to_dict=False) + res = RoleRelation.get_by(parent_id=rid, to_dict=False) return [i.parent_id for i in res] @@ -82,27 +83,37 @@ class RoleRelationCRUD(object): return RoleRelation.create(parent_id=parent_id, child_id=child_id) - @staticmethod - def delete(_id): + @classmethod + def delete(cls, _id): existed = RoleRelation.get_by_id(_id) or abort(400, "RoleRelation <{0}> does not exist".format(_id)) + child_ids = cls.recursive_child_ids(existed.child_id) + for child_id in child_ids: + role_rebuild.apply_async(args=(child_id,), queue=ACL_QUEUE) + existed.soft_delete() - @staticmethod - def delete2(parent_id, child_id): + @classmethod + def delete2(cls, parent_id, child_id): existed = RoleRelation.get_by(parent_id=parent_id, child_id=child_id, first=True, to_dict=False) existed or abort(400, "RoleRelation < {0} -> {1} > does not exist".format(parent_id, child_id)) + child_ids = cls.recursive_child_ids(existed.child_id) + for child_id in child_ids: + role_rebuild.apply_async(args=(child_id,), queue=ACL_QUEUE) + existed.soft_delete() class RoleCRUD(object): @staticmethod - def search(q, app_id, page=1, page_size=None, user_role=False): - query = db.session.query(Role).filter(Role.deleted.is_(False)).filter(Role.app_id == app_id) + def search(q, app_id, page=1, page_size=None, user_role=True): + query = db.session.query(Role).filter(Role.deleted.is_(False)) + query = query.filter(Role.app_id == app_id).filter(Role.uid.is_(None)) - if not user_role: - query = query.filter(Role.uid.is_(None)) + if user_role: + query1 = db.session.query(Role).filter(Role.deleted.is_(False)).filter(Role.uid.isnot(None)) + query = query.union(query1) if q: query = query.filter(Role.name.ilike('%{0}%'.format(q))) @@ -134,9 +145,6 @@ class RoleCRUD(object): def delete_role(cls, rid): role = Role.get_by_id(rid) or abort(404, "Role <{0}> does not exist".format(rid)) - parent_ids = RoleRelationCRUD.get_parent_ids(rid) - child_ids = RoleRelationCRUD.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): @@ -145,7 +153,7 @@ class RoleCRUD(object): 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) + role_rebuild.apply_async(args=(list(RoleRelationCRUD.recursive_child_ids(rid)), ), queue=ACL_QUEUE) RoleCache.clean(rid) RoleRelationCache.clean(rid) @@ -166,20 +174,21 @@ class RoleCRUD(object): @staticmethod def get_group_ids(resource_id): - return [i.group_id for i in ResourceGroupItems.get_by(resource_id, to_dict=False)] + return [i.group_id for i in ResourceGroupItems.get_by(resource_id=resource_id, to_dict=False)] @classmethod - def has_permission(cls, rid, resource_name, perm): - resource = Resource.get_by(name=resource_name, first=True, to_dict=False) + def has_permission(cls, rid, resource_name, resource_type, app_id, perm): + resource_type = ResourceType.get_by(app_id=app_id, name=resource_type, first=True, to_dict=False) + resource_type or abort(404, "ResourceType <{0}> is not found".format(resource_type)) + type_id = resource_type.id + resource = Resource.get_by(name=resource_name, resource_type_id=type_id, first=True, to_dict=False) resource = resource or abort(403, "Resource <{0}> is not in ACL".format(resource_name)) parent_ids = RoleRelationCRUD.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 diff --git a/api/models/acl.py b/api/models/acl.py index ec374b2..3388945 100644 --- a/api/models/acl.py +++ b/api/models/acl.py @@ -136,9 +136,6 @@ class RoleRelation(Model): 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" @@ -156,8 +153,6 @@ class ResourceGroup(Model): 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" @@ -167,8 +162,6 @@ class Resource(Model): 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" @@ -185,8 +178,6 @@ class Permission(Model): 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" diff --git a/api/settings.py.example b/api/settings.py.example index fec4f5b..593fbbc 100644 --- a/api/settings.py.example +++ b/api/settings.py.example @@ -79,3 +79,6 @@ USE_ACL = False # # elastic search ES_HOST = '127.0.0.1' USE_ES = False + + +BOOL_TRUE = ['true', 'TRUE', 'True', True, '1', 1, "Yes", "YES", "yes", 'Y', 'y'] diff --git a/api/tasks/acl.py b/api/tasks/acl.py index 9377014..8555559 100644 --- a/api/tasks/acl.py +++ b/api/tasks/acl.py @@ -13,4 +13,4 @@ def role_rebuild(rids): for rid in rids: RoleRelationCache.rebuild(rid) - current_app.logger.info("%d rebuild.........." % rids) + current_app.logger.info("Role {0} rebuild..........".format(rids)) diff --git a/api/tasks/cmdb.py b/api/tasks/cmdb.py index 1cdb2f8..357ddc0 100644 --- a/api/tasks/cmdb.py +++ b/api/tasks/cmdb.py @@ -50,8 +50,8 @@ def ci_relation_cache(parent_id, child_id): children = json.loads(children) if children is not None else {} cr = CIRelation.get_by(first_ci_id=parent_id, second_ci_id=child_id, first=True, to_dict=False) - if child_id not in children: - children[child_id] = cr.second_ci.type_id + if str(child_id) not in children: + children[str(child_id)] = cr.second_ci.type_id rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION) @@ -63,8 +63,8 @@ def ci_relation_delete(parent_id, child_id): children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0] children = json.loads(children) if children is not None else {} - if child_id in children: - children.pop(child_id) + if str(child_id) in children: + children.pop(str(child_id)) rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION) diff --git a/api/views/account.py b/api/views/account.py index cd133a8..df7c22a 100644 --- a/api/views/account.py +++ b/api/views/account.py @@ -6,12 +6,15 @@ import jwt from flask import abort from flask import current_app from flask import request +from flask import session from flask_login import login_user, logout_user from api.lib.decorator import args_required from api.lib.perm.auth import auth_abandoned -from api.models.acl import User +from api.models.acl import User, Role from api.resource import APIView +from api.lib.perm.acl.role import RoleRelationCRUD +from api.lib.perm.acl.cache import RoleCache class LoginView(APIView): @@ -37,6 +40,18 @@ class LoginView(APIView): 'exp': datetime.datetime.now() + datetime.timedelta(minutes=24 * 60 * 7)}, current_app.config['SECRET_KEY']) + role = Role.get_by(uid=user.uid, first=True, to_dict=False) + if role: + parent_ids = RoleRelationCRUD.recursive_parent_ids(role.id) + parent_roles = [RoleCache.get(i).name for i in parent_ids] + else: + parent_roles = [] + session["acl"] = dict(uid=user.uid, + avatar=user.avatar, + userName=user.username, + nickName=user.nickname, + parentRoles=parent_roles) + return self.jsonify(token=token.decode()) diff --git a/api/views/acl/role.py b/api/views/acl/role.py index b896778..f649304 100644 --- a/api/views/acl/role.py +++ b/api/views/acl/role.py @@ -1,5 +1,6 @@ # -*- coding:utf-8 -*- +from flask import current_app from flask import request from api.lib.decorator import args_required @@ -21,7 +22,8 @@ class RoleView(APIView): page_size = get_page_size(request.values.get("page_size")) q = request.values.get('q') app_id = request.values.get('app_id') - user_role = request.values.get('user_role', False) + user_role = request.values.get('user_role', True) + user_role = True if user_role in current_app.config.get("BOOL_TRUE") else False numfound, roles = RoleCRUD.search(q, app_id, page, page_size, user_role) diff --git a/api/views/acl/user.py b/api/views/acl/user.py index 82b6f17..bf3cb9a 100644 --- a/api/views/acl/user.py +++ b/api/views/acl/user.py @@ -17,9 +17,9 @@ class GetUserInfoView(APIView): url_prefix = "/users/info" def get(self): - name = session.get("acl", {}).get("nickName") or session.get("CAS_USERNAME") or current_user.nickname - role = dict(permissions=session.get("acl", {}).get("parentRoles", []) or ["admin"]) - avatar = session.get("acl", {}).get("avatar") or current_user.avatar + name = session.get("CAS_USERNAME") or current_user.nickname + role = dict(permissions=session.get("acl", {}).get("parentRoles", [])) + avatar = current_user.avatar return self.jsonify(result=dict(name=name, role=role, avatar=avatar)) diff --git a/api/views/cmdb/ci.py b/api/views/cmdb/ci.py index a45c4bb..0e9ed9b 100644 --- a/api/views/cmdb/ci.py +++ b/api/views/cmdb/ci.py @@ -10,7 +10,7 @@ from flask import request from api.lib.cmdb.cache import CITypeCache from api.lib.cmdb.ci import CIManager from api.lib.cmdb.const import ExistPolicy -from api.lib.cmdb.const import ResourceType, PermEnum +from api.lib.cmdb.const import ResourceTypeEnum, PermEnum from api.lib.cmdb.const import RetKey from api.lib.cmdb.search import SearchError from api.lib.cmdb.search.ci.db.search import Search as SearchFromDB @@ -73,7 +73,7 @@ class CIView(APIView): ci_dict[k] = v.strip() if isinstance(v, six.string_types) else v return ci_dict - @has_perm_from_args("ci_type", ResourceType.CI, PermEnum.ADD) + @has_perm_from_args("ci_type", ResourceTypeEnum.CI, PermEnum.ADD) def post(self): ci_type = request.values.get("ci_type") _no_attribute_policy = request.values.get("_no_attribute_policy", ExistPolicy.IGNORE) @@ -87,7 +87,7 @@ class CIView(APIView): _no_attribute_policy=_no_attribute_policy, **ci_dict) return self.jsonify(ci_id=ci_id) - @has_perm_from_args("ci_id", ResourceType.CI, PermEnum.UPDATE, CIManager.get_type_name) + @has_perm_from_args("ci_id", ResourceTypeEnum.CI, PermEnum.UPDATE, CIManager.get_type_name) def put(self, ci_id=None): args = request.values ci_type = args.get("ci_type") @@ -104,7 +104,7 @@ class CIView(APIView): **ci_dict) return self.jsonify(ci_id=ci_id) - @has_perm_from_args("ci_id", ResourceType.CI, PermEnum.DELETE, CIManager.get_type_name) + @has_perm_from_args("ci_id", ResourceTypeEnum.CI, PermEnum.DELETE, CIManager.get_type_name) def delete(self, ci_id): manager = CIManager() manager.delete(ci_id) @@ -163,7 +163,7 @@ class CISearchView(APIView): class CIUnique(APIView): url_prefix = "/ci//unique" - @has_perm_from_args("ci_id", ResourceType.CI, PermEnum.UPDATE, CIManager.get_type_name) + @has_perm_from_args("ci_id", ResourceTypeEnum.CI, PermEnum.UPDATE, CIManager.get_type_name) def put(self, ci_id): params = request.values unique_name = params.keys()[0] diff --git a/api/views/cmdb/ci_relation.py b/api/views/cmdb/ci_relation.py index 13e05c2..38ed735 100644 --- a/api/views/cmdb/ci_relation.py +++ b/api/views/cmdb/ci_relation.py @@ -7,6 +7,7 @@ from flask import abort from flask import current_app from flask import request +from api.lib.cmdb.cache import RelationTypeCache from api.lib.cmdb.ci import CIRelationManager from api.lib.cmdb.search import SearchError from api.lib.cmdb.search.ci_relation.search import Search @@ -83,11 +84,15 @@ class GetSecondCIsView(APIView): def get(self, first_ci_id): page = get_page(request.values.get("page", 1)) count = get_page_size(request.values.get("count")) - relation_type = request.values.get("relation_type", "contain") + relation_type = request.values.get("relation_type") + try: + relation_type_id = RelationTypeCache.get(relation_type).id if relation_type else None + except AttributeError: + return abort(400, "invalid relation type <{0}>".format(relation_type)) manager = CIRelationManager() numfound, total, second_cis = manager.get_second_cis( - first_ci_id, page=page, per_page=count, relation_type=relation_type) + first_ci_id, page=page, per_page=count, relation_type_id=relation_type_id) return self.jsonify(numfound=numfound, total=total, diff --git a/api/views/cmdb/preference.py b/api/views/cmdb/preference.py index be839e0..8747cf0 100644 --- a/api/views/cmdb/preference.py +++ b/api/views/cmdb/preference.py @@ -4,7 +4,7 @@ from flask import request from api.lib.cmdb.ci_type import CITypeManager -from api.lib.cmdb.const import ResourceType, PermEnum, RoleEnum +from api.lib.cmdb.const import ResourceTypeEnum, PermEnum, RoleEnum from api.lib.cmdb.preference import PreferenceManager from api.lib.decorator import args_required from api.lib.perm.acl.acl import has_perm_from_args @@ -31,7 +31,7 @@ class PreferenceShowAttributesView(APIView): return self.jsonify(attributes=attributes, is_subscribed=is_subscribed) - @has_perm_from_args("id_or_name", ResourceType.CI, PermEnum.READ, CITypeManager.get_name_by_id) + @has_perm_from_args("id_or_name", ResourceTypeEnum.CI, PermEnum.READ, CITypeManager.get_name_by_id) @args_required("attr") def post(self, id_or_name): id_or_name = int(id_or_name) @@ -42,7 +42,7 @@ class PreferenceShowAttributesView(APIView): return self.jsonify(type_id=id_or_name, attr_order=list(zip(attr_list, orders))) - @has_perm_from_args("id_or_name", ResourceType.CI, PermEnum.READ, CITypeManager.get_name_by_id) + @has_perm_from_args("id_or_name", ResourceTypeEnum.CI, PermEnum.READ, CITypeManager.get_name_by_id) def put(self, id_or_name): return self.post(id_or_name) @@ -53,7 +53,7 @@ class PreferenceTreeApiView(APIView): def get(self): return self.jsonify(PreferenceManager.get_tree_view()) - @has_perm_from_args("type_id", ResourceType.CI, PermEnum.READ, CITypeManager.get_name_by_id) + @has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.READ, CITypeManager.get_name_by_id) @args_required("type_id") @args_required("levels") def post(self): @@ -85,9 +85,11 @@ class PreferenceRelationApiView(APIView): return self.jsonify(views=views, id2type=id2type, name2id=name2id) + @role_required(RoleEnum.CONFIG) def put(self): return self.post() + @role_required(RoleEnum.CONFIG) @args_required("name") def delete(self): name = request.values.get("name")