diff --git a/cmdb-api/api/lib/cmdb/ci.py b/cmdb-api/api/lib/cmdb/ci.py index 3e7c181..7c2da6c 100644 --- a/cmdb-api/api/lib/cmdb/ci.py +++ b/cmdb-api/api/lib/cmdb/ci.py @@ -441,10 +441,13 @@ class CIManager(object): now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') ci = self.confirm_ci_existed(ci_id) - raw_dict = copy.deepcopy(ci_dict) - attrs = CITypeAttributeManager.get_all_attributes(ci.type_id) ci_type_attrs_name = {attr.name: attr for _, attr in attrs} + ci_type_attrs_alias2name = {attr.alias: attr.name for _, attr in attrs} + ci_dict = {ci_type_attrs_alias2name[k] if k in ci_type_attrs_alias2name else k: v for k, v in ci_dict.items()} + + raw_dict = copy.deepcopy(ci_dict) + ci_attr2type_attr = {type_attr.attr_id: type_attr for type_attr, _ in attrs} for _, attr in attrs: if attr.default and attr.default.get('default') == AttributeDefaultValueEnum.UPDATED_AT: @@ -825,6 +828,105 @@ class CIManager(object): return data.get('v') + def baseline(self, ci_ids, before_date): + ci_list = self.get_cis_by_ids(ci_ids, ret_key=RetKey.ALIAS) + if not ci_list: + return dict() + + ci2changed = dict() + changed = AttributeHistoryManger.get_records_for_attributes( + before_date, None, None, 1, 100000, None, None, ci_ids=ci_ids, more=True)[1] + for records in changed: + for change in records[1]: + if change['is_computed'] or change['is_password']: + continue + + if change.get('default') and change['default'].get('default') == AttributeDefaultValueEnum.UPDATED_AT: + continue + + ci2changed.setdefault(change['ci_id'], {}) + item = (change['old'], + change['new'], + change.get('is_list'), + change.get('value_type'), + change['operate_type']) + if change.get('is_list'): + ci2changed[change['ci_id']].setdefault(change.get('attr_alias'), []).append(item) + else: + ci2changed[change['ci_id']].update({change.get('attr_alias'): item}) + + type2show_name = {} + result = [] + for ci in ci_list: + list_attr2item = {} + for alias_name, v in (ci2changed.get(ci['_id']) or {}).items(): + if not alias_name: + continue + if alias_name == ci.get('unique_alias'): + continue + + if ci.get('_type') not in type2show_name: + ci_type = CITypeCache.get(ci.get('_type')) + show_id = ci_type.show_id or ci_type.unique_id + type2show_name[ci['_type']] = AttributeCache.get(show_id).alias + + if isinstance(v, list): + for old, new, is_list, value_type, operate_type in v: + if alias_name not in list_attr2item: + list_attr2item[alias_name] = dict(instance=ci.get(type2show_name[ci['_type']]), + attr_name=alias_name, + value_type=value_type, + is_list=is_list, + ci_type=ci.get('ci_type'), + unique_alias=ci.get('unique_alias'), + unique_value=ci.get(ci['unique_alias']), + cur=copy.deepcopy(ci.get(alias_name)), + to=ci.get(alias_name) or []) + + old = ValueTypeMap.deserialize[value_type](old) if old else old + new = ValueTypeMap.deserialize[value_type](new) if new else new + if operate_type == OperateType.ADD: + list_attr2item[alias_name]['to'].remove(new) + elif operate_type == OperateType.DELETE and old not in list_attr2item[alias_name]['to']: + list_attr2item[alias_name]['to'].append(old) + continue + + old, value_type = v[0], v[3] + old = ValueTypeMap.deserialize[value_type](old) if old else old + if isinstance(old, (datetime.datetime, datetime.date)): + old = str(old) + if ci.get(alias_name) != old: + item = dict(instance=ci.get(type2show_name[ci['_type']]), + attr_name=alias_name, + value_type=value_type, + ci_type=ci.get('ci_type'), + unique_alias=ci.get('unique_alias'), + unique_value=ci.get(ci['unique_alias']), + cur=ci.get(alias_name), + to=old) + result.append(item) + + for alias_name, item in list_attr2item.items(): + if sorted(item['cur'] or []) != sorted(item['to'] or []): + result.append(item) + + return result + + def rollback(self, ci_id, before_date): + baseline_ci = self.baseline([ci_id], before_date) + + payload = dict() + for item in baseline_ci: + payload[item.get('attr_name')] = item.get('to') + + if payload: + payload['ci_type'] = baseline_ci[0]['ci_type'] + payload[baseline_ci[0]['unique_alias']] = baseline_ci[0]['unique_value'] + + self.update(ci_id, **payload) + + return payload + class CIRelationManager(object): """ diff --git a/cmdb-api/api/lib/cmdb/ci_type.py b/cmdb-api/api/lib/cmdb/ci_type.py index cace664..35a7396 100644 --- a/cmdb-api/api/lib/cmdb/ci_type.py +++ b/cmdb-api/api/lib/cmdb/ci_type.py @@ -253,6 +253,13 @@ class CITypeManager(object): for item in CITypeInheritance.get_by(child_id=type_id, to_dict=False): item.delete(commit=False) + try: + from api.models.cmdb import CITypeReconciliation + for item in CITypeReconciliation.get_by(type_id=type_id, to_dict=False): + item.delete(commit=False) + except Exception: + pass + db.session.commit() ci_type.soft_delete() @@ -414,9 +421,6 @@ class CITypeGroupManager(object): existed = CITypeGroup.get_by_id(gid) or abort( 404, ErrFormat.ci_type_group_not_found.format("id={}".format(gid))) if name is not None and name != existed.name: - if RoleEnum.CONFIG not in session.get("acl", {}).get("parentRoles", []) and not is_app_admin("cmdb"): - return abort(403, ErrFormat.role_required.format(RoleEnum.CONFIG)) - existed.update(name=name) max_order = max([i.order or 0 for i in CITypeGroupItem.get_by(group_id=gid, to_dict=False)] or [0]) diff --git a/cmdb-api/api/lib/cmdb/const.py b/cmdb-api/api/lib/cmdb/const.py index 31cc6e5..376eafb 100644 --- a/cmdb-api/api/lib/cmdb/const.py +++ b/cmdb-api/api/lib/cmdb/const.py @@ -55,6 +55,9 @@ class CITypeOperateType(BaseEnum): DELETE_UNIQUE_CONSTRAINT = "11" # 删除联合唯一 ADD_RELATION = "12" # 新增关系 DELETE_RELATION = "13" # 删除关系 + ADD_RECONCILIATION = "14" # 删除关系 + UPDATE_RECONCILIATION = "15" # 删除关系 + DELETE_RECONCILIATION = "16" # 删除关系 class RetKey(BaseEnum): @@ -98,6 +101,12 @@ class AttributeDefaultValueEnum(BaseEnum): AUTO_INC_ID = "$auto_inc_id" +class ExecuteStatusEnum(BaseEnum): + COMPLETED = '0' + FAILED = '1' + RUNNING = '2' + + CMDB_QUEUE = "one_cmdb_async" REDIS_PREFIX_CI = "ONE_CMDB" REDIS_PREFIX_CI_RELATION = "CMDB_CI_RELATION" diff --git a/cmdb-api/api/lib/cmdb/history.py b/cmdb-api/api/lib/cmdb/history.py index 2e793f6..b5e388d 100644 --- a/cmdb-api/api/lib/cmdb/history.py +++ b/cmdb-api/api/lib/cmdb/history.py @@ -26,7 +26,7 @@ from api.models.cmdb import OperationRecord class AttributeHistoryManger(object): @staticmethod def get_records_for_attributes(start, end, username, page, page_size, operate_type, type_id, - ci_id=None, attr_id=None): + ci_id=None, attr_id=None, ci_ids=None, more=False): records = db.session.query(OperationRecord, AttributeHistory).join( AttributeHistory, OperationRecord.id == AttributeHistory.record_id) @@ -48,6 +48,9 @@ class AttributeHistoryManger(object): if ci_id is not None: records = records.filter(AttributeHistory.ci_id == ci_id) + if ci_ids and isinstance(ci_ids, list): + records = records.filter(AttributeHistory.ci_id.in_(ci_ids)) + if attr_id is not None: records = records.filter(AttributeHistory.attr_id == attr_id) @@ -62,6 +65,12 @@ class AttributeHistoryManger(object): if attr_hist['attr']: attr_hist['attr_name'] = attr_hist['attr'].name attr_hist['attr_alias'] = attr_hist['attr'].alias + if more: + attr_hist['is_list'] = attr_hist['attr'].is_list + attr_hist['is_computed'] = attr_hist['attr'].is_computed + attr_hist['is_password'] = attr_hist['attr'].is_password + attr_hist['default'] = attr_hist['attr'].default + attr_hist['value_type'] = attr_hist['attr'].value_type attr_hist.pop("attr") if record_id not in res: @@ -161,6 +170,7 @@ class AttributeHistoryManger(object): record = i.OperationRecord item = dict(attr_name=attr.name, attr_alias=attr.alias, + value_type=attr.value_type, operate_type=hist.operate_type, username=user and user.nickname, old=hist.old, @@ -271,7 +281,7 @@ class CITypeHistoryManager(object): return numfound, result @staticmethod - def add(operate_type, type_id, attr_id=None, trigger_id=None, unique_constraint_id=None, change=None): + def add(operate_type, type_id, attr_id=None, trigger_id=None, unique_constraint_id=None, change=None, rc_id=None): if type_id is None and attr_id is not None: from api.models.cmdb import CITypeAttribute type_ids = [i.type_id for i in CITypeAttribute.get_by(attr_id=attr_id, to_dict=False)] @@ -284,6 +294,7 @@ class CITypeHistoryManager(object): uid=current_user.uid, attr_id=attr_id, trigger_id=trigger_id, + rc_id=rc_id, unique_constraint_id=unique_constraint_id, change=change) diff --git a/cmdb-api/api/lib/cmdb/resp_format.py b/cmdb-api/api/lib/cmdb/resp_format.py index 7e89cbe..1d72ea3 100644 --- a/cmdb-api/api/lib/cmdb/resp_format.py +++ b/cmdb-api/api/lib/cmdb/resp_format.py @@ -78,6 +78,8 @@ class ErrFormat(CommonErrFormat): unique_constraint_invalid = _l("Uniquely constrained attributes cannot be JSON and multi-valued") ci_type_trigger_duplicate = _l("Duplicated trigger") # 重复的触发器 ci_type_trigger_not_found = _l("Trigger {} does not exist") # 触发器 {} 不存在 + ci_type_reconciliation_duplicate = _l("Duplicated reconciliation rule") # 重复的校验规则 + ci_type_reconciliation_not_found = _l("Reconciliation rule {} does not exist") # 规则 {} 不存在 record_not_found = _l("Operation record {} does not exist") # 操作记录 {} 不存在 cannot_delete_unique = _l("Unique identifier cannot be deleted") # 不能删除唯一标识 diff --git a/cmdb-api/api/lib/cmdb/value.py b/cmdb-api/api/lib/cmdb/value.py index 901a9b6..215f044 100644 --- a/cmdb-api/api/lib/cmdb/value.py +++ b/cmdb-api/api/lib/cmdb/value.py @@ -275,34 +275,27 @@ class AttributeValueManager(object): if attr.is_list: existed_attrs = value_table.get_by(attr_id=attr.id, ci_id=ci.id, to_dict=False) existed_values = [i.value for i in existed_attrs] + added = set(value) - set(existed_values) + deleted = set(existed_values) - set(value) + for v in added: + value_table.create(ci_id=ci.id, attr_id=attr.id, value=v, flush=False, commit=False) + changed.append((ci.id, attr.id, OperateType.ADD, None, v, ci.type_id)) - # Comparison array starts from which position changes - min_len = min(len(value), len(existed_values)) - index = 0 - while index < min_len: - if value[index] != existed_values[index]: - break - index += 1 - added = value[index:] - deleted = existed_values[index:] - - # Delete first and then add to ensure id sorting for v in deleted: existed_attr = existed_attrs[existed_values.index(v)] existed_attr.delete(flush=False, commit=False) changed.append((ci.id, attr.id, OperateType.DELETE, v, None, ci.type_id)) - for v in added: - value_table.create(ci_id=ci.id, attr_id=attr.id, value=v, flush=False, commit=False) - changed.append((ci.id, attr.id, OperateType.ADD, None, v, ci.type_id)) else: existed_attr = value_table.get_by(attr_id=attr.id, ci_id=ci.id, first=True, to_dict=False) existed_value = existed_attr and existed_attr.value + existed_value = (ValueTypeMap.serialize[attr.value_type](existed_value) if + existed_value or existed_value == 0 else existed_value) if existed_value is None and value is not None: value_table.create(ci_id=ci.id, attr_id=attr.id, value=value, flush=False, commit=False) changed.append((ci.id, attr.id, OperateType.ADD, None, value, ci.type_id)) else: - if existed_value != value: + if existed_value != value and existed_attr: if value is None: existed_attr.delete(flush=False, commit=False) else: diff --git a/cmdb-api/api/lib/perm/acl/cache.py b/cmdb-api/api/lib/perm/acl/cache.py index 1f6dadd..dcf9543 100644 --- a/cmdb-api/api/lib/perm/acl/cache.py +++ b/cmdb-api/api/lib/perm/acl/cache.py @@ -224,16 +224,22 @@ class RoleRelationCache(object): @classmethod @flush_db def rebuild(cls, rid, app_id): - cls.clean(rid, app_id) - - 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) + if app_id is None: + app_ids = [None] + [i.id for i in App.get_by(to_dict=False)] else: - HasResourceRoleCache.remove(rid, app_id) - cls.get_resources2(rid, app_id) + app_ids = [app_id] + + for _app_id in app_ids: + cls.clean(rid, _app_id) + + 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 @flush_db diff --git a/cmdb-api/api/lib/perm/acl/permission.py b/cmdb-api/api/lib/perm/acl/permission.py index 0169ddd..4ca3518 100644 --- a/cmdb-api/api/lib/perm/acl/permission.py +++ b/cmdb-api/api/lib/perm/acl/permission.py @@ -274,12 +274,14 @@ class PermissionCRUD(object): 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: + if perm is None: + continue + exists = 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 exists: existed.deleted = True existed.deleted_at = datetime.datetime.now() db.session.add(existed) diff --git a/cmdb-api/api/lib/perm/acl/resource.py b/cmdb-api/api/lib/perm/acl/resource.py index 8b1135a..3628b3a 100644 --- a/cmdb-api/api/lib/perm/acl/resource.py +++ b/cmdb-api/api/lib/perm/acl/resource.py @@ -2,7 +2,6 @@ from flask import abort -from flask import current_app from api.extensions import db from api.lib.perm.acl.audit import AuditCRUD @@ -127,11 +126,18 @@ class ResourceTypeCRUD(object): existed_ids = [i.id for i in existed] current_ids = [] + rebuild_rids = set() for i in existed: if i.name not in perms: - i.soft_delete() + i.soft_delete(commit=False) + for rp in RolePermission.get_by(perm_id=i.id, to_dict=False): + rp.soft_delete(commit=False) + rebuild_rids.add((rp.app_id, rp.rid)) else: current_ids.append(i.id) + db.session.commit() + for _app_id, _rid in rebuild_rids: + role_rebuild.apply_async(args=(_rid, _app_id), queue=ACL_QUEUE) for i in perms: if i not in existed_names: diff --git a/cmdb-api/api/lib/perm/acl/role.py b/cmdb-api/api/lib/perm/acl/role.py index 0c2a28c..15569df 100644 --- a/cmdb-api/api/lib/perm/acl/role.py +++ b/cmdb-api/api/lib/perm/acl/role.py @@ -3,12 +3,14 @@ import time +import redis_lock import six from flask import abort from flask import current_app from sqlalchemy import or_ from api.extensions import db +from api.extensions import rd 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 @@ -62,7 +64,9 @@ class RoleRelationCRUD(object): id2parents = {} for i in res: - id2parents.setdefault(rid2uid.get(i.child_id, i.child_id), []).append(RoleCache.get(i.parent_id).to_dict()) + parent = RoleCache.get(i.parent_id) + if parent: + id2parents.setdefault(rid2uid.get(i.child_id, i.child_id), []).append(parent.to_dict()) return id2parents @@ -141,24 +145,27 @@ class RoleRelationCRUD(object): @classmethod def add(cls, role, parent_id, child_ids, app_id): - result = [] - for child_id in child_ids: - existed = RoleRelation.get_by(parent_id=parent_id, child_id=child_id, app_id=app_id) - if existed: - continue + with redis_lock.Lock(rd.r, "ROLE_RELATION_ADD"): + db.session.commit() - RoleRelationCache.clean(parent_id, app_id) - RoleRelationCache.clean(child_id, app_id) + result = [] + for child_id in child_ids: + existed = RoleRelation.get_by(parent_id=parent_id, child_id=child_id, app_id=app_id) + if existed: + continue - if parent_id in cls.recursive_child_ids(child_id, app_id): - return abort(400, ErrFormat.inheritance_dead_loop) + RoleRelationCache.clean(parent_id, app_id) + RoleRelationCache.clean(child_id, app_id) - if app_id is None: - for app in AppCRUD.get_all(): - if app.name != "acl": - 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) - result.append(RoleRelation.create(parent_id=parent_id, child_id=child_id, app_id=app_id).to_dict()) + 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, {}, {}, diff --git a/cmdb-api/api/tasks/acl.py b/cmdb-api/api/tasks/acl.py index d9eb6a9..94f37b6 100644 --- a/cmdb-api/api/tasks/acl.py +++ b/cmdb-api/api/tasks/acl.py @@ -3,12 +3,13 @@ import json import re -from celery_once import QueueOnce +import redis_lock from flask import current_app from werkzeug.exceptions import BadRequest from werkzeug.exceptions import NotFound from api.extensions import celery +from api.extensions import rd from api.lib.decorator import flush_db from api.lib.decorator import reconnect_db from api.lib.perm.acl.audit import AuditCRUD @@ -25,14 +26,14 @@ from api.models.acl import Role from api.models.acl import Trigger -@celery.task(name="acl.role_rebuild", - queue=ACL_QUEUE,) +@celery.task(name="acl.role_rebuild", queue=ACL_QUEUE, ) @flush_db @reconnect_db def role_rebuild(rids, app_id): rids = rids if isinstance(rids, list) else [rids] for rid in rids: - RoleRelationCache.rebuild(rid, app_id) + with redis_lock.Lock(rd.r, "ROLE_REBUILD_{}_{}".format(rid, app_id)): + RoleRelationCache.rebuild(rid, app_id) current_app.logger.info("Role {0} App {1} rebuild..........".format(rids, app_id)) diff --git a/cmdb-api/api/views/cmdb/ci.py b/cmdb-api/api/views/cmdb/ci.py index c568b27..08cebe7 100644 --- a/cmdb-api/api/views/cmdb/ci.py +++ b/cmdb-api/api/views/cmdb/ci.py @@ -16,6 +16,7 @@ from api.lib.cmdb.const import RetKey from api.lib.cmdb.perms import has_perm_for_ci from api.lib.cmdb.search import SearchError from api.lib.cmdb.search.ci import search +from api.lib.decorator import args_required from api.lib.perm.acl.acl import has_perm_from_args from api.lib.utils import get_page from api.lib.utils import get_page_size @@ -254,3 +255,23 @@ class CIPasswordView(APIView): def post(self, ci_id, attr_id): return self.get(ci_id, attr_id) + + +class CIBaselineView(APIView): + url_prefix = ("/ci/baseline", "/ci//baseline/rollback") + + @args_required("before_date") + def get(self): + ci_ids = handle_arg_list(request.values.get('ci_ids')) + before_date = request.values.get('before_date') + + return self.jsonify(CIManager().baseline(list(map(int, ci_ids)), before_date)) + + @args_required("before_date") + def post(self, ci_id): + if 'rollback' in request.url: + before_date = request.values.get('before_date') + + return self.jsonify(**CIManager().rollback(ci_id, before_date)) + + return self.get(ci_id) diff --git a/cmdb-api/api/views/cmdb/ci_type.py b/cmdb-api/api/views/cmdb/ci_type.py index 27963cf..5f44a20 100644 --- a/cmdb-api/api/views/cmdb/ci_type.py +++ b/cmdb-api/api/views/cmdb/ci_type.py @@ -7,7 +7,6 @@ from io import BytesIO from flask import abort from flask import current_app from flask import request -from flask import session from api.lib.cmdb.cache import AttributeCache from api.lib.cmdb.cache import CITypeCache @@ -23,6 +22,8 @@ from api.lib.cmdb.const import PermEnum, ResourceTypeEnum, RoleEnum from api.lib.cmdb.perms import CIFilterPermsCRUD from api.lib.cmdb.preference import PreferenceManager from api.lib.cmdb.resp_format import ErrFormat +from api.lib.common_setting.decorator import perms_role_required +from api.lib.common_setting.role_perm_base import CMDBApp from api.lib.decorator import args_required from api.lib.decorator import args_validate from api.lib.perm.acl.acl import ACLManager @@ -36,6 +37,8 @@ from api.lib.perm.auth import auth_with_app_token from api.lib.utils import handle_arg_list from api.resource import APIView +app_cli = CMDBApp() + class CITypeView(APIView): url_prefix = ("/ci_types", "/ci_types/", "/ci_types/", @@ -125,7 +128,8 @@ class CITypeGroupView(APIView): return self.jsonify(CITypeGroupManager.get(need_other, config_required)) - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Configuration, + app_cli.op.create_CIType_group, app_cli.admin_name) @args_required("name") @args_validate(CITypeGroupManager.cls) def post(self): @@ -134,12 +138,11 @@ class CITypeGroupView(APIView): return self.jsonify(group.to_dict()) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Configuration, + app_cli.op.update_CIType_group, app_cli.admin_name) @args_validate(CITypeGroupManager.cls) def put(self, gid=None): if "/order" in request.url: - if RoleEnum.CONFIG not in session.get("acl", {}).get("parentRoles", []) and not is_app_admin("cmdb"): - return abort(403, ErrFormat.role_required.format(RoleEnum.CONFIG)) - group_ids = request.values.get('group_ids') CITypeGroupManager.order(group_ids) @@ -152,7 +155,8 @@ class CITypeGroupView(APIView): return self.jsonify(gid=gid) - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Configuration, + app_cli.op.delete_CIType_group, app_cli.admin_name) def delete(self, gid): type_ids = request.values.get("type_ids") CITypeGroupManager.delete(gid, type_ids) @@ -352,14 +356,16 @@ class CITypeAttributeGroupView(APIView): class CITypeTemplateView(APIView): url_prefix = ("/ci_types/template/import", "/ci_types/template/export", "/ci_types//template/export") - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Configuration, + app_cli.op.download_CIType, app_cli.admin_name) def get(self, type_id=None): # export if type_id is not None: return self.jsonify(dict(ci_type_template=CITypeTemplateManager.export_template_by_type(type_id))) return self.jsonify(dict(ci_type_template=CITypeTemplateManager.export_template())) - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Configuration, + app_cli.op.download_CIType, app_cli.admin_name) def post(self): # import tpt = request.values.get('ci_type_template') or {} @@ -379,7 +385,8 @@ class CITypeCanDefineComputed(APIView): class CITypeTemplateFileView(APIView): url_prefix = ("/ci_types/template/import/file", "/ci_types/template/export/file") - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Configuration, + app_cli.op.download_CIType, app_cli.admin_name) def get(self): # export tpt_json = CITypeTemplateManager.export_template() tpt_json = dict(ci_type_template=tpt_json) @@ -394,7 +401,8 @@ class CITypeTemplateFileView(APIView): mimetype='application/json', max_age=0) - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Configuration, + app_cli.op.download_CIType, app_cli.admin_name) def post(self): # import f = request.files.get('file') diff --git a/cmdb-api/api/views/cmdb/ci_type_relation.py b/cmdb-api/api/views/cmdb/ci_type_relation.py index 410a977..eeae0a5 100644 --- a/cmdb-api/api/views/cmdb/ci_type_relation.py +++ b/cmdb-api/api/views/cmdb/ci_type_relation.py @@ -11,6 +11,8 @@ from api.lib.cmdb.const import ResourceTypeEnum from api.lib.cmdb.const import RoleEnum from api.lib.cmdb.preference import PreferenceManager from api.lib.cmdb.resp_format import ErrFormat +from api.lib.common_setting.decorator import perms_role_required +from api.lib.common_setting.role_perm_base import CMDBApp from api.lib.decorator import args_required from api.lib.perm.acl.acl import ACLManager from api.lib.perm.acl.acl import has_perm_from_args @@ -18,6 +20,8 @@ from api.lib.perm.acl.acl import is_app_admin from api.lib.perm.acl.acl import role_required from api.resource import APIView +app_cli = CMDBApp() + class GetChildrenView(APIView): url_prefix = ("/ci_type_relations//children", @@ -41,7 +45,8 @@ class GetParentsView(APIView): class CITypeRelationView(APIView): url_prefix = ("/ci_type_relations", "/ci_type_relations//") - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Service_Tree_Definition, + app_cli.op.read, app_cli.admin_name) def get(self): res, type2attributes = CITypeRelationManager.get() @@ -69,7 +74,8 @@ class CITypeRelationView(APIView): class CITypeRelationDelete2View(APIView): url_prefix = "/ci_type_relations/" - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Model_Relationships, + app_cli.op.read, app_cli.admin_name) def delete(self, ctr_id): CITypeRelationManager.delete(ctr_id) diff --git a/cmdb-api/api/views/cmdb/custom_dashboard.py b/cmdb-api/api/views/cmdb/custom_dashboard.py index 1c20ff9..7b3ac10 100644 --- a/cmdb-api/api/views/cmdb/custom_dashboard.py +++ b/cmdb-api/api/views/cmdb/custom_dashboard.py @@ -3,14 +3,16 @@ from flask import request -from api.lib.cmdb.const import RoleEnum from api.lib.cmdb.custom_dashboard import CustomDashboardManager from api.lib.cmdb.custom_dashboard import SystemConfigManager +from api.lib.common_setting.decorator import perms_role_required +from api.lib.common_setting.role_perm_base import CMDBApp from api.lib.decorator import args_required from api.lib.decorator import args_validate -from api.lib.perm.acl.acl import role_required from api.resource import APIView +app_cli = CMDBApp() + class CustomDashboardApiView(APIView): url_prefix = ("/custom_dashboard", "/custom_dashboard/", "/custom_dashboard/batch", @@ -19,7 +21,8 @@ class CustomDashboardApiView(APIView): def get(self): return self.jsonify(CustomDashboardManager.get()) - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Customized_Dashboard, + app_cli.op.read, app_cli.admin_name) @args_validate(CustomDashboardManager.cls) def post(self): if request.url.endswith("/preview"): @@ -32,7 +35,8 @@ class CustomDashboardApiView(APIView): return self.jsonify(res) - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Customized_Dashboard, + app_cli.op.read, app_cli.admin_name) @args_validate(CustomDashboardManager.cls) def put(self, _id=None): if _id is not None: @@ -47,7 +51,8 @@ class CustomDashboardApiView(APIView): return self.jsonify(id2options=request.values.get('id2options')) - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Customized_Dashboard, + app_cli.op.read, app_cli.admin_name) def delete(self, _id): CustomDashboardManager.delete(_id) @@ -57,12 +62,14 @@ class CustomDashboardApiView(APIView): class SystemConfigApiView(APIView): url_prefix = ("/system_config",) - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Service_Tree_Definition, + app_cli.op.read, app_cli.admin_name) @args_required("name", value_required=True) def get(self): return self.jsonify(SystemConfigManager.get(request.values['name'])) - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Service_Tree_Definition, + app_cli.op.read, app_cli.admin_name) @args_validate(SystemConfigManager.cls) @args_required("name", value_required=True) @args_required("option", value_required=True) @@ -74,7 +81,8 @@ class SystemConfigApiView(APIView): def put(self, _id=None): return self.post() - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Service_Tree_Definition, + app_cli.op.read, app_cli.admin_name) @args_required("name") def delete(self): CustomDashboardManager.delete(request.values['name']) diff --git a/cmdb-api/api/views/cmdb/history.py b/cmdb-api/api/views/cmdb/history.py index ceaa2db..09dc159 100644 --- a/cmdb-api/api/views/cmdb/history.py +++ b/cmdb-api/api/views/cmdb/history.py @@ -5,28 +5,29 @@ import datetime from flask import abort from flask import request -from flask import session from api.lib.cmdb.ci import CIManager from api.lib.cmdb.const import PermEnum from api.lib.cmdb.const import ResourceTypeEnum -from api.lib.cmdb.const import RoleEnum from api.lib.cmdb.history import AttributeHistoryManger from api.lib.cmdb.history import CITriggerHistoryManager from api.lib.cmdb.history import CITypeHistoryManager from api.lib.cmdb.resp_format import ErrFormat +from api.lib.common_setting.decorator import perms_role_required +from api.lib.common_setting.role_perm_base import CMDBApp from api.lib.perm.acl.acl import has_perm_from_args -from api.lib.perm.acl.acl import is_app_admin -from api.lib.perm.acl.acl import role_required from api.lib.utils import get_page from api.lib.utils import get_page_size from api.resource import APIView +app_cli = CMDBApp() + class RecordView(APIView): url_prefix = ("/history/records/attribute", "/history/records/relation") - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Operation_Audit, + app_cli.op.read, app_cli.admin_name) def get(self): page = get_page(request.values.get("page", 1)) page_size = get_page_size(request.values.get("page_size")) @@ -80,18 +81,21 @@ class CIHistoryView(APIView): class CITriggerHistoryView(APIView): - url_prefix = ("/history/ci_triggers/", "/history/ci_triggers") + url_prefix = ("/history/ci_triggers/",) @has_perm_from_args("ci_id", ResourceTypeEnum.CI, PermEnum.READ, CIManager.get_type_name) - def get(self, ci_id=None): - if ci_id is not None: - result = CITriggerHistoryManager.get_by_ci_id(ci_id) + def get(self, ci_id): + result = CITriggerHistoryManager.get_by_ci_id(ci_id) - return self.jsonify(result) + return self.jsonify(result) - if RoleEnum.CONFIG not in session.get("acl", {}).get("parentRoles", []) and not is_app_admin("cmdb"): - return abort(403, ErrFormat.role_required.format(RoleEnum.CONFIG)) +class CIsTriggerHistoryView(APIView): + url_prefix = ("/history/ci_triggers",) + + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Operation_Audit, + app_cli.op.read, app_cli.admin_name) + def get(self): type_id = request.values.get("type_id") trigger_id = request.values.get("trigger_id") operate_type = request.values.get("operate_type") @@ -115,7 +119,8 @@ class CITriggerHistoryView(APIView): class CITypeHistoryView(APIView): url_prefix = "/history/ci_types" - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Operation_Audit, + app_cli.op.read, app_cli.admin_name) def get(self): type_id = request.values.get("type_id") username = request.values.get("username") diff --git a/cmdb-api/api/views/cmdb/preference.py b/cmdb-api/api/views/cmdb/preference.py index 3da2fa7..1f5e171 100644 --- a/cmdb-api/api/views/cmdb/preference.py +++ b/cmdb-api/api/views/cmdb/preference.py @@ -8,20 +8,22 @@ from flask import request from api.lib.cmdb.ci_type import CITypeManager from api.lib.cmdb.const import PermEnum from api.lib.cmdb.const import ResourceTypeEnum -from api.lib.cmdb.const import RoleEnum from api.lib.cmdb.perms import CIFilterPermsCRUD from api.lib.cmdb.preference import PreferenceManager from api.lib.cmdb.resp_format import ErrFormat +from api.lib.common_setting.decorator import perms_role_required +from api.lib.common_setting.role_perm_base import CMDBApp from api.lib.decorator import args_required from api.lib.decorator import args_validate from api.lib.perm.acl.acl import ACLManager from api.lib.perm.acl.acl import has_perm_from_args from api.lib.perm.acl.acl import is_app_admin -from api.lib.perm.acl.acl import role_required from api.lib.perm.acl.acl import validate_permission from api.lib.utils import handle_arg_list from api.resource import APIView +app_cli = CMDBApp() + class PreferenceShowCITypesView(APIView): url_prefix = ("/preference/ci_types", "/preference/ci_types2") @@ -104,7 +106,8 @@ class PreferenceRelationApiView(APIView): return self.jsonify(views=views, id2type=id2type, name2id=name2id) - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Service_Tree_Definition, + app_cli.op.read, app_cli.admin_name) @args_required("name") @args_required("cr_ids") @args_validate(PreferenceManager.pref_rel_cls) @@ -118,14 +121,16 @@ class PreferenceRelationApiView(APIView): return self.jsonify(views=views, id2type=id2type, name2id=name2id) - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Service_Tree_Definition, + app_cli.op.read, app_cli.admin_name) @args_required("name") def put(self, _id): views, id2type, name2id = PreferenceManager.create_or_update_relation_view(_id=_id, **request.values) return self.jsonify(views=views, id2type=id2type, name2id=name2id) - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Service_Tree_Definition, + app_cli.op.read, app_cli.admin_name) @args_required("name") def delete(self): name = request.values.get("name") diff --git a/cmdb-api/api/views/cmdb/relation_type.py b/cmdb-api/api/views/cmdb/relation_type.py index e24dada..c76d7f9 100644 --- a/cmdb-api/api/views/cmdb/relation_type.py +++ b/cmdb-api/api/views/cmdb/relation_type.py @@ -4,14 +4,16 @@ from flask import abort from flask import request -from api.lib.cmdb.const import RoleEnum from api.lib.cmdb.relation_type import RelationTypeManager from api.lib.cmdb.resp_format import ErrFormat +from api.lib.common_setting.decorator import perms_role_required +from api.lib.common_setting.role_perm_base import CMDBApp from api.lib.decorator import args_required from api.lib.decorator import args_validate -from api.lib.perm.acl.acl import role_required from api.resource import APIView +app_cli = CMDBApp() + class RelationTypeView(APIView): url_prefix = ("/relation_types", "/relation_types/") @@ -19,7 +21,8 @@ class RelationTypeView(APIView): def get(self): return self.jsonify([i.to_dict() for i in RelationTypeManager.get_all()]) - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Relationship_Types, + app_cli.op.read, app_cli.admin_name) @args_required("name") @args_validate(RelationTypeManager.cls) def post(self): @@ -28,7 +31,8 @@ class RelationTypeView(APIView): return self.jsonify(rel.to_dict()) - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Relationship_Types, + app_cli.op.read, app_cli.admin_name) @args_required("name") @args_validate(RelationTypeManager.cls) def put(self, rel_id): @@ -37,7 +41,8 @@ class RelationTypeView(APIView): return self.jsonify(rel.to_dict()) - @role_required(RoleEnum.CONFIG) + @perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.Relationship_Types, + app_cli.op.read, app_cli.admin_name) def delete(self, rel_id): RelationTypeManager.delete(rel_id)