pref(api): import and export of CIType templates

pref(api): import and export of CIType templates
This commit is contained in:
pycook 2023-12-22 14:32:03 +08:00 committed by GitHub
parent c430515377
commit 20f3e917fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 363 additions and 159 deletions

View File

@ -189,7 +189,8 @@ class AttributeManager(object):
return attr return attr
def get_attribute(self, key, choice_web_hook_parse=True, choice_other_parse=True): 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"): if attr.get("is_choice"):
attr["choice_value"] = self.get_choice_values( attr["choice_value"] = self.get_choice_values(
attr["id"], attr["id"],

View File

@ -5,8 +5,10 @@ import copy
import toposort import toposort
from flask import abort from flask import abort
from flask import current_app from flask import current_app
from flask import session
from flask_login import current_user from flask_login import current_user
from toposort import toposort_flatten from toposort import toposort_flatten
from werkzeug.exceptions import BadRequest
from api.extensions import db from api.extensions import db
from api.lib.cmdb.attribute import AttributeManager from api.lib.cmdb.attribute import AttributeManager
@ -75,12 +77,13 @@ class CITypeManager(object):
def get_ci_types(type_name=None): def get_ci_types(type_name=None):
resources = None resources = None
if current_app.config.get('USE_ACL') and not is_app_admin('cmdb'): 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) ci_types = CIType.get_by() if type_name is None else CIType.get_by_like(name=type_name)
res = list() res = list()
for type_dict in ci_types: 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: if resources is None or type_dict['name'] in resources:
res.append(type_dict) res.append(type_dict)
@ -113,6 +116,9 @@ class CITypeManager(object):
@classmethod @classmethod
@kwargs_required("name") @kwargs_required("name")
def add(cls, **kwargs): 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 = 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) 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) CITypeCache.clean(ci_type.name)
if current_app.config.get("USE_ACL"): if current_app.config.get("USE_ACL"):
try:
ACLManager().add_resource(ci_type.name, ResourceTypeEnum.CI) ACLManager().add_resource(ci_type.name, ResourceTypeEnum.CI)
except BadRequest:
pass
ACLManager().grant_resource_to_role(ci_type.name, ACLManager().grant_resource_to_role(ci_type.name,
RoleEnum.CMDB_READ_ALL, RoleEnum.CMDB_READ_ALL,
ResourceTypeEnum.CI, ResourceTypeEnum.CI,
@ -243,7 +253,6 @@ class CITypeGroupManager(object):
else: else:
resources = {i['name']: i['permissions'] for i in resources if PermEnum.READ in i.get("permissions")} 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) groups = sorted(CITypeGroup.get_by(), key=lambda x: x['order'] or 0)
group_types = set() group_types = set()
for group in groups: for group in groups:
@ -283,7 +292,10 @@ class CITypeGroupManager(object):
""" """
existed = CITypeGroup.get_by_id(gid) or abort( existed = CITypeGroup.get_by_id(gid) or abort(
404, ErrFormat.ci_type_group_not_found.format("id={}".format(gid))) 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) 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]) 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() grouped = list()
attributes = CITypeAttributeManager.get_attributes_by_type_id(type_id) 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: for group in groups:
items = CITypeAttributeGroupItem.get_by(group_id=group["id"], to_dict=False) items = CITypeAttributeGroupItem.get_by(group_id=group["id"], to_dict=False)
@ -891,97 +903,58 @@ class CITypeAttributeGroupManager(object):
class CITypeTemplateManager(object): class CITypeTemplateManager(object):
@staticmethod @staticmethod
def __import(cls, data): def __import(cls, data, unique_key='name'):
id2obj_dicts = {i['id']: i for i in data} id2obj_dicts = {i[unique_key]: i for i in data}
existed = cls.get_by(deleted=None, to_dict=False) existed = cls.get_by(to_dict=False)
id2existed = {i.id: i for i in existed} id2existed = {getattr(i, unique_key): i for i in existed}
existed_ids = [i.id for i in existed] existed_ids = [getattr(i, unique_key) for i in existed]
existed_no_delete_ids = [i.id for i in existed if not i.deleted]
id_map = dict()
# add # add
for added_id in set(id2obj_dicts.keys()) - set(existed_ids): 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: if cls == CIType:
CITypeManager.add(**id2obj_dicts[added_id]) __id = CITypeManager.add(**id2obj_dicts[added_id])
CITypeCache.clean(__id)
elif cls == CITypeRelation: elif cls == CITypeRelation:
CITypeRelationManager.add(id2obj_dicts[added_id].get('parent_id'), __id = CITypeRelationManager.add(id2obj_dicts[added_id].get('parent_id'),
id2obj_dicts[added_id].get('child_id'), id2obj_dicts[added_id].get('child_id'),
id2obj_dicts[added_id].get('relation_type_id'), id2obj_dicts[added_id].get('relation_type_id'),
id2obj_dicts[added_id].get('constraint'), id2obj_dicts[added_id].get('constraint'),
) )
else: 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 # update
for updated_id in set(id2obj_dicts.keys()) & set(existed_ids): for updated_id in set(id2obj_dicts.keys()) & set(existed_ids):
if cls == CIType: _id = id2obj_dicts[updated_id].pop('id', None)
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)
else:
id2existed[updated_id].update(flush=True, **id2obj_dicts[updated_id]) id2existed[updated_id].update(flush=True, **id2obj_dicts[updated_id])
# delete id_map[_id] = id2existed[updated_id].id
for deleted_id in set(existed_no_delete_ids) - set(id2obj_dicts.keys()):
if cls == Attribute:
AttributeCache.clean(id2existed[updated_id])
if cls == CIType: if cls == CIType:
id2existed[deleted_id].soft_delete(flush=True) CITypeCache.clean(id2existed[updated_id].id)
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: try:
db.session.commit() db.session.commit()
except Exception as e: except Exception as e:
db.session.rollback() db.session.rollback()
raise Exception(str(e)) raise Exception(str(e))
def _import_ci_types(self, ci_types): return id_map
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)
def _import_attributes(self, type2attributes): def _import_attributes(self, type2attributes):
attributes = [attr for type_id in type2attributes for attr in type2attributes[type_id]] 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('default_show', None)
i.pop('is_required', None) i.pop('is_required', None)
i.pop('order', 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) choice_value = i.pop('choice_value', None)
if not choice_value:
i['is_choice'] = False
attrs.append((i, choice_value)) 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: for i, choice_value in attrs:
if choice_value: if choice_value and not i.get('choice_web_hook') and not i.get('choice_other'):
AttributeManager.add_choice_values(i['id'], i['value_type'], choice_value) 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 @staticmethod
def _import_type_attributes(type2attributes): def _import_ci_type_relations(ci_type_relations, type_id_map, relation_type_id_map):
# add type attribute 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: for type_id in type2attributes:
existed = CITypeAttribute.get_by(type_id=type_id, to_dict=False) existed = CITypeAttributesCache.get2(type_id_map.get(int(type_id), type_id))
existed_attr_ids = {i.attr_id: i for i in existed} existed_attr_names = {attr.name: ta for ta, attr in existed}
new_attr_ids = {i['id']: i for i in type2attributes[type_id]}
handled = set()
for attr in type2attributes[type_id]: for attr in type2attributes[type_id]:
payload = dict(type_id=type_id, payload = dict(type_id=type_id_map.get(int(type_id), type_id),
attr_id=attr['id'], attr_id=attr_id_map.get(attr['id'], attr['id']),
default_show=attr['default_show'], default_show=attr['default_show'],
is_required=attr['is_required'], is_required=attr['is_required'],
order=attr['order']) order=attr['order'])
if attr['id'] not in existed_attr_ids: # new if attr['name'] not in handled:
if attr['name'] not in existed_attr_names: # new
CITypeAttribute.create(flush=True, **payload) CITypeAttribute.create(flush=True, **payload)
else: # update else: # update
existed_attr_ids[attr['id']].update(**payload) existed_attr_names[attr['name']].update(flush=True, **payload)
# delete handled.add(attr['name'])
for i in existed:
if i.attr_id not in new_attr_ids: try:
i.soft_delete() 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 @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: 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 []: for group in type2attribute_group[type_id] or []:
_group = copy.deepcopy(group) _group = copy.deepcopy(group)
_group.pop('attributes', None) _group.pop('attributes', None)
_group.pop('id', 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) existed = CITypeAttributeGroup.create(flush=True, **_group)
for i in existed:
i.soft_delete()
for order, attr in enumerate(group['attributes'] or []): 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 @staticmethod
def _import_auto_discovery_rules(rules): 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 AutoDiscoveryRuleCRUD
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCITypeCRUD from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCITypeCRUD
for rule in rules: for rule in rules:
ci_type = CITypeCache.get(rule.pop('type_name', None)) ci_type = CITypeCache.get(rule.pop('type_name', None))
adr = rule.pop('adr', {}) or {}
if ci_type: if ci_type:
rule['type_id'] = ci_type.id rule['type_id'] = ci_type.id
if rule.get('adr_name'): if rule.get('adr_name'):
ad_rule = AutoDiscoveryRuleCRUD.get_by_name(rule.pop("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: if ad_rule:
rule['adr_id'] = ad_rule.id 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("id", None)
rule.pop("created_at", None) rule.pop("created_at", None)
rule.pop("updated_at", None) rule.pop("updated_at", None)
rule['uid'] = current_user.uid rule['uid'] = current_user.uid
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: try:
AutoDiscoveryCITypeCRUD.add(**rule) AutoDiscoveryCITypeCRUD().add(**rule)
except Exception as e: except Exception as e:
current_app.logger.warning("import auto discovery rules failed: {}".format(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): def import_template(self, tpt):
import time import time
s = time.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)) current_app.logger.info('import attributes cost: {}'.format(time.time() - s))
s = time.time() 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)) current_app.logger.info('import ci_types cost: {}'.format(time.time() - s))
s = time.time() 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)) current_app.logger.info('import ci_type_groups cost: {}'.format(time.time() - s))
s = time.time() 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)) current_app.logger.info('import relation_types cost: {}'.format(time.time() - s))
s = time.time() 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)) current_app.logger.info('import ci_type_relations cost: {}'.format(time.time() - s))
s = time.time() 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)) current_app.logger.info('import type2attributes cost: {}'.format(time.time() - s))
s = time.time() 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)) current_app.logger.info('import type2attribute_group cost: {}'.format(time.time() - s))
s = time.time() s = time.time()
self._import_auto_discovery_rules(tpt.get('ci_type_auto_discovery_rules') or []) 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)) 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 @staticmethod
def export_template(): def export_template():
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCITypeCRUD from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCITypeCRUD
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryRuleCRUD 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() ad_rules = AutoDiscoveryCITypeCRUD.get_all()
rules = [] rules = []
@ -1116,23 +1229,91 @@ class CITypeTemplateManager(object):
if r.get('adr_id'): if r.get('adr_id'):
adr = AutoDiscoveryRuleCRUD.get_by_id(r.pop('adr_id')) adr = AutoDiscoveryRuleCRUD.get_by_id(r.pop('adr_id'))
r['adr_name'] = adr and adr.name 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) rules.append(r)
tpt = dict( tpt['ci_type_auto_discovery_rules'] = rules
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()
)
for ci_type in tpt['ci_types']: 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( tpt['type2attributes'][ci_type['id']] = CITypeAttributeManager.get_attributes_by_type_id(
ci_type['id'], choice_web_hook_parse=False, choice_other_parse=False) 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']) tpt['type2attribute_group'][ci_type['id']] = CITypeAttributeGroupManager.get_by_type_id(ci_type['id'])
return tpt return tpt

View File

@ -69,6 +69,7 @@ class ResourceTypeEnum(BaseEnum):
CI_TYPE_RELATION = "CITypeRelation" # create/delete/grant CI_TYPE_RELATION = "CITypeRelation" # create/delete/grant
RELATION_VIEW = "RelationView" # read/update/delete/grant RELATION_VIEW = "RelationView" # read/update/delete/grant
CI_FILTER = "CIFilter" # read CI_FILTER = "CIFilter" # read
PAGE = "page" # read
class PermEnum(BaseEnum): class PermEnum(BaseEnum):

View File

@ -143,11 +143,14 @@ class CIFilterPermsCRUD(DBMixin):
first=True, to_dict=False) first=True, to_dict=False)
if obj is not None: if obj is not None:
resource = None
if current_app.config.get('USE_ACL'): 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() obj.soft_delete()
return resource
def has_perm_for_ci(arg_name, resource_type, perm, callback=None, app=None): def has_perm_for_ci(arg_name, resource_type, perm, callback=None, app=None):
def decorator_has_perm(func): def decorator_has_perm(func):

