# -*- coding:utf-8 -*- import json import re from celery_once import QueueOnce from flask import current_app from werkzeug.exceptions import BadRequest, NotFound from api.extensions import celery from api.extensions import db from api.lib.perm.acl.cache import AppCache from api.lib.perm.acl.cache import RoleCache from api.lib.perm.acl.cache import RoleRelationCache from api.lib.perm.acl.cache import UserCache from api.lib.perm.acl.const import ACL_QUEUE from api.lib.perm.acl.record import OperateRecordCRUD from api.lib.perm.acl.audit import AuditCRUD, AuditOperateType, AuditOperateSource from api.models.acl import Resource from api.models.acl import Role from api.models.acl import Trigger @celery.task(base=QueueOnce, name="acl.role_rebuild", queue=ACL_QUEUE, once={"graceful": True, "unlock_before_run": True}) def role_rebuild(rids, app_id): rids = rids if isinstance(rids, list) else [rids] for rid in rids: RoleRelationCache.rebuild(rid, app_id) current_app.logger.info("Role {0} App {1} rebuild..........".format(rids, app_id)) @celery.task(name="acl.update_resource_to_build_role", queue=ACL_QUEUE) def update_resource_to_build_role(resource_id, app_id, group_id=None): rids = [i.id for i in Role.get_by(__func_isnot__key_uid=None, fl='id', to_dict=False)] rids += [i.id for i in Role.get_by(app_id=app_id, fl='id', to_dict=False)] rids += [i.id for i in Role.get_by(__func_is___key_uid=None, __func_is___key_app_id=None, fl='id', to_dict=False)] current_app.logger.info(rids) for rid in rids: if resource_id and resource_id in RoleRelationCache.get_resources(rid, app_id).get('id2perms', {}): RoleRelationCache.rebuild2(rid, app_id) if group_id and group_id in RoleRelationCache.get_resources(rid, app_id).get('group2perms', {}): RoleRelationCache.rebuild2(rid, app_id) @celery.task(name="acl.apply_trigger", queue=ACL_QUEUE) def apply_trigger(_id, resource_id=None, operator_uid=None): db.session.remove() from api.lib.perm.acl.permission import PermissionCRUD trigger = Trigger.get_by_id(_id) if trigger is None: return uid = json.loads(trigger.uid or '[]') if resource_id is None: wildcard = (trigger.wildcard or '') if wildcard and uid: query = Resource.get_by(__func_in___key_uid=uid, app_id=trigger.app_id, resource_type_id=trigger.resource_type_id, fl=['id', 'app_id'], only_query=True) try: re.compile(wildcard) resources = query.filter(Resource.name.op('regexp')(wildcard)).all() except: resources = query.filter(Resource.name.ilike(wildcard.replace('*', '%'))).all() elif wildcard: query = Resource.get_by(app_id=trigger.app_id, resource_type_id=trigger.resource_type_id, only_query=True) try: re.compile(wildcard) resources = query.filter(Resource.name.op('regexp')(wildcard)).all() except: resources = query.filter(Resource.name.ilike(wildcard.replace('*', '%'))).all() elif uid: resources = Resource.get_by(__func_in___key_uid=uid, app_id=trigger.app_id, resource_type_id=trigger.resource_type_id, to_dict=False) else: resources = [] else: resources = [Resource.get_by_id(resource_id)] perms = json.loads(trigger.permissions) roles = json.loads(trigger.roles) for resource in resources: for rid in roles: try: PermissionCRUD.grant(rid, perms, resource.id, rebuild=False, source=AuditOperateSource.trigger) except (NotFound, BadRequest): pass AuditCRUD.add_trigger_log(trigger.app_id, trigger.id, AuditOperateType.trigger_apply, {}, trigger.to_dict(), {'uid': uid, 'resource_ids': [r.id for r in resources], 'perms': perms, 'rids': roles}, uid=operator_uid, source=AuditOperateSource.trigger) if resources: role_rebuild(roles, resources[0].app_id) @celery.task(name="acl.cancel_trigger", queue=ACL_QUEUE) def cancel_trigger(_id, resource_id=None, operator_uid=None): db.session.remove() from api.lib.perm.acl.permission import PermissionCRUD trigger = Trigger.get_by_id(_id) if trigger is None: return uid = json.loads(trigger.uid or '[]') if resource_id is None: wildcard = (trigger.wildcard or '') if wildcard and uid: query = Resource.get_by(__func_in___key_uid=uid, app_id=trigger.app_id, resource_type_id=trigger.resource_type_id, fl=['id', 'app_id'], only_query=True) try: re.compile(wildcard) resources = query.filter(Resource.name.op('regexp')(wildcard)).all() except: resources = query.filter(Resource.name.ilike(wildcard.replace('*', '%'))).all() elif wildcard: query = Resource.get_by(app_id=trigger.app_id, resource_type_id=trigger.resource_type_id, only_query=True) try: re.compile(wildcard) resources = query.filter(Resource.name.op('regexp')(wildcard)).all() except: resources = query.filter(Resource.name.ilike(wildcard.replace('*', '%'))).all() elif uid: resources = Resource.get_by(__func_in___key_uid=uid, app_id=trigger.app_id, resource_type_id=trigger.resource_type_id, to_dict=False) else: resources = [] else: resources = [Resource.get_by_id(resource_id)] perms = json.loads(trigger.permissions) roles = json.loads(trigger.roles) for resource in resources: if not resource: continue for rid in roles: try: PermissionCRUD.revoke(rid, perms, resource.id, rebuild=False, source=AuditOperateSource.trigger) except (NotFound, BadRequest): pass AuditCRUD.add_trigger_log(trigger.app_id, trigger.id, AuditOperateType.trigger_cancel, {}, trigger.to_dict(), {'uid': uid, 'resource_ids': [r.id for r in resources if r], 'perms': perms, 'rids': roles}, uid=operator_uid, source=AuditOperateSource.trigger) if resources: role_rebuild(roles, resources[0].app_id) @celery.task(name="acl.op_record", queue=ACL_QUEUE) def op_record(app, rolename, operate_type, obj): if isinstance(app, int): app = AppCache.get(app) app = app and app.name if isinstance(rolename, int): u = UserCache.get(rolename) if u: rolename = u.username if not u: r = RoleCache.get(rolename) if r: rolename = r.name OperateRecordCRUD.add(app, rolename, operate_type, obj)