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/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/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',
}
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()
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/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
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
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:
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
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 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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()