View File

@ -4,6 +4,8 @@ from api.lib.resp_format import CommonErrFormat
class ErrFormat(CommonErrFormat): class ErrFormat(CommonErrFormat):
ci_type_config = "模型配置"
invalid_relation_type = "无效的关系类型: {}" invalid_relation_type = "无效的关系类型: {}"
ci_type_not_found = "模型不存在!" ci_type_not_found = "模型不存在!"
argument_attributes_must_be_list = "参数 attributes 类型必须是列表" argument_attributes_must_be_list = "参数 attributes 类型必须是列表"

View File

@ -117,15 +117,15 @@ class ACLManager(object):
if group: if group:
PermissionCRUD.grant(role.id, permissions, group_id=group.id) 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) resource = self._get_resource(name, resource_type_name)
if resource: if resource:
PermissionCRUD.grant(rid, permissions, resource_id=resource.id) PermissionCRUD.grant(rid, permissions, resource_id=resource.id, rebuild=rebuild)
else: else:
group = self._get_resource_group(name) group = self._get_resource_group(name)
if group: 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): def revoke_resource_from_role(self, name, role, resource_type_name=None, permissions=None):
resource = self._get_resource(name, resource_type_name) resource = self._get_resource(name, resource_type_name)
@ -138,21 +138,23 @@ class ACLManager(object):
if group: if group:
PermissionCRUD.revoke(role.id, permissions, group_id=group.id) 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) resource = self._get_resource(name, resource_type_name)
if resource: if resource:
PermissionCRUD.revoke(rid, permissions, resource_id=resource.id) PermissionCRUD.revoke(rid, permissions, resource_id=resource.id, rebuild=rebuild)
else: else:
group = self._get_resource_group(name) group = self._get_resource_group(name)
if group: 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): def del_resource(self, name, resource_type_name=None):
resource = self._get_resource(name, resource_type_name) resource = self._get_resource(name, resource_type_name)
if resource: if resource:
ResourceCRUD.delete(resource.id) ResourceCRUD.delete(resource.id)
return resource
def has_permission(self, resource_name, resource_type, perm, resource_id=None): def has_permission(self, resource_name, resource_type, perm, resource_id=None):
if is_app_admin(self.app_id): if is_app_admin(self.app_id):
return True return True

