From 6ff77a140cfd1c93475e76766f4b6fa14de1b4b8 Mon Sep 17 00:00:00 2001 From: Evan Sung Date: Fri, 20 Oct 2023 22:38:19 -0500 Subject: [PATCH 1/8] fix(common): fix 'ACLManager' object has no attribute 'create_app' (#217) --- cmdb-api/api/commands/click_common_setting.py | 7 +++---- cmdb-api/api/lib/common_setting/acl.py | 12 +++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/cmdb-api/api/commands/click_common_setting.py b/cmdb-api/api/commands/click_common_setting.py index ffd454f..775548f 100644 --- a/cmdb-api/api/commands/click_common_setting.py +++ b/cmdb-api/api/commands/click_common_setting.py @@ -216,10 +216,9 @@ class InitDepartment(object): ) try: app = acl.validate_app() - if app: - return acl - - acl.create_app(payload) + if not app: + acl.create_app(payload) + return acl except Exception as e: current_app.logger.error(e) if '不存在' in str(e): diff --git a/cmdb-api/api/lib/common_setting/acl.py b/cmdb-api/api/lib/common_setting/acl.py index 3cdf6dc..f705377 100644 --- a/cmdb-api/api/lib/common_setting/acl.py +++ b/cmdb-api/api/lib/common_setting/acl.py @@ -1,13 +1,13 @@ # -*- coding:utf-8 -*- -from flask import abort from flask import current_app from api.lib.common_setting.resp_format import ErrFormat +from api.lib.perm.acl.app import AppCRUD from api.lib.perm.acl.cache import RoleCache, AppCache +from api.lib.perm.acl.permission import PermissionCRUD +from api.lib.perm.acl.resource import ResourceTypeCRUD, ResourceCRUD from api.lib.perm.acl.role import RoleCRUD, RoleRelationCRUD from api.lib.perm.acl.user import UserCRUD -from api.lib.perm.acl.resource import ResourceTypeCRUD, ResourceCRUD -from api.lib.perm.acl.permission import PermissionCRUD class ACLManager(object): @@ -133,3 +133,9 @@ class ACLManager(object): def grant_resource(self, rid, resource_id, perms): PermissionCRUD.grant(rid, perms, resource_id=resource_id, group_id=None) + + @staticmethod + def create_app(payload): + rt = AppCRUD.add(**payload) + + return rt.to_dict() From 05d2795e79eeef306b8dd2b00e5b9068ee876e8b Mon Sep 17 00:00:00 2001 From: pycook Date: Mon, 23 Oct 2023 13:57:06 +0800 Subject: [PATCH 2/8] fix: acl cache --- cmdb-api/api/lib/decorator.py | 6 +++++- cmdb-api/api/lib/perm/acl/cache.py | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/cmdb-api/api/lib/decorator.py b/cmdb-api/api/lib/decorator.py index 185bd3c..26247e6 100644 --- a/cmdb-api/api/lib/decorator.py +++ b/cmdb-api/api/lib/decorator.py @@ -8,6 +8,7 @@ from flask import current_app from flask import request from sqlalchemy.exc import InvalidRequestError from sqlalchemy.exc import OperationalError +from sqlalchemy.exc import PendingRollbackError from sqlalchemy.exc import StatementError from api.extensions import db @@ -98,7 +99,10 @@ def reconnect_db(func): def _flush_db(): - db.session.commit() + try: + db.session.commit() + except (StatementError, OperationalError, InvalidRequestError, PendingRollbackError): + db.session.rollback() def flush_db(func): diff --git a/cmdb-api/api/lib/perm/acl/cache.py b/cmdb-api/api/lib/perm/acl/cache.py index bf45731..7204dca 100644 --- a/cmdb-api/api/lib/perm/acl/cache.py +++ b/cmdb-api/api/lib/perm/acl/cache.py @@ -4,7 +4,7 @@ import msgpack from api.extensions import cache -from api.extensions import db +from api.lib.decorator import flush_db from api.lib.utils import Lock from api.models.acl import App from api.models.acl import Permission @@ -221,9 +221,9 @@ class RoleRelationCache(object): return msgpack.loads(r_g, raw=False) @classmethod + @flush_db def rebuild(cls, rid, app_id): cls.clean(rid, app_id) - db.session.remove() cls.get_parent_ids(rid, app_id) cls.get_child_ids(rid, app_id) @@ -235,9 +235,9 @@ class RoleRelationCache(object): cls.get_resources2(rid, app_id) @classmethod + @flush_db def rebuild2(cls, rid, app_id): cache.delete(cls.PREFIX_RESOURCES2.format(rid, app_id)) - db.session.remove() cls.get_resources2(rid, app_id) @classmethod From e2f993bc1106ed25eac92fc21c5119da64a07fd7 Mon Sep 17 00:00:00 2001 From: pycook Date: Mon, 23 Oct 2023 14:37:01 +0800 Subject: [PATCH 3/8] feat: add cryptography to requirements --- cmdb-api/Pipfile | 1 + cmdb-api/requirements.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/cmdb-api/Pipfile b/cmdb-api/Pipfile index 60206f8..20e831a 100644 --- a/cmdb-api/Pipfile +++ b/cmdb-api/Pipfile @@ -26,6 +26,7 @@ Flask-Bcrypt = "==1.0.1" Flask-Cors = ">=3.0.8" ldap3 = "==2.9.1" pycryptodome = "==3.12.0" +cryptography = "==41.0.2" # Caching Flask-Caching = ">=1.0.0" # Environment variable parsing diff --git a/cmdb-api/requirements.txt b/cmdb-api/requirements.txt index 5df5a7b..8f92c05 100644 --- a/cmdb-api/requirements.txt +++ b/cmdb-api/requirements.txt @@ -30,6 +30,7 @@ more-itertools==5.0.0 msgpack-python==0.5.6 Pillow==9.3.0 pycryptodome==3.12.0 +cryptography==41.0.2 PyJWT==2.4.0 PyMySQL==1.1.0 ldap3==2.9.1 From 0ef67360ad7aacc30b1d1f783095dde2a17ecb5e Mon Sep 17 00:00:00 2001 From: wang-liang0615 <53748875+wang-liang0615@users.noreply.github.com> Date: Tue, 24 Oct 2023 05:59:08 +0800 Subject: [PATCH 4/8] =?UTF-8?q?fix:acl=E6=96=B0=E5=A2=9E=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E5=BC=82=E5=B8=B8=E9=97=AE=E9=A2=98=20(#223)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmdb-ui/src/modules/acl/views/users.vue | 392 ++++++++++++------------ 1 file changed, 198 insertions(+), 194 deletions(-) diff --git a/cmdb-ui/src/modules/acl/views/users.vue b/cmdb-ui/src/modules/acl/views/users.vue index 0c922a8..5f88add 100644 --- a/cmdb-ui/src/modules/acl/views/users.vue +++ b/cmdb-ui/src/modules/acl/views/users.vue @@ -1,194 +1,198 @@ - - - - - + + + + + From d69efeea25e151f1c460cdcbf2cb688bc309a06a Mon Sep 17 00:00:00 2001 From: wang-liang0615 <53748875+wang-liang0615@users.noreply.github.com> Date: Tue, 24 Oct 2023 06:04:21 +0800 Subject: [PATCH 5/8] =?UTF-8?q?fix:=E5=85=B3=E7=B3=BB=E8=A7=86=E5=9B=BE?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=85=B3=E7=B3=BB=E6=8E=A5=E5=8F=A3=E4=BC=A0?= =?UTF-8?q?=E5=8F=82=E4=BF=AE=E6=94=B9=20(#224)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix:acl新增用户展示异常问题 * fix:关系视图删除关系接口传参修改 --- cmdb-ui/src/modules/cmdb/views/relation_views/index.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmdb-ui/src/modules/cmdb/views/relation_views/index.vue b/cmdb-ui/src/modules/cmdb/views/relation_views/index.vue index 24c75d7..b22e385 100644 --- a/cmdb-ui/src/modules/cmdb/views/relation_views/index.vue +++ b/cmdb-ui/src/modules/cmdb/views/relation_views/index.vue @@ -940,10 +940,10 @@ export default { ), onOk() { const _tempTree = that.treeKeys[that.treeKeys.length - 1].split('%') - const firstCIObj = JSON.parse(_tempTree[2]) + const first_ci_id = Number(_tempTree[0]) batchDeleteCIRelation( that.selectedRowKeys.map((item) => item._id), - firstCIObj + [first_ci_id] ).then((res) => { that.$refs.xTable.clearCheckboxRow() that.$refs.xTable.clearCheckboxReserve() From 07a63bef6e4f8e135785baa47ff0cb5b787cc4c8 Mon Sep 17 00:00:00 2001 From: simontigers <47096077+simontigers@users.noreply.github.com> Date: Tue, 24 Oct 2023 14:20:40 +0800 Subject: [PATCH 6/8] fix: add_employee_from_acl (#225) --- cmdb-api/api/lib/common_setting/employee.py | 13 +++++++++++++ cmdb-api/api/lib/perm/acl/user.py | 6 +++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/cmdb-api/api/lib/common_setting/employee.py b/cmdb-api/api/lib/common_setting/employee.py index f2f82e0..19d7f6d 100644 --- a/cmdb-api/api/lib/common_setting/employee.py +++ b/cmdb-api/api/lib/common_setting/employee.py @@ -121,6 +121,19 @@ class EmployeeCRUD(object): employee = CreateEmployee().create_single(**data) return employee.to_dict() + @staticmethod + def add_employee_from_acl_created(**kwargs): + try: + kwargs['acl_uid'] = kwargs.pop('uid') + kwargs['acl_rid'] = kwargs.pop('rid') + kwargs['department_id'] = 0 + + Employee.create( + **kwargs + ) + except Exception as e: + abort(400, str(e)) + @staticmethod def add(**kwargs): try: diff --git a/cmdb-api/api/lib/perm/acl/user.py b/cmdb-api/api/lib/perm/acl/user.py index bcf379e..d6f2fdd 100644 --- a/cmdb-api/api/lib/perm/acl/user.py +++ b/cmdb-api/api/lib/perm/acl/user.py @@ -58,10 +58,14 @@ class UserCRUD(object): kwargs['employee_id'] = '{0:04d}'.format(biggest_employee_id + 1) user = User.create(**kwargs) - RoleCRUD.add_role(user.username, uid=user.uid) + role = RoleCRUD.add_role(user.username, uid=user.uid) AuditCRUD.add_role_log(None, AuditOperateType.create, AuditScope.user, user.uid, {}, user.to_dict(), {}, {} ) + from api.lib.common_setting.employee import EmployeeCRUD + payload = {column: getattr(user, column) for column in ['uid', 'username', 'nickname', 'email', 'block']} + payload['rid'] = role.id + EmployeeCRUD.add_employee_from_acl_created(**payload) return user From a6eb2f0d21ed4a8f6e029d659e2467736ff430f3 Mon Sep 17 00:00:00 2001 From: pycook Date: Tue, 24 Oct 2023 19:32:43 +0800 Subject: [PATCH 7/8] feat: Predefined values support executing scripts (#227) --- cmdb-api/api/lib/cmdb/attribute.py | 68 +++++++++++++------- cmdb-api/api/lib/cmdb/search/ci/db/search.py | 7 +- cmdb-api/api/lib/cmdb/utils.py | 12 ++-- 3 files changed, 57 insertions(+), 30 deletions(-) diff --git a/cmdb-api/api/lib/cmdb/attribute.py b/cmdb-api/api/lib/cmdb/attribute.py index 2384d5c..1380f14 100644 --- a/cmdb-api/api/lib/cmdb/attribute.py +++ b/cmdb-api/api/lib/cmdb/attribute.py @@ -60,22 +60,33 @@ class AttributeManager(object): return [] @staticmethod - def _get_choice_values_from_other_ci(choice_other): + def _get_choice_values_from_other(choice_other): from api.lib.cmdb.search import SearchError from api.lib.cmdb.search.ci import search - type_ids = choice_other.get('type_ids') - attr_id = choice_other.get('attr_id') - other_filter = choice_other.get('filter') or '' + if choice_other.get('type_ids'): + type_ids = choice_other.get('type_ids') + attr_id = choice_other.get('attr_id') + other_filter = choice_other.get('filter') or '' - query = "_type:({}),{}".format(";".join(map(str, type_ids)), other_filter) - s = search(query, fl=[str(attr_id)], facet=[str(attr_id)], count=1) - try: - _, _, _, _, _, facet = s.search() - return [[i[0], {}] for i in (list(facet.values()) or [[]])[0]] - except SearchError as e: - current_app.logger.error("get choice values from other ci failed: {}".format(e)) - return [] + query = "_type:({}),{}".format(";".join(map(str, type_ids)), other_filter) + s = search(query, fl=[str(attr_id)], facet=[str(attr_id)], count=1) + try: + _, _, _, _, _, facet = s.search() + return [[i[0], {}] for i in (list(facet.values()) or [[]])[0]] + except SearchError as e: + current_app.logger.error("get choice values from other ci failed: {}".format(e)) + return [] + + elif choice_other.get('script'): + try: + x = compile(choice_other['script'], '', "exec") + exec(x) + res = locals()['ChoiceValue']().values() or [] + return [[i, {}] for i in res] + except Exception as e: + current_app.logger.error("get choice values from script: {}".format(e)) + return [] @classmethod def get_choice_values(cls, attr_id, value_type, choice_web_hook, choice_other, @@ -87,7 +98,7 @@ class AttributeManager(object): return [] elif choice_other: if choice_other_parse and isinstance(choice_other, dict): - return cls._get_choice_values_from_other_ci(choice_other) + return cls._get_choice_values_from_other(choice_other) else: return [] @@ -96,7 +107,8 @@ class AttributeManager(object): return [] choice_values = choice_table.get_by(fl=["value", "option"], attr_id=attr_id) - return [[choice_value['value'], choice_value['option']] for choice_value in choice_values] + return [[ValueTypeMap.serialize[value_type](choice_value['value']), choice_value['option']] + for choice_value in choice_values] @staticmethod def add_choice_values(_id, value_type, choice_values): @@ -218,10 +230,15 @@ class AttributeManager(object): if name in BUILTIN_KEYWORDS: return abort(400, ErrFormat.attribute_name_cannot_be_builtin) - if kwargs.get('choice_other'): - if (not isinstance(kwargs['choice_other'], dict) or not kwargs['choice_other'].get('type_ids') or - not kwargs['choice_other'].get('attr_id')): - return abort(400, ErrFormat.attribute_choice_other_invalid) + while kwargs.get('choice_other'): + if isinstance(kwargs['choice_other'], dict): + if kwargs['choice_other'].get('script'): + break + + if kwargs['choice_other'].get('type_ids') and kwargs['choice_other'].get('attr_id'): + break + + return abort(400, ErrFormat.attribute_choice_other_invalid) alias = kwargs.pop("alias", "") alias = name if not alias else alias @@ -232,6 +249,8 @@ class AttributeManager(object): kwargs.get('is_computed') and cls.can_create_computed_attribute() + kwargs.get('choice_other') and kwargs['choice_other'].get('script') and cls.can_create_computed_attribute() + attr = Attribute.create(flush=True, name=name, alias=alias, @@ -337,10 +356,15 @@ class AttributeManager(object): self._change_index(attr, attr.is_index, kwargs['is_index']) - if kwargs.get('choice_other'): - if (not isinstance(kwargs['choice_other'], dict) or not kwargs['choice_other'].get('type_ids') or - not kwargs['choice_other'].get('attr_id')): - return abort(400, ErrFormat.attribute_choice_other_invalid) + while kwargs.get('choice_other'): + if isinstance(kwargs['choice_other'], dict): + if kwargs['choice_other'].get('script'): + break + + if kwargs['choice_other'].get('type_ids') and kwargs['choice_other'].get('attr_id'): + break + + return abort(400, ErrFormat.attribute_choice_other_invalid) existed2 = attr.to_dict() if not existed2['choice_web_hook'] and not existed2.get('choice_other') and existed2['is_choice']: diff --git a/cmdb-api/api/lib/cmdb/search/ci/db/search.py b/cmdb-api/api/lib/cmdb/search/ci/db/search.py index 0e26453..fee7251 100644 --- a/cmdb-api/api/lib/cmdb/search/ci/db/search.py +++ b/cmdb-api/api/lib/cmdb/search/ci/db/search.py @@ -28,6 +28,7 @@ from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_NO_ATTR from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_TYPE from api.lib.cmdb.search.ci.db.query_sql import QUERY_UNION_CI_ATTRIBUTE_IS_NULL from api.lib.cmdb.utils import TableMap +from api.lib.cmdb.utils import ValueTypeMap from api.lib.perm.acl.acl import ACLManager from api.lib.perm.acl.acl import is_app_admin from api.lib.utils import handle_arg_list @@ -524,15 +525,15 @@ class Search(object): if k: table_name = TableMap(attr=attr).table_name query_sql = FACET_QUERY.format(table_name, self.query_sql, attr.id) - # current_app.logger.warning(query_sql) result = db.session.execute(query_sql).fetchall() facet[k] = result facet_result = dict() for k, v in facet.items(): if not k.startswith('_'): - a = getattr(AttributeCache.get(k), self.ret_key) - facet_result[a] = [(f[0], f[1], a) for f in v] + attr = AttributeCache.get(k) + a = getattr(attr, self.ret_key) + facet_result[a] = [(ValueTypeMap.serialize[attr.value_type](f[0]), f[1], a) for f in v] return facet_result diff --git a/cmdb-api/api/lib/cmdb/utils.py b/cmdb-api/api/lib/cmdb/utils.py index c6d4630..e529670 100644 --- a/cmdb-api/api/lib/cmdb/utils.py +++ b/cmdb-api/api/lib/cmdb/utils.py @@ -12,7 +12,7 @@ import api.models.cmdb as model from api.lib.cmdb.cache import AttributeCache from api.lib.cmdb.const import ValueTypeEnum -TIME_RE = re.compile(r"^(20|21|22|23|[0-1]\d):[0-5]\d:[0-5]\d$") +TIME_RE = re.compile(r"^20|21|22|23|[0-1]\d:[0-5]\d:[0-5]\d$") def string2int(x): @@ -21,7 +21,7 @@ def string2int(x): def str2datetime(x): try: - return datetime.datetime.strptime(x, "%Y-%m-%d") + return datetime.datetime.strptime(x, "%Y-%m-%d").date() except ValueError: pass @@ -44,8 +44,8 @@ class ValueTypeMap(object): ValueTypeEnum.FLOAT: float, ValueTypeEnum.TEXT: lambda x: x if isinstance(x, six.string_types) else str(x), ValueTypeEnum.TIME: lambda x: x if isinstance(x, six.string_types) else str(x), - ValueTypeEnum.DATE: lambda x: x.strftime("%Y-%m-%d"), - ValueTypeEnum.DATETIME: lambda x: x.strftime("%Y-%m-%d %H:%M:%S"), + ValueTypeEnum.DATE: lambda x: x.strftime("%Y-%m-%d") if not isinstance(x, six.string_types) else x, + ValueTypeEnum.DATETIME: lambda x: x.strftime("%Y-%m-%d %H:%M:%S") if not isinstance(x, six.string_types) else x, ValueTypeEnum.JSON: lambda x: json.loads(x) if isinstance(x, six.string_types) and x else x, } @@ -64,6 +64,8 @@ class ValueTypeMap(object): ValueTypeEnum.FLOAT: model.FloatChoice, ValueTypeEnum.TEXT: model.TextChoice, ValueTypeEnum.TIME: model.TextChoice, + ValueTypeEnum.DATE: model.TextChoice, + ValueTypeEnum.DATETIME: model.TextChoice, } table = { @@ -97,7 +99,7 @@ class ValueTypeMap(object): ValueTypeEnum.DATE: 'text', ValueTypeEnum.TIME: 'text', ValueTypeEnum.FLOAT: 'float', - ValueTypeEnum.JSON: 'object' + ValueTypeEnum.JSON: 'object', } From 514353959349dfc8d525a5293ea35e77844756fb Mon Sep 17 00:00:00 2001 From: kdyq007 Date: Tue, 24 Oct 2023 19:47:46 +0800 Subject: [PATCH 8/8] =?UTF-8?q?=E5=85=B3=E9=97=AD=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E5=AF=86=E7=A0=81=E5=8A=A0=E5=AF=86=EF=BC=9B=E5=8A=A0=E5=BC=BA?= =?UTF-8?q?=20ldap=20=E7=94=A8=E6=88=B7=E9=AA=8C=E8=AF=81=20(#216)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [更新] python-ldap 更新到 ldap3 * [更新] 关闭前端密码加密;加强 ldap 用户验证 * Update app.js --------- Co-authored-by: sherlock Co-authored-by: pycook --- cmdb-api/api/models/acl.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmdb-api/api/models/acl.py b/cmdb-api/api/models/acl.py index a96bb1c..b0683ec 100644 --- a/cmdb-api/api/models/acl.py +++ b/cmdb-api/api/models/acl.py @@ -74,6 +74,8 @@ class UserQuery(BaseQuery): conn = Connection(server, user=who, password=password) conn.bind() + if conn.result['result'] != 0: + raise LDAPBindError conn.unbind() if not user: