From 6843eb57c4a74c51838acfa2fa716707c9ad2a4c Mon Sep 17 00:00:00 2001 From: pycook Date: Tue, 15 Aug 2023 19:47:59 +0800 Subject: [PATCH 1/3] [update] delete roles, users, attributes --- cmdb-api/api/lib/cmdb/attribute.py | 11 +++++++---- cmdb-api/api/lib/cmdb/ci.py | 8 +++++--- cmdb-api/api/lib/cmdb/resp_format.py | 3 ++- cmdb-api/api/lib/database.py | 9 ++++++--- cmdb-api/api/lib/perm/acl/resp_format.py | 1 + cmdb-api/api/lib/perm/acl/role.py | 14 +++++++++----- cmdb-api/api/lib/perm/acl/user.py | 4 ++++ cmdb-api/api/views/cmdb/attribute.py | 3 +++ cmdb-api/api/views/cmdb/auto_discovery.py | 4 ++-- cmdb-api/api/views/cmdb/ci_type.py | 4 ++-- 10 files changed, 41 insertions(+), 20 deletions(-) diff --git a/cmdb-api/api/lib/cmdb/attribute.py b/cmdb-api/api/lib/cmdb/attribute.py index 83f2029..4a8e265 100644 --- a/cmdb-api/api/lib/cmdb/attribute.py +++ b/cmdb-api/api/lib/cmdb/attribute.py @@ -8,6 +8,7 @@ from flask_login import current_user from api.extensions import db from api.lib.cmdb.cache import AttributeCache +from api.lib.cmdb.cache import CITypeCache from api.lib.cmdb.const import CITypeOperateType from api.lib.cmdb.const import PermEnum, ResourceTypeEnum, RoleEnum from api.lib.cmdb.const import ValueTypeEnum @@ -319,7 +320,12 @@ class AttributeManager(object): if CIType.get_by(unique_id=attr.id, first=True, to_dict=False) is not None: return abort(400, ErrFormat.attribute_is_unique_id) - if attr.uid and attr.uid != current_user.uid: + ref = CITypeAttribute.get_by(attr_id=_id, to_dict=False, first=True) + if ref is not None: + ci_type = CITypeCache.get(ref.type_id) + return abort(400, ErrFormat.attribute_is_ref_by_type.format(ci_type.alias)) + + if attr.uid != current_user.uid and not is_app_admin('cmdb'): return abort(403, ErrFormat.cannot_delete_attribute) if attr.is_choice: @@ -331,9 +337,6 @@ class AttributeManager(object): attr.soft_delete() - for i in CITypeAttribute.get_by(attr_id=_id, to_dict=False): - i.soft_delete() - for i in PreferenceShowAttributes.get_by(attr_id=_id, to_dict=False): i.soft_delete() diff --git a/cmdb-api/api/lib/cmdb/ci.py b/cmdb-api/api/lib/cmdb/ci.py index a51bac0..b70f4d2 100644 --- a/cmdb-api/api/lib/cmdb/ci.py +++ b/cmdb-api/api/lib/cmdb/ci.py @@ -38,8 +38,8 @@ from api.lib.decorator import kwargs_required from api.lib.perm.acl.acl import ACLManager from api.lib.perm.acl.acl import is_app_admin from api.lib.perm.acl.acl import validate_permission -from api.lib.utils import handle_arg_list from api.lib.utils import Lock +from api.lib.utils import handle_arg_list from api.models.cmdb import CI from api.models.cmdb import CIRelation from api.models.cmdb import CITypeAttribute @@ -49,6 +49,8 @@ from api.tasks.cmdb import ci_delete from api.tasks.cmdb import ci_relation_cache from api.tasks.cmdb import ci_relation_delete +PRIVILEGED_USERS = {"worker", "cmdb_agent", "agent"} + class CIManager(object): """ manage CI interface @@ -316,7 +318,7 @@ class CIManager(object): ci_attr2type_attr = {type_attr.attr_id: type_attr for type_attr, _ in attrs} ci = None - need_lock = current_user.username not in ("worker", "cmdb_agent", "agent") + need_lock = current_user.username not in current_app.config.get('PRIVILEGED_USERS', PRIVILEGED_USERS) with Lock(ci_type_name, need_lock=need_lock): existed = cls.ci_is_exist(unique_key, unique_value, ci_type.id) if existed is not None: @@ -411,7 +413,7 @@ class CIManager(object): limit_attrs = self._valid_ci_for_no_read(ci) if not _is_admin else {} - need_lock = current_user.username not in ("worker", "cmdb_agent", "agent") + need_lock = current_user.username not in current_app.config.get('PRIVILEGED_USERS', PRIVILEGED_USERS) with Lock(ci.ci_type.name, need_lock=need_lock): self._valid_unique_constraint(ci.type_id, ci_dict, ci_id) diff --git a/cmdb-api/api/lib/cmdb/resp_format.py b/cmdb-api/api/lib/cmdb/resp_format.py index 7474c48..27208ad 100644 --- a/cmdb-api/api/lib/cmdb/resp_format.py +++ b/cmdb-api/api/lib/cmdb/resp_format.py @@ -11,6 +11,7 @@ class ErrFormat(CommonErrFormat): attribute_not_found = "属性 {} 不存在!" attribute_is_unique_id = "该属性是模型的唯一标识,不能被删除!" + attribute_is_ref_by_type = "该属性被模型 {} 引用, 不能删除!" attribute_value_type_cannot_change = "属性的值类型不允许修改!" attribute_list_value_cannot_change = "多值不被允许修改!" attribute_index_cannot_change = "修改索引 非管理员不被允许!" @@ -20,7 +21,7 @@ class ErrFormat(CommonErrFormat): add_attribute_failed = "创建属性 {} 失败!" update_attribute_failed = "修改属性 {} 失败!" cannot_edit_attribute = "您没有权限修改该属性!" - cannot_delete_attribute = "您没有权限删除该属性!" + cannot_delete_attribute = "目前只允许 属性创建人、管理员 删除属性!" attribute_name_cannot_be_builtin = "属性字段名不能是内置字段: id, _id, ci_id, type, _type, ci_type" ci_not_found = "CI {} 不存在" diff --git a/cmdb-api/api/lib/database.py b/cmdb-api/api/lib/database.py index 5145bd4..dfe1fb1 100644 --- a/cmdb-api/api/lib/database.py +++ b/cmdb-api/api/lib/database.py @@ -80,10 +80,10 @@ class CRUDMixin(FormatMixin): db.session.rollback() raise CommitException(str(e)) - def soft_delete(self, flush=False): + def soft_delete(self, flush=False, commit=True): setattr(self, "deleted", True) setattr(self, "deleted_at", datetime.datetime.now()) - self.save(flush=flush) + self.save(flush=flush, commit=commit) @classmethod def get_by_id(cls, _id): @@ -138,8 +138,11 @@ class CRUDMixin(FormatMixin): return result[0] if first and result else (None if first else result) @classmethod - def get_by_like(cls, to_dict=True, **kwargs): + def get_by_like(cls, to_dict=True, deleted=False, **kwargs): query = db.session.query(cls) + if hasattr(cls, "deleted") and deleted is not None: + query = query.filter(cls.deleted.is_(deleted)) + for k, v in kwargs.items(): query = query.filter(getattr(cls, k).ilike('%{0}%'.format(v))) return [i.to_dict() if to_dict else i for i in query] diff --git a/cmdb-api/api/lib/perm/acl/resp_format.py b/cmdb-api/api/lib/perm/acl/resp_format.py index 114010d..25f6bdc 100644 --- a/cmdb-api/api/lib/perm/acl/resp_format.py +++ b/cmdb-api/api/lib/perm/acl/resp_format.py @@ -17,6 +17,7 @@ class ErrFormat(CommonErrFormat): role_exists = "角色 {} 已经存在!" global_role_not_found = "全局角色 {} 不存在!" global_role_exists = "全局角色 {} 已经存在!" + user_role_delete_invalid = "删除用户角色, 请在 用户管理 页面操作!" resource_no_permission = "您没有资源: {} 的 {} 权限" admin_required = "需要管理员权限" diff --git a/cmdb-api/api/lib/perm/acl/role.py b/cmdb-api/api/lib/perm/acl/role.py index 712c7d5..9649913 100644 --- a/cmdb-api/api/lib/perm/acl/role.py +++ b/cmdb-api/api/lib/perm/acl/role.py @@ -285,11 +285,13 @@ class RoleCRUD(object): return role @classmethod - def delete_role(cls, rid): + def delete_role(cls, rid, force=False): from api.lib.perm.acl.acl import is_admin role = Role.get_by_id(rid) or abort(404, ErrFormat.role_not_found.format("rid={}".format(rid))) + not force and role.uid and abort(400, ErrFormat.user_role_delete_invalid) + if not role.app_id and not is_admin(): return abort(403, ErrFormat.admin_required) @@ -301,18 +303,20 @@ class RoleCRUD(object): for i in RoleRelation.get_by(parent_id=rid, to_dict=False): child_ids.append(i.child_id) - i.soft_delete() + i.soft_delete(commit=False) for i in RoleRelation.get_by(child_id=rid, to_dict=False): parent_ids.append(i.parent_id) - i.soft_delete() + i.soft_delete(commit=False) role_permissions = [] for i in RolePermission.get_by(rid=rid, to_dict=False): role_permissions.append(i.to_dict()) - i.soft_delete() + i.soft_delete(commit=False) - role.soft_delete() + role.soft_delete(commit=False) + + db.session.commit() role_rebuild.apply_async(args=(recursive_child_ids, role.app_id), queue=ACL_QUEUE) diff --git a/cmdb-api/api/lib/perm/acl/user.py b/cmdb-api/api/lib/perm/acl/user.py index 9c9141d..942fae7 100644 --- a/cmdb-api/api/lib/perm/acl/user.py +++ b/cmdb-api/api/lib/perm/acl/user.py @@ -107,6 +107,10 @@ class UserCRUD(object): UserCache.clean(user) + role = RoleCRUD.get_by_name(user.username, app_id=None) + if role: + RoleCRUD.delete_role(role[0]['id'], force=True) + AuditCRUD.add_role_log(None, AuditOperateType.delete, AuditScope.user, user.uid, origin, {}, {}, {}) diff --git a/cmdb-api/api/views/cmdb/attribute.py b/cmdb-api/api/views/cmdb/attribute.py index 08864f1..c48de55 100644 --- a/cmdb-api/api/views/cmdb/attribute.py +++ b/cmdb-api/api/views/cmdb/attribute.py @@ -63,6 +63,7 @@ class AttributeView(APIView): current_app.logger.debug(params) attr_id = AttributeManager.add(**params) + return self.jsonify(attr_id=attr_id) @args_validate(AttributeManager.cls) @@ -72,8 +73,10 @@ class AttributeView(APIView): params["choice_value"] = choice_value current_app.logger.debug(params) AttributeManager().update(attr_id, **params) + return self.jsonify(attr_id=attr_id) def delete(self, attr_id): attr_name = AttributeManager.delete(attr_id) + return self.jsonify(message="attribute {0} deleted".format(attr_name)) diff --git a/cmdb-api/api/views/cmdb/auto_discovery.py b/cmdb-api/api/views/cmdb/auto_discovery.py index d10b1c2..958d831 100644 --- a/cmdb-api/api/views/cmdb/auto_discovery.py +++ b/cmdb-api/api/views/cmdb/auto_discovery.py @@ -75,9 +75,9 @@ class AutoDiscoveryRuleTemplateFileView(APIView): return self.send_file(bf, as_attachment=True, - attachment_filename="cmdb_auto_discovery.json", + download_name="cmdb_auto_discovery.json", mimetype='application/json', - cache_timeout=0) + max_age=0) def post(self): f = request.files.get('file') diff --git a/cmdb-api/api/views/cmdb/ci_type.py b/cmdb-api/api/views/cmdb/ci_type.py index 7d748e1..ba2686f 100644 --- a/cmdb-api/api/views/cmdb/ci_type.py +++ b/cmdb-api/api/views/cmdb/ci_type.py @@ -350,9 +350,9 @@ class CITypeTemplateFileView(APIView): return self.send_file(bf, as_attachment=True, - attachment_filename="cmdb_template.json", + download_name="cmdb_template.json", mimetype='application/json', - cache_timeout=0) + max_age=0) @role_required(RoleEnum.CONFIG) def post(self): # import From 7740031bd8ffe97c9a8b76a98077ed47088cde3a Mon Sep 17 00:00:00 2001 From: "hu.sima" Date: Tue, 15 Aug 2023 20:19:45 +0800 Subject: [PATCH 2/3] fix: import_user_from_acl --- cmdb-api/api/commands/init_common_setting.py | 21 ++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/cmdb-api/api/commands/init_common_setting.py b/cmdb-api/api/commands/init_common_setting.py index 88483c1..a26db5a 100644 --- a/cmdb-api/api/commands/init_common_setting.py +++ b/cmdb-api/api/commands/init_common_setting.py @@ -22,13 +22,24 @@ class InitEmployee(object): Import users from ACL """ + InitDepartment().init() acl = ACLManager('acl') user_list = acl.get_all_users() username_list = [e['username'] for e in Employee.get_by()] for user in user_list: + acl_uid = user['uid'] + block = 1 if user['block'] else 0 + acl_rid = self.get_rid_by_uid(acl_uid) if user['username'] in username_list: + existed = Employee.get_by(first=True, username=user['username']) + if existed: + existed.update( + acl_uid=acl_uid, + acl_rid=acl_rid, + block=block, + ) continue try: form = EmployeeAddForm(MultiDict(user)) @@ -36,8 +47,9 @@ class InitEmployee(object): raise Exception( ','.join(['{}: {}'.format(filed, ','.join(msg)) for filed, msg in form.errors.items()])) data = form.data - data['acl_uid'] = user['uid'] - data['block'] = 1 if user['block'] else 0 + data['acl_uid'] = acl_uid + data['acl_rid'] = acl_rid + data['block'] = block data.pop('password') Employee.create( **data @@ -46,6 +58,11 @@ class InitEmployee(object): self.log.error(ErrFormat.acl_import_user_failed.format(user['username'], str(e))) self.log.error(e) + def get_rid_by_uid(self, uid): + from api.models.acl import Role + role = Role.get_by(first=True, uid=uid) + return role['id'] if role is not None else 0 + class InitDepartment(object): def __init__(self): From 3f49586c9f36b63d77c41120942ceddc11215ea9 Mon Sep 17 00:00:00 2001 From: "hu.sima" Date: Tue, 15 Aug 2023 20:45:28 +0800 Subject: [PATCH 3/3] fix: init-import-user-from-acl --- cmdb-api/api/commands/init_common_setting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmdb-api/api/commands/init_common_setting.py b/cmdb-api/api/commands/init_common_setting.py index a26db5a..ea5afc9 100644 --- a/cmdb-api/api/commands/init_common_setting.py +++ b/cmdb-api/api/commands/init_common_setting.py @@ -33,7 +33,7 @@ class InitEmployee(object): block = 1 if user['block'] else 0 acl_rid = self.get_rid_by_uid(acl_uid) if user['username'] in username_list: - existed = Employee.get_by(first=True, username=user['username']) + existed = Employee.get_by(first=True, username=user['username'], to_dict=False) if existed: existed.update( acl_uid=acl_uid,