View File

@ -394,4 +394,10 @@ class AuditCRUD(object):
if logout_at is None: if logout_at is None:
payload['login_at'] = datetime.datetime.now() 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 return AuditLoginLog.create(**payload).id

View File

@ -63,6 +63,7 @@ class UserCRUD(object):
AuditCRUD.add_role_log(None, AuditOperateType.create, AuditCRUD.add_role_log(None, AuditOperateType.create,
AuditScope.user, user.uid, {}, user.to_dict(), {}, {} AuditScope.user, user.uid, {}, user.to_dict(), {}, {}
) )
if add_from != 'common': if add_from != 'common':
from api.lib.common_setting.employee import EmployeeCRUD from api.lib.common_setting.employee import EmployeeCRUD
payload = {column: getattr(user, column) for column in ['uid', 'username', 'nickname', 'email', 'block']} payload = {column: getattr(user, column) for column in ['uid', 'username', 'nickname', 'email', 'block']}

View File

@ -145,7 +145,7 @@ class User(CRUDModel, SoftDeleteMixin):
class RoleQuery(BaseQuery): class RoleQuery(BaseQuery):
def authenticate(self, login, password): 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: if role:
authenticated = role.check_password(password) authenticated = role.check_password(password)

View File

@ -29,6 +29,7 @@ from api.models.acl import Trigger
name="acl.role_rebuild", name="acl.role_rebuild",
queue=ACL_QUEUE, queue=ACL_QUEUE,
once={"graceful": True, "unlock_before_run": True}) once={"graceful": True, "unlock_before_run": True})
@flush_db
@reconnect_db @reconnect_db
def role_rebuild(rids, app_id): def role_rebuild(rids, app_id):
rids = rids if isinstance(rids, list) else [rids] 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) @celery.task(name="acl.op_record", queue=ACL_QUEUE)
@reconnect_db @reconnect_db
def op_record(app, rolename, operate_type, obj): def op_record(app, role_name, operate_type, obj):
if isinstance(app, int): if isinstance(app, int):
app = AppCache.get(app) app = AppCache.get(app)
app = app and app.name app = app and app.name
if isinstance(rolename, int): if isinstance(role_name, int):
u = UserCache.get(rolename) u = UserCache.get(role_name)
if u: if u:
rolename = u.username role_name = u.username
if not u: if not u:
r = RoleCache.get(rolename) r = RoleCache.get(role_name)
if r: if r:
rolename = r.name role_name = r.name
OperateRecordCRUD.add(app, rolename, operate_type, obj) OperateRecordCRUD.add(app, role_name, operate_type, obj)

