mirror of
https://github.com/veops/cmdb.git
synced 2025-08-07 22:17:37 +08:00
前后端全面升级
This commit is contained in:
@@ -3,20 +3,22 @@
|
||||
|
||||
from functools import wraps
|
||||
|
||||
from flask import request
|
||||
from flask import abort
|
||||
from flask import request
|
||||
|
||||
from api.lib.perm.acl.cache import AppCache
|
||||
from api.lib.perm.acl.cache import AppCache, AppAccessTokenCache
|
||||
from api.lib.perm.acl.resp_format import ErrFormat
|
||||
|
||||
|
||||
def validate_app(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
app_id = request.values.get('app_id')
|
||||
app = AppCache.get(app_id)
|
||||
if app is None:
|
||||
return abort(400, "App <{0}> does not exist".format(app_id))
|
||||
request.values['app_id'] = app.id
|
||||
if not request.headers.get('App-Access-Token', '').strip():
|
||||
app_id = request.values.get('app_id')
|
||||
app = AppCache.get(app_id)
|
||||
if app is None:
|
||||
return abort(400, ErrFormat.app_not_found.format("id={}".format(app_id)))
|
||||
request.values['app_id'] = app.id
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
|
@@ -1,37 +1,69 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import functools
|
||||
import hashlib
|
||||
|
||||
import requests
|
||||
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.extensions import cache
|
||||
from api.lib.perm.acl.audit import AuditCRUD
|
||||
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.resp_format import ErrFormat
|
||||
from api.lib.perm.acl.role import RoleCRUD
|
||||
from api.lib.perm.acl.role import RoleRelationCRUD
|
||||
from api.models.acl import App
|
||||
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()
|
||||
|
||||
def get_access_token():
|
||||
url = "{0}/acl/apps/token".format(current_app.config.get('ACL_URI'))
|
||||
payload = dict(app_id=current_app.config.get('APP_ID'),
|
||||
secret_key=hashlib.md5(current_app.config.get('APP_SECRET_KEY').encode('utf-8')).hexdigest())
|
||||
try:
|
||||
res = requests.post(url, data=payload).json()
|
||||
return res.get("token")
|
||||
except Exception as e:
|
||||
current_app.logger.error(str(e))
|
||||
|
||||
|
||||
class AccessTokenCache(object):
|
||||
TOKEN_KEY = 'TICKET::AccessToken'
|
||||
|
||||
@classmethod
|
||||
def get(cls):
|
||||
if cache.get(cls.TOKEN_KEY) is not None and cache.get(cls.TOKEN_KEY) != "":
|
||||
return cache.get(cls.TOKEN_KEY)
|
||||
|
||||
res = get_access_token() or ""
|
||||
|
||||
cache.set(cls.TOKEN_KEY, res, timeout=60 * 60)
|
||||
return res
|
||||
|
||||
@classmethod
|
||||
def clean(cls):
|
||||
cache.clear(cls.TOKEN_KEY)
|
||||
|
||||
|
||||
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 __init__(self, app=None):
|
||||
self.app = AppCache.get(app or 'cmdb')
|
||||
if not self.app:
|
||||
raise Exception(ErrFormat.app_not_found.format(app))
|
||||
self.app_id = self.app.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))
|
||||
resource_type or abort(404, ErrFormat.resource_type_not_found.format(resource_type_name))
|
||||
|
||||
return Resource.get_by(resource_type_id=resource_type.id,
|
||||
app_id=self.app_id,
|
||||
@@ -52,13 +84,23 @@ class ACLManager(object):
|
||||
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)
|
||||
return Role.get_by(name=name, app_id=self.app_id, first=True, to_dict=False) or \
|
||||
Role.get_by(name=name, 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))
|
||||
resource_type or abort(404, ErrFormat.resource_type_not_found.format(resource_type_name))
|
||||
|
||||
ResourceCRUD.add(name, resource_type.id, self.app_id)
|
||||
uid = AuditCRUD.get_current_operate_uid()
|
||||
ResourceCRUD.add(name, resource_type.id, self.app_id, uid)
|
||||
|
||||
def update_resource(self, name, new_name, resource_type_name=None):
|
||||
resource = self._get_resource(name, resource_type_name)
|
||||
|
||||
if resource is None:
|
||||
self.add_resource(new_name, resource_type_name)
|
||||
else:
|
||||
ResourceCRUD.update(resource.id, new_name)
|
||||
|
||||
def grant_resource_to_role(self, name, role, resource_type_name=None, permissions=None):
|
||||
resource = self._get_resource(name, resource_type_name)
|
||||
@@ -72,21 +114,103 @@ class ACLManager(object):
|
||||
if group:
|
||||
PermissionCRUD.grant(role.id, permissions, group_id=group.id)
|
||||
|
||||
def grant_resource_to_role_by_rid(self, name, rid, resource_type_name=None, permissions=None):
|
||||
resource = self._get_resource(name, resource_type_name)
|
||||
|
||||
if resource:
|
||||
PermissionCRUD.grant(rid, permissions, resource_id=resource.id)
|
||||
else:
|
||||
group = self._get_resource_group(name)
|
||||
if group:
|
||||
PermissionCRUD.grant(rid, permissions, group_id=group.id)
|
||||
|
||||
def revoke_resource_from_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.revoke(role.id, permissions, resource_id=resource.id)
|
||||
else:
|
||||
group = self._get_resource_group(name)
|
||||
if group:
|
||||
PermissionCRUD.revoke(role.id, permissions, group_id=group.id)
|
||||
|
||||
def revoke_resource_from_role_by_rid(self, name, rid, resource_type_name=None, permissions=None):
|
||||
resource = self._get_resource(name, resource_type_name)
|
||||
|
||||
if resource:
|
||||
PermissionCRUD.revoke(rid, permissions, resource_id=resource.id)
|
||||
else:
|
||||
group = self._get_resource_group(name)
|
||||
if group:
|
||||
PermissionCRUD.revoke(rid, 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):
|
||||
def has_permission(self, resource_name, resource_type, perm, resource_id=None):
|
||||
if is_app_admin(self.app_id):
|
||||
return True
|
||||
|
||||
role = self._get_role(g.user.username)
|
||||
|
||||
role or abort(404, "Role <{0}> is not found".format(g.user.username))
|
||||
role or abort(404, ErrFormat.role_not_found.format(g.user.username))
|
||||
|
||||
return RoleCRUD.has_permission(role.id, resource_name, resource_type, self.app_id, perm)
|
||||
return RoleCRUD.has_permission(role.id, resource_name, resource_type, self.app_id, perm,
|
||||
resource_id=resource_id)
|
||||
|
||||
@staticmethod
|
||||
def get_user_info(username, app_id=None):
|
||||
user = UserCache.get(username)
|
||||
if not user:
|
||||
user = RoleCache.get_by_name(app_id, username) or RoleCache.get_by_name(None, username) # FIXME
|
||||
|
||||
if not user:
|
||||
return abort(404, ErrFormat.user_not_found.format(username))
|
||||
user = user.to_dict()
|
||||
|
||||
role = Role.get_by(uid=user['uid'], first=True, to_dict=False) if user.get('uid') else None
|
||||
if role is not None:
|
||||
user["rid"] = role.id
|
||||
if app_id is None:
|
||||
parent_ids = []
|
||||
apps = App.get_by(to_dict=False)
|
||||
for app in apps:
|
||||
parent_ids.extend(RoleRelationCRUD.recursive_parent_ids(role.id, app.id))
|
||||
else:
|
||||
parent_ids = RoleRelationCRUD.recursive_parent_ids(role.id, app_id)
|
||||
|
||||
user['parents'] = [RoleCache.get(rid).name for rid in set(parent_ids) if RoleCache.get(rid)]
|
||||
else:
|
||||
user['parents'] = []
|
||||
user['rid'] = user['id'] if user.get('id') else None
|
||||
if user['rid']:
|
||||
parent_ids = RoleRelationCRUD.recursive_parent_ids(user['rid'], app_id)
|
||||
user['parents'] = [RoleCache.get(rid).name for rid in set(parent_ids) if RoleCache.get(rid)]
|
||||
|
||||
return user
|
||||
|
||||
def get_resources(self, resource_type_name=None):
|
||||
role = self._get_role(g.user.username)
|
||||
|
||||
role or abort(404, ErrFormat.role_not_found.format(g.user.username))
|
||||
rid = role.id
|
||||
|
||||
return RoleCRUD.recursive_resources(rid, self.app_id, resource_type_name).get('resources')
|
||||
|
||||
@staticmethod
|
||||
def authenticate_with_token(token):
|
||||
url = "{0}/acl/auth_with_token".format(current_app.config.get('ACL_URI'))
|
||||
try:
|
||||
return requests.post(url, json={"token": token},
|
||||
headers={'App-Access-Token': AccessTokenCache.get()}).json()
|
||||
except:
|
||||
return {}
|
||||
|
||||
|
||||
def validate_permission(resources, resource_type, perm):
|
||||
def validate_permission(resources, resource_type, perm, app=None):
|
||||
if not resources:
|
||||
return
|
||||
|
||||
@@ -96,11 +220,11 @@ def validate_permission(resources, resource_type, perm):
|
||||
|
||||
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")
|
||||
if not ACLManager(app).has_permission(resource, resource_type, perm):
|
||||
return abort(403, ErrFormat.resource_no_permission.format(resource, perm))
|
||||
|
||||
|
||||
def has_perm(resources, resource_type, perm):
|
||||
def has_perm(resources, resource_type, perm, app=None):
|
||||
def decorator_has_perm(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper_has_perm(*args, **kwargs):
|
||||
@@ -108,10 +232,10 @@ def has_perm(resources, resource_type, perm):
|
||||
return
|
||||
|
||||
if current_app.config.get("USE_ACL"):
|
||||
if is_app_admin():
|
||||
if is_app_admin(app):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
validate_permission(resources, resource_type, perm)
|
||||
validate_permission(resources, resource_type, perm, app)
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
@@ -121,11 +245,14 @@ def has_perm(resources, resource_type, 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
|
||||
app = AppCache.get(app)
|
||||
if app is None:
|
||||
return False
|
||||
|
||||
app_id = app.id
|
||||
if 'acl_admin' in session.get("acl", {}).get("parentRoles", []):
|
||||
return True
|
||||
|
||||
for role_name in session.get("acl", {}).get("parentRoles", []):
|
||||
role = RoleCache.get_by_name(app_id, role_name)
|
||||
@@ -135,7 +262,27 @@ def is_app_admin(app=None):
|
||||
return False
|
||||
|
||||
|
||||
def has_perm_from_args(arg_name, resource_type, perm, callback=None):
|
||||
def is_admin():
|
||||
if 'acl_admin' in session.get("acl", {}).get("parentRoles", []):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def admin_required(app=None):
|
||||
def decorator_admin_required(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper_admin_required(*args, **kwargs):
|
||||
if is_app_admin(app):
|
||||
return func(*args, **kwargs)
|
||||
return abort(403, ErrFormat.admin_required)
|
||||
|
||||
return wrapper_admin_required
|
||||
|
||||
return decorator_admin_required
|
||||
|
||||
|
||||
def has_perm_from_args(arg_name, resource_type, perm, callback=None, app=None):
|
||||
def decorator_has_perm(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper_has_perm(*args, **kwargs):
|
||||
@@ -146,10 +293,10 @@ 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():
|
||||
if is_app_admin(app):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
validate_permission(resource, resource_type, perm)
|
||||
validate_permission(resource, resource_type, perm, app)
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
@@ -158,7 +305,7 @@ def has_perm_from_args(arg_name, resource_type, perm, callback=None):
|
||||
return decorator_has_perm
|
||||
|
||||
|
||||
def role_required(role_name):
|
||||
def role_required(role_name, app=None):
|
||||
def decorator_role_required(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper_role_required(*args, **kwargs):
|
||||
@@ -166,8 +313,11 @@ def role_required(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))
|
||||
if getattr(g.user, 'username', None) == "worker":
|
||||
return func(*args, **kwargs)
|
||||
|
||||
if role_name not in session.get("acl", {}).get("parentRoles", []) and not is_app_admin(app):
|
||||
return abort(403, ErrFormat.role_required.format(role_name))
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper_role_required
|
||||
|
91
cmdb-api/api/lib/perm/acl/app.py
Normal file
91
cmdb-api/api/lib/perm/acl/app.py
Normal file
@@ -0,0 +1,91 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import datetime
|
||||
import hashlib
|
||||
|
||||
import jwt
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
|
||||
from api.extensions import db
|
||||
from api.lib.perm.acl.audit import AuditCRUD, AuditOperateType, AuditScope
|
||||
from api.lib.perm.acl.resp_format import ErrFormat
|
||||
from api.models.acl import App
|
||||
|
||||
|
||||
class AppCRUD(object):
|
||||
cls = App
|
||||
|
||||
@staticmethod
|
||||
def get_all():
|
||||
return App.get_by(to_dict=False)
|
||||
|
||||
@staticmethod
|
||||
def get(app_id):
|
||||
return App.get_by_id(app_id)
|
||||
|
||||
@staticmethod
|
||||
def search(q, page=1, page_size=None):
|
||||
query = db.session.query(App).filter(App.deleted.is_(False))
|
||||
if q:
|
||||
query = query.filter(App.name.ilike('%{0}%'.format(q)))
|
||||
|
||||
numfound = query.count()
|
||||
res = query.offset((page - 1) * page_size).limit(page_size)
|
||||
|
||||
return numfound, res
|
||||
|
||||
@classmethod
|
||||
def add(cls, name, description):
|
||||
App.get_by(name=name) and abort(400, ErrFormat.app_is_ready_existed.format(name))
|
||||
|
||||
from api.lib.perm.acl.user import UserCRUD
|
||||
app_id, secret_key = UserCRUD.gen_key_secret()
|
||||
|
||||
app = App.create(name=name, description=description, app_id=app_id, secret_key=secret_key)
|
||||
AuditCRUD.add_resource_log(app.id, AuditOperateType.create, AuditScope.app, app.id, {}, app.to_dict(), {})
|
||||
return app
|
||||
|
||||
@classmethod
|
||||
def update(cls, _id, **kwargs):
|
||||
kwargs.pop('id', None)
|
||||
|
||||
existed = App.get_by_id(_id) or abort(404, ErrFormat.app_not_found.format("id={}".format(_id)))
|
||||
|
||||
origin = existed.to_dict()
|
||||
existed = existed.update(**kwargs)
|
||||
|
||||
AuditCRUD.add_resource_log(existed.id, AuditOperateType.update,
|
||||
AuditScope.app, existed.id, origin, existed.to_dict(), {})
|
||||
|
||||
return existed
|
||||
|
||||
@classmethod
|
||||
def delete(cls, _id):
|
||||
app = App.get_by_id(_id) or abort(404, ErrFormat.app_not_found.format("id={}".format(_id)))
|
||||
origin = app.to_dict()
|
||||
|
||||
app.soft_delete()
|
||||
|
||||
AuditCRUD.add_resource_log(app.id, AuditOperateType.delete,
|
||||
AuditScope.app, app.id, origin, {}, {})
|
||||
|
||||
@staticmethod
|
||||
def _get_by_key(key):
|
||||
return App.get_by(app_id=key, first=True, to_dict=False)
|
||||
|
||||
@classmethod
|
||||
def gen_token(cls, key, secret):
|
||||
app = cls._get_by_key(key) or abort(404, ErrFormat.app_not_found.format("key={}".format(key)))
|
||||
secret != hashlib.md5(app.secret_key.encode('utf-8')).hexdigest() and abort(403, ErrFormat.app_secret_invalid)
|
||||
|
||||
token = jwt.encode({
|
||||
'sub': app.name,
|
||||
'iat': datetime.datetime.now(),
|
||||
'exp': datetime.datetime.now() + datetime.timedelta(minutes=2 * 60)},
|
||||
current_app.config['SECRET_KEY'])
|
||||
|
||||
try:
|
||||
return token.decode()
|
||||
except AttributeError:
|
||||
return token
|
355
cmdb-api/api/lib/perm/acl/audit.py
Normal file
355
cmdb-api/api/lib/perm/acl/audit.py
Normal file
@@ -0,0 +1,355 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import itertools
|
||||
import json
|
||||
from enum import Enum
|
||||
from typing import List
|
||||
|
||||
from flask import g, has_request_context, request
|
||||
from flask_login import current_user
|
||||
from sqlalchemy import func
|
||||
|
||||
from api.lib.perm.acl import AppCache
|
||||
from api.models.acl import AuditRoleLog, AuditResourceLog, AuditPermissionLog, AuditTriggerLog, RolePermission, \
|
||||
Resource, ResourceGroup, Permission, Role, ResourceType
|
||||
|
||||
|
||||
class AuditScope(str, Enum):
|
||||
app = 'app'
|
||||
resource = 'resource'
|
||||
resource_type = 'resource_type'
|
||||
resource_group = 'resource_group'
|
||||
|
||||
user = 'user'
|
||||
role = 'role'
|
||||
role_relation = 'role_relation'
|
||||
|
||||
|
||||
class AuditOperateType(str, Enum):
|
||||
read = 'read'
|
||||
create = 'create'
|
||||
update = 'update'
|
||||
delete = 'delete'
|
||||
|
||||
user_login = 'user_login'
|
||||
role_relation_add = 'role_relation_add'
|
||||
role_relation_delete = 'role_relation_delete'
|
||||
grant = 'grant'
|
||||
revoke = 'revoke'
|
||||
trigger_apply = 'trigger_apply'
|
||||
trigger_cancel = 'trigger_cancel'
|
||||
|
||||
|
||||
class AuditOperateSource(str, Enum):
|
||||
api = 'api'
|
||||
acl = 'acl'
|
||||
trigger = 'trigger'
|
||||
|
||||
|
||||
class AuditCRUD(object):
|
||||
|
||||
@staticmethod
|
||||
def get_current_operate_uid(uid=None):
|
||||
|
||||
user_id = uid or (hasattr(g, 'user') and getattr(g.user, 'uid', None)) \
|
||||
or getattr(current_user, 'user_id', None)
|
||||
|
||||
if has_request_context() and request.headers.get('X-User-Id'):
|
||||
_user_id = request.headers['X-User-Id']
|
||||
user_id = int(_user_id) if _user_id.isdigit() else uid
|
||||
|
||||
return user_id
|
||||
|
||||
@staticmethod
|
||||
def get_operate_source(source):
|
||||
if has_request_context() and request.headers.get('App-Access-Token'):
|
||||
source = AuditOperateSource.api
|
||||
|
||||
return source
|
||||
|
||||
@staticmethod
|
||||
def search_permission(app_id, q=None, page=1, page_size=10, start=None, end=None):
|
||||
criterion = []
|
||||
if app_id:
|
||||
app = AppCache.get(app_id)
|
||||
criterion.append(AuditPermissionLog.app_id == app.id)
|
||||
|
||||
if start:
|
||||
criterion.append(AuditPermissionLog.created_at >= start)
|
||||
if end:
|
||||
criterion.append(AuditPermissionLog.created_at <= end)
|
||||
|
||||
kwargs = {expr.split(':')[0]: expr.split(':')[1] for expr in q.split(',')} if q else {}
|
||||
for k, v in kwargs.items():
|
||||
if k == 'resource_type_id':
|
||||
criterion.append(AuditPermissionLog.resource_type_id == int(v))
|
||||
elif k == 'rid':
|
||||
criterion.append(AuditPermissionLog.rid == int(v))
|
||||
elif k == 'resource_id':
|
||||
criterion.append(func.json_contains(AuditPermissionLog.resource_ids, v) == 1)
|
||||
|
||||
elif k == 'operate_uid':
|
||||
criterion.append(AuditPermissionLog.operate_uid == v)
|
||||
elif k == 'operate_type':
|
||||
criterion.append(AuditPermissionLog.operate_type == v)
|
||||
|
||||
records = AuditPermissionLog.query.filter(
|
||||
AuditPermissionLog.deleted == 0,
|
||||
*criterion) \
|
||||
.order_by(AuditPermissionLog.id.desc()) \
|
||||
.offset((page - 1) * page_size) \
|
||||
.limit(page_size).all()
|
||||
|
||||
data = {
|
||||
'data': [r.to_dict() for r in records],
|
||||
'id2resources': {},
|
||||
'id2roles': {},
|
||||
'id2groups': {},
|
||||
'id2perms': {},
|
||||
'id2resource_types': {},
|
||||
}
|
||||
|
||||
resource_ids = set(itertools.chain(*[r.resource_ids for r in records]))
|
||||
group_ids = set(itertools.chain(*[r.group_ids for r in records]))
|
||||
permission_ids = set(itertools.chain(*[r.permission_ids for r in records]))
|
||||
resource_type_ids = {r.resource_type_id for r in records}
|
||||
rids = {r.rid for r in records}
|
||||
|
||||
if rids:
|
||||
roles = Role.query.filter(Role.id.in_(rids)).all()
|
||||
data['id2roles'] = {r.id: r.to_dict() for r in roles}
|
||||
|
||||
if resource_type_ids:
|
||||
resource_types = ResourceType.query.filter(ResourceType.id.in_(resource_type_ids)).all()
|
||||
data['id2resource_types'] = {r.id: r.to_dict() for r in resource_types}
|
||||
|
||||
if resource_ids:
|
||||
resources = Resource.query.filter(Resource.id.in_(resource_ids)).all()
|
||||
data['id2resources'] = {r.id: r.to_dict() for r in resources}
|
||||
|
||||
if group_ids:
|
||||
groups = ResourceGroup.query.filter(ResourceGroup.id.in_(group_ids)).all()
|
||||
data['id2groups'] = {_g.id: _g.to_dict() for _g in groups}
|
||||
|
||||
if permission_ids:
|
||||
perms = Permission.query.filter(Permission.id.in_(permission_ids)).all()
|
||||
|
||||
data['id2perms'] = {_p.id: _p.to_dict() for _p in perms}
|
||||
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def search_role(app_id, q=None, page=1, page_size=10, start=None, end=None):
|
||||
criterion = []
|
||||
if app_id:
|
||||
app = AppCache.get(app_id)
|
||||
criterion.append(AuditRoleLog.app_id == app.id)
|
||||
|
||||
if start:
|
||||
criterion.append(AuditRoleLog.created_at >= start)
|
||||
if end:
|
||||
criterion.append(AuditRoleLog.created_at <= end)
|
||||
|
||||
kwargs = {expr.split(':')[0]: expr.split(':')[1] for expr in q.split(',')} if q else {}
|
||||
for k, v in kwargs.items():
|
||||
if k == 'scope':
|
||||
criterion.append(AuditRoleLog.scope == v)
|
||||
elif k == 'link_id':
|
||||
criterion.append(AuditRoleLog.link_id == int(v))
|
||||
elif k == 'operate_uid':
|
||||
criterion.append(AuditRoleLog.operate_uid == v)
|
||||
elif k == 'operate_type':
|
||||
criterion.append(AuditRoleLog.operate_type == v)
|
||||
|
||||
records = AuditRoleLog.query.filter(AuditRoleLog.deleted == 0, *criterion) \
|
||||
.order_by(AuditRoleLog.id.desc()) \
|
||||
.offset((page - 1) * page_size) \
|
||||
.limit(page_size).all()
|
||||
|
||||
data = {
|
||||
'data': [r.to_dict() for r in records],
|
||||
'id2roles': {}
|
||||
}
|
||||
|
||||
role_permissions = list(itertools.chain(*[r.extra.get('role_permissions', []) for r in records]))
|
||||
_rids = set()
|
||||
if role_permissions:
|
||||
|
||||
resource_ids = set([r['resource_id'] for r in role_permissions])
|
||||
group_ids = set([r['group_id'] for r in role_permissions])
|
||||
perm_ids = set([r['perm_id'] for r in role_permissions])
|
||||
_rids.update(set([r['rid'] for r in role_permissions]))
|
||||
|
||||
if resource_ids:
|
||||
resources = Resource.query.filter(Resource.id.in_(resource_ids)).all()
|
||||
data['id2resources'] = {r.id: r.to_dict() for r in resources}
|
||||
|
||||
if group_ids:
|
||||
groups = ResourceGroup.query.filter(ResourceGroup.id.in_(group_ids)).all()
|
||||
data['id2groups'] = {_g.id: _g.to_dict() for _g in groups}
|
||||
|
||||
if perm_ids:
|
||||
perms = Permission.query.filter(Permission.id.in_(perm_ids)).all()
|
||||
|
||||
data['id2perms'] = {_p.id: _p.to_dict() for _p in perms}
|
||||
|
||||
rids = set(itertools.chain(*[r.extra.get('child_ids', []) + r.extra.get('parent_ids', [])
|
||||
for r in records]))
|
||||
rids.update(_rids)
|
||||
if rids:
|
||||
roles = Role.query.filter(Role.id.in_(rids)).all()
|
||||
data['id2roles'].update({r.id: r.to_dict() for r in roles})
|
||||
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def search_resource(app_id, q=None, page=1, page_size=10, start=None, end=None):
|
||||
criterion = []
|
||||
if app_id:
|
||||
app = AppCache.get(app_id)
|
||||
criterion.append(AuditResourceLog.app_id == app.id)
|
||||
|
||||
if start:
|
||||
criterion.append(AuditResourceLog.created_at >= start)
|
||||
if end:
|
||||
criterion.append(AuditResourceLog.created_at <= end)
|
||||
|
||||
kwargs = {expr.split(':')[0]: expr.split(':')[1] for expr in q.split(',')} if q else {}
|
||||
for k, v in kwargs.items():
|
||||
if k == 'scope':
|
||||
criterion.append(AuditResourceLog.scope == v)
|
||||
elif k == 'link_id':
|
||||
criterion.append(AuditResourceLog.link_id == int(v))
|
||||
elif k == 'operate_uid':
|
||||
criterion.append(AuditResourceLog.operate_uid == v)
|
||||
elif k == 'operate_type':
|
||||
criterion.append(AuditResourceLog.operate_type == v)
|
||||
|
||||
records = AuditResourceLog.query.filter(
|
||||
AuditResourceLog.deleted == 0,
|
||||
*criterion) \
|
||||
.order_by(AuditResourceLog.id.desc()) \
|
||||
.offset((page - 1) * page_size) \
|
||||
.limit(page_size).all()
|
||||
|
||||
data = {
|
||||
'data': [r.to_dict() for r in records],
|
||||
}
|
||||
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def search_trigger(app_id, q=None, page=1, page_size=10, start=None, end=None):
|
||||
criterion = []
|
||||
if app_id:
|
||||
app = AppCache.get(app_id)
|
||||
criterion.append(AuditTriggerLog.app_id == app.id)
|
||||
|
||||
if start:
|
||||
criterion.append(AuditTriggerLog.created_at >= start)
|
||||
if end:
|
||||
criterion.append(AuditTriggerLog.created_at <= end)
|
||||
|
||||
kwargs = {expr.split(':')[0]: expr.split(':')[1] for expr in q.split(',')} if q else {}
|
||||
for k, v in kwargs.items():
|
||||
if k == 'trigger_id':
|
||||
criterion.append(AuditTriggerLog.trigger_id == int(v))
|
||||
elif k == 'operate_uid':
|
||||
criterion.append(AuditTriggerLog.operate_uid == v)
|
||||
elif k == 'operate_type':
|
||||
criterion.append(AuditTriggerLog.operate_type == v)
|
||||
|
||||
records = AuditTriggerLog.query.filter(
|
||||
AuditTriggerLog.deleted == 0,
|
||||
*criterion) \
|
||||
.order_by(AuditTriggerLog.id.desc()) \
|
||||
.offset((page - 1) * page_size) \
|
||||
.limit(page_size).all()
|
||||
|
||||
data = {
|
||||
'data': [r.to_dict() for r in records],
|
||||
'id2roles': {},
|
||||
'id2resource_types': {},
|
||||
}
|
||||
|
||||
rids = set(itertools.chain(*[json.loads(r.origin.get('roles', "[]")) +
|
||||
json.loads(r.current.get('roles', "[]"))
|
||||
for r in records]))
|
||||
resource_type_ids = set([r.origin.get('resource_type_id') for r in records
|
||||
if r.origin.get('resource_type_id')] +
|
||||
[r.current.get('resource_type_id') for r in records
|
||||
if r.current.get('resource_type_id')])
|
||||
if rids:
|
||||
roles = Role.query.filter(Role.id.in_(rids)).all()
|
||||
data['id2roles'] = {r.id: r.to_dict() for r in roles}
|
||||
|
||||
if resource_type_ids:
|
||||
resource_types = ResourceType.query.filter(ResourceType.id.in_(resource_type_ids)).all()
|
||||
data['id2resource_types'] = {r.id: r.to_dict() for r in resource_types}
|
||||
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def add_role_log(cls, app_id, operate_type: AuditOperateType,
|
||||
scope: AuditScope, link_id: int, origin: dict, current: dict, extra: dict,
|
||||
uid=None, source=AuditOperateSource.acl):
|
||||
|
||||
user_id = cls.get_current_operate_uid(uid)
|
||||
|
||||
AuditRoleLog.create(app_id=app_id, operate_uid=user_id, operate_type=operate_type.value,
|
||||
scope=scope.value,
|
||||
link_id=link_id,
|
||||
origin=origin,
|
||||
current=current,
|
||||
extra=extra,
|
||||
source=source.value)
|
||||
|
||||
@classmethod
|
||||
def add_resource_log(cls, app_id, operate_type: AuditOperateType,
|
||||
scope: AuditScope, link_id: int, origin: dict, current: dict, extra: dict,
|
||||
uid=None, source=AuditOperateSource.acl):
|
||||
user_id = cls.get_current_operate_uid(uid)
|
||||
|
||||
source = cls.get_operate_source(source)
|
||||
|
||||
AuditResourceLog.create(app_id=app_id, operate_uid=user_id, operate_type=operate_type.value,
|
||||
scope=scope.value,
|
||||
link_id=link_id,
|
||||
origin=origin,
|
||||
current=current,
|
||||
extra=extra,
|
||||
source=source.value)
|
||||
|
||||
@classmethod
|
||||
def add_permission_log(cls, app_id, operate_type: AuditOperateType,
|
||||
rid: int, rt_id: int, role_permissions: List[RolePermission],
|
||||
uid=None, source=AuditOperateSource.acl):
|
||||
|
||||
if not role_permissions:
|
||||
return
|
||||
user_id = cls.get_current_operate_uid(uid)
|
||||
source = cls.get_operate_source(source)
|
||||
|
||||
resource_ids = list({r.resource_id for r in role_permissions if r.resource_id})
|
||||
permission_ids = list({r.perm_id for r in role_permissions if r.perm_id})
|
||||
group_ids = list({r.group_id for r in role_permissions if r.group_id})
|
||||
|
||||
AuditPermissionLog.create(app_id=app_id, operate_uid=user_id,
|
||||
operate_type=operate_type.value,
|
||||
rid=rid,
|
||||
resource_type_id=rt_id,
|
||||
resource_ids=resource_ids,
|
||||
permission_ids=permission_ids,
|
||||
group_ids=group_ids,
|
||||
source=source.value)
|
||||
|
||||
@classmethod
|
||||
def add_trigger_log(cls, app_id, trigger_id, operate_type: AuditOperateType,
|
||||
origin: dict, current: dict, extra: dict,
|
||||
uid=None, source=AuditOperateSource.acl):
|
||||
|
||||
user_id = cls.get_current_operate_uid(uid)
|
||||
source = cls.get_operate_source(source)
|
||||
|
||||
AuditTriggerLog.create(app_id=app_id, trigger_id=trigger_id, operate_uid=user_id,
|
||||
operate_type=operate_type.value,
|
||||
origin=origin, current=current, extra=extra, source=source.value)
|
@@ -1,13 +1,32 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
|
||||
import msgpack
|
||||
|
||||
from api.extensions import cache
|
||||
from api.extensions import db
|
||||
from api.lib.utils import Lock
|
||||
from api.models.acl import App
|
||||
from api.models.acl import Permission
|
||||
from api.models.acl import Resource
|
||||
from api.models.acl import ResourceGroup
|
||||
from api.models.acl import Role
|
||||
from api.models.acl import User
|
||||
|
||||
|
||||
class AppAccessTokenCache(object):
|
||||
PREFIX = "AppAccessTokenCache::token::{}"
|
||||
|
||||
@classmethod
|
||||
def get_app_id(cls, token):
|
||||
app_id = cache.get(cls.PREFIX.format(token))
|
||||
return app_id
|
||||
|
||||
@classmethod
|
||||
def set(cls, token, app, timeout=7200):
|
||||
cache.set(token, cls.PREFIX.format(app.app_id), timeout=timeout)
|
||||
|
||||
|
||||
class AppCache(object):
|
||||
PREFIX_ID = "App::id::{0}"
|
||||
PREFIX_NAME = "App::name::{0}"
|
||||
@@ -37,16 +56,19 @@ class UserCache(object):
|
||||
PREFIX_ID = "User::uid::{0}"
|
||||
PREFIX_NAME = "User::username::{0}"
|
||||
PREFIX_NICK = "User::nickname::{0}"
|
||||
PREFIX_WXID = "User::wxid::{0}"
|
||||
|
||||
@classmethod
|
||||
def get(cls, key):
|
||||
user = cache.get(cls.PREFIX_ID.format(key)) or \
|
||||
cache.get(cls.PREFIX_NAME.format(key)) or \
|
||||
cache.get(cls.PREFIX_NICK.format(key))
|
||||
cache.get(cls.PREFIX_NICK.format(key)) or \
|
||||
cache.get(cls.PREFIX_WXID.format(key))
|
||||
if not user:
|
||||
user = User.query.get(key) or \
|
||||
User.query.get_by_username(key) or \
|
||||
User.query.get_by_nickname(key)
|
||||
User.query.get_by_nickname(key) or \
|
||||
User.query.get_by_wxid(key)
|
||||
if user:
|
||||
cls.set(user)
|
||||
|
||||
@@ -57,12 +79,16 @@ class UserCache(object):
|
||||
cache.set(cls.PREFIX_ID.format(user.uid), user)
|
||||
cache.set(cls.PREFIX_NAME.format(user.username), user)
|
||||
cache.set(cls.PREFIX_NICK.format(user.nickname), user)
|
||||
if user.wx_id:
|
||||
cache.set(cls.PREFIX_WXID.format(user.wx_id), user)
|
||||
|
||||
@classmethod
|
||||
def clean(cls, user):
|
||||
cache.delete(cls.PREFIX_ID.format(user.uid))
|
||||
cache.delete(cls.PREFIX_NAME.format(user.username))
|
||||
cache.delete(cls.PREFIX_NICK.format(user.nickname))
|
||||
if user.wx_id:
|
||||
cache.delete(cls.PREFIX_WXID.format(user.wx_id))
|
||||
|
||||
|
||||
class RoleCache(object):
|
||||
@@ -74,6 +100,9 @@ class RoleCache(object):
|
||||
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 None and app_id is None: # try global role
|
||||
role = Role.get_by(name=name, first=True, to_dict=False)
|
||||
|
||||
if role is not None:
|
||||
cache.set(cls.PREFIX_NAME.format(app_id, name), role)
|
||||
|
||||
@@ -98,77 +127,196 @@ class RoleCache(object):
|
||||
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}"
|
||||
class HasResourceRoleCache(object):
|
||||
PREFIX_KEY = "HasResourceRoleCache::AppId::{0}"
|
||||
|
||||
@classmethod
|
||||
def get_parent_ids(cls, rid):
|
||||
parent_ids = cache.get(cls.PREFIX_PARENT.format(rid))
|
||||
def get(cls, app_id):
|
||||
return cache.get(cls.PREFIX_KEY.format(app_id)) or {}
|
||||
|
||||
@classmethod
|
||||
def add(cls, rid, app_id):
|
||||
with Lock('HasResourceRoleCache'):
|
||||
c = cls.get(app_id)
|
||||
c[rid] = 1
|
||||
cache.set(cls.PREFIX_KEY.format(app_id), c, timeout=0)
|
||||
|
||||
@classmethod
|
||||
def remove(cls, rid, app_id):
|
||||
with Lock('HasResourceRoleCache'):
|
||||
c = cls.get(app_id)
|
||||
c.pop(rid, None)
|
||||
cache.set(cls.PREFIX_KEY.format(app_id), c, timeout=0)
|
||||
|
||||
|
||||
class RoleRelationCache(object):
|
||||
PREFIX_PARENT = "RoleRelationParent::id::{0}::AppId::{1}"
|
||||
PREFIX_CHILDREN = "RoleRelationChildren::id::{0}::AppId::{1}"
|
||||
PREFIX_RESOURCES = "RoleRelationResources::id::{0}::AppId::{1}"
|
||||
PREFIX_RESOURCES2 = "RoleRelationResources2::id::{0}::AppId::{1}"
|
||||
|
||||
@classmethod
|
||||
def get_parent_ids(cls, rid, app_id):
|
||||
parent_ids = cache.get(cls.PREFIX_PARENT.format(rid, app_id))
|
||||
if not parent_ids:
|
||||
from api.lib.perm.acl.role import RoleRelationCRUD
|
||||
parent_ids = RoleRelationCRUD.get_parent_ids(rid)
|
||||
cache.set(cls.PREFIX_PARENT.format(rid), parent_ids, timeout=0)
|
||||
parent_ids = RoleRelationCRUD.get_parent_ids(rid, app_id)
|
||||
cache.set(cls.PREFIX_PARENT.format(rid, app_id), parent_ids, timeout=0)
|
||||
|
||||
return parent_ids
|
||||
|
||||
@classmethod
|
||||
def get_child_ids(cls, rid):
|
||||
child_ids = cache.get(cls.PREFIX_CHILDREN.format(rid))
|
||||
def get_child_ids(cls, rid, app_id):
|
||||
child_ids = cache.get(cls.PREFIX_CHILDREN.format(rid, app_id))
|
||||
if not child_ids:
|
||||
from api.lib.perm.acl.role import RoleRelationCRUD
|
||||
child_ids = RoleRelationCRUD.get_child_ids(rid)
|
||||
cache.set(cls.PREFIX_CHILDREN.format(rid), child_ids, timeout=0)
|
||||
child_ids = RoleRelationCRUD.get_child_ids(rid, app_id)
|
||||
cache.set(cls.PREFIX_CHILDREN.format(rid, app_id), child_ids, timeout=0)
|
||||
|
||||
return child_ids
|
||||
|
||||
@classmethod
|
||||
def get_resources(cls, rid):
|
||||
def get_resources(cls, rid, app_id):
|
||||
"""
|
||||
:param rid:
|
||||
:param app_id:
|
||||
:return: {id2perms: {resource_id: [perm,]}, group2perms: {group_id: [perm, ]}}
|
||||
"""
|
||||
resources = cache.get(cls.PREFIX_RESOURCES.format(rid))
|
||||
resources = cache.get(cls.PREFIX_RESOURCES.format(rid, app_id))
|
||||
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)
|
||||
resources = RoleCRUD.get_resources(rid, app_id)
|
||||
if resources['id2perms'] or resources['group2perms']:
|
||||
cache.set(cls.PREFIX_RESOURCES.format(rid, app_id), resources, timeout=0)
|
||||
|
||||
return resources or {}
|
||||
|
||||
@classmethod
|
||||
def rebuild(cls, rid):
|
||||
cls.clean(rid)
|
||||
db.session.close()
|
||||
cls.get_parent_ids(rid)
|
||||
cls.get_child_ids(rid)
|
||||
cls.get_resources(rid)
|
||||
def get_resources2(cls, rid, app_id):
|
||||
r_g = cache.get(cls.PREFIX_RESOURCES2.format(rid, app_id))
|
||||
if not r_g:
|
||||
res = cls.get_resources(rid, app_id)
|
||||
id2perms = res['id2perms']
|
||||
group2perms = res['group2perms']
|
||||
|
||||
resources, groups = dict(), dict()
|
||||
for _id in id2perms:
|
||||
resource = ResourceCache.get(_id)
|
||||
if not resource:
|
||||
continue
|
||||
resource = resource.to_dict()
|
||||
resource.update(dict(permissions=id2perms[_id]))
|
||||
resources[_id] = resource
|
||||
|
||||
for _id in group2perms:
|
||||
group = ResourceGroupCache.get(_id)
|
||||
if not group:
|
||||
continue
|
||||
group = group.to_dict()
|
||||
group.update(dict(permissions=group2perms[_id]))
|
||||
groups[_id] = group
|
||||
r_g = msgpack.dumps(dict(resources=resources, groups=groups))
|
||||
cache.set(cls.PREFIX_RESOURCES2.format(rid, app_id), r_g, timeout=0)
|
||||
|
||||
return msgpack.loads(r_g, raw=False)
|
||||
|
||||
@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))
|
||||
def rebuild(cls, rid, app_id):
|
||||
cls.clean(rid, app_id)
|
||||
db.session.remove()
|
||||
|
||||
cls.get_parent_ids(rid, app_id)
|
||||
cls.get_child_ids(rid, app_id)
|
||||
resources = cls.get_resources(rid, app_id)
|
||||
if resources.get('id2perms') or resources.get('group2perms'):
|
||||
HasResourceRoleCache.add(rid, app_id)
|
||||
else:
|
||||
HasResourceRoleCache.remove(rid, app_id)
|
||||
cls.get_resources2(rid, app_id)
|
||||
|
||||
@classmethod
|
||||
def rebuild2(cls, rid, app_id):
|
||||
cache.delete(cls.PREFIX_RESOURCES2.format(rid, app_id))
|
||||
db.session.remove()
|
||||
cls.get_resources2(rid, app_id)
|
||||
|
||||
@classmethod
|
||||
def clean(cls, rid, app_id):
|
||||
cache.delete(cls.PREFIX_PARENT.format(rid, app_id))
|
||||
cache.delete(cls.PREFIX_CHILDREN.format(rid, app_id))
|
||||
cache.delete(cls.PREFIX_RESOURCES.format(rid, app_id))
|
||||
cache.delete(cls.PREFIX_RESOURCES2.format(rid, app_id))
|
||||
|
||||
|
||||
class PermissionCache(object):
|
||||
PREFIX_ID = "Permission::id::{0}"
|
||||
PREFIX_NAME = "Permission::name::{0}"
|
||||
PREFIX_ID = "Permission::id::{0}::ResourceTypeId::{1}"
|
||||
PREFIX_NAME = "Permission::name::{0}::ResourceTypeId::{1}"
|
||||
|
||||
@classmethod
|
||||
def get(cls, key):
|
||||
perm = cache.get(cls.PREFIX_ID.format(key))
|
||||
perm = perm or cache.get(cls.PREFIX_NAME.format(key))
|
||||
def get(cls, key, rt_id):
|
||||
perm = cache.get(cls.PREFIX_ID.format(key, rt_id))
|
||||
perm = perm or cache.get(cls.PREFIX_NAME.format(key, rt_id))
|
||||
if perm is None:
|
||||
perm = Permission.get_by_id(key)
|
||||
perm = perm or Permission.get_by(name=key, first=True, to_dict=False)
|
||||
perm = perm or Permission.get_by(name=key, resource_type_id=rt_id, first=True, to_dict=False)
|
||||
if perm is not None:
|
||||
cache.set(cls.PREFIX_ID.format(key), perm)
|
||||
cache.set(cls.PREFIX_ID.format(perm.id, rt_id), perm)
|
||||
cache.set(cls.PREFIX_NAME.format(perm.name, rt_id), perm)
|
||||
|
||||
return perm
|
||||
|
||||
|
||||
class ResourceCache(object):
|
||||
PREFIX_ID = "Resource::id::{0}"
|
||||
PREFIX_NAME = "Resource::type_id::{0}::name::{1}"
|
||||
|
||||
@classmethod
|
||||
def clean(cls, key):
|
||||
cache.delete(cls.PREFIX_ID.format(key))
|
||||
cache.delete(cls.PREFIX_NAME.format(key))
|
||||
def get(cls, key, type_id=None):
|
||||
resource = cache.get(cls.PREFIX_ID.format(key)) or cache.get(cls.PREFIX_NAME.format(type_id, key))
|
||||
if resource is None:
|
||||
resource = Resource.get_by_id(key) or Resource.get_by(name=key,
|
||||
resource_type_id=type_id,
|
||||
to_dict=False,
|
||||
first=True)
|
||||
if resource is not None:
|
||||
cls.set(resource)
|
||||
|
||||
return resource
|
||||
|
||||
@classmethod
|
||||
def set(cls, resource):
|
||||
cache.set(cls.PREFIX_ID.format(resource.id), resource)
|
||||
cache.set(cls.PREFIX_NAME.format(resource.resource_type_id, resource.name), resource)
|
||||
|
||||
@classmethod
|
||||
def clean(cls, resource):
|
||||
cache.delete(cls.PREFIX_ID.format(resource.id))
|
||||
cache.delete(cls.PREFIX_NAME.format(resource.resource_type_id, resource.name))
|
||||
|
||||
|
||||
class ResourceGroupCache(object):
|
||||
PREFIX_ID = "ResourceGroup::id::{0}"
|
||||
PREFIX_NAME = "ResourceGroup::type_id::{0}::name::{1}"
|
||||
|
||||
@classmethod
|
||||
def get(cls, key, type_id=None):
|
||||
group = cache.get(cls.PREFIX_ID.format(key)) or cache.get(cls.PREFIX_NAME.format(type_id, key))
|
||||
if group is None:
|
||||
group = ResourceGroup.get_by_id(key) or ResourceGroup.get_by(name=key,
|
||||
resource_type_id=type_id,
|
||||
to_dict=False,
|
||||
first=True)
|
||||
if group is not None:
|
||||
cls.set(group)
|
||||
|
||||
return group
|
||||
|
||||
@classmethod
|
||||
def set(cls, group):
|
||||
cache.set(cls.PREFIX_ID.format(group.id), group)
|
||||
cache.set(cls.PREFIX_NAME.format(group.resource_type_id, group.name), group)
|
||||
|
||||
@classmethod
|
||||
def clean(cls, group):
|
||||
cache.delete(cls.PREFIX_ID.format(group.id))
|
||||
cache.delete(cls.PREFIX_NAME.format(group.resource_type_id, group.name))
|
||||
|
@@ -1,5 +1,13 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from api.lib.cmdb.const import CMDB_QUEUE
|
||||
from api.lib.utils import BaseEnum
|
||||
|
||||
ACL_QUEUE = CMDB_QUEUE
|
||||
ACL_QUEUE = "acl_async"
|
||||
|
||||
|
||||
class OperateType(BaseEnum):
|
||||
LOGIN = "0"
|
||||
READ = "1"
|
||||
UPDATE = "2"
|
||||
CREATE = "3"
|
||||
DELETE = "4"
|
||||
|
@@ -1,48 +1,302 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import datetime
|
||||
|
||||
from flask import abort
|
||||
|
||||
from api.extensions import db
|
||||
from api.lib.perm.acl.audit import AuditCRUD, AuditOperateType, AuditOperateSource
|
||||
from api.lib.perm.acl.cache import PermissionCache
|
||||
from api.lib.perm.acl.cache import RoleCache
|
||||
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.role import RoleRelationCRUD
|
||||
from api.models.acl import Resource
|
||||
from api.models.acl import ResourceGroup
|
||||
from api.models.acl import ResourceType
|
||||
from api.models.acl import RolePermission
|
||||
from api.tasks.acl import role_rebuild
|
||||
|
||||
|
||||
class PermissionCRUD(object):
|
||||
@staticmethod
|
||||
def get_all(resource_id=None, group_id=None):
|
||||
def get_all(resource_id=None, group_id=None, need_users=True):
|
||||
result = dict()
|
||||
|
||||
if resource_id is not None:
|
||||
r = Resource.get_by_id(resource_id)
|
||||
if not r:
|
||||
return result
|
||||
rt_id = r.resource_type_id
|
||||
perms = RolePermission.get_by(resource_id=resource_id, to_dict=False)
|
||||
else:
|
||||
rg = ResourceGroup.get_by_id(group_id)
|
||||
if not rg:
|
||||
return result
|
||||
rt_id = rg.resource_type_id
|
||||
perms = RolePermission.get_by(group_id=group_id, to_dict=False)
|
||||
|
||||
rid2obj = dict()
|
||||
uid2obj = dict()
|
||||
for perm in perms:
|
||||
perm_dict = PermissionCache.get(perm.perm_id).to_dict()
|
||||
perm_dict = PermissionCache.get(perm.perm_id, rt_id)
|
||||
perm_dict = perm_dict and perm_dict.to_dict()
|
||||
if not perm_dict:
|
||||
continue
|
||||
perm_dict.update(dict(rid=perm.rid))
|
||||
result.setdefault(RoleCache.get(perm.rid).name, []).append(perm_dict)
|
||||
|
||||
if perm.rid not in rid2obj:
|
||||
rid2obj[perm.rid] = RoleCache.get(perm.rid)
|
||||
|
||||
role = rid2obj[perm.rid]
|
||||
if role and role.uid:
|
||||
if role.uid not in uid2obj:
|
||||
uid2obj[role.uid] = UserCache.get(role.uid)
|
||||
|
||||
name = uid2obj[role.uid].nickname
|
||||
elif role:
|
||||
name = role.name
|
||||
else:
|
||||
continue
|
||||
|
||||
result.setdefault(name,
|
||||
dict(perms=[],
|
||||
users=RoleRelationCRUD.get_users_by_rid(perm.rid, perm.app_id, rid2obj, uid2obj)
|
||||
if need_users else [])
|
||||
)['perms'].append(perm_dict)
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def grant(rid, perms, resource_id=None, group_id=None):
|
||||
for perm in perms:
|
||||
perm = PermissionCache.get(perm)
|
||||
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)
|
||||
@classmethod
|
||||
def get_all2(cls, resource_name, resource_type_name, app_id):
|
||||
rt = ResourceType.get_by(name=resource_type_name, first=True, to_dict=False)
|
||||
rt or abort(404, ErrFormat.resource_type_not_found.format(resource_type_name))
|
||||
|
||||
role_rebuild.apply_async(args=(rid,), queue=ACL_QUEUE)
|
||||
r = Resource.get_by(name=resource_name, resource_type_id=rt.id, app_id=app_id, first=True, to_dict=False)
|
||||
|
||||
return r and cls.get_all(r.id)
|
||||
|
||||
@staticmethod
|
||||
def revoke(rid, perms, resource_id=None, group_id=None):
|
||||
def grant(rid, perms, resource_id=None, group_id=None, rebuild=True, source=AuditOperateSource.acl):
|
||||
app_id = None
|
||||
rt_id = None
|
||||
|
||||
from api.lib.perm.acl.resource import ResourceTypeCRUD
|
||||
|
||||
if resource_id is not None:
|
||||
from api.models.acl import Resource
|
||||
|
||||
resource = Resource.get_by_id(resource_id) or abort(404, ErrFormat.resource_not_found.format(
|
||||
"id={}".format(resource_id)))
|
||||
|
||||
app_id = resource.app_id
|
||||
rt_id = resource.resource_type_id
|
||||
if not perms:
|
||||
perms = [i.get('name') for i in ResourceTypeCRUD.get_perms(resource.resource_type_id)]
|
||||
|
||||
elif group_id is not None:
|
||||
from api.models.acl import ResourceGroup
|
||||
|
||||
group = ResourceGroup.get_by_id(group_id) or \
|
||||
abort(404, ErrFormat.resource_group_not_found.format("id={}".format(group_id)))
|
||||
app_id = group.app_id
|
||||
rt_id = group.resource_type_id
|
||||
if not perms:
|
||||
perms = [i.get('name') for i in ResourceTypeCRUD.get_perms(group.resource_type_id)]
|
||||
|
||||
_role_permissions = []
|
||||
|
||||
for _perm in set(perms):
|
||||
perm = PermissionCache.get(_perm, rt_id)
|
||||
if not perm:
|
||||
continue
|
||||
|
||||
existed = RolePermission.get_by(rid=rid,
|
||||
app_id=app_id,
|
||||
perm_id=perm.id,
|
||||
group_id=group_id,
|
||||
resource_id=resource_id)
|
||||
|
||||
if not existed:
|
||||
__role_permission = RolePermission.create(rid=rid,
|
||||
app_id=app_id,
|
||||
perm_id=perm.id,
|
||||
group_id=group_id,
|
||||
resource_id=resource_id)
|
||||
_role_permissions.append(__role_permission)
|
||||
|
||||
if rebuild:
|
||||
role_rebuild.apply_async(args=(rid, app_id), queue=ACL_QUEUE)
|
||||
|
||||
AuditCRUD.add_permission_log(app_id, AuditOperateType.grant, rid, rt_id, _role_permissions,
|
||||
source=source)
|
||||
|
||||
@staticmethod
|
||||
def batch_grant_by_resource_names(rid, perms, resource_type_id, resource_names,
|
||||
resource_ids=None, perm_map=None, app_id=None):
|
||||
|
||||
from api.lib.perm.acl.resource import ResourceTypeCRUD
|
||||
|
||||
if resource_names:
|
||||
resource_ids = []
|
||||
from api.models.acl import Resource
|
||||
|
||||
for n in resource_names:
|
||||
resource = Resource.get_by(name=n, resource_type_id=resource_type_id, first=True, to_dict=False)
|
||||
if resource:
|
||||
app_id = resource.app_id
|
||||
if not perms:
|
||||
perms = [i.get('name') for i in ResourceTypeCRUD.get_perms(resource.resource_type_id)]
|
||||
|
||||
resource_ids.append(resource.id)
|
||||
resource_ids = resource_ids or []
|
||||
|
||||
_role_permissions = []
|
||||
if isinstance(perm_map, dict):
|
||||
perm2resource = dict()
|
||||
for resource_id in resource_ids:
|
||||
for _perm in (perm_map.get(str(resource_id)) or []):
|
||||
perm2resource.setdefault(_perm, []).append(resource_id)
|
||||
for _perm in perm2resource:
|
||||
perm = PermissionCache.get(_perm, resource_type_id)
|
||||
existeds = RolePermission.get_by(rid=rid,
|
||||
app_id=app_id,
|
||||
perm_id=perm.id,
|
||||
__func_in___key_resource_id=perm2resource[_perm],
|
||||
to_dict=False)
|
||||
|
||||
for resource_id in (set(perm2resource[_perm]) - set([i.resource_id for i in existeds])):
|
||||
_role_permission = RolePermission.create(flush=False,
|
||||
commit=False,
|
||||
rid=rid,
|
||||
app_id=app_id,
|
||||
perm_id=perm.id,
|
||||
resource_id=resource_id,
|
||||
)
|
||||
_role_permissions.append(_role_permission)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
else:
|
||||
for _perm in perms:
|
||||
perm = PermissionCache.get(_perm, resource_type_id)
|
||||
for resource_id in resource_ids:
|
||||
existed = RolePermission.get_by(rid=rid,
|
||||
app_id=app_id,
|
||||
perm_id=perm.id,
|
||||
resource_id=resource_id)
|
||||
|
||||
if not existed:
|
||||
_role_permission = RolePermission.create(rid=rid,
|
||||
app_id=app_id,
|
||||
perm_id=perm.id,
|
||||
resource_id=resource_id)
|
||||
_role_permissions.append(_role_permission)
|
||||
|
||||
role_rebuild.apply_async(args=(rid, app_id), queue=ACL_QUEUE)
|
||||
|
||||
AuditCRUD.add_permission_log(app_id, AuditOperateType.grant, rid, resource_type_id, _role_permissions)
|
||||
|
||||
@staticmethod
|
||||
def revoke(rid, perms, resource_id=None, group_id=None, rebuild=True, source=AuditOperateSource.acl):
|
||||
app_id = None
|
||||
rt_id = None
|
||||
|
||||
from api.lib.perm.acl.resource import ResourceTypeCRUD
|
||||
if resource_id is not None:
|
||||
from api.models.acl import Resource
|
||||
|
||||
resource = Resource.get_by_id(resource_id) or \
|
||||
abort(404, ErrFormat.resource_not_found.format("id={}".format(resource_id)))
|
||||
app_id = resource.app_id
|
||||
rt_id = resource.resource_type_id
|
||||
if not perms:
|
||||
perms = [i.get('name') for i in ResourceTypeCRUD.get_perms(resource.resource_type_id)]
|
||||
|
||||
elif group_id is not None:
|
||||
from api.models.acl import ResourceGroup
|
||||
|
||||
group = ResourceGroup.get_by_id(group_id) or \
|
||||
abort(404, ErrFormat.resource_group_not_found.format("id={}".format(group_id)))
|
||||
app_id = group.app_id
|
||||
|
||||
rt_id = group.resource_type_id
|
||||
if not perms:
|
||||
perms = [i.get('name') for i in ResourceTypeCRUD.get_perms(group.resource_type_id)]
|
||||
_role_permissions = []
|
||||
|
||||
for perm in perms:
|
||||
perm = PermissionCache.get(perm)
|
||||
perm = PermissionCache.get(perm, rt_id)
|
||||
if not perm:
|
||||
continue
|
||||
existed = RolePermission.get_by(rid=rid,
|
||||
perm_id=perm.id,
|
||||
group_id=group_id,
|
||||
resource_id=resource_id,
|
||||
first=True,
|
||||
to_dict=False)
|
||||
existed and existed.soft_delete()
|
||||
if existed:
|
||||
existed.soft_delete()
|
||||
_role_permissions.append(existed)
|
||||
|
||||
role_rebuild.apply_async(args=(rid,), queue=ACL_QUEUE)
|
||||
if rebuild:
|
||||
role_rebuild.apply_async(args=(rid, app_id), queue=ACL_QUEUE)
|
||||
|
||||
AuditCRUD.add_permission_log(app_id, AuditOperateType.revoke, rid, rt_id, _role_permissions,
|
||||
source=source)
|
||||
|
||||
@staticmethod
|
||||
def batch_revoke_by_resource_names(rid, perms, resource_type_id, resource_names,
|
||||
resource_ids=None, perm_map=None, app_id=None):
|
||||
|
||||
from api.lib.perm.acl.resource import ResourceTypeCRUD
|
||||
if resource_names:
|
||||
resource_ids = []
|
||||
from api.models.acl import Resource
|
||||
|
||||
for n in resource_names:
|
||||
resource = Resource.get_by(name=n, resource_type_id=resource_type_id, first=True, to_dict=False)
|
||||
if resource:
|
||||
app_id = resource.app_id
|
||||
if not perms:
|
||||
perms = [i.get('name') for i in ResourceTypeCRUD.get_perms(resource.resource_type_id)]
|
||||
|
||||
resource_ids.append(resource.id)
|
||||
resource_ids = resource_ids or []
|
||||
|
||||
_role_permissions = []
|
||||
if isinstance(perm_map, dict):
|
||||
perm2resource = dict()
|
||||
for resource_id in resource_ids:
|
||||
for _perm in (perm_map.get(str(resource_id)) or []):
|
||||
perm2resource.setdefault(_perm, []).append(resource_id)
|
||||
for _perm in perm2resource:
|
||||
perm = PermissionCache.get(_perm, resource_type_id)
|
||||
existeds = RolePermission.get_by(rid=rid,
|
||||
app_id=app_id,
|
||||
perm_id=perm.id,
|
||||
__func_in___key_resource_id=perm2resource[_perm],
|
||||
to_dict=False)
|
||||
for existed in existeds:
|
||||
existed.deleted = True
|
||||
existed.deleted_at = datetime.datetime.now()
|
||||
db.session.add(existed)
|
||||
_role_permissions.append(existed)
|
||||
|
||||
db.session.commit()
|
||||
else:
|
||||
for _perm in perms:
|
||||
perm = PermissionCache.get(_perm, resource_type_id)
|
||||
for resource_id in resource_ids:
|
||||
existed = RolePermission.get_by(rid=rid,
|
||||
app_id=app_id,
|
||||
perm_id=perm.id,
|
||||
resource_id=resource_id,
|
||||
first=True, to_dict=False)
|
||||
if existed:
|
||||
existed.soft_delete()
|
||||
_role_permissions.append(existed)
|
||||
|
||||
role_rebuild.apply_async(args=(rid, app_id), queue=ACL_QUEUE)
|
||||
|
||||
AuditCRUD.add_permission_log(app_id, AuditOperateType.revoke, rid, resource_type_id, _role_permissions)
|
||||
|
21
cmdb-api/api/lib/perm/acl/record.py
Normal file
21
cmdb-api/api/lib/perm/acl/record.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from api.models.acl import OperationRecord
|
||||
|
||||
|
||||
class OperateRecordCRUD(object):
|
||||
@staticmethod
|
||||
def search(page, page_size, **kwargs):
|
||||
query = OperationRecord.get_by(only_query=True)
|
||||
for k, v in kwargs.items():
|
||||
if hasattr(OperationRecord, k) and v:
|
||||
query = query.filter(getattr(OperationRecord, k) == v)
|
||||
|
||||
numfound = query.count()
|
||||
res = query.offset((page - 1) * page_size).limit(page_size)
|
||||
|
||||
return numfound, res
|
||||
|
||||
@staticmethod
|
||||
def add(app, rolename, operate, obj):
|
||||
OperationRecord.create(app=app, rolename=rolename, operate=operate, obj=obj)
|
@@ -2,9 +2,16 @@
|
||||
|
||||
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
|
||||
from api.extensions import db
|
||||
from api.lib.perm.acl.audit import AuditCRUD, AuditOperateType, 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
|
||||
@@ -12,9 +19,12 @@ 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(
|
||||
@@ -33,6 +43,15 @@ class ResourceTypeCRUD(object):
|
||||
|
||||
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)
|
||||
@@ -40,12 +59,16 @@ class ResourceTypeCRUD(object):
|
||||
|
||||
@classmethod
|
||||
def add(cls, app_id, name, description, perms):
|
||||
ResourceType.get_by(name=name, app_id=app_id) and abort(
|
||||
400, "ResourceType <{0}> is already existed".format(name))
|
||||
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)
|
||||
|
||||
cls.update_perms(rt.id, perms, 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
|
||||
|
||||
@@ -53,49 +76,79 @@ class ResourceTypeCRUD(object):
|
||||
def update(cls, rt_id, **kwargs):
|
||||
kwargs.pop('app_id', None)
|
||||
|
||||
rt = ResourceType.get_by_id(rt_id) or abort(404, "ResourceType <{0}> is not found".format(rt_id))
|
||||
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, "ResourceType <{0}> is duplicated".format(kwargs['name']))
|
||||
return abort(400, ErrFormat.resource_type_exists.format(kwargs['name']))
|
||||
|
||||
if 'perms' in kwargs:
|
||||
cls.update_perms(rt_id, kwargs.pop('perms'), rt.app_id)
|
||||
perms = kwargs.pop('perms', None)
|
||||
current_perm_ids = []
|
||||
existed_perm_ids = []
|
||||
|
||||
return rt.update(**kwargs)
|
||||
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, "ResourceType <{0}> is not found".format(rt_id))
|
||||
rt = ResourceType.get_by_id(rt_id) or \
|
||||
abort(404, ErrFormat.resource_type_not_found.format("id={}".format(rt_id)))
|
||||
|
||||
if Resource.get_by(resource_type_id=rt_id):
|
||||
return abort(400, "At least one instance of this type exists and cannot be deleted")
|
||||
Resource.get_by(resource_type_id=rt_id) and abort(400, ErrFormat.resource_type_cannot_delete)
|
||||
|
||||
cls.update_perms(rt_id, [], rt.app_id)
|
||||
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 = []
|
||||
|
||||
for i in existed:
|
||||
if i.name not in perms:
|
||||
i.soft_delete()
|
||||
else:
|
||||
current_ids.append(i.id)
|
||||
|
||||
for i in perms:
|
||||
if i not in existed_names:
|
||||
Permission.create(resource_type_id=rt_id,
|
||||
name=i,
|
||||
app_id=app_id)
|
||||
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, page=1, page_size=None):
|
||||
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)
|
||||
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)))
|
||||
@@ -111,14 +164,20 @@ class ResourceGroupCRUD(object):
|
||||
return [i.resource.to_dict() for i in items]
|
||||
|
||||
@staticmethod
|
||||
def add(name, type_id, app_id):
|
||||
ResourceGroup.get_by(name=name, resource_type_id=type_id, app_id=app_id) and abort(
|
||||
400, "ResourceGroup <{0}> is already existed".format(name))
|
||||
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)
|
||||
|
||||
return ResourceGroup.create(name=name, resource_type_id=type_id, app_id=app_id)
|
||||
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]
|
||||
|
||||
@@ -130,60 +189,153 @@ class ResourceGroupCRUD(object):
|
||||
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, "ResourceGroup <{0}> is not found".format(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()
|
||||
role_rebuild.apply_async(args=(i.rid,), queue=ACL_QUEUE)
|
||||
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 search(q, app_id, resource_type_id=None, page=1, page_size=None):
|
||||
query = db.session.query(Resource).filter(
|
||||
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:
|
||||
i['user'] = UserCache.get(i['uid']).nickname if i['uid'] else ''
|
||||
|
||||
return numfound, query.offset((page - 1) * page_size).limit(page_size)
|
||||
return numfound, res
|
||||
|
||||
@staticmethod
|
||||
def add(name, type_id, app_id):
|
||||
Resource.get_by(name=name, resource_type_id=type_id, app_id=app_id) and abort(
|
||||
400, "Resource <{0}> is already existed".format(name))
|
||||
@classmethod
|
||||
def add(cls, name, type_id, app_id, uid=None):
|
||||
type_id = cls._parse_resource_type_id(type_id, app_id)
|
||||
|
||||
return Resource.create(name=name, resource_type_id=type_id, app_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)
|
||||
current_app.logger.info(triggers)
|
||||
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):
|
||||
resource = Resource.get_by_id(_id) or abort(404, "Resource <{0}> is not found".format(_id))
|
||||
# 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, "Resource <{0}> is duplicated".format(name))
|
||||
return abort(400, ErrFormat.resource_exists.format(name))
|
||||
|
||||
return resource.update(name=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):
|
||||
resource = Resource.get_by_id(_id) or abort(404, "Resource <{0}> is not found".format(_id))
|
||||
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()
|
||||
role_rebuild.apply_async(args=(i.rid,), queue=ACL_QUEUE)
|
||||
rebuilds.append((i.rid, i.app_id))
|
||||
|
||||
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, {}, {})
|
||||
|
||||
@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)
|
||||
|
42
cmdb-api/api/lib/perm/acl/resp_format.py
Normal file
42
cmdb-api/api/lib/perm/acl/resp_format.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from api.lib.resp_format import CommonErrFormat
|
||||
|
||||
|
||||
class ErrFormat(CommonErrFormat):
|
||||
auth_only_with_app_token_failed = "应用 Token验证失败"
|
||||
session_invalid = "您不是应用管理员 或者 session失效(尝试一下退出重新登录)"
|
||||
|
||||
resource_type_not_found = "资源类型 {} 不存在!"
|
||||
resource_type_exists = "资源类型 {} 已经存在!"
|
||||
resource_type_cannot_delete = "因为该类型下有资源的存在, 不能删除!"
|
||||
|
||||
user_not_found = "用户 {} 不存在!"
|
||||
user_exists = "用户 {} 已经存在!"
|
||||
role_not_found = "角色 {} 不存在!"
|
||||
role_exists = "角色 {} 已经存在!"
|
||||
global_role_not_found = "全局角色 {} 不存在!"
|
||||
global_role_exists = "全局角色 {} 已经存在!"
|
||||
|
||||
resource_no_permission = "您没有资源: {} 的 {} 权限"
|
||||
admin_required = "需要管理员权限"
|
||||
role_required = "需要角色: {}"
|
||||
|
||||
app_is_ready_existed = "应用 {} 已经存在"
|
||||
app_not_found = "应用 {} 不存在!"
|
||||
app_secret_invalid = "应用的Secret无效"
|
||||
|
||||
resource_not_found = "资源 {} 不存在!"
|
||||
resource_exists = "资源 {} 已经存在!"
|
||||
|
||||
resource_group_not_found = "资源组 {} 不存在!"
|
||||
resource_group_exists = "资源组 {} 已经存在!"
|
||||
|
||||
inheritance_dead_loop = "继承检测到了死循环"
|
||||
role_relation_not_found = "角色关系 {} 不存在!"
|
||||
|
||||
trigger_not_found = "触发器 {} 不存在!"
|
||||
trigger_exists = "触发器 {} 已经存在!"
|
||||
trigger_disabled = "触发器 {} 已经被禁用!"
|
||||
|
||||
invalid_password = "密码不正确!"
|
@@ -1,25 +1,39 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
|
||||
import time
|
||||
|
||||
import six
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
|
||||
from api.extensions import db
|
||||
from api.lib.perm.acl.app import AppCRUD
|
||||
from api.lib.perm.acl.audit import AuditCRUD, AuditOperateType, AuditScope
|
||||
from api.lib.perm.acl.cache import AppCache
|
||||
from api.lib.perm.acl.cache import HasResourceRoleCache
|
||||
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.models.acl import Resource
|
||||
from api.lib.perm.acl.const import OperateType
|
||||
from api.lib.perm.acl.resource import ResourceGroupCRUD
|
||||
from api.lib.perm.acl.resp_format import ErrFormat
|
||||
from api.models.acl import Resource, ResourceGroup
|
||||
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
|
||||
from api.tasks.acl import op_record
|
||||
from api.tasks.acl import role_rebuild
|
||||
|
||||
|
||||
class RoleRelationCRUD(object):
|
||||
cls = RoleRelation
|
||||
|
||||
@staticmethod
|
||||
def get_parents(rids=None, uids=None):
|
||||
def get_parents(rids=None, uids=None, app_id=None, all_app=False):
|
||||
rid2uid = dict()
|
||||
if uids is not None:
|
||||
uids = [uids] if isinstance(uids, six.integer_types) else uids
|
||||
@@ -29,8 +43,22 @@ class RoleRelationCRUD(object):
|
||||
else:
|
||||
rids = [rids] if isinstance(rids, six.integer_types) else rids
|
||||
|
||||
res = db.session.query(RoleRelation).filter(
|
||||
RoleRelation.child_id.in_(rids)).filter(RoleRelation.deleted.is_(False))
|
||||
if app_id is not None:
|
||||
res = db.session.query(RoleRelation).filter(
|
||||
RoleRelation.child_id.in_(rids)).filter(RoleRelation.app_id == app_id).filter(
|
||||
RoleRelation.deleted.is_(False)).union(
|
||||
db.session.query(RoleRelation).filter(
|
||||
RoleRelation.child_id.in_(rids)).filter(RoleRelation.app_id.is_(None)).filter(
|
||||
RoleRelation.deleted.is_(False)))
|
||||
|
||||
elif not all_app:
|
||||
res = db.session.query(RoleRelation).filter(
|
||||
RoleRelation.child_id.in_(rids)).filter(RoleRelation.app_id.is_(None)).filter(
|
||||
RoleRelation.deleted.is_(False))
|
||||
else:
|
||||
res = db.session.query(RoleRelation).filter(
|
||||
RoleRelation.child_id.in_(rids)).filter(RoleRelation.deleted.is_(False))
|
||||
|
||||
id2parents = {}
|
||||
for i in res:
|
||||
id2parents.setdefault(rid2uid.get(i.child_id, i.child_id), []).append(RoleCache.get(i.parent_id).to_dict())
|
||||
@@ -38,24 +66,28 @@ class RoleRelationCRUD(object):
|
||||
return id2parents
|
||||
|
||||
@staticmethod
|
||||
def get_parent_ids(rid):
|
||||
res = RoleRelation.get_by(child_id=rid, to_dict=False)
|
||||
|
||||
return [i.parent_id for i in res]
|
||||
def get_parent_ids(rid, app_id):
|
||||
if app_id is not None:
|
||||
return [i.parent_id for i in RoleRelation.get_by(child_id=rid, app_id=app_id, to_dict=False)] + \
|
||||
[i.parent_id for i in RoleRelation.get_by(child_id=rid, app_id=None, to_dict=False)]
|
||||
else:
|
||||
return [i.parent_id for i in RoleRelation.get_by(child_id=rid, app_id=app_id, to_dict=False)]
|
||||
|
||||
@staticmethod
|
||||
def get_child_ids(rid):
|
||||
res = RoleRelation.get_by(parent_id=rid, to_dict=False)
|
||||
|
||||
return [i.child_id for i in res]
|
||||
def get_child_ids(rid, app_id):
|
||||
if app_id is not None:
|
||||
return [i.child_id for i in RoleRelation.get_by(parent_id=rid, app_id=app_id, to_dict=False)] + \
|
||||
[i.child_id for i in RoleRelation.get_by(parent_id=rid, app_id=None, to_dict=False)]
|
||||
else:
|
||||
return [i.child_id for i in RoleRelation.get_by(parent_id=rid, app_id=app_id, to_dict=False)]
|
||||
|
||||
@classmethod
|
||||
def recursive_parent_ids(cls, rid):
|
||||
def recursive_parent_ids(cls, rid, app_id):
|
||||
all_parent_ids = set()
|
||||
|
||||
def _get_parent(_id):
|
||||
all_parent_ids.add(_id)
|
||||
parent_ids = RoleRelationCache.get_parent_ids(_id)
|
||||
parent_ids = RoleRelationCache.get_parent_ids(_id, app_id)
|
||||
for parent_id in parent_ids:
|
||||
_get_parent(parent_id)
|
||||
|
||||
@@ -64,12 +96,12 @@ class RoleRelationCRUD(object):
|
||||
return all_parent_ids
|
||||
|
||||
@classmethod
|
||||
def recursive_child_ids(cls, rid):
|
||||
def recursive_child_ids(cls, rid, app_id):
|
||||
all_child_ids = set()
|
||||
|
||||
def _get_children(_id):
|
||||
all_child_ids.add(_id)
|
||||
child_ids = RoleRelationCache.get_child_ids(_id)
|
||||
child_ids = RoleRelationCache.get_child_ids(_id, app_id)
|
||||
for child_id in child_ids:
|
||||
_get_children(child_id)
|
||||
|
||||
@@ -78,55 +110,124 @@ class RoleRelationCRUD(object):
|
||||
return all_child_ids
|
||||
|
||||
@classmethod
|
||||
def add(cls, parent_id, child_id):
|
||||
RoleRelation.get_by(parent_id=parent_id, child_id=child_id) and abort(400, "It's already existed")
|
||||
def get_users_by_rid(cls, rid, app_id, rid2obj=None, uid2obj=None):
|
||||
rid2obj = rid2obj or dict()
|
||||
uid2obj = uid2obj or dict()
|
||||
|
||||
if parent_id in cls.recursive_child_ids(child_id):
|
||||
return abort(400, "Circulation inheritance!!!")
|
||||
users = []
|
||||
rids = cls.recursive_child_ids(rid, app_id)
|
||||
for rid in rids:
|
||||
if rid not in rid2obj:
|
||||
rid2obj[rid] = RoleCache.get(rid)
|
||||
|
||||
RoleRelationCache.clean(parent_id)
|
||||
RoleRelationCache.clean(child_id)
|
||||
role = rid2obj[rid]
|
||||
if role and role.uid:
|
||||
if role.uid and role.uid not in uid2obj:
|
||||
uid2obj[role.uid] = UserCache.get(role.uid)
|
||||
|
||||
return RoleRelation.create(parent_id=parent_id, child_id=child_id)
|
||||
u = uid2obj.get(role.uid)
|
||||
u = u and u.to_dict()
|
||||
if u:
|
||||
u.update(dict(role=role.to_dict()))
|
||||
users.append(u)
|
||||
|
||||
# todo role read log
|
||||
# user_id = AuditCRUD.get_current_operate_uid()
|
||||
# audit_role_log.apply_async(args=(app_id, user_id, result.copy()),
|
||||
# queue=ACL_QUEUE)
|
||||
|
||||
return users
|
||||
|
||||
@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)
|
||||
def add(cls, role, parent_id, child_ids, app_id):
|
||||
result = []
|
||||
for child_id in child_ids:
|
||||
role_rebuild.apply_async(args=(child_id,), queue=ACL_QUEUE)
|
||||
existed = RoleRelation.get_by(parent_id=parent_id, child_id=child_id, app_id=app_id)
|
||||
if existed:
|
||||
continue
|
||||
|
||||
RoleRelationCache.clean(existed.parent_id)
|
||||
RoleRelationCache.clean(existed.child_id)
|
||||
RoleRelationCache.clean(parent_id, app_id)
|
||||
RoleRelationCache.clean(child_id, app_id)
|
||||
|
||||
if parent_id in cls.recursive_child_ids(child_id, app_id):
|
||||
return abort(400, ErrFormat.inheritance_dead_loop)
|
||||
|
||||
if app_id is None:
|
||||
for app in AppCRUD.get_all():
|
||||
if app.name != "acl":
|
||||
RoleRelationCache.clean(child_id, app.id)
|
||||
|
||||
result.append(RoleRelation.create(parent_id=parent_id, child_id=child_id, app_id=app_id).to_dict())
|
||||
|
||||
AuditCRUD.add_role_log(app_id, AuditOperateType.role_relation_add,
|
||||
AuditScope.role_relation, role.id, {}, {},
|
||||
{'child_ids': list(child_ids), 'parent_ids': [parent_id], }
|
||||
)
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def delete(cls, _id, app_id):
|
||||
existed = RoleRelation.get_by_id(_id) or abort(
|
||||
400, ErrFormat.role_relation_not_found.format("id={}".format(_id)))
|
||||
|
||||
child_ids = cls.recursive_child_ids(existed.child_id, app_id)
|
||||
for child_id in child_ids:
|
||||
role_rebuild.apply_async(args=(child_id, app_id), queue=ACL_QUEUE)
|
||||
role = RoleCache.get(existed.parent_id)
|
||||
|
||||
existed.soft_delete()
|
||||
|
||||
RoleRelationCache.clean(existed.parent_id, app_id)
|
||||
RoleRelationCache.clean(existed.child_id, app_id)
|
||||
|
||||
AuditCRUD.add_role_log(app_id, AuditOperateType.role_relation_delete,
|
||||
AuditScope.role_relation, role.id, {}, {},
|
||||
{'child_ids': list(child_ids), 'parent_ids': [existed.parent_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))
|
||||
def delete2(cls, parent_id, child_id, app_id):
|
||||
existed = RoleRelation.get_by(parent_id=parent_id, child_id=child_id, app_id=app_id, first=True, to_dict=False)
|
||||
existed or abort(400, ErrFormat.role_relation_not_found.format("{} -> {}".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)
|
||||
|
||||
RoleRelationCache.clean(existed.parent_id)
|
||||
RoleRelationCache.clean(existed.child_id)
|
||||
role = RoleCache.get(existed.parent_id)
|
||||
|
||||
existed.soft_delete()
|
||||
|
||||
child_ids = cls.recursive_child_ids(existed.child_id, app_id)
|
||||
for child_id in child_ids:
|
||||
role_rebuild.apply_async(args=(child_id, app_id), queue=ACL_QUEUE)
|
||||
|
||||
RoleRelationCache.clean(existed.parent_id, app_id)
|
||||
RoleRelationCache.clean(existed.child_id, app_id)
|
||||
|
||||
AuditCRUD.add_role_log(app_id, AuditOperateType.role_relation_delete,
|
||||
AuditScope.role_relation, role.id, {}, {},
|
||||
{'child_ids': list(child_ids), 'parent_ids': [existed.parent_id], }
|
||||
)
|
||||
|
||||
|
||||
class RoleCRUD(object):
|
||||
cls = Role
|
||||
|
||||
@staticmethod
|
||||
def search(q, app_id, page=1, page_size=None, user_role=True):
|
||||
def search(q, app_id, page=1, page_size=None, user_role=True, is_all=False, user_only=False):
|
||||
query = db.session.query(Role).filter(Role.deleted.is_(False))
|
||||
query = query.filter(Role.app_id == app_id).filter(Role.uid.is_(None))
|
||||
query1 = query.filter(Role.app_id == app_id).filter(Role.uid.is_(None))
|
||||
query2 = query.filter(Role.app_id.is_(None)).filter(Role.uid.is_(None))
|
||||
query = query1.union(query2)
|
||||
|
||||
if user_role:
|
||||
query1 = db.session.query(Role).filter(Role.deleted.is_(False)).filter(Role.uid.isnot(None))
|
||||
query = query.union(query1)
|
||||
|
||||
if user_only:
|
||||
query = db.session.query(Role).filter(Role.deleted.is_(False)).filter(Role.uid.isnot(None))
|
||||
|
||||
if not is_all:
|
||||
role_ids = list(HasResourceRoleCache.get(app_id).keys())
|
||||
query = query.filter(Role.id.in_(role_ids))
|
||||
|
||||
if q:
|
||||
query = query.filter(Role.name.ilike('%{0}%'.format(q)))
|
||||
|
||||
@@ -135,46 +236,99 @@ class RoleCRUD(object):
|
||||
return numfound, query.offset((page - 1) * page_size).limit(page_size)
|
||||
|
||||
@staticmethod
|
||||
def add_role(name, app_id=None, 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))
|
||||
def add_role(name, app_id=None, is_app_admin=False, uid=None, password=None):
|
||||
if app_id and AppCache.get(app_id).name == "acl":
|
||||
app_id = None
|
||||
|
||||
return Role.create(name=name,
|
||||
Role.get_by(name=name, app_id=app_id) and abort(400, ErrFormat.role_exists.format(name))
|
||||
|
||||
if app_id is not None:
|
||||
Role.get_by(name=name, app_id=None) and abort(400, ErrFormat.global_role_exists.format(name))
|
||||
|
||||
from api.lib.perm.acl.user import UserCRUD
|
||||
key, secret = UserCRUD.gen_key_secret()
|
||||
|
||||
role = Role.create(name=name,
|
||||
app_id=app_id,
|
||||
is_app_admin=is_app_admin,
|
||||
password=password,
|
||||
key=key,
|
||||
secret=secret,
|
||||
uid=uid)
|
||||
|
||||
AuditCRUD.add_role_log(app_id, AuditOperateType.create,
|
||||
AuditScope.role, role.id, {}, role.to_dict(), {})
|
||||
|
||||
return role
|
||||
|
||||
@staticmethod
|
||||
def update_role(rid, **kwargs):
|
||||
kwargs.pop('app_id', None)
|
||||
|
||||
role = Role.get_by_id(rid) or abort(404, "Role <{0}> does not exist".format(rid))
|
||||
role = Role.get_by_id(rid) or abort(404, ErrFormat.role_not_found.format("rid={}".format(rid)))
|
||||
|
||||
origin = role.to_dict()
|
||||
|
||||
RoleCache.clean(rid)
|
||||
|
||||
return role.update(**kwargs)
|
||||
role = role.update(**kwargs)
|
||||
AuditCRUD.add_role_log(role.app_id, AuditOperateType.update,
|
||||
AuditScope.role, role.id, origin, role.to_dict(), {},
|
||||
)
|
||||
|
||||
return role
|
||||
|
||||
@staticmethod
|
||||
def get_by_name(name, app_id):
|
||||
|
||||
role = Role.get_by(name=name, app_id=app_id)
|
||||
|
||||
return role
|
||||
|
||||
@classmethod
|
||||
def delete_role(cls, rid):
|
||||
role = Role.get_by_id(rid) or abort(404, "Role <{0}> does not exist".format(rid))
|
||||
from api.lib.perm.acl.acl import is_admin
|
||||
|
||||
role = Role.get_by_id(rid) or abort(404, ErrFormat.role_not_found.format("rid={}".format(rid)))
|
||||
|
||||
if not role.app_id and not is_admin():
|
||||
return abort(403, ErrFormat.admin_required)
|
||||
|
||||
origin = role.to_dict()
|
||||
|
||||
child_ids = []
|
||||
parent_ids = []
|
||||
recursive_child_ids = list(RoleRelationCRUD.recursive_child_ids(rid, role.app_id))
|
||||
|
||||
for i in RoleRelation.get_by(parent_id=rid, to_dict=False):
|
||||
child_ids.append(i.child_id)
|
||||
i.soft_delete()
|
||||
|
||||
for i in RoleRelation.get_by(child_id=rid, to_dict=False):
|
||||
parent_ids.append(i.parent_id)
|
||||
i.soft_delete()
|
||||
|
||||
role_permissions = []
|
||||
for i in RolePermission.get_by(rid=rid, to_dict=False):
|
||||
role_permissions.append(i.to_dict())
|
||||
i.soft_delete()
|
||||
|
||||
role_rebuild.apply_async(args=(list(RoleRelationCRUD.recursive_child_ids(rid)), ), queue=ACL_QUEUE)
|
||||
|
||||
RoleCache.clean(rid)
|
||||
RoleRelationCache.clean(rid)
|
||||
|
||||
role.soft_delete()
|
||||
|
||||
role_rebuild.apply_async(args=(recursive_child_ids, role.app_id), queue=ACL_QUEUE)
|
||||
|
||||
RoleCache.clean(rid)
|
||||
RoleRelationCache.clean(rid, role.app_id)
|
||||
|
||||
AuditCRUD.add_role_log(role.app_id, AuditOperateType.delete,
|
||||
AuditScope.role, role.id, origin, {},
|
||||
{'child_ids': child_ids, 'parent_ids': parent_ids,
|
||||
'role_permissions': role_permissions, },
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_resources(rid):
|
||||
res = RolePermission.get_by(rid=rid, to_dict=False)
|
||||
def get_resources(rid, app_id):
|
||||
res = RolePermission.get_by(rid=rid, app_id=app_id, to_dict=False)
|
||||
id2perms = dict(id2perms={}, group2perms={})
|
||||
for i in res:
|
||||
if i.resource_id:
|
||||
@@ -184,24 +338,90 @@ class RoleCRUD(object):
|
||||
|
||||
return id2perms
|
||||
|
||||
@staticmethod
|
||||
def _extend_resources(rid, resource_type_id, app_id):
|
||||
res = RoleRelationCache.get_resources2(rid, app_id)
|
||||
resources = {_id: res['resources'][_id] for _id in res['resources']
|
||||
if not resource_type_id or resource_type_id == res['resources'][_id]['resource_type_id']}
|
||||
groups = {_id: res['groups'][_id] for _id in res['groups']
|
||||
if not resource_type_id or resource_type_id == res['groups'][_id]['resource_type_id']}
|
||||
|
||||
return resources, groups
|
||||
|
||||
@classmethod
|
||||
def recursive_resources(cls, rid, app_id, resource_type_id=None, group_flat=True, to_record=False):
|
||||
def _merge(a, b):
|
||||
for i in b:
|
||||
if i in a:
|
||||
a[i]['permissions'] = list(set(a[i]['permissions'] + b[i]['permissions']))
|
||||
else:
|
||||
a[i] = b[i]
|
||||
|
||||
return a
|
||||
|
||||
try:
|
||||
resource_type_id = resource_type_id and int(resource_type_id)
|
||||
except ValueError:
|
||||
resource_type = ResourceType.get_by(name=resource_type_id, app_id=app_id, first=True, to_dict=False)
|
||||
resource_type_id = resource_type and resource_type.id
|
||||
|
||||
result = dict(resources=dict(), groups=dict())
|
||||
s = time.time()
|
||||
parent_ids = RoleRelationCRUD.recursive_parent_ids(rid, app_id)
|
||||
current_app.logger.info('parent ids {0}: {1}'.format(parent_ids, time.time() - s))
|
||||
for parent_id in parent_ids:
|
||||
|
||||
_resources, _groups = cls._extend_resources(parent_id, resource_type_id, app_id)
|
||||
current_app.logger.info('middle1: {0}'.format(time.time() - s))
|
||||
_merge(result['resources'], _resources)
|
||||
current_app.logger.info('middle2: {0}'.format(time.time() - s))
|
||||
current_app.logger.info(len(_groups))
|
||||
if not group_flat:
|
||||
_merge(result['groups'], _groups)
|
||||
else:
|
||||
for rg_id in _groups:
|
||||
items = ResourceGroupCRUD.get_items(rg_id)
|
||||
for item in items:
|
||||
if not resource_type_id or resource_type_id == item['resource_type_id']:
|
||||
item.setdefault('permissions', [])
|
||||
item['permissions'] = list(set(item['permissions'] + _groups[rg_id]['permissions']))
|
||||
result['resources'][item['id']] = item
|
||||
current_app.logger.info('End: {0}'.format(time.time() - s))
|
||||
|
||||
result['resources'] = list(result['resources'].values())
|
||||
result['groups'] = list(result['groups'].values())
|
||||
|
||||
if to_record:
|
||||
op_record.apply_async(args=(app_id, rid, OperateType.READ, ["resources"]),
|
||||
queue=ACL_QUEUE)
|
||||
|
||||
# todo role read log
|
||||
# user_id = AuditCRUD.get_current_operate_uid()
|
||||
# audit_role_log.apply_async(args=(app_id, user_id, result.copy()),
|
||||
# queue=ACL_QUEUE)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def get_group_ids(resource_id):
|
||||
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, 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))
|
||||
def has_permission(cls, rid, resource_name, resource_type_name, app_id, perm, resource_id=None):
|
||||
current_app.logger.debug((rid, resource_name, resource_type_name, app_id, perm))
|
||||
if not resource_id:
|
||||
resource_type = ResourceType.get_by(app_id=app_id, name=resource_type_name, first=True, to_dict=False)
|
||||
resource_type or abort(404, ErrFormat.resource_type_not_found.format(resource_type_name))
|
||||
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, ErrFormat.resource_not_found.format(resource_name))
|
||||
resource_id = resource.id
|
||||
|
||||
parent_ids = RoleRelationCRUD.recursive_parent_ids(rid)
|
||||
|
||||
group_ids = cls.get_group_ids(resource.id)
|
||||
parent_ids = RoleRelationCRUD.recursive_parent_ids(rid, app_id)
|
||||
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, [])
|
||||
id2perms = RoleRelationCache.get_resources(parent_id, app_id)
|
||||
current_app.logger.debug(id2perms)
|
||||
perms = id2perms['id2perms'].get(resource_id, [])
|
||||
if perms and {perm}.issubset(set(perms)):
|
||||
return True
|
||||
|
||||
@@ -213,19 +433,60 @@ class RoleCRUD(object):
|
||||
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))
|
||||
def get_permissions(cls, rid, resource_name, app_id):
|
||||
|
||||
parent_ids = RoleRelationCRUD.recursive_parent_ids(rid)
|
||||
resource = Resource.get_by(name=resource_name, first=True, to_dict=False)
|
||||
resource = resource or abort(403, ErrFormat.resource_not_found.format(resource_name))
|
||||
|
||||
parent_ids = RoleRelationCRUD.recursive_parent_ids(rid, app_id)
|
||||
group_ids = cls.get_group_ids(resource.id)
|
||||
|
||||
perms = []
|
||||
for parent_id in parent_ids:
|
||||
id2perms = RoleRelationCache.get_resources(parent_id)
|
||||
id2perms = RoleRelationCache.get_resources(parent_id, app_id)
|
||||
perms += id2perms['id2perms'].get(parent_id, [])
|
||||
|
||||
for group_id in group_ids:
|
||||
perms += id2perms['group2perms'].get(group_id, [])
|
||||
|
||||
return set(perms)
|
||||
|
||||
@classmethod
|
||||
def list_resources(cls, app_id, rids, resource_type_id=None, q=None):
|
||||
|
||||
query = db.session.query(Resource, RolePermission).filter(
|
||||
Resource.app_id == app_id,
|
||||
Resource.deleted.is_(False),
|
||||
RolePermission.deleted.is_(False),
|
||||
RolePermission.rid.in_(rids),
|
||||
).join(
|
||||
RolePermission, Resource.id == RolePermission.resource_id
|
||||
)
|
||||
|
||||
if resource_type_id:
|
||||
query = query.filter(Resource.resource_type_id == resource_type_id)
|
||||
|
||||
if q:
|
||||
query = query.filter(Resource.resource_type_id == resource_type_id)
|
||||
|
||||
return query.all()
|
||||
|
||||
@classmethod
|
||||
def list_resource_groups(cls, app_id, rids, resource_type_id=None, q=None):
|
||||
|
||||
query = db.session.query(ResourceGroup, RolePermission).filter(
|
||||
ResourceGroup.app_id == app_id,
|
||||
ResourceGroup.deleted.is_(False),
|
||||
RolePermission.deleted.is_(False),
|
||||
RolePermission.rid.in_(rids),
|
||||
).join(
|
||||
RolePermission, ResourceGroup.id == RolePermission.group_id
|
||||
)
|
||||
|
||||
if resource_type_id:
|
||||
query = query.filter(ResourceGroup.resource_type_id == resource_type_id)
|
||||
|
||||
if q:
|
||||
query = query.filter(ResourceGroup.resource_type_id == resource_type_id)
|
||||
|
||||
return query.all()
|
||||
|
162
cmdb-api/api/lib/perm/acl/trigger.py
Normal file
162
cmdb-api/api/lib/perm/acl/trigger.py
Normal file
@@ -0,0 +1,162 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
|
||||
import copy
|
||||
import json
|
||||
import re
|
||||
from fnmatch import fnmatch
|
||||
|
||||
from flask import abort, current_app
|
||||
|
||||
from api.lib.perm.acl.audit import AuditCRUD, AuditOperateType
|
||||
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.models.acl import Trigger
|
||||
from api.tasks.acl import apply_trigger, cancel_trigger
|
||||
|
||||
|
||||
class TriggerCRUD(object):
|
||||
cls = Trigger
|
||||
|
||||
@staticmethod
|
||||
def get(app_id):
|
||||
triggers = Trigger.get_by(app_id=app_id)
|
||||
for trigger in triggers:
|
||||
trigger['uid'] = json.loads(trigger['uid'] or '[]')
|
||||
trigger['users'] = [UserCache.get(i).username for i in trigger['uid']]
|
||||
trigger['roles'] = json.loads(trigger['roles'] or '[]')
|
||||
trigger['permissions'] = json.loads(trigger['permissions'] or '[]')
|
||||
|
||||
return triggers
|
||||
|
||||
@staticmethod
|
||||
def add(app_id, **kwargs):
|
||||
kwargs.pop('app_id', None)
|
||||
kwargs['roles'] = json.dumps(kwargs['roles'] or [])
|
||||
kwargs['permissions'] = json.dumps(kwargs['permissions'] or [])
|
||||
|
||||
kwargs['uid'] = json.dumps(kwargs.get('uid') or [])
|
||||
|
||||
_kwargs = copy.deepcopy(kwargs)
|
||||
_kwargs.pop('name', None)
|
||||
|
||||
Trigger.get_by(app_id=app_id, **_kwargs) and abort(400, ErrFormat.trigger_exists.format(""))
|
||||
t = Trigger.create(app_id=app_id, **kwargs)
|
||||
|
||||
AuditCRUD.add_trigger_log(app_id, t.id, AuditOperateType.create, {}, t.to_dict(), {})
|
||||
|
||||
return t
|
||||
|
||||
@staticmethod
|
||||
def update(_id, **kwargs):
|
||||
existed = Trigger.get_by_id(_id) or abort(404, ErrFormat.trigger_not_found.format("id={}".format(_id)))
|
||||
origin = existed.to_dict()
|
||||
kwargs['roles'] = json.dumps(kwargs['roles'] or [])
|
||||
kwargs['uid'] = json.dumps(kwargs['uid'] or [])
|
||||
kwargs['permissions'] = json.dumps(kwargs['permissions'] or [])
|
||||
|
||||
existed.update(**kwargs)
|
||||
|
||||
AuditCRUD.add_trigger_log(existed.app_id, existed.id, AuditOperateType.update,
|
||||
origin, existed.to_dict(), {})
|
||||
|
||||
return existed
|
||||
|
||||
@staticmethod
|
||||
def delete(_id):
|
||||
existed = Trigger.get_by_id(_id) or abort(404, ErrFormat.trigger_not_found.format("id={}".format(_id)))
|
||||
origin = existed.to_dict()
|
||||
|
||||
existed.soft_delete()
|
||||
|
||||
AuditCRUD.add_trigger_log(existed.app_id, existed.id, AuditOperateType.delete,
|
||||
origin, {}, {}
|
||||
)
|
||||
|
||||
return existed
|
||||
|
||||
@staticmethod
|
||||
def apply(_id):
|
||||
trigger = Trigger.get_by_id(_id) or abort(404, ErrFormat.trigger_not_found.format("id={}".format(_id)))
|
||||
if not trigger.enabled:
|
||||
return abort(400, ErrFormat.trigger_disabled.format("id={}".format(_id)))
|
||||
|
||||
user_id = AuditCRUD.get_current_operate_uid()
|
||||
|
||||
apply_trigger.apply_async(args=(_id,), kwargs=dict(operator_uid=user_id), queue=ACL_QUEUE)
|
||||
|
||||
@staticmethod
|
||||
def cancel(_id):
|
||||
trigger = Trigger.get_by_id(_id) or abort(404, ErrFormat.trigger_not_found.format("id={}".format(_id)))
|
||||
if not trigger.enabled:
|
||||
return abort(400, ErrFormat.trigger_disabled.format("id={}".format(_id)))
|
||||
|
||||
user_id = AuditCRUD.get_current_operate_uid()
|
||||
|
||||
cancel_trigger.apply_async(args=(_id,), kwargs=dict(operator_uid=user_id), queue=ACL_QUEUE)
|
||||
|
||||
@staticmethod
|
||||
def match_triggers(app_id, resource_name, resource_type_id, uid):
|
||||
triggers = Trigger.get_by(app_id=app_id, enabled=True, resource_type_id=resource_type_id, to_dict=False)
|
||||
|
||||
def _fnmatch(name, wildcard):
|
||||
import re
|
||||
|
||||
try:
|
||||
return re.compile(wildcard).findall(name)
|
||||
except:
|
||||
return fnmatch(name, trigger.wildcard)
|
||||
|
||||
uid = int(uid) if uid else uid
|
||||
_match_triggers = []
|
||||
for trigger in triggers:
|
||||
uids = json.loads(trigger.uid or '[]')
|
||||
if trigger.wildcard and uids:
|
||||
if _fnmatch(resource_name, trigger.wildcard) and uid in uids:
|
||||
_match_triggers.append(trigger)
|
||||
elif trigger.wildcard:
|
||||
if _fnmatch(resource_name, trigger.wildcard):
|
||||
_match_triggers.append(trigger)
|
||||
elif uids:
|
||||
if uid in uids:
|
||||
_match_triggers.append(trigger)
|
||||
|
||||
return _match_triggers
|
||||
|
||||
@staticmethod
|
||||
def get_resources(app_id, resource_type_id, wildcard, uid):
|
||||
from api.models.acl import Resource
|
||||
|
||||
wildcard = wildcard or ''
|
||||
|
||||
if wildcard and uid:
|
||||
query = Resource.get_by(__func_in___key_uid=uid,
|
||||
app_id=app_id,
|
||||
resource_type_id=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 wildcard:
|
||||
query = Resource.get_by(app_id=app_id,
|
||||
resource_type_id=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=app_id,
|
||||
resource_type_id=resource_type_id,
|
||||
to_dict=False)
|
||||
else:
|
||||
resources = []
|
||||
|
||||
return resources
|
@@ -9,13 +9,17 @@ from flask import abort
|
||||
from flask import g
|
||||
|
||||
from api.extensions import db
|
||||
from api.lib.perm.acl.audit import AuditCRUD, AuditOperateType, AuditScope
|
||||
from api.lib.perm.acl.cache import UserCache
|
||||
from api.lib.perm.acl.resp_format import ErrFormat
|
||||
from api.lib.perm.acl.role import RoleCRUD
|
||||
from api.models.acl import Role
|
||||
from api.models.acl import User
|
||||
|
||||
|
||||
class UserCRUD(object):
|
||||
cls = User
|
||||
|
||||
@staticmethod
|
||||
def search(q, page=1, page_size=None):
|
||||
query = db.session.query(User).filter(User.deleted.is_(False))
|
||||
@@ -27,7 +31,7 @@ class UserCRUD(object):
|
||||
return numfound, query.offset((page - 1) * page_size).limit(page_size)
|
||||
|
||||
@staticmethod
|
||||
def _gen_key_secret():
|
||||
def gen_key_secret():
|
||||
key = uuid.uuid4().hex
|
||||
secret = ''.join(random.sample(string.ascii_letters + string.digits + '~!@#$%^&*?', 32))
|
||||
|
||||
@@ -36,62 +40,76 @@ class UserCRUD(object):
|
||||
@classmethod
|
||||
def add(cls, **kwargs):
|
||||
existed = User.get_by(username=kwargs['username'], email=kwargs['email'])
|
||||
existed and abort(400, "User <{0}> is already existed".format(kwargs['username']))
|
||||
existed and abort(400, ErrFormat.user_exists.format(kwargs['username']))
|
||||
|
||||
is_admin = kwargs.pop('is_admin', False)
|
||||
kwargs['nickname'] = kwargs.get('nickname') or kwargs['username']
|
||||
kwargs['block'] = 0
|
||||
kwargs['key'], kwargs['secret'] = cls._gen_key_secret()
|
||||
kwargs['key'], kwargs['secret'] = cls.gen_key_secret()
|
||||
|
||||
user_employee = db.session.query(User).filter(User.deleted.is_(False)).order_by(
|
||||
User.employee_id.desc()).first()
|
||||
|
||||
biggest_employee_id = int(float(user_employee.employee_id)) \
|
||||
if user_employee is not None else 0
|
||||
|
||||
kwargs['employee_id'] = '{0:04d}'.format(biggest_employee_id + 1)
|
||||
user = User.create(**kwargs)
|
||||
|
||||
role = RoleCRUD.add_role(user.username, uid=user.uid)
|
||||
|
||||
if is_admin:
|
||||
from api.lib.perm.acl.cache import AppCache
|
||||
from api.lib.perm.acl.role import RoleRelationCRUD
|
||||
admin_r = Role.get_by(name='admin', first=True, to_dict=False) or \
|
||||
RoleCRUD.add_role('admin', AppCache.get('cmdb').id, True)
|
||||
|
||||
RoleRelationCRUD.add(admin_r.id, role.id)
|
||||
RoleCRUD.add_role(user.username, uid=user.uid)
|
||||
AuditCRUD.add_role_log(None, AuditOperateType.create,
|
||||
AuditScope.user, user.uid, {}, user.to_dict(), {}, {}
|
||||
)
|
||||
|
||||
return user
|
||||
|
||||
@staticmethod
|
||||
def update(uid, **kwargs):
|
||||
user = User.get_by(uid=uid, to_dict=False, first=True) or abort(404, "User <{0}> does not exist".format(uid))
|
||||
user = User.get_by(uid=uid, to_dict=False, first=True) or abort(
|
||||
404, ErrFormat.user_not_found.format("uid={}".format(uid)))
|
||||
|
||||
if kwargs.get("username"):
|
||||
other = User.get_by(username=kwargs['username'], first=True, to_dict=False)
|
||||
if other is not None and other.uid != user.uid:
|
||||
return abort(400, "User <{0}> cannot be duplicated".format(kwargs['username']))
|
||||
return abort(400, ErrFormat.user_exists.format(kwargs['username']))
|
||||
|
||||
UserCache.clean(user)
|
||||
|
||||
origin = user.to_dict()
|
||||
if kwargs.get("username") and kwargs['username'] != user.username:
|
||||
role = Role.get_by(name=user.username, first=True, to_dict=False)
|
||||
if role is not None:
|
||||
RoleCRUD.update_role(role.id, **dict(name=kwargs['name']))
|
||||
RoleCRUD.update_role(role.id, **dict(name=kwargs['username']))
|
||||
|
||||
return user.update(**kwargs)
|
||||
user = user.update(**kwargs)
|
||||
|
||||
AuditCRUD.add_role_log(None, AuditOperateType.update,
|
||||
AuditScope.user, user.uid, origin, user.to_dict(), {}, {}
|
||||
)
|
||||
|
||||
return user
|
||||
|
||||
@classmethod
|
||||
def reset_key_secret(cls):
|
||||
key, secret = cls._gen_key_secret()
|
||||
key, secret = cls.gen_key_secret()
|
||||
g.user.update(key=key, secret=secret)
|
||||
|
||||
UserCache.clean(g.user)
|
||||
|
||||
return key, secret
|
||||
|
||||
@classmethod
|
||||
def delete(cls, uid):
|
||||
if hasattr(g, 'user') and uid == g.user.uid:
|
||||
return abort(400, "You cannot delete yourself")
|
||||
user = User.get_by(uid=uid, to_dict=False, first=True) or abort(
|
||||
404, ErrFormat.user_not_found.format("uid={}".format(uid)))
|
||||
|
||||
user = User.get_by(uid=uid, to_dict=False, first=True) or abort(404, "User <{0}> does not exist".format(uid))
|
||||
origin = user.to_dict()
|
||||
|
||||
user.soft_delete()
|
||||
|
||||
UserCache.clean(user)
|
||||
|
||||
for i in Role.get_by(uid=uid, to_dict=False):
|
||||
i.delete()
|
||||
AuditCRUD.add_role_log(None, AuditOperateType.delete,
|
||||
AuditScope.user, user.uid, origin, {}, {}, {})
|
||||
|
||||
user.delete()
|
||||
@staticmethod
|
||||
def get_employees():
|
||||
return User.get_by(__func_isnot__key_employee_id=None, to_dict=True)
|
||||
|
@@ -1,5 +1,4 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
@@ -13,20 +12,54 @@ from flask import request
|
||||
from flask import session
|
||||
from flask_login import login_user
|
||||
|
||||
from api.lib.perm.acl.acl import ACLManager
|
||||
from api.lib.perm.acl.acl import is_app_admin
|
||||
from api.lib.perm.acl.cache import AppCache
|
||||
from api.lib.perm.acl.cache import UserCache
|
||||
from api.lib.perm.acl.resp_format import ErrFormat
|
||||
from api.models.acl import Role
|
||||
from api.models.acl import User
|
||||
|
||||
|
||||
def reset_session(user, role=None):
|
||||
from api.lib.perm.acl.acl import ACLManager
|
||||
if role is not None:
|
||||
user_info = ACLManager.get_user_info(role)
|
||||
else:
|
||||
user_info = ACLManager.get_user_info(user.username)
|
||||
|
||||
session["acl"] = dict(uid=user_info.get("uid"),
|
||||
avatar=user.avatar if user else user_info.get("avatar"),
|
||||
userId=user_info.get("uid"),
|
||||
userName=user_info.get("username"),
|
||||
nickName=user_info.get("nickname"),
|
||||
parentRoles=user_info.get("parents"),
|
||||
childRoles=user_info.get("children"),
|
||||
roleName=user_info.get("role"))
|
||||
session["uid"] = user_info.get("uuid")
|
||||
|
||||
|
||||
def _auth_with_key():
|
||||
key = request.values.get('_key')
|
||||
secret = request.values.get('_secret')
|
||||
if not key:
|
||||
return False
|
||||
|
||||
path = request.path
|
||||
keys = sorted(request.values.keys())
|
||||
req_args = [request.values[k] for k in keys if k not in ("_key", "_secret")]
|
||||
req_args = [str(request.values[k]) for k in keys if k not in ("_key", "_secret") and
|
||||
not isinstance(request.values[k], (dict, list))]
|
||||
user, authenticated = User.query.authenticate_with_key(key, secret, req_args, path)
|
||||
if user and authenticated:
|
||||
login_user(user)
|
||||
reset_session(user)
|
||||
return True
|
||||
|
||||
role, authenticated = Role.query.authenticate_with_key(key, secret, req_args, path)
|
||||
if role and authenticated:
|
||||
reset_session(None, role=role.name)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@@ -53,19 +86,20 @@ def _auth_with_token():
|
||||
return False
|
||||
|
||||
login_user(user)
|
||||
g.user = user
|
||||
reset_session(user)
|
||||
return True
|
||||
except jwt.ExpiredSignatureError:
|
||||
return False
|
||||
except (jwt.InvalidTokenError, Exception):
|
||||
except (jwt.InvalidTokenError, Exception) as e:
|
||||
current_app.logger.error(str(e))
|
||||
return False
|
||||
|
||||
|
||||
def _auth_with_ip_white_list():
|
||||
ip = request.remote_addr
|
||||
ip = request.headers.get('X-Real-IP') or request.remote_addr
|
||||
key = request.values.get('_key')
|
||||
secret = request.values.get('_secret')
|
||||
|
||||
current_app.logger.info(ip)
|
||||
if not key and not secret and ip.strip() in current_app.config.get("WHITE_LIST", []): # TODO
|
||||
user = UserCache.get("worker")
|
||||
login_user(user)
|
||||
@@ -73,24 +107,87 @@ def _auth_with_ip_white_list():
|
||||
return False
|
||||
|
||||
|
||||
def _auth_with_app_token():
|
||||
if _auth_with_session():
|
||||
if not is_app_admin(request.values.get('app_id')) and request.method != "GET":
|
||||
return False
|
||||
elif is_app_admin(request.values.get('app_id')):
|
||||
return True
|
||||
|
||||
if _auth_with_key() and is_app_admin('acl'):
|
||||
return True
|
||||
|
||||
auth_headers = request.headers.get('App-Access-Token', '').strip()
|
||||
if not auth_headers:
|
||||
return False
|
||||
|
||||
try:
|
||||
token = auth_headers
|
||||
data = jwt.decode(token, current_app.config['SECRET_KEY'], algorithms=['HS256'])
|
||||
current_app.logger.warning(data)
|
||||
app = AppCache.get(data['sub'])
|
||||
if not app:
|
||||
return False
|
||||
|
||||
request.values['app_id'] = app.id
|
||||
|
||||
return True
|
||||
except jwt.ExpiredSignatureError:
|
||||
return False
|
||||
except (jwt.InvalidTokenError, Exception) as e:
|
||||
current_app.logger.error(str(e))
|
||||
return False
|
||||
|
||||
|
||||
def _auth_with_acl_token():
|
||||
token = request.headers.get('Authorization', "")
|
||||
if not token.startswith('Bearer '):
|
||||
abort(401, ErrFormat.unauthorized)
|
||||
|
||||
_token = token.split(' ')[-1]
|
||||
|
||||
result = ACLManager().authenticate_with_token(_token)
|
||||
if result.get('authenticated') and result.get('user'):
|
||||
user = User.query.filter_by(email=result.get("user", {}).get("email", "")).first()
|
||||
login_user(user)
|
||||
reset_session(user)
|
||||
return user
|
||||
elif result.get('authenticated') is False:
|
||||
abort(401, ErrFormat.unauthorized)
|
||||
|
||||
|
||||
def auth_required(func):
|
||||
if request.json is not None:
|
||||
setattr(request, 'values', request.json)
|
||||
else:
|
||||
setattr(request, 'values', request.values.to_dict())
|
||||
|
||||
current_app.logger.debug(request.values)
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
|
||||
if not getattr(func, 'authenticated', True):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
if _auth_with_session() or _auth_with_key() or _auth_with_token() or _auth_with_ip_white_list():
|
||||
if getattr(func, 'auth_only_with_app_token', False) and _auth_with_app_token():
|
||||
return func(*args, **kwargs)
|
||||
elif getattr(func, 'auth_only_with_app_token', False):
|
||||
if _auth_with_key() and is_app_admin('acl'):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
if request.headers.get('App-Access-Token', '').strip():
|
||||
return abort(403, ErrFormat.auth_only_with_app_token_failed)
|
||||
else:
|
||||
return abort(403, ErrFormat.session_invalid)
|
||||
|
||||
if getattr(func, 'auth_with_app_token', False) and _auth_with_app_token():
|
||||
return func(*args, **kwargs)
|
||||
|
||||
abort(401)
|
||||
elif _auth_with_session() or _auth_with_key() or _auth_with_token() or _auth_with_ip_white_list():
|
||||
return func(*args, **kwargs)
|
||||
|
||||
if _auth_with_acl_token():
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return abort(401, ErrFormat.unauthorized)
|
||||
|
||||
return wrapper
|
||||
|
||||
@@ -103,3 +200,33 @@ def auth_abandoned(func):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def auth_with_app_token(func):
|
||||
setattr(func, 'auth_with_app_token', True)
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def auth_only_for_acl(func):
|
||||
setattr(func, 'auth_only_with_app_token', True)
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def auth_with_acl_token(func):
|
||||
setattr(func, 'auth_with_acl_token', True)
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper()
|
||||
|
Reference in New Issue
Block a user