mirror of https://github.com/veops/cmdb.git
353 lines
13 KiB
Python
353 lines
13 KiB
Python
# -*- coding:utf-8 -*-
|
|
|
|
|
|
from flask import abort
|
|
|
|
from api.extensions import db
|
|
from api.lib.perm.acl.audit import AuditCRUD
|
|
from api.lib.perm.acl.audit import AuditOperateType
|
|
from api.lib.perm.acl.audit import AuditScope
|
|
from api.lib.perm.acl.cache import ResourceCache
|
|
from api.lib.perm.acl.cache import ResourceGroupCache
|
|
from api.lib.perm.acl.cache import UserCache
|
|
from api.lib.perm.acl.const import ACL_QUEUE
|
|
from api.lib.perm.acl.resp_format import ErrFormat
|
|
from api.lib.perm.acl.trigger import TriggerCRUD
|
|
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
|
|
from api.tasks.acl import update_resource_to_build_role
|
|
|
|
|
|
class ResourceTypeCRUD(object):
|
|
cls = ResourceType
|
|
|
|
@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()
|
|
res = query.offset((page - 1) * page_size).limit(page_size)
|
|
rt_ids = [i.id for i in res]
|
|
perms = db.session.query(Permission).filter(Permission.deleted.is_(False)).filter(
|
|
Permission.resource_type_id.in_(rt_ids))
|
|
id2perms = dict()
|
|
for perm in perms:
|
|
id2perms.setdefault(perm.resource_type_id, []).append(perm.to_dict())
|
|
|
|
return numfound, res, id2perms
|
|
|
|
@classmethod
|
|
def id2name(cls):
|
|
return {i.id: i.name for i in ResourceType.get_by(to_dict=False)}
|
|
|
|
@staticmethod
|
|
def get_by_name(app_id, name):
|
|
resource_type = ResourceType.get_by(first=True, app_id=app_id, name=name, to_dict=False)
|
|
return resource_type
|
|
|
|
@staticmethod
|
|
def get_perms(rt_id):
|
|
perms = Permission.get_by(resource_type_id=rt_id, to_dict=False)
|
|
return [i.to_dict() for i in perms]
|
|
|
|
@classmethod
|
|
def add(cls, app_id, name, description, perms):
|
|
ResourceType.get_by(name=name, app_id=app_id) and abort(400, ErrFormat.resource_type_exists.format(name))
|
|
|
|
rt = ResourceType.create(name=name, description=description, app_id=app_id)
|
|
|
|
_, current_perm_ids = cls.update_perms(rt.id, perms, app_id)
|
|
|
|
AuditCRUD.add_resource_log(app_id, AuditOperateType.create,
|
|
AuditScope.resource_type, rt.id, {}, rt.to_dict(),
|
|
{'permission_ids': {'current': current_perm_ids, 'origin': []}, }
|
|
)
|
|
|
|
return rt
|
|
|
|
@classmethod
|
|
def update(cls, rt_id, **kwargs):
|
|
kwargs.pop('app_id', None)
|
|
|
|
rt = ResourceType.get_by_id(rt_id) or abort(404,
|
|
ErrFormat.resource_type_not_found.format("id={}".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 and other.id != rt_id:
|
|
return abort(400, ErrFormat.resource_type_exists.format(kwargs['name']))
|
|
|
|
perms = kwargs.pop('perms', None)
|
|
current_perm_ids = []
|
|
existed_perm_ids = []
|
|
|
|
if perms:
|
|
existed_perm_ids, current_perm_ids = cls.update_perms(rt_id, perms, rt.app_id)
|
|
|
|
origin = rt.to_dict()
|
|
rt = rt.update(**kwargs)
|
|
|
|
AuditCRUD.add_resource_log(rt.app_id, AuditOperateType.update,
|
|
AuditScope.resource_type, rt.id, origin, rt.to_dict(),
|
|
{'permission_ids': {'current': current_perm_ids, 'origin': existed_perm_ids}, }
|
|
)
|
|
|
|
return rt
|
|
|
|
@classmethod
|
|
def delete(cls, rt_id):
|
|
rt = ResourceType.get_by_id(rt_id) or abort(
|
|
404, ErrFormat.resource_type_not_found.format("id={}".format(rt_id)))
|
|
|
|
Resource.get_by(resource_type_id=rt_id) and abort(400, ErrFormat.resource_type_cannot_delete)
|
|
|
|
origin = rt.to_dict()
|
|
|
|
existed_perm_ids, _ = cls.update_perms(rt_id, [], rt.app_id)
|
|
|
|
rt.soft_delete()
|
|
|
|
AuditCRUD.add_resource_log(rt.app_id, AuditOperateType.delete,
|
|
AuditScope.resource_type, rt.id, origin, {},
|
|
{'permission_ids': {'current': [], 'origin': existed_perm_ids}, }
|
|
)
|
|
|
|
@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]
|
|
existed_ids = [i.id for i in existed]
|
|
current_ids = []
|
|
|
|
rebuild_rids = set()
|
|
for i in existed:
|
|
if i.name not in perms:
|
|
i.soft_delete(commit=False)
|
|
for rp in RolePermission.get_by(perm_id=i.id, to_dict=False):
|
|
rp.soft_delete(commit=False)
|
|
rebuild_rids.add((rp.app_id, rp.rid))
|
|
else:
|
|
current_ids.append(i.id)
|
|
db.session.commit()
|
|
for _app_id, _rid in rebuild_rids:
|
|
role_rebuild.apply_async(args=(_rid, _app_id), queue=ACL_QUEUE)
|
|
|
|
for i in perms:
|
|
if i not in existed_names:
|
|
p = Permission.create(resource_type_id=rt_id,
|
|
name=i,
|
|
app_id=app_id)
|
|
current_ids.append(p.id)
|
|
|
|
return existed_ids, current_ids
|
|
|
|
|
|
class ResourceGroupCRUD(object):
|
|
cls = ResourceGroup
|
|
|
|
@staticmethod
|
|
def search(q, app_id, resource_type_id, page=1, page_size=None):
|
|
query = db.session.query(ResourceGroup).filter(
|
|
ResourceGroup.deleted.is_(False)).filter(ResourceGroup.app_id == app_id).filter(
|
|
ResourceGroup.resource_type_id == resource_type_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, uid=None):
|
|
ResourceGroup.get_by(name=name, resource_type_id=type_id, app_id=app_id) and abort(
|
|
400, ErrFormat.resource_group_exists.format(name))
|
|
rg = ResourceGroup.create(name=name, resource_type_id=type_id, app_id=app_id, uid=uid)
|
|
|
|
AuditCRUD.add_resource_log(app_id, AuditOperateType.create,
|
|
AuditScope.resource_group, rg.id, {}, rg.to_dict(), {})
|
|
return rg
|
|
|
|
@staticmethod
|
|
def update(rg_id, items):
|
|
rg = ResourceGroup.get_by_id(rg_id) or abort(
|
|
404, ErrFormat.resource_group_not_found.format("id={}".format(rg_id)))
|
|
|
|
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)
|
|
|
|
AuditCRUD.add_resource_log(rg.app_id, AuditOperateType.update,
|
|
AuditScope.resource_group, rg.id, rg.to_dict(), rg.to_dict(),
|
|
{'resource_ids': {'current': items, 'origin': existed_ids}, }
|
|
)
|
|
|
|
@staticmethod
|
|
def delete(rg_id):
|
|
rg = ResourceGroup.get_by_id(rg_id) or abort(
|
|
404, ErrFormat.resource_group_not_found.format("id={}".format(rg_id)))
|
|
|
|
origin = rg.to_dict()
|
|
rg.soft_delete()
|
|
|
|
items = ResourceGroupItems.get_by(group_id=rg_id, to_dict=False)
|
|
existed_ids = []
|
|
|
|
for item in items:
|
|
existed_ids.append(item.resource_id)
|
|
item.soft_delete()
|
|
|
|
rebuild = set()
|
|
for i in RolePermission.get_by(group_id=rg_id, to_dict=False):
|
|
i.soft_delete()
|
|
rebuild.add(i.rid)
|
|
|
|
for _rid in rebuild:
|
|
role_rebuild.apply_async(args=(_rid, rg.app_id), queue=ACL_QUEUE)
|
|
|
|
ResourceGroupCache.clean(rg)
|
|
|
|
AuditCRUD.add_resource_log(rg.app_id, AuditOperateType.delete,
|
|
AuditScope.resource_group, rg.id, origin, {},
|
|
{'resource_ids': {'current': [], 'origin': existed_ids}, }
|
|
|
|
)
|
|
|
|
|
|
class ResourceCRUD(object):
|
|
cls = Resource
|
|
|
|
@staticmethod
|
|
def _parse_resource_type_id(type_id, app_id):
|
|
try:
|
|
type_id = int(type_id)
|
|
except ValueError:
|
|
_type = ResourceType.get_by(name=type_id, app_id=app_id, first=True, to_dict=False)
|
|
type_id = _type and _type.id
|
|
|
|
return type_id
|
|
|
|
@classmethod
|
|
def search(cls, q, u, app_id, resource_type_id=None, page=1, page_size=None):
|
|
query = Resource.query.filter(
|
|
Resource.deleted.is_(False)).filter(Resource.app_id == app_id)
|
|
|
|
if q:
|
|
query = query.filter(Resource.name.ilike("%{0}%".format(q)))
|
|
|
|
if u and UserCache.get(u):
|
|
query = query.filter(Resource.uid == UserCache.get(u).uid)
|
|
|
|
if resource_type_id:
|
|
resource_type_id = cls._parse_resource_type_id(resource_type_id, app_id)
|
|
|
|
query = query.filter(Resource.resource_type_id == resource_type_id)
|
|
|
|
numfound = query.count()
|
|
res = [i.to_dict() for i in query.offset((page - 1) * page_size).limit(page_size)]
|
|
for i in res:
|
|
user = UserCache.get(i['uid']) if i['uid'] else ''
|
|
i['user'] = user and user.nickname
|
|
|
|
return numfound, res
|
|
|
|
@classmethod
|
|
def add(cls, name, type_id, app_id, uid=None):
|
|
type_id = cls._parse_resource_type_id(type_id, app_id)
|
|
|
|
Resource.get_by(name=name, resource_type_id=type_id, app_id=app_id) and abort(
|
|
400, ErrFormat.resource_exists.format(name))
|
|
|
|
r = Resource.create(name=name, resource_type_id=type_id, app_id=app_id, uid=uid)
|
|
|
|
from api.tasks.acl import apply_trigger
|
|
triggers = TriggerCRUD.match_triggers(app_id, r.name, r.resource_type_id, uid)
|
|
for trigger in triggers:
|
|
# auto trigger should be no uid
|
|
apply_trigger.apply_async(args=(trigger.id,),
|
|
kwargs=dict(resource_id=r.id, ), queue=ACL_QUEUE)
|
|
|
|
AuditCRUD.add_resource_log(app_id, AuditOperateType.create,
|
|
AuditScope.resource, r.id, {}, r.to_dict(), {})
|
|
|
|
return r
|
|
|
|
@staticmethod
|
|
def update(_id, name):
|
|
# todo trigger rebuild
|
|
resource = Resource.get_by_id(_id) or abort(404, ErrFormat.resource_not_found.format("id={}".format(_id)))
|
|
|
|
origin = resource.to_dict()
|
|
|
|
other = Resource.get_by(name=name, resource_type_id=resource.resource_type_id, to_dict=False, first=True)
|
|
if other and other.id != _id:
|
|
return abort(400, ErrFormat.resource_exists.format(name))
|
|
|
|
ResourceCache.clean(resource)
|
|
|
|
resource = resource.update(name=name)
|
|
|
|
update_resource_to_build_role.apply_async(args=(_id, resource.app_id), queue=ACL_QUEUE)
|
|
|
|
AuditCRUD.add_resource_log(resource.app_id, AuditOperateType.update,
|
|
AuditScope.resource, resource.id, origin, resource.to_dict(), {})
|
|
|
|
return resource
|
|
|
|
@staticmethod
|
|
def delete(_id, rebuild=True):
|
|
resource = Resource.get_by_id(_id) or abort(404, ErrFormat.resource_not_found.format("id={}".format(_id)))
|
|
|
|
origin = resource.to_dict()
|
|
resource.soft_delete()
|
|
|
|
ResourceCache.clean(resource)
|
|
|
|
rebuilds = []
|
|
for i in RolePermission.get_by(resource_id=_id, to_dict=False):
|
|
i.soft_delete()
|
|
rebuilds.append((i.rid, i.app_id))
|
|
|
|
if rebuild:
|
|
for rid, app_id in set(rebuilds):
|
|
role_rebuild.apply_async(args=(rid, app_id), queue=ACL_QUEUE)
|
|
|
|
AuditCRUD.add_resource_log(resource.app_id, AuditOperateType.delete,
|
|
AuditScope.resource, resource.id, origin, {}, {})
|
|
|
|
return rebuilds
|
|
|
|
@classmethod
|
|
def delete_by_name(cls, name, type_id, app_id):
|
|
resource = Resource.get_by(name=name, resource_type_id=type_id, app_id=app_id) or abort(
|
|
400, ErrFormat.resource_exists.format(name))
|
|
|
|
return cls.delete(resource.id)
|
|
|
|
@classmethod
|
|
def update_by_name(cls, name, type_id, app_id, new_name):
|
|
resource = Resource.get_by(name=name, resource_type_id=type_id, app_id=app_id) or abort(
|
|
400, ErrFormat.resource_exists.format(name))
|
|
|
|
return cls.update(resource.id, new_name)
|