View File

@ -105,7 +105,6 @@ class CITypeGroupView(APIView):
return self.jsonify(group.to_dict()) return self.jsonify(group.to_dict())
@role_required(RoleEnum.CONFIG)
@args_validate(CITypeGroupManager.cls) @args_validate(CITypeGroupManager.cls)
def put(self, gid=None): def put(self, gid=None):
if "/order" in request.url: 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) t = CITypeCache.get(type_id) or CITypeCache.get(type_name) or abort(404, ErrFormat.ci_type_not_found)
type_id = t.id type_id = t.id
unique_id = t.unique_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) attr_filter = CIFilterPermsCRUD.get_attr_filter(type_id)
attributes = CITypeAttributeManager.get_attributes_by_type_id(type_id) attributes = CITypeAttributeManager.get_attributes_by_type_id(type_id)
@ -319,12 +319,14 @@ class CITypeAttributeGroupView(APIView):
class CITypeTemplateView(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/<int:type_id>/template/export")
@role_required(RoleEnum.CONFIG) @role_required(RoleEnum.CONFIG)
def get(self): # export def get(self, type_id=None): # export
return self.jsonify( if type_id is not None:
dict(ci_type_template=CITypeTemplateManager.export_template())) 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) @role_required(RoleEnum.CONFIG)
def post(self): # import def post(self): # import
@ -458,11 +460,10 @@ class CITypeGrantView(APIView):
_type = CITypeCache.get(type_id) _type = CITypeCache.get(type_id)
type_name = _type and _type.name or abort(404, ErrFormat.ci_type_not_found) type_name = _type and _type.name or abort(404, ErrFormat.ci_type_not_found)
acl = ACLManager('cmdb') acl = ACLManager('cmdb')
if not acl.has_permission(type_name, ResourceTypeEnum.CI_TYPE, PermEnum.GRANT) and \ if not acl.has_permission(type_name, ResourceTypeEnum.CI_TYPE, PermEnum.GRANT) and not is_app_admin('cmdb'):
not is_app_admin('cmdb'):
return abort(403, ErrFormat.no_permission.format(type_name, PermEnum.GRANT)) 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) CIFilterPermsCRUD().add(type_id=type_id, rid=rid, **request.values)
@ -482,22 +483,28 @@ class CITypeRevokeView(APIView):
_type = CITypeCache.get(type_id) _type = CITypeCache.get(type_id)
type_name = _type and _type.name or abort(404, ErrFormat.ci_type_not_found) type_name = _type and _type.name or abort(404, ErrFormat.ci_type_not_found)
acl = ACLManager('cmdb') acl = ACLManager('cmdb')
if not acl.has_permission(type_name, ResourceTypeEnum.CI_TYPE, PermEnum.GRANT) and \ if not acl.has_permission(type_name, ResourceTypeEnum.CI_TYPE, PermEnum.GRANT) and not is_app_admin('cmdb'):
not is_app_admin('cmdb'):
return abort(403, ErrFormat.no_permission.format(type_name, PermEnum.GRANT)) 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)
if PermEnum.READ in perms:
CIFilterPermsCRUD().delete(type_id=type_id, rid=rid)
app_id = AppCache.get('cmdb').id app_id = AppCache.get('cmdb').id
resource = None
if PermEnum.READ in perms:
resource = CIFilterPermsCRUD().delete(type_id=type_id, rid=rid)
users = RoleRelationCRUD.get_users_by_rid(rid, app_id) users = RoleRelationCRUD.get_users_by_rid(rid, app_id)
for i in (users or []): for i in (users or []):
if i.get('role', {}).get('id') and not RoleCRUD.has_permission( 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): i.get('role').get('id'), type_name, ResourceTypeEnum.CI_TYPE, app_id, PermEnum.READ):
PreferenceManager.delete_by_type_id(type_id, i.get('uid')) 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) return self.jsonify(type_id=type_id, rid=rid)
@ -507,4 +514,3 @@ class CITypeFilterPermissionView(APIView):
@auth_with_app_token @auth_with_app_token
def get(self, type_id): def get(self, type_id):
return self.jsonify(CIFilterPermsCRUD().get(type_id)) return self.jsonify(CIFilterPermsCRUD().get(type_id))