diff --git a/cmdb-api/api/lib/cmdb/attribute.py b/cmdb-api/api/lib/cmdb/attribute.py index 817bac2..37aa31a 100644 --- a/cmdb-api/api/lib/cmdb/attribute.py +++ b/cmdb-api/api/lib/cmdb/attribute.py @@ -189,7 +189,8 @@ class AttributeManager(object): return attr def get_attribute(self, key, choice_web_hook_parse=True, choice_other_parse=True): - attr = AttributeCache.get(key).to_dict() + attr = AttributeCache.get(key) or dict() + attr = attr and attr.to_dict() if attr.get("is_choice"): attr["choice_value"] = self.get_choice_values( attr["id"], diff --git a/cmdb-api/api/lib/cmdb/ci_type.py b/cmdb-api/api/lib/cmdb/ci_type.py index b7e74d5..79c963d 100644 --- a/cmdb-api/api/lib/cmdb/ci_type.py +++ b/cmdb-api/api/lib/cmdb/ci_type.py @@ -5,8 +5,10 @@ import copy import toposort from flask import abort from flask import current_app +from flask import session from flask_login import current_user from toposort import toposort_flatten +from werkzeug.exceptions import BadRequest from api.extensions import db from api.lib.cmdb.attribute import AttributeManager @@ -75,12 +77,13 @@ class CITypeManager(object): def get_ci_types(type_name=None): resources = None if current_app.config.get('USE_ACL') and not is_app_admin('cmdb'): - resources = set([i.get('name') for i in ACLManager().get_resources("CIType")]) + resources = set([i.get('name') for i in ACLManager().get_resources(ResourceTypeEnum.CI_TYPE)]) ci_types = CIType.get_by() if type_name is None else CIType.get_by_like(name=type_name) res = list() for type_dict in ci_types: - type_dict["unique_key"] = AttributeCache.get(type_dict["unique_id"]).name + attr = AttributeCache.get(type_dict["unique_id"]) + type_dict["unique_key"] = attr and attr.name if resources is None or type_dict['name'] in resources: res.append(type_dict) @@ -113,6 +116,9 @@ class CITypeManager(object): @classmethod @kwargs_required("name") def add(cls, **kwargs): + if current_app.config.get('USE_ACL') and not is_app_admin('cmdb'): + if ErrFormat.ci_type_config not in {i['name'] for i in ACLManager().get_resources(ResourceTypeEnum.PAGE)}: + return abort(403, ErrFormat.no_permission2) unique_key = kwargs.pop("unique_key", None) or kwargs.pop("unique_id", None) unique_key = AttributeCache.get(unique_key) or abort(404, ErrFormat.unique_key_not_define) @@ -131,7 +137,11 @@ class CITypeManager(object): CITypeCache.clean(ci_type.name) if current_app.config.get("USE_ACL"): - ACLManager().add_resource(ci_type.name, ResourceTypeEnum.CI) + try: + ACLManager().add_resource(ci_type.name, ResourceTypeEnum.CI) + except BadRequest: + pass + ACLManager().grant_resource_to_role(ci_type.name, RoleEnum.CMDB_READ_ALL, ResourceTypeEnum.CI, @@ -243,7 +253,6 @@ class CITypeGroupManager(object): else: resources = {i['name']: i['permissions'] for i in resources if PermEnum.READ in i.get("permissions")} - current_app.logger.info(resources) groups = sorted(CITypeGroup.get_by(), key=lambda x: x['order'] or 0) group_types = set() for group in groups: @@ -283,7 +292,10 @@ 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: + 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]) @@ -725,7 +737,7 @@ class CITypeAttributeGroupManager(object): grouped = list() attributes = CITypeAttributeManager.get_attributes_by_type_id(type_id) - id2attr = {i['id']: i for i in attributes} + id2attr = {i.get('id'): i for i in attributes} for group in groups: items = CITypeAttributeGroupItem.get_by(group_id=group["id"], to_dict=False) @@ -891,97 +903,58 @@ class CITypeAttributeGroupManager(object): class CITypeTemplateManager(object): @staticmethod - def __import(cls, data): - id2obj_dicts = {i['id']: i for i in data} - existed = cls.get_by(deleted=None, to_dict=False) - id2existed = {i.id: i for i in existed} - existed_ids = [i.id for i in existed] - existed_no_delete_ids = [i.id for i in existed if not i.deleted] + def __import(cls, data, unique_key='name'): + id2obj_dicts = {i[unique_key]: i for i in data} + existed = cls.get_by(to_dict=False) + id2existed = {getattr(i, unique_key): i for i in existed} + existed_ids = [getattr(i, unique_key) for i in existed] + id_map = dict() # add for added_id in set(id2obj_dicts.keys()) - set(existed_ids): + _id = id2obj_dicts[added_id].pop('id', None) + id2obj_dicts[added_id].pop('created_at', None) + id2obj_dicts[added_id].pop('updated_at', None) + id2obj_dicts[added_id].pop('uid', None) + if cls == CIType: - CITypeManager.add(**id2obj_dicts[added_id]) + __id = CITypeManager.add(**id2obj_dicts[added_id]) + CITypeCache.clean(__id) elif cls == CITypeRelation: - CITypeRelationManager.add(id2obj_dicts[added_id].get('parent_id'), - id2obj_dicts[added_id].get('child_id'), - id2obj_dicts[added_id].get('relation_type_id'), - id2obj_dicts[added_id].get('constraint'), - ) + __id = CITypeRelationManager.add(id2obj_dicts[added_id].get('parent_id'), + id2obj_dicts[added_id].get('child_id'), + id2obj_dicts[added_id].get('relation_type_id'), + id2obj_dicts[added_id].get('constraint'), + ) else: - cls.create(flush=True, **id2obj_dicts[added_id]) + obj = cls.create(flush=True, **id2obj_dicts[added_id]) + if cls == Attribute: + AttributeCache.clean(obj) + __id = obj.id + + id_map[_id] = __id # update for updated_id in set(id2obj_dicts.keys()) & set(existed_ids): + _id = id2obj_dicts[updated_id].pop('id', None) + + id2existed[updated_id].update(flush=True, **id2obj_dicts[updated_id]) + + id_map[_id] = id2existed[updated_id].id + + if cls == Attribute: + AttributeCache.clean(id2existed[updated_id]) + if cls == CIType: - deleted = id2existed[updated_id].deleted - CITypeManager.update(updated_id, **id2obj_dicts[updated_id]) - if deleted and current_app.config.get("USE_ACL"): - type_name = id2obj_dicts[updated_id]['name'] - ACLManager().add_resource(type_name, ResourceTypeEnum.CI) - ACLManager().grant_resource_to_role(type_name, - RoleEnum.CMDB_READ_ALL, - ResourceTypeEnum.CI, - permissions=[PermEnum.READ]) - ACLManager().grant_resource_to_role(type_name, - current_user.username, - ResourceTypeEnum.CI) + CITypeCache.clean(id2existed[updated_id].id) - else: - id2existed[updated_id].update(flush=True, **id2obj_dicts[updated_id]) - - # delete - for deleted_id in set(existed_no_delete_ids) - set(id2obj_dicts.keys()): - if cls == CIType: - id2existed[deleted_id].soft_delete(flush=True) - - CITypeCache.clean(deleted_id) - - CITypeHistoryManager.add(CITypeOperateType.DELETE, deleted_id, change=id2existed[deleted_id].to_dict()) - - if current_app.config.get("USE_ACL"): - ACLManager().del_resource(id2existed[deleted_id].name, ResourceTypeEnum.CI) - else: - id2existed[deleted_id].soft_delete(flush=True) try: db.session.commit() except Exception as e: db.session.rollback() raise Exception(str(e)) - def _import_ci_types(self, ci_types): - for i in ci_types: - i.pop("unique_key", None) - - self.__import(CIType, ci_types) - - def _import_ci_type_groups(self, ci_type_groups): - _ci_type_groups = copy.deepcopy(ci_type_groups) - for i in _ci_type_groups: - i.pop('ci_types', None) - - self.__import(CITypeGroup, _ci_type_groups) - - # import group type items - for group in ci_type_groups: - existed = CITypeGroupItem.get_by(group_id=group['id'], to_dict=False) - for i in existed: - i.soft_delete() - - for order, ci_type in enumerate(group.get('ci_types') or []): - payload = dict(group_id=group['id'], type_id=ci_type['id'], order=order) - CITypeGroupItem.create(**payload) - - def _import_relation_types(self, relation_types): - self.__import(RelationType, relation_types) - - def _import_ci_type_relations(self, ci_type_relations): - for i in ci_type_relations: - i.pop('parent', None) - i.pop('child', None) - i.pop('relation_type', None) - - self.__import(CITypeRelation, ci_type_relations) + return id_map def _import_attributes(self, type2attributes): attributes = [attr for type_id in type2attributes for attr in type2attributes[type_id]] @@ -990,122 +963,262 @@ class CITypeTemplateManager(object): i.pop('default_show', None) i.pop('is_required', None) i.pop('order', None) + i.pop('choice_web_hook', None) + i.pop('choice_other', None) + i.pop('order', None) choice_value = i.pop('choice_value', None) + if not choice_value: + i['is_choice'] = False attrs.append((i, choice_value)) - self.__import(Attribute, [i[0] for i in attrs]) + attr_id_map = self.__import(Attribute, [i[0] for i in copy.deepcopy(attrs)]) for i, choice_value in attrs: - if choice_value: - AttributeManager.add_choice_values(i['id'], i['value_type'], choice_value) + if choice_value and not i.get('choice_web_hook') and not i.get('choice_other'): + AttributeManager.add_choice_values(attr_id_map.get(i['id'], i['id']), i['value_type'], choice_value) + + return attr_id_map + + def _import_ci_types(self, ci_types, attr_id_map): + for i in ci_types: + i.pop("unique_key", None) + i['unique_id'] = attr_id_map.get(i['unique_id'], i['unique_id']) + i['uid'] = current_user.uid + + return self.__import(CIType, ci_types) + + def _import_ci_type_groups(self, ci_type_groups, type_id_map): + _ci_type_groups = copy.deepcopy(ci_type_groups) + for i in _ci_type_groups: + i.pop('ci_types', None) + + group_id_map = self.__import(CITypeGroup, _ci_type_groups) + + # import group type items + for group in ci_type_groups: + for order, ci_type in enumerate(group.get('ci_types') or []): + payload = dict(group_id=group_id_map.get(group['id'], group['id']), + type_id=type_id_map.get(ci_type['id'], ci_type['id']), + order=order) + existed = CITypeGroupItem.get_by(group_id=payload['group_id'], type_id=payload['type_id'], + first=True, to_dict=False) + if existed is None: + CITypeGroupItem.create(flush=True, **payload) + else: + existed.update(flush=True, **payload) + + try: + db.session.commit() + except Exception as e: + db.session.rollback() + raise Exception(str(e)) + + def _import_relation_types(self, relation_types): + return self.__import(RelationType, relation_types) @staticmethod - def _import_type_attributes(type2attributes): - # add type attribute + def _import_ci_type_relations(ci_type_relations, type_id_map, relation_type_id_map): + for i in ci_type_relations: + i.pop('parent', None) + i.pop('child', None) + i.pop('relation_type', None) + + i['parent_id'] = type_id_map.get(i['parent_id'], i['parent_id']) + i['child_id'] = type_id_map.get(i['child_id'], i['child_id']) + i['relation_type_id'] = relation_type_id_map.get(i['relation_type_id'], i['relation_type_id']) + + try: + CITypeRelationManager.add(i.get('parent_id'), + i.get('child_id'), + i.get('relation_type_id'), + i.get('constraint'), + ) + except BadRequest: + pass + + @staticmethod + def _import_type_attributes(type2attributes, type_id_map, attr_id_map): + for type_id in type2attributes: + CITypeAttributesCache.clean(type_id_map.get(int(type_id), type_id)) for type_id in type2attributes: - existed = CITypeAttribute.get_by(type_id=type_id, to_dict=False) - existed_attr_ids = {i.attr_id: i for i in existed} - new_attr_ids = {i['id']: i for i in type2attributes[type_id]} + existed = CITypeAttributesCache.get2(type_id_map.get(int(type_id), type_id)) + existed_attr_names = {attr.name: ta for ta, attr in existed} + handled = set() for attr in type2attributes[type_id]: - payload = dict(type_id=type_id, - attr_id=attr['id'], + payload = dict(type_id=type_id_map.get(int(type_id), type_id), + attr_id=attr_id_map.get(attr['id'], attr['id']), default_show=attr['default_show'], is_required=attr['is_required'], order=attr['order']) - if attr['id'] not in existed_attr_ids: # new - CITypeAttribute.create(flush=True, **payload) - else: # update - existed_attr_ids[attr['id']].update(**payload) + if attr['name'] not in handled: + if attr['name'] not in existed_attr_names: # new + CITypeAttribute.create(flush=True, **payload) + else: # update + existed_attr_names[attr['name']].update(flush=True, **payload) - # delete - for i in existed: - if i.attr_id not in new_attr_ids: - i.soft_delete() + handled.add(attr['name']) + + try: + db.session.commit() + except Exception as e: + db.session.rollback() + raise Exception(str(e)) + + for type_id in type2attributes: + CITypeAttributesCache.clean(type_id_map.get(int(type_id), type_id)) @staticmethod - def _import_attribute_group(type2attribute_group): + def _import_attribute_group(type2attribute_group, type_id_map, attr_id_map): for type_id in type2attribute_group: - existed = CITypeAttributeGroup.get_by(type_id=type_id, to_dict=False) - for i in existed: - i.soft_delete() - for group in type2attribute_group[type_id] or []: _group = copy.deepcopy(group) _group.pop('attributes', None) _group.pop('id', None) - new = CITypeAttributeGroup.create(**_group) + existed = CITypeAttributeGroup.get_by(name=_group['name'], + type_id=type_id_map.get(_group['type_id'], _group['type_id']), + first=True, to_dict=False) + if existed is None: + _group['type_id'] = type_id_map.get(_group['type_id'], _group['type_id']) - existed = CITypeAttributeGroupItem.get_by(group_id=new.id, to_dict=False) - for i in existed: - i.soft_delete() + existed = CITypeAttributeGroup.create(flush=True, **_group) for order, attr in enumerate(group['attributes'] or []): - CITypeAttributeGroupItem.create(group_id=new.id, attr_id=attr['id'], order=order) + item_existed = CITypeAttributeGroupItem.get_by(group_id=existed.id, + attr_id=attr_id_map.get(attr['id'], attr['id']), + first=True, to_dict=False) + if item_existed is None: + CITypeAttributeGroupItem.create(group_id=existed.id, + attr_id=attr_id_map.get(attr['id'], attr['id']), + order=order) + else: + item_existed.update(flush=True, order=order) + + try: + db.session.commit() + except Exception as e: + db.session.rollback() + raise Exception(str(e)) @staticmethod def _import_auto_discovery_rules(rules): from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryRuleCRUD from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCITypeCRUD + for rule in rules: ci_type = CITypeCache.get(rule.pop('type_name', None)) + adr = rule.pop('adr', {}) or {} + if ci_type: rule['type_id'] = ci_type.id if rule.get('adr_name'): ad_rule = AutoDiscoveryRuleCRUD.get_by_name(rule.pop("adr_name")) + adr.pop('created_at', None) + adr.pop('updated_at', None) + adr.pop('id', None) + if ad_rule: rule['adr_id'] = ad_rule.id + ad_rule.update(**adr) + + elif adr: + ad_rule = AutoDiscoveryRuleCRUD().add(**adr) + rule['adr_id'] = ad_rule.id + else: + continue rule.pop("id", None) rule.pop("created_at", None) rule.pop("updated_at", None) rule['uid'] = current_user.uid - try: - AutoDiscoveryCITypeCRUD.add(**rule) - except Exception as e: - current_app.logger.warning("import auto discovery rules failed: {}".format(e)) + + existed = False + for i in AutoDiscoveryCIType.get_by(type_id=ci_type.id, adr_id=rule['adr_id'], to_dict=False): + if ((i.extra_option or {}).get('alias') or None) == ( + (rule.get('extra_option') or {}).get('alias') or None): + existed = True + AutoDiscoveryCITypeCRUD().update(i.id, **rule) + break + + if not existed: + try: + AutoDiscoveryCITypeCRUD().add(**rule) + except Exception as e: + current_app.logger.warning("import auto discovery rules failed: {}".format(e)) + + @staticmethod + def _import_icons(icons): + from api.lib.common_setting.upload_file import CommonFileCRUD + for icon_name in icons: + if icons[icon_name]: + try: + CommonFileCRUD().save_str_to_file(icon_name, icons[icon_name]) + except Exception as e: + current_app.logger.warning("save icon failed: {}".format(e)) def import_template(self, tpt): import time s = time.time() - self._import_attributes(tpt.get('type2attributes') or {}) + attr_id_map = self._import_attributes(tpt.get('type2attributes') or {}) current_app.logger.info('import attributes cost: {}'.format(time.time() - s)) s = time.time() - self._import_ci_types(tpt.get('ci_types') or []) + ci_type_id_map = self._import_ci_types(tpt.get('ci_types') or [], attr_id_map) current_app.logger.info('import ci_types cost: {}'.format(time.time() - s)) s = time.time() - self._import_ci_type_groups(tpt.get('ci_type_groups') or []) + self._import_ci_type_groups(tpt.get('ci_type_groups') or [], ci_type_id_map) current_app.logger.info('import ci_type_groups cost: {}'.format(time.time() - s)) s = time.time() - self._import_relation_types(tpt.get('relation_types') or []) + relation_type_id_map = self._import_relation_types(tpt.get('relation_types') or []) current_app.logger.info('import relation_types cost: {}'.format(time.time() - s)) s = time.time() - self._import_ci_type_relations(tpt.get('ci_type_relations') or []) + self._import_ci_type_relations(tpt.get('ci_type_relations') or [], ci_type_id_map, relation_type_id_map) current_app.logger.info('import ci_type_relations cost: {}'.format(time.time() - s)) s = time.time() - self._import_type_attributes(tpt.get('type2attributes') or {}) + self._import_type_attributes(tpt.get('type2attributes') or {}, ci_type_id_map, attr_id_map) current_app.logger.info('import type2attributes cost: {}'.format(time.time() - s)) s = time.time() - self._import_attribute_group(tpt.get('type2attribute_group') or {}) + self._import_attribute_group(tpt.get('type2attribute_group') or {}, ci_type_id_map, attr_id_map) current_app.logger.info('import type2attribute_group cost: {}'.format(time.time() - s)) s = time.time() self._import_auto_discovery_rules(tpt.get('ci_type_auto_discovery_rules') or []) current_app.logger.info('import ci_type_auto_discovery_rules cost: {}'.format(time.time() - s)) + s = time.time() + self._import_icons(tpt.get('icons') or {}) + current_app.logger.info('import icons cost: {}'.format(time.time() - s)) + @staticmethod def export_template(): from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCITypeCRUD from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryRuleCRUD + from api.lib.common_setting.upload_file import CommonFileCRUD + + tpt = dict( + ci_types=CITypeManager.get_ci_types(), + ci_type_groups=CITypeGroupManager.get(), + relation_types=[i.to_dict() for i in RelationTypeManager.get_all()], + ci_type_relations=CITypeRelationManager.get(), + ci_type_auto_discovery_rules=list(), + type2attributes=dict(), + type2attribute_group=dict(), + icons=dict() + ) + + def get_icon_value(icon): + try: + return CommonFileCRUD().get_file_binary_str(icon) + except: + return "" ad_rules = AutoDiscoveryCITypeCRUD.get_all() rules = [] @@ -1116,23 +1229,91 @@ class CITypeTemplateManager(object): if r.get('adr_id'): adr = AutoDiscoveryRuleCRUD.get_by_id(r.pop('adr_id')) r['adr_name'] = adr and adr.name + r['adr'] = adr and adr.to_dict() or {} + + icon_url = r['adr'].get('option', {}).get('icon', {}).get('url') + if icon_url and icon_url not in tpt['icons']: + tpt['icons'][icon_url] = get_icon_value(icon_url) rules.append(r) - tpt = dict( - ci_types=CITypeManager.get_ci_types(), - ci_type_groups=CITypeGroupManager.get(), - relation_types=[i.to_dict() for i in RelationTypeManager.get_all()], - ci_type_relations=CITypeRelationManager.get(), - ci_type_auto_discovery_rules=rules, - type2attributes=dict(), - type2attribute_group=dict() - ) + tpt['ci_type_auto_discovery_rules'] = rules for ci_type in tpt['ci_types']: + if ci_type['icon'] and len(ci_type['icon'].split('$$')) > 3: + icon_url = ci_type['icon'].split('$$')[3] + if icon_url not in tpt['icons']: + tpt['icons'][icon_url] = get_icon_value(icon_url) + tpt['type2attributes'][ci_type['id']] = CITypeAttributeManager.get_attributes_by_type_id( ci_type['id'], choice_web_hook_parse=False, choice_other_parse=False) + for attr in tpt['type2attributes'][ci_type['id']]: + for i in (attr.get('choice_value') or []): + if (i[1] or {}).get('icon', {}).get('url') and len(i[1]['icon']['url'].split('$$')) > 3: + icon_url = i[1]['icon']['url'].split('$$')[3] + if icon_url not in tpt['icons']: + tpt['icons'][icon_url] = get_icon_value(icon_url) + + tpt['type2attribute_group'][ci_type['id']] = CITypeAttributeGroupManager.get_by_type_id(ci_type['id']) + + return tpt + + @staticmethod + def export_template_by_type(type_id): + ci_type = CITypeCache.get(type_id) or abort(404, ErrFormat.ci_type_not_found2.format("id={}".format(type_id))) + + from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCITypeCRUD + from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryRuleCRUD + from api.lib.common_setting.upload_file import CommonFileCRUD + + tpt = dict( + ci_types=CITypeManager.get_ci_types(type_name=ci_type.name), + ci_type_auto_discovery_rules=list(), + type2attributes=dict(), + type2attribute_group=dict(), + icons=dict() + ) + + def get_icon_value(icon): + try: + return CommonFileCRUD().get_file_binary_str(icon) + except: + return "" + + ad_rules = AutoDiscoveryCITypeCRUD.get_by_type_id(ci_type.id) + rules = [] + for r in ad_rules: + r = r.to_dict() + r['type_name'] = ci_type and ci_type.name + if r.get('adr_id'): + adr = AutoDiscoveryRuleCRUD.get_by_id(r.pop('adr_id')) + r['adr_name'] = adr and adr.name + r['adr'] = adr and adr.to_dict() or {} + + icon_url = r['adr'].get('option', {}).get('icon', {}).get('url') + if icon_url and icon_url not in tpt['icons']: + tpt['icons'][icon_url] = get_icon_value(icon_url) + + rules.append(r) + tpt['ci_type_auto_discovery_rules'] = rules + + for ci_type in tpt['ci_types']: + if ci_type['icon'] and len(ci_type['icon'].split('$$')) > 3: + icon_url = ci_type['icon'].split('$$')[3] + if icon_url not in tpt['icons']: + tpt['icons'][icon_url] = get_icon_value(icon_url) + + tpt['type2attributes'][ci_type['id']] = CITypeAttributeManager.get_attributes_by_type_id( + ci_type['id'], choice_web_hook_parse=False, choice_other_parse=False) + + for attr in tpt['type2attributes'][ci_type['id']]: + for i in (attr.get('choice_value') or []): + if (i[1] or {}).get('icon', {}).get('url') and len(i[1]['icon']['url'].split('$$')) > 3: + icon_url = i[1]['icon']['url'].split('$$')[3] + if icon_url not in tpt['icons']: + tpt['icons'][icon_url] = get_icon_value(icon_url) + tpt['type2attribute_group'][ci_type['id']] = CITypeAttributeGroupManager.get_by_type_id(ci_type['id']) return tpt diff --git a/cmdb-api/api/lib/cmdb/const.py b/cmdb-api/api/lib/cmdb/const.py index c48fd1b..31cc6e5 100644 --- a/cmdb-api/api/lib/cmdb/const.py +++ b/cmdb-api/api/lib/cmdb/const.py @@ -69,6 +69,7 @@ class ResourceTypeEnum(BaseEnum): CI_TYPE_RELATION = "CITypeRelation" # create/delete/grant RELATION_VIEW = "RelationView" # read/update/delete/grant CI_FILTER = "CIFilter" # read + PAGE = "page" # read class PermEnum(BaseEnum): diff --git a/cmdb-api/api/lib/cmdb/perms.py b/cmdb-api/api/lib/cmdb/perms.py index 3b5ad6f..f034f37 100644 --- a/cmdb-api/api/lib/cmdb/perms.py +++ b/cmdb-api/api/lib/cmdb/perms.py @@ -143,11 +143,14 @@ class CIFilterPermsCRUD(DBMixin): first=True, to_dict=False) if obj is not None: + resource = None if current_app.config.get('USE_ACL'): - ACLManager().del_resource(str(obj.id), ResourceTypeEnum.CI_FILTER) + resource = ACLManager().del_resource(str(obj.id), ResourceTypeEnum.CI_FILTER) obj.soft_delete() + return resource + def has_perm_for_ci(arg_name, resource_type, perm, callback=None, app=None): def decorator_has_perm(func): diff --git a/cmdb-api/api/lib/cmdb/resp_format.py b/cmdb-api/api/lib/cmdb/resp_format.py index b7a3495..12a532f 100644 --- a/cmdb-api/api/lib/cmdb/resp_format.py +++ b/cmdb-api/api/lib/cmdb/resp_format.py @@ -4,6 +4,8 @@ from api.lib.resp_format import CommonErrFormat class ErrFormat(CommonErrFormat): + ci_type_config = "模型配置" + invalid_relation_type = "无效的关系类型: {}" ci_type_not_found = "模型不存在!" argument_attributes_must_be_list = "参数 attributes 类型必须是列表" diff --git a/cmdb-api/api/lib/perm/acl/acl.py b/cmdb-api/api/lib/perm/acl/acl.py index 423d3be..fbb2642 100644 --- a/cmdb-api/api/lib/perm/acl/acl.py +++ b/cmdb-api/api/lib/perm/acl/acl.py @@ -117,15 +117,15 @@ 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): + def grant_resource_to_role_by_rid(self, name, rid, resource_type_name=None, permissions=None, rebuild=True): resource = self._get_resource(name, resource_type_name) if resource: - PermissionCRUD.grant(rid, permissions, resource_id=resource.id) + PermissionCRUD.grant(rid, permissions, resource_id=resource.id, rebuild=rebuild) else: group = self._get_resource_group(name) if group: - PermissionCRUD.grant(rid, permissions, group_id=group.id) + PermissionCRUD.grant(rid, permissions, group_id=group.id, rebuild=rebuild) def revoke_resource_from_role(self, name, role, resource_type_name=None, permissions=None): resource = self._get_resource(name, resource_type_name) @@ -138,21 +138,23 @@ class ACLManager(object): 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): + def revoke_resource_from_role_by_rid(self, name, rid, resource_type_name=None, permissions=None, rebuild=True): resource = self._get_resource(name, resource_type_name) if resource: - PermissionCRUD.revoke(rid, permissions, resource_id=resource.id) + PermissionCRUD.revoke(rid, permissions, resource_id=resource.id, rebuild=rebuild) else: group = self._get_resource_group(name) if group: - PermissionCRUD.revoke(rid, permissions, group_id=group.id) + PermissionCRUD.revoke(rid, permissions, group_id=group.id, rebuild=rebuild) def del_resource(self, name, resource_type_name=None): resource = self._get_resource(name, resource_type_name) if resource: ResourceCRUD.delete(resource.id) + return resource + def has_permission(self, resource_name, resource_type, perm, resource_id=None): if is_app_admin(self.app_id): return True diff --git a/cmdb-api/api/lib/perm/acl/audit.py b/cmdb-api/api/lib/perm/acl/audit.py index bfce104..f741345 100644 --- a/cmdb-api/api/lib/perm/acl/audit.py +++ b/cmdb-api/api/lib/perm/acl/audit.py @@ -394,4 +394,10 @@ class AuditCRUD(object): if logout_at is None: payload['login_at'] = datetime.datetime.now() + try: + from api.lib.common_setting.employee import EmployeeCRUD + EmployeeCRUD.update_last_login_by_uid(current_user.uid) + except: + pass + return AuditLoginLog.create(**payload).id diff --git a/cmdb-api/api/lib/perm/acl/user.py b/cmdb-api/api/lib/perm/acl/user.py index 1d56b74..d17af58 100644 --- a/cmdb-api/api/lib/perm/acl/user.py +++ b/cmdb-api/api/lib/perm/acl/user.py @@ -63,6 +63,7 @@ class UserCRUD(object): AuditCRUD.add_role_log(None, AuditOperateType.create, AuditScope.user, user.uid, {}, user.to_dict(), {}, {} ) + if add_from != 'common': from api.lib.common_setting.employee import EmployeeCRUD payload = {column: getattr(user, column) for column in ['uid', 'username', 'nickname', 'email', 'block']} diff --git a/cmdb-api/api/models/acl.py b/cmdb-api/api/models/acl.py index d4c9efa..e3396c3 100644 --- a/cmdb-api/api/models/acl.py +++ b/cmdb-api/api/models/acl.py @@ -145,7 +145,7 @@ class User(CRUDModel, SoftDeleteMixin): class RoleQuery(BaseQuery): def authenticate(self, login, password): - role = self.filter(Role.name == login).first() + role = self.filter(Role.name == login).filter(Role.deleted.is_(False)).first() if role: authenticated = role.check_password(password) diff --git a/cmdb-api/api/tasks/acl.py b/cmdb-api/api/tasks/acl.py index 750eb2e..1e86d36 100644 --- a/cmdb-api/api/tasks/acl.py +++ b/cmdb-api/api/tasks/acl.py @@ -29,6 +29,7 @@ from api.models.acl import Trigger name="acl.role_rebuild", queue=ACL_QUEUE, once={"graceful": True, "unlock_before_run": True}) +@flush_db @reconnect_db def role_rebuild(rids, app_id): rids = rids if isinstance(rids, list) else [rids] @@ -190,18 +191,18 @@ def cancel_trigger(_id, resource_id=None, operator_uid=None): @celery.task(name="acl.op_record", queue=ACL_QUEUE) @reconnect_db -def op_record(app, rolename, operate_type, obj): +def op_record(app, role_name, operate_type, obj): if isinstance(app, int): app = AppCache.get(app) app = app and app.name - if isinstance(rolename, int): - u = UserCache.get(rolename) + if isinstance(role_name, int): + u = UserCache.get(role_name) if u: - rolename = u.username + role_name = u.username if not u: - r = RoleCache.get(rolename) + r = RoleCache.get(role_name) if r: - rolename = r.name + role_name = r.name - OperateRecordCRUD.add(app, rolename, operate_type, obj) + OperateRecordCRUD.add(app, role_name, operate_type, obj) diff --git a/cmdb-api/api/views/cmdb/ci_type.py b/cmdb-api/api/views/cmdb/ci_type.py index 1704f1b..c26d2aa 100644 --- a/cmdb-api/api/views/cmdb/ci_type.py +++ b/cmdb-api/api/views/cmdb/ci_type.py @@ -105,7 +105,6 @@ class CITypeGroupView(APIView): return self.jsonify(group.to_dict()) - @role_required(RoleEnum.CONFIG) @args_validate(CITypeGroupManager.cls) def put(self, gid=None): if "/order" in request.url: @@ -167,7 +166,8 @@ class CITypeAttributeView(APIView): t = CITypeCache.get(type_id) or CITypeCache.get(type_name) or abort(404, ErrFormat.ci_type_not_found) type_id = t.id unique_id = t.unique_id - unique = AttributeCache.get(unique_id).name + unique = AttributeCache.get(unique_id) + unique = unique and unique.name attr_filter = CIFilterPermsCRUD.get_attr_filter(type_id) attributes = CITypeAttributeManager.get_attributes_by_type_id(type_id) @@ -319,12 +319,14 @@ class CITypeAttributeGroupView(APIView): class CITypeTemplateView(APIView): - url_prefix = ("/ci_types/template/import", "/ci_types/template/export") + url_prefix = ("/ci_types/template/import", "/ci_types/template/export", "/ci_types//template/export") @role_required(RoleEnum.CONFIG) - def get(self): # export - return self.jsonify( - dict(ci_type_template=CITypeTemplateManager.export_template())) + 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) def post(self): # import @@ -458,11 +460,10 @@ class CITypeGrantView(APIView): _type = CITypeCache.get(type_id) type_name = _type and _type.name or abort(404, ErrFormat.ci_type_not_found) acl = ACLManager('cmdb') - if not acl.has_permission(type_name, ResourceTypeEnum.CI_TYPE, PermEnum.GRANT) and \ - not is_app_admin('cmdb'): + if not acl.has_permission(type_name, ResourceTypeEnum.CI_TYPE, PermEnum.GRANT) and not is_app_admin('cmdb'): return abort(403, ErrFormat.no_permission.format(type_name, PermEnum.GRANT)) - acl.grant_resource_to_role_by_rid(type_name, rid, ResourceTypeEnum.CI_TYPE, perms) + acl.grant_resource_to_role_by_rid(type_name, rid, ResourceTypeEnum.CI_TYPE, perms, rebuild=False) CIFilterPermsCRUD().add(type_id=type_id, rid=rid, **request.values) @@ -482,22 +483,28 @@ class CITypeRevokeView(APIView): _type = CITypeCache.get(type_id) type_name = _type and _type.name or abort(404, ErrFormat.ci_type_not_found) acl = ACLManager('cmdb') - if not acl.has_permission(type_name, ResourceTypeEnum.CI_TYPE, PermEnum.GRANT) and \ - not is_app_admin('cmdb'): + if not acl.has_permission(type_name, ResourceTypeEnum.CI_TYPE, PermEnum.GRANT) and not is_app_admin('cmdb'): return abort(403, ErrFormat.no_permission.format(type_name, PermEnum.GRANT)) - acl.revoke_resource_from_role_by_rid(type_name, rid, ResourceTypeEnum.CI_TYPE, perms) + acl.revoke_resource_from_role_by_rid(type_name, rid, ResourceTypeEnum.CI_TYPE, perms, rebuild=False) + app_id = AppCache.get('cmdb').id + resource = None if PermEnum.READ in perms: - CIFilterPermsCRUD().delete(type_id=type_id, rid=rid) + resource = CIFilterPermsCRUD().delete(type_id=type_id, rid=rid) - app_id = AppCache.get('cmdb').id users = RoleRelationCRUD.get_users_by_rid(rid, app_id) for i in (users or []): if i.get('role', {}).get('id') and not RoleCRUD.has_permission( i.get('role').get('id'), type_name, ResourceTypeEnum.CI_TYPE, app_id, PermEnum.READ): PreferenceManager.delete_by_type_id(type_id, i.get('uid')) + if not resource: + from api.tasks.acl import role_rebuild + from api.lib.perm.acl.const import ACL_QUEUE + + role_rebuild.apply_async(args=(app_id, rid), queue=ACL_QUEUE) + return self.jsonify(type_id=type_id, rid=rid) @@ -507,4 +514,3 @@ class CITypeFilterPermissionView(APIView): @auth_with_app_token def get(self, type_id): return self.jsonify(CIFilterPermsCRUD().get(type_id)) -