mirror of
https://github.com/veops/cmdb.git
synced 2025-09-11 05:46:54 +08:00
Compare commits
2 Commits
2.3.9
...
deploy_on_
Author | SHA1 | Date | |
---|---|---|---|
|
c1a6adc32c | ||
|
7f49ae3dfb |
@@ -87,7 +87,7 @@ docker-compose up -d
|
|||||||
- 第一步: 先安装 docker 环境, 以及docker-compose
|
- 第一步: 先安装 docker 环境, 以及docker-compose
|
||||||
- 第二步: 直接使用项目根目录下的install.sh 文件进行 `安装`、`启动`、`暂停`、`查状态`、`删除`、`卸载`
|
- 第二步: 直接使用项目根目录下的install.sh 文件进行 `安装`、`启动`、`暂停`、`查状态`、`删除`、`卸载`
|
||||||
```shell
|
```shell
|
||||||
curl -so install.sh https://raw.githubusercontent.com/veops/cmdb/master/install.sh
|
curl -so install.sh https://raw.githubusercontent.com/veops/cmdb/deploy_on_kylin_docker/install.sh
|
||||||
sh install.sh install
|
sh install.sh install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@@ -62,7 +62,6 @@ alembic = "==1.7.7"
|
|||||||
hvac = "==2.0.0"
|
hvac = "==2.0.0"
|
||||||
colorama = ">=0.4.6"
|
colorama = ">=0.4.6"
|
||||||
pycryptodomex = ">=3.19.0"
|
pycryptodomex = ">=3.19.0"
|
||||||
lz4 = ">=4.3.2"
|
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
# Testing
|
# Testing
|
||||||
|
@@ -19,8 +19,7 @@ from flask.json.provider import DefaultJSONProvider
|
|||||||
import api.views.entry
|
import api.views.entry
|
||||||
from api.extensions import (bcrypt, cache, celery, cors, db, es, login_manager, migrate, rd)
|
from api.extensions import (bcrypt, cache, celery, cors, db, es, login_manager, migrate, rd)
|
||||||
from api.extensions import inner_secrets
|
from api.extensions import inner_secrets
|
||||||
from api.lib.perm.authentication.cas import CAS
|
from api.flask_cas import CAS
|
||||||
from api.lib.perm.authentication.oauth2 import OAuth2
|
|
||||||
from api.lib.secrets.secrets import InnerKVManger
|
from api.lib.secrets.secrets import InnerKVManger
|
||||||
from api.models.acl import User
|
from api.models.acl import User
|
||||||
|
|
||||||
@@ -97,7 +96,6 @@ def create_app(config_object="settings"):
|
|||||||
register_shell_context(app)
|
register_shell_context(app)
|
||||||
register_commands(app)
|
register_commands(app)
|
||||||
CAS(app)
|
CAS(app)
|
||||||
OAuth2(app)
|
|
||||||
app.wsgi_app = ReverseProxy(app.wsgi_app)
|
app.wsgi_app = ReverseProxy(app.wsgi_app)
|
||||||
configure_upload_dir(app)
|
configure_upload_dir(app)
|
||||||
|
|
||||||
@@ -194,11 +192,10 @@ def configure_logger(app):
|
|||||||
app.logger.addHandler(handler)
|
app.logger.addHandler(handler)
|
||||||
|
|
||||||
log_file = app.config['LOG_PATH']
|
log_file = app.config['LOG_PATH']
|
||||||
if log_file and log_file != "/dev/stdout":
|
file_handler = RotatingFileHandler(log_file,
|
||||||
file_handler = RotatingFileHandler(log_file,
|
maxBytes=2 ** 30,
|
||||||
maxBytes=2 ** 30,
|
backupCount=7)
|
||||||
backupCount=7)
|
file_handler.setLevel(getattr(logging, app.config['LOG_LEVEL']))
|
||||||
file_handler.setLevel(getattr(logging, app.config['LOG_LEVEL']))
|
file_handler.setFormatter(formatter)
|
||||||
file_handler.setFormatter(formatter)
|
app.logger.addHandler(file_handler)
|
||||||
app.logger.addHandler(file_handler)
|
|
||||||
app.logger.setLevel(getattr(logging, app.config['LOG_LEVEL']))
|
app.logger.setLevel(getattr(logging, app.config['LOG_LEVEL']))
|
||||||
|
@@ -117,15 +117,7 @@ def cmdb_init_acl():
|
|||||||
# 1. add resource type
|
# 1. add resource type
|
||||||
for resource_type in ResourceTypeEnum.all():
|
for resource_type in ResourceTypeEnum.all():
|
||||||
try:
|
try:
|
||||||
perms = PermEnum.all()
|
ResourceTypeCRUD.add(app_id, resource_type, '', PermEnum.all())
|
||||||
if resource_type in (ResourceTypeEnum.CI_FILTER, ResourceTypeEnum.PAGE):
|
|
||||||
perms = [PermEnum.READ]
|
|
||||||
elif resource_type == ResourceTypeEnum.CI_TYPE_RELATION:
|
|
||||||
perms = [PermEnum.ADD, PermEnum.DELETE, PermEnum.GRANT]
|
|
||||||
elif resource_type == ResourceTypeEnum.RELATION_VIEW:
|
|
||||||
perms = [PermEnum.READ, PermEnum.UPDATE, PermEnum.DELETE, PermEnum.GRANT]
|
|
||||||
|
|
||||||
ResourceTypeCRUD.add(app_id, resource_type, '', perms)
|
|
||||||
except AbortException:
|
except AbortException:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@@ -299,20 +299,3 @@ def common_check_new_columns():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error(f"add new column [{column.name}] in table [{table_name}] err:")
|
current_app.logger.error(f"add new column [{column.name}] in table [{table_name}] err:")
|
||||||
current_app.logger.error(e)
|
current_app.logger.error(e)
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
|
||||||
@with_appcontext
|
|
||||||
def common_sync_file_to_db():
|
|
||||||
from api.lib.common_setting.upload_file import CommonFileCRUD
|
|
||||||
CommonFileCRUD.sync_file_to_db()
|
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
|
||||||
@with_appcontext
|
|
||||||
@click.option('--value', type=click.INT, default=-1)
|
|
||||||
def set_auth_auto_redirect_enable(value):
|
|
||||||
if value < 0:
|
|
||||||
return
|
|
||||||
from api.lib.common_setting.common_data import CommonDataCRUD
|
|
||||||
CommonDataCRUD.set_auth_auto_redirect_enable(value)
|
|
||||||
|
@@ -15,7 +15,7 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
from flask import _request_ctx_stack as stack
|
from flask import _request_ctx_stack as stack
|
||||||
|
|
||||||
from . import routing
|
from api.flask_cas import routing
|
||||||
|
|
||||||
|
|
||||||
class CAS(object):
|
class CAS(object):
|
@@ -119,4 +119,4 @@ def create_cas_validate_url(cas_url, cas_route, service, ticket,
|
|||||||
('service', service),
|
('service', service),
|
||||||
('ticket', ticket),
|
('ticket', ticket),
|
||||||
('renew', renew),
|
('renew', renew),
|
||||||
)
|
)
|
@@ -1,24 +1,14 @@
|
|||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
import datetime
|
|
||||||
import uuid
|
import json
|
||||||
|
|
||||||
import bs4
|
import bs4
|
||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
from flask import current_app
|
from flask import current_app, session, request, url_for, redirect
|
||||||
from flask import redirect
|
from flask_login import login_user, logout_user
|
||||||
from flask import request
|
|
||||||
from flask import session
|
|
||||||
from flask import url_for
|
|
||||||
from flask_login import login_user
|
|
||||||
from flask_login import logout_user
|
|
||||||
from six.moves.urllib.parse import urlparse
|
|
||||||
from six.moves.urllib_request import urlopen
|
from six.moves.urllib_request import urlopen
|
||||||
|
|
||||||
from api.lib.common_setting.common_data import AuthenticateDataCRUD
|
|
||||||
from api.lib.common_setting.const import AuthenticateType
|
|
||||||
from api.lib.perm.acl.audit import AuditCRUD
|
|
||||||
from api.lib.perm.acl.cache import UserCache
|
from api.lib.perm.acl.cache import UserCache
|
||||||
from api.lib.perm.acl.resp_format import ErrFormat
|
|
||||||
from .cas_urls import create_cas_login_url
|
from .cas_urls import create_cas_login_url
|
||||||
from .cas_urls import create_cas_logout_url
|
from .cas_urls import create_cas_logout_url
|
||||||
from .cas_urls import create_cas_validate_url
|
from .cas_urls import create_cas_validate_url
|
||||||
@@ -26,7 +16,6 @@ from .cas_urls import create_cas_validate_url
|
|||||||
blueprint = Blueprint('cas', __name__)
|
blueprint = Blueprint('cas', __name__)
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/api/cas/login')
|
|
||||||
@blueprint.route('/api/sso/login')
|
@blueprint.route('/api/sso/login')
|
||||||
def login():
|
def login():
|
||||||
"""
|
"""
|
||||||
@@ -40,20 +29,16 @@ def login():
|
|||||||
If validation was successful the logged in username is saved in
|
If validation was successful the logged in username is saved in
|
||||||
the user's session under the key `CAS_USERNAME_SESSION_KEY`.
|
the user's session under the key `CAS_USERNAME_SESSION_KEY`.
|
||||||
"""
|
"""
|
||||||
config = AuthenticateDataCRUD(AuthenticateType.CAS).get()
|
|
||||||
|
|
||||||
cas_token_session_key = current_app.config['CAS_TOKEN_SESSION_KEY']
|
cas_token_session_key = current_app.config['CAS_TOKEN_SESSION_KEY']
|
||||||
if request.values.get("next"):
|
if request.values.get("next"):
|
||||||
session["next"] = request.values.get("next")
|
session["next"] = request.values.get("next")
|
||||||
|
|
||||||
# _service = url_for('cas.login', _external=True)
|
_service = url_for('cas.login', _external=True, next=session["next"]) \
|
||||||
_service = "{}://{}{}".format(urlparse(request.referrer).scheme,
|
if session.get("next") else url_for('cas.login', _external=True)
|
||||||
urlparse(request.referrer).netloc,
|
|
||||||
url_for('cas.login'))
|
|
||||||
|
|
||||||
redirect_url = create_cas_login_url(
|
redirect_url = create_cas_login_url(
|
||||||
config['cas_server'],
|
current_app.config['CAS_SERVER'],
|
||||||
config['cas_login_route'],
|
current_app.config['CAS_LOGIN_ROUTE'],
|
||||||
_service)
|
_service)
|
||||||
|
|
||||||
if 'ticket' in request.args:
|
if 'ticket' in request.args:
|
||||||
@@ -62,38 +47,30 @@ def login():
|
|||||||
if request.args.get('ticket'):
|
if request.args.get('ticket'):
|
||||||
|
|
||||||
if validate(request.args['ticket']):
|
if validate(request.args['ticket']):
|
||||||
redirect_url = session.get("next") or config.get("cas_after_login") or "/"
|
redirect_url = session.get("next") or \
|
||||||
|
current_app.config.get("CAS_AFTER_LOGIN")
|
||||||
username = session.get("CAS_USERNAME")
|
username = session.get("CAS_USERNAME")
|
||||||
user = UserCache.get(username)
|
user = UserCache.get(username)
|
||||||
login_user(user)
|
login_user(user)
|
||||||
|
|
||||||
session.permanent = True
|
session.permanent = True
|
||||||
|
|
||||||
_id = AuditCRUD.add_login_log(username, True, ErrFormat.login_succeed)
|
|
||||||
session['LOGIN_ID'] = _id
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
del session[cas_token_session_key]
|
del session[cas_token_session_key]
|
||||||
redirect_url = create_cas_login_url(
|
redirect_url = create_cas_login_url(
|
||||||
config['cas_server'],
|
current_app.config['CAS_SERVER'],
|
||||||
config['cas_login_route'],
|
current_app.config['CAS_LOGIN_ROUTE'],
|
||||||
url_for('cas.login', _external=True),
|
url_for('cas.login', _external=True),
|
||||||
renew=True)
|
renew=True)
|
||||||
|
|
||||||
AuditCRUD.add_login_log(session.get("CAS_USERNAME"), False, ErrFormat.invalid_password)
|
|
||||||
|
|
||||||
current_app.logger.info("redirect to: {0}".format(redirect_url))
|
current_app.logger.info("redirect to: {0}".format(redirect_url))
|
||||||
return redirect(redirect_url)
|
return redirect(redirect_url)
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/api/cas/logout')
|
|
||||||
@blueprint.route('/api/sso/logout')
|
@blueprint.route('/api/sso/logout')
|
||||||
def logout():
|
def logout():
|
||||||
"""
|
"""
|
||||||
When the user accesses this route they are logged out.
|
When the user accesses this route they are logged out.
|
||||||
"""
|
"""
|
||||||
config = AuthenticateDataCRUD(AuthenticateType.CAS).get()
|
|
||||||
current_app.logger.info(config)
|
|
||||||
|
|
||||||
cas_username_session_key = current_app.config['CAS_USERNAME_SESSION_KEY']
|
cas_username_session_key = current_app.config['CAS_USERNAME_SESSION_KEY']
|
||||||
cas_token_session_key = current_app.config['CAS_TOKEN_SESSION_KEY']
|
cas_token_session_key = current_app.config['CAS_TOKEN_SESSION_KEY']
|
||||||
@@ -105,14 +82,12 @@ def logout():
|
|||||||
"next" in session and session.pop("next")
|
"next" in session and session.pop("next")
|
||||||
|
|
||||||
redirect_url = create_cas_logout_url(
|
redirect_url = create_cas_logout_url(
|
||||||
config['cas_server'],
|
current_app.config['CAS_SERVER'],
|
||||||
config['cas_logout_route'],
|
current_app.config['CAS_LOGOUT_ROUTE'],
|
||||||
url_for('cas.login', _external=True, next=request.referrer))
|
url_for('cas.login', _external=True, next=request.referrer))
|
||||||
|
|
||||||
logout_user()
|
logout_user()
|
||||||
|
|
||||||
AuditCRUD.add_login_log(None, None, None, _id=session.get('LOGIN_ID'), logout_at=datetime.datetime.now())
|
|
||||||
|
|
||||||
current_app.logger.debug('Redirecting to: {0}'.format(redirect_url))
|
current_app.logger.debug('Redirecting to: {0}'.format(redirect_url))
|
||||||
|
|
||||||
return redirect(redirect_url)
|
return redirect(redirect_url)
|
||||||
@@ -125,15 +100,14 @@ def validate(ticket):
|
|||||||
and the validated username is saved in the session under the
|
and the validated username is saved in the session under the
|
||||||
key `CAS_USERNAME_SESSION_KEY`.
|
key `CAS_USERNAME_SESSION_KEY`.
|
||||||
"""
|
"""
|
||||||
config = AuthenticateDataCRUD(AuthenticateType.CAS).get()
|
|
||||||
|
|
||||||
cas_username_session_key = current_app.config['CAS_USERNAME_SESSION_KEY']
|
cas_username_session_key = current_app.config['CAS_USERNAME_SESSION_KEY']
|
||||||
|
|
||||||
current_app.logger.debug("validating token {0}".format(ticket))
|
current_app.logger.debug("validating token {0}".format(ticket))
|
||||||
|
|
||||||
cas_validate_url = create_cas_validate_url(
|
cas_validate_url = create_cas_validate_url(
|
||||||
config['cas_validate_server'],
|
current_app.config['CAS_VALIDATE_SERVER'],
|
||||||
config['cas_validate_route'],
|
current_app.config['CAS_VALIDATE_ROUTE'],
|
||||||
url_for('cas.login', _external=True),
|
url_for('cas.login', _external=True),
|
||||||
ticket)
|
ticket)
|
||||||
|
|
||||||
@@ -141,35 +115,23 @@ def validate(ticket):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
response = urlopen(cas_validate_url).read()
|
response = urlopen(cas_validate_url).read()
|
||||||
ticket_id = _parse_tag(response, "cas:user")
|
ticketid = _parse_tag(response, "cas:user")
|
||||||
strs = [s.strip() for s in ticket_id.split('|') if s.strip()]
|
strs = [s.strip() for s in ticketid.split('|') if s.strip()]
|
||||||
username, is_valid = None, False
|
username, is_valid = None, False
|
||||||
if len(strs) == 1:
|
if len(strs) == 1:
|
||||||
username = strs[0]
|
username = strs[0]
|
||||||
is_valid = True
|
is_valid = True
|
||||||
|
user_info = json.loads(_parse_tag(response, "cas:other"))
|
||||||
|
current_app.logger.info(user_info)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
current_app.logger.error("CAS returned unexpected result")
|
current_app.logger.error("CAS returned unexpected result")
|
||||||
is_valid = False
|
is_valid = False
|
||||||
return is_valid
|
return is_valid
|
||||||
|
|
||||||
if is_valid:
|
if is_valid:
|
||||||
current_app.logger.debug("{}: {}".format(cas_username_session_key, username))
|
current_app.logger.debug("valid")
|
||||||
session[cas_username_session_key] = username
|
session[cas_username_session_key] = username
|
||||||
user = UserCache.get(username)
|
user = UserCache.get(username)
|
||||||
if user is None:
|
|
||||||
current_app.logger.info("create user: {}".format(username))
|
|
||||||
from api.lib.perm.acl.user import UserCRUD
|
|
||||||
soup = bs4.BeautifulSoup(response)
|
|
||||||
cas_user_map = config.get('cas_user_map')
|
|
||||||
user_dict = dict()
|
|
||||||
for k in cas_user_map:
|
|
||||||
v = soup.find(cas_user_map[k]['tag'], cas_user_map[k].get('attrs', {}))
|
|
||||||
user_dict[k] = v and v.text or None
|
|
||||||
user_dict['password'] = uuid.uuid4().hex
|
|
||||||
if "email" not in user_dict:
|
|
||||||
user_dict['email'] = username
|
|
||||||
|
|
||||||
UserCRUD.add(**user_dict)
|
|
||||||
|
|
||||||
from api.lib.perm.acl.acl import ACLManager
|
from api.lib.perm.acl.acl import ACLManager
|
||||||
user_info = ACLManager.get_user_info(username)
|
user_info = ACLManager.get_user_info(username)
|
||||||
@@ -202,5 +164,4 @@ def _parse_tag(string, tag):
|
|||||||
|
|
||||||
if soup.find(tag) is None:
|
if soup.find(tag) is None:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
return soup.find(tag).string.strip()
|
return soup.find(tag).string.strip()
|
@@ -189,8 +189,7 @@ 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) or dict()
|
attr = AttributeCache.get(key).to_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"],
|
||||||
|
@@ -3,6 +3,11 @@ import datetime
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from flask import abort
|
||||||
|
from flask import current_app
|
||||||
|
from flask_login import current_user
|
||||||
|
from sqlalchemy import func
|
||||||
|
|
||||||
from api.extensions import db
|
from api.extensions import db
|
||||||
from api.lib.cmdb.auto_discovery.const import ClOUD_MAP
|
from api.lib.cmdb.auto_discovery.const import ClOUD_MAP
|
||||||
from api.lib.cmdb.cache import CITypeAttributeCache
|
from api.lib.cmdb.cache import CITypeAttributeCache
|
||||||
@@ -23,10 +28,6 @@ from api.lib.utils import AESCrypto
|
|||||||
from api.models.cmdb import AutoDiscoveryCI
|
from api.models.cmdb import AutoDiscoveryCI
|
||||||
from api.models.cmdb import AutoDiscoveryCIType
|
from api.models.cmdb import AutoDiscoveryCIType
|
||||||
from api.models.cmdb import AutoDiscoveryRule
|
from api.models.cmdb import AutoDiscoveryRule
|
||||||
from flask import abort
|
|
||||||
from flask import current_app
|
|
||||||
from flask_login import current_user
|
|
||||||
from sqlalchemy import func
|
|
||||||
|
|
||||||
PWD = os.path.abspath(os.path.dirname(__file__))
|
PWD = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
@@ -250,17 +251,20 @@ class AutoDiscoveryCITypeCRUD(DBMixin):
|
|||||||
current_app.logger.warning(e)
|
current_app.logger.warning(e)
|
||||||
return abort(400, str(e))
|
return abort(400, str(e))
|
||||||
|
|
||||||
@staticmethod
|
def _can_add(self, **kwargs):
|
||||||
def _can_add(**kwargs):
|
self.cls.get_by(type_id=kwargs['type_id'], adr_id=kwargs.get('adr_id') or None) and abort(
|
||||||
|
400, ErrFormat.ad_duplicate)
|
||||||
|
|
||||||
|
# self.__valid_exec_target(kwargs.get('agent_id'), kwargs.get('query_expr'))
|
||||||
|
|
||||||
if kwargs.get('adr_id'):
|
if kwargs.get('adr_id'):
|
||||||
AutoDiscoveryRule.get_by_id(kwargs['adr_id']) or abort(
|
adr = AutoDiscoveryRule.get_by_id(kwargs['adr_id']) or abort(
|
||||||
404, ErrFormat.adr_not_found.format("id={}".format(kwargs['adr_id'])))
|
404, ErrFormat.adr_not_found.format("id={}".format(kwargs['adr_id'])))
|
||||||
# if not adr.is_plugin:
|
if not adr.is_plugin:
|
||||||
# other = self.cls.get_by(adr_id=adr.id, first=True, to_dict=False)
|
other = self.cls.get_by(adr_id=adr.id, first=True, to_dict=False)
|
||||||
# if other:
|
if other:
|
||||||
# ci_type = CITypeCache.get(other.type_id)
|
ci_type = CITypeCache.get(other.type_id)
|
||||||
# return abort(400, ErrFormat.adr_default_ref_once.format(ci_type.alias))
|
return abort(400, ErrFormat.adr_default_ref_once.format(ci_type.alias))
|
||||||
|
|
||||||
if kwargs.get('is_plugin') and kwargs.get('plugin_script'):
|
if kwargs.get('is_plugin') and kwargs.get('plugin_script'):
|
||||||
kwargs = check_plugin_script(**kwargs)
|
kwargs = check_plugin_script(**kwargs)
|
||||||
|
@@ -395,9 +395,8 @@ class CIManager(object):
|
|||||||
k not in ci_type_attrs_alias and _no_attribute_policy == ExistPolicy.REJECT):
|
k not in ci_type_attrs_alias and _no_attribute_policy == ExistPolicy.REJECT):
|
||||||
return abort(400, ErrFormat.attribute_not_found.format(k))
|
return abort(400, ErrFormat.attribute_not_found.format(k))
|
||||||
|
|
||||||
_attr_name = ((ci_type_attrs_name.get(k) and ci_type_attrs_name[k].name) or
|
if limit_attrs and ci_type_attrs_name.get(k) not in limit_attrs and (
|
||||||
(ci_type_attrs_alias.get(k) and ci_type_attrs_alias[k].name))
|
ci_type_attrs_alias.get(k) not in limit_attrs):
|
||||||
if limit_attrs and _attr_name not in limit_attrs:
|
|
||||||
return abort(403, ErrFormat.ci_filter_perm_attr_no_permission.format(k))
|
return abort(403, ErrFormat.ci_filter_perm_attr_no_permission.format(k))
|
||||||
|
|
||||||
ci_dict = {k: v for k, v in ci_dict.items() if k in ci_type_attrs_name or k in ci_type_attrs_alias}
|
ci_dict = {k: v for k, v in ci_dict.items() if k in ci_type_attrs_name or k in ci_type_attrs_alias}
|
||||||
@@ -515,9 +514,9 @@ class CIManager(object):
|
|||||||
ci_delete_trigger.apply_async(args=(trigger, OperateType.DELETE, ci_dict), queue=CMDB_QUEUE)
|
ci_delete_trigger.apply_async(args=(trigger, OperateType.DELETE, ci_dict), queue=CMDB_QUEUE)
|
||||||
|
|
||||||
attrs = CITypeAttribute.get_by(type_id=ci.type_id, to_dict=False)
|
attrs = CITypeAttribute.get_by(type_id=ci.type_id, to_dict=False)
|
||||||
attrs = [AttributeCache.get(attr.attr_id) for attr in attrs]
|
attr_names = set([AttributeCache.get(attr.attr_id).name for attr in attrs])
|
||||||
for attr in attrs:
|
for attr_name in attr_names:
|
||||||
value_table = TableMap(attr=attr).table
|
value_table = TableMap(attr_name=attr_name).table
|
||||||
for item in value_table.get_by(ci_id=ci_id, to_dict=False):
|
for item in value_table.get_by(ci_id=ci_id, to_dict=False):
|
||||||
item.delete(commit=False)
|
item.delete(commit=False)
|
||||||
|
|
||||||
|
@@ -5,10 +5,8 @@ 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
|
||||||
@@ -24,7 +22,6 @@ from api.lib.cmdb.const import ResourceTypeEnum
|
|||||||
from api.lib.cmdb.const import RoleEnum
|
from api.lib.cmdb.const import RoleEnum
|
||||||
from api.lib.cmdb.const import ValueTypeEnum
|
from api.lib.cmdb.const import ValueTypeEnum
|
||||||
from api.lib.cmdb.history import CITypeHistoryManager
|
from api.lib.cmdb.history import CITypeHistoryManager
|
||||||
from api.lib.cmdb.perms import CIFilterPermsCRUD
|
|
||||||
from api.lib.cmdb.relation_type import RelationTypeManager
|
from api.lib.cmdb.relation_type import RelationTypeManager
|
||||||
from api.lib.cmdb.resp_format import ErrFormat
|
from api.lib.cmdb.resp_format import ErrFormat
|
||||||
from api.lib.cmdb.value import AttributeValueManager
|
from api.lib.cmdb.value import AttributeValueManager
|
||||||
@@ -78,13 +75,12 @@ 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(ResourceTypeEnum.CI_TYPE)])
|
resources = set([i.get('name') for i in ACLManager().get_resources("CIType")])
|
||||||
|
|
||||||
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:
|
||||||
attr = AttributeCache.get(type_dict["unique_id"])
|
type_dict["unique_key"] = AttributeCache.get(type_dict["unique_id"]).name
|
||||||
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)
|
||||||
|
|
||||||
@@ -117,9 +113,6 @@ 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)
|
||||||
@@ -138,11 +131,7 @@ 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,
|
||||||
@@ -254,6 +243,7 @@ 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:
|
||||||
@@ -293,10 +283,7 @@ 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 and name != existed.name:
|
if name is not None:
|
||||||
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])
|
||||||
@@ -589,11 +576,6 @@ class CITypeRelationManager(object):
|
|||||||
ci_type_dict = CITypeCache.get(type_id).to_dict()
|
ci_type_dict = CITypeCache.get(type_id).to_dict()
|
||||||
ci_type_dict["ctr_id"] = relation_inst.id
|
ci_type_dict["ctr_id"] = relation_inst.id
|
||||||
ci_type_dict["attributes"] = CITypeAttributeManager.get_attributes_by_type_id(ci_type_dict["id"])
|
ci_type_dict["attributes"] = CITypeAttributeManager.get_attributes_by_type_id(ci_type_dict["id"])
|
||||||
attr_filter = CIFilterPermsCRUD.get_attr_filter(type_id)
|
|
||||||
if attr_filter:
|
|
||||||
ci_type_dict["attributes"] = [attr for attr in (ci_type_dict["attributes"] or [])
|
|
||||||
if attr['name'] in attr_filter]
|
|
||||||
|
|
||||||
ci_type_dict["relation_type"] = relation_inst.relation_type.name
|
ci_type_dict["relation_type"] = relation_inst.relation_type.name
|
||||||
ci_type_dict["constraint"] = relation_inst.constraint
|
ci_type_dict["constraint"] = relation_inst.constraint
|
||||||
|
|
||||||
@@ -743,7 +725,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.get('id'): i for i in attributes}
|
id2attr = {i['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)
|
||||||
@@ -909,58 +891,97 @@ class CITypeAttributeGroupManager(object):
|
|||||||
|
|
||||||
class CITypeTemplateManager(object):
|
class CITypeTemplateManager(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __import(cls, data, unique_key='name'):
|
def __import(cls, data):
|
||||||
id2obj_dicts = {i[unique_key]: i for i in data}
|
id2obj_dicts = {i['id']: i for i in data}
|
||||||
existed = cls.get_by(to_dict=False)
|
existed = cls.get_by(deleted=None, to_dict=False)
|
||||||
id2existed = {getattr(i, unique_key): i for i in existed}
|
id2existed = {i.id: i for i in existed}
|
||||||
existed_ids = [getattr(i, unique_key) 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]
|
||||||
|
|
||||||
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:
|
||||||
__id = CITypeManager.add(**id2obj_dicts[added_id])
|
CITypeManager.add(**id2obj_dicts[added_id])
|
||||||
CITypeCache.clean(__id)
|
|
||||||
elif cls == CITypeRelation:
|
elif cls == CITypeRelation:
|
||||||
__id = CITypeRelationManager.add(id2obj_dicts[added_id].get('parent_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:
|
||||||
obj = cls.create(flush=True, **id2obj_dicts[added_id])
|
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):
|
||||||
_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:
|
if cls == CIType:
|
||||||
CITypeCache.clean(id2existed[updated_id].id)
|
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])
|
||||||
|
|
||||||
|
# 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:
|
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))
|
||||||
|
|
||||||
return id_map
|
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)
|
||||||
|
|
||||||
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]]
|
||||||
@@ -969,262 +990,122 @@ 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))
|
||||||
|
|
||||||
attr_id_map = self.__import(Attribute, [i[0] for i in copy.deepcopy(attrs)])
|
self.__import(Attribute, [i[0] for i in attrs])
|
||||||
|
|
||||||
for i, choice_value in attrs:
|
for i, choice_value in attrs:
|
||||||
if choice_value and not i.get('choice_web_hook') and not i.get('choice_other'):
|
if choice_value:
|
||||||
AttributeManager.add_choice_values(attr_id_map.get(i['id'], i['id']), i['value_type'], choice_value)
|
AttributeManager.add_choice_values(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_ci_type_relations(ci_type_relations, type_id_map, relation_type_id_map):
|
def _import_type_attributes(type2attributes):
|
||||||
for i in ci_type_relations:
|
# add type attribute
|
||||||
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 = CITypeAttributesCache.get2(type_id_map.get(int(type_id), type_id))
|
existed = CITypeAttribute.get_by(type_id=type_id, to_dict=False)
|
||||||
existed_attr_names = {attr.name: ta for ta, attr in existed}
|
existed_attr_ids = {i.attr_id: i for i 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_map.get(int(type_id), type_id),
|
payload = dict(type_id=type_id,
|
||||||
attr_id=attr_id_map.get(attr['id'], attr['id']),
|
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['name'] not in handled:
|
if attr['id'] not in existed_attr_ids: # new
|
||||||
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)
|
|
||||||
|
|
||||||
handled.add(attr['name'])
|
# delete
|
||||||
|
for i in existed:
|
||||||
try:
|
if i.attr_id not in new_attr_ids:
|
||||||
db.session.commit()
|
i.soft_delete()
|
||||||
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, type_id_map, attr_id_map):
|
def _import_attribute_group(type2attribute_group):
|
||||||
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)
|
||||||
existed = CITypeAttributeGroup.get_by(name=_group['name'],
|
new = CITypeAttributeGroup.create(**_group)
|
||||||
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 = CITypeAttributeGroup.create(flush=True, **_group)
|
existed = CITypeAttributeGroupItem.get_by(group_id=new.id, to_dict=False)
|
||||||
|
for i in existed:
|
||||||
|
i.soft_delete()
|
||||||
|
|
||||||
for order, attr in enumerate(group['attributes'] or []):
|
for order, attr in enumerate(group['attributes'] or []):
|
||||||
item_existed = CITypeAttributeGroupItem.get_by(group_id=existed.id,
|
CITypeAttributeGroupItem.create(group_id=new.id, attr_id=attr['id'], order=order)
|
||||||
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
|
||||||
|
try:
|
||||||
existed = False
|
AutoDiscoveryCITypeCRUD.add(**rule)
|
||||||
for i in AutoDiscoveryCIType.get_by(type_id=ci_type.id, adr_id=rule['adr_id'], to_dict=False):
|
except Exception as e:
|
||||||
if ((i.extra_option or {}).get('alias') or None) == (
|
current_app.logger.warning("import auto discovery rules failed: {}".format(e))
|
||||||
(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):
|
def import_template(self, tpt):
|
||||||
import time
|
import time
|
||||||
s = time.time()
|
s = time.time()
|
||||||
attr_id_map = self._import_attributes(tpt.get('type2attributes') or {})
|
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()
|
||||||
ci_type_id_map = self._import_ci_types(tpt.get('ci_types') or [], attr_id_map)
|
self._import_ci_types(tpt.get('ci_types') or [])
|
||||||
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 [], ci_type_id_map)
|
self._import_ci_type_groups(tpt.get('ci_type_groups') or [])
|
||||||
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()
|
||||||
relation_type_id_map = self._import_relation_types(tpt.get('relation_types') or [])
|
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 [], ci_type_id_map, relation_type_id_map)
|
self._import_ci_type_relations(tpt.get('ci_type_relations') or [])
|
||||||
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 {}, ci_type_id_map, attr_id_map)
|
self._import_type_attributes(tpt.get('type2attributes') or {})
|
||||||
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 {}, ci_type_id_map, attr_id_map)
|
self._import_attribute_group(tpt.get('type2attribute_group') or {})
|
||||||
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 = []
|
||||||
@@ -1235,91 +1116,23 @@ 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['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(
|
tpt = dict(
|
||||||
ci_types=CITypeManager.get_ci_types(type_name=ci_type.name),
|
ci_types=CITypeManager.get_ci_types(),
|
||||||
ci_type_auto_discovery_rules=list(),
|
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(),
|
type2attributes=dict(),
|
||||||
type2attribute_group=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']:
|
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'])
|
tpt['type2attribute_group'][ci_type['id']] = CITypeAttributeGroupManager.get_by_type_id(ci_type['id'])
|
||||||
|
|
||||||
return tpt
|
return tpt
|
||||||
|
@@ -69,7 +69,6 @@ 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):
|
||||||
|
@@ -135,7 +135,7 @@ class AttributeHistoryManger(object):
|
|||||||
from api.lib.cmdb.ci import CIManager
|
from api.lib.cmdb.ci import CIManager
|
||||||
cis = CIManager().get_cis_by_ids(list(ci_ids),
|
cis = CIManager().get_cis_by_ids(list(ci_ids),
|
||||||
unique_required=True)
|
unique_required=True)
|
||||||
cis = {i['_id']: i for i in cis if i}
|
cis = {i['_id']: i for i in cis}
|
||||||
|
|
||||||
return total, res, cis
|
return total, res, cis
|
||||||
|
|
||||||
|
@@ -143,14 +143,11 @@ 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'):
|
||||||
resource = ACLManager().del_resource(str(obj.id), ResourceTypeEnum.CI_FILTER)
|
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):
|
||||||
|
@@ -4,8 +4,6 @@ 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 类型必须是列表"
|
||||||
|
@@ -28,8 +28,7 @@ class Search(object):
|
|||||||
count=None,
|
count=None,
|
||||||
sort=None,
|
sort=None,
|
||||||
reverse=False,
|
reverse=False,
|
||||||
ancestor_ids=None,
|
ancestor_ids=None):
|
||||||
has_m2m=None):
|
|
||||||
self.orig_query = query
|
self.orig_query = query
|
||||||
self.fl = fl
|
self.fl = fl
|
||||||
self.facet_field = facet_field
|
self.facet_field = facet_field
|
||||||
@@ -46,15 +45,14 @@ class Search(object):
|
|||||||
level[0] if isinstance(level, list) and level else level)
|
level[0] if isinstance(level, list) and level else level)
|
||||||
|
|
||||||
self.ancestor_ids = ancestor_ids
|
self.ancestor_ids = ancestor_ids
|
||||||
self.has_m2m = has_m2m or False
|
self.has_m2m = False
|
||||||
if not self.has_m2m:
|
if self.ancestor_ids:
|
||||||
if self.ancestor_ids:
|
self.has_m2m = True
|
||||||
self.has_m2m = True
|
else:
|
||||||
else:
|
level = level[0] if isinstance(level, list) and level else level
|
||||||
level = level[0] if isinstance(level, list) and level else level
|
for _l, c in self.level2constraint.items():
|
||||||
for _l, c in self.level2constraint.items():
|
if _l < int(level) and c == ConstraintEnum.Many2Many:
|
||||||
if _l < int(level) and c == ConstraintEnum.Many2Many:
|
self.has_m2m = True
|
||||||
self.has_m2m = True
|
|
||||||
|
|
||||||
def _get_ids(self, ids):
|
def _get_ids(self, ids):
|
||||||
if self.level[-1] == 1 and len(ids) == 1:
|
if self.level[-1] == 1 and len(ids) == 1:
|
||||||
@@ -92,12 +90,12 @@ class Search(object):
|
|||||||
key = list(set(["{},{}".format(i, j) for idx, i in enumerate(key) for j in _tmp[idx]]))
|
key = list(set(["{},{}".format(i, j) for idx, i in enumerate(key) for j in _tmp[idx]]))
|
||||||
prefix = REDIS_PREFIX_CI_RELATION2
|
prefix = REDIS_PREFIX_CI_RELATION2
|
||||||
|
|
||||||
_tmp = list(map(lambda x: json.loads(x).keys() if x else [], rd.get(key, prefix) or []))
|
|
||||||
ids = [j for i in _tmp for j in i]
|
|
||||||
|
|
||||||
if not key:
|
if not key:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
_tmp = list(map(lambda x: json.loads(x).keys() if x else [], rd.get(key, prefix) or []))
|
||||||
|
ids = [j for i in _tmp for j in i]
|
||||||
|
|
||||||
if level in self.level:
|
if level in self.level:
|
||||||
merge_ids.extend(ids)
|
merge_ids.extend(ids)
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@ import api.models.cmdb as model
|
|||||||
from api.lib.cmdb.cache import AttributeCache
|
from api.lib.cmdb.cache import AttributeCache
|
||||||
from api.lib.cmdb.const import ValueTypeEnum
|
from api.lib.cmdb.const import ValueTypeEnum
|
||||||
|
|
||||||
TIME_RE = re.compile(r'(?:[01]\d|2[0-3]):[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):
|
def string2int(x):
|
||||||
|
@@ -1,24 +1,14 @@
|
|||||||
import copy
|
from flask import abort
|
||||||
import json
|
|
||||||
|
|
||||||
from flask import abort, current_app
|
|
||||||
from ldap3 import Connection
|
|
||||||
from ldap3 import Server
|
|
||||||
from ldap3.core.exceptions import LDAPBindError, LDAPSocketOpenError
|
|
||||||
from ldap3 import AUTO_BIND_NO_TLS
|
|
||||||
|
|
||||||
from api.extensions import db
|
from api.extensions import db
|
||||||
from api.lib.common_setting.resp_format import ErrFormat
|
from api.lib.common_setting.resp_format import ErrFormat
|
||||||
from api.models.common_setting import CommonData
|
from api.models.common_setting import CommonData
|
||||||
from api.lib.utils import AESCrypto
|
|
||||||
from api.lib.common_setting.const import AuthCommonConfig, AuthenticateType, AuthCommonConfigAutoRedirect, TestType
|
|
||||||
|
|
||||||
|
|
||||||
class CommonDataCRUD(object):
|
class CommonDataCRUD(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_data_by_type(data_type):
|
def get_data_by_type(data_type):
|
||||||
CommonDataCRUD.check_auth_type(data_type)
|
|
||||||
return CommonData.get_by(data_type=data_type)
|
return CommonData.get_by(data_type=data_type)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -28,8 +18,6 @@ class CommonDataCRUD(object):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def create_new_data(data_type, **kwargs):
|
def create_new_data(data_type, **kwargs):
|
||||||
try:
|
try:
|
||||||
CommonDataCRUD.check_auth_type(data_type)
|
|
||||||
|
|
||||||
return CommonData.create(data_type=data_type, **kwargs)
|
return CommonData.create(data_type=data_type, **kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
@@ -41,7 +29,6 @@ class CommonDataCRUD(object):
|
|||||||
if not existed:
|
if not existed:
|
||||||
abort(404, ErrFormat.common_data_not_found.format(_id))
|
abort(404, ErrFormat.common_data_not_found.format(_id))
|
||||||
try:
|
try:
|
||||||
CommonDataCRUD.check_auth_type(existed.data_type)
|
|
||||||
return existed.update(**kwargs)
|
return existed.update(**kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
@@ -53,230 +40,7 @@ class CommonDataCRUD(object):
|
|||||||
if not existed:
|
if not existed:
|
||||||
abort(404, ErrFormat.common_data_not_found.format(_id))
|
abort(404, ErrFormat.common_data_not_found.format(_id))
|
||||||
try:
|
try:
|
||||||
CommonDataCRUD.check_auth_type(existed.data_type)
|
|
||||||
existed.soft_delete()
|
existed.soft_delete()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
abort(400, str(e))
|
abort(400, str(e))
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def check_auth_type(data_type):
|
|
||||||
if data_type in list(AuthenticateType.all()) + [AuthCommonConfig]:
|
|
||||||
abort(400, ErrFormat.common_data_not_support_auth_type.format(data_type))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def set_auth_auto_redirect_enable(_value: int):
|
|
||||||
existed = CommonData.get_by(first=True, data_type=AuthCommonConfig, to_dict=False)
|
|
||||||
if not existed:
|
|
||||||
CommonDataCRUD.create_new_data(AuthCommonConfig, data={AuthCommonConfigAutoRedirect: _value})
|
|
||||||
else:
|
|
||||||
data = existed.data
|
|
||||||
data = copy.deepcopy(existed.data) if data else {}
|
|
||||||
data[AuthCommonConfigAutoRedirect] = _value
|
|
||||||
CommonDataCRUD.update_data(existed.id, data=data)
|
|
||||||
return True
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_auth_auto_redirect_enable():
|
|
||||||
existed = CommonData.get_by(first=True, data_type=AuthCommonConfig)
|
|
||||||
if not existed:
|
|
||||||
return 0
|
|
||||||
data = existed.get('data', {})
|
|
||||||
if not data:
|
|
||||||
return 0
|
|
||||||
return data.get(AuthCommonConfigAutoRedirect, 0)
|
|
||||||
|
|
||||||
|
|
||||||
class AuthenticateDataCRUD(object):
|
|
||||||
common_type_list = [AuthCommonConfig]
|
|
||||||
|
|
||||||
def __init__(self, _type):
|
|
||||||
self._type = _type
|
|
||||||
self.record = None
|
|
||||||
self.decrypt_data = {}
|
|
||||||
|
|
||||||
def get_support_type_list(self):
|
|
||||||
return list(AuthenticateType.all()) + self.common_type_list
|
|
||||||
|
|
||||||
def get(self):
|
|
||||||
if not self.decrypt_data:
|
|
||||||
self.decrypt_data = self.get_decrypt_data()
|
|
||||||
|
|
||||||
return self.decrypt_data
|
|
||||||
|
|
||||||
def get_by_key(self, _key):
|
|
||||||
if not self.decrypt_data:
|
|
||||||
self.decrypt_data = self.get_decrypt_data()
|
|
||||||
|
|
||||||
return self.decrypt_data.get(_key, None)
|
|
||||||
|
|
||||||
def get_record(self, to_dict=False) -> CommonData:
|
|
||||||
return CommonData.get_by(first=True, data_type=self._type, to_dict=to_dict)
|
|
||||||
|
|
||||||
def get_record_with_decrypt(self) -> dict:
|
|
||||||
record = CommonData.get_by(first=True, data_type=self._type, to_dict=True)
|
|
||||||
if not record:
|
|
||||||
return {}
|
|
||||||
data = self.get_decrypt_dict(record.get('data', ''))
|
|
||||||
record['data'] = data
|
|
||||||
return record
|
|
||||||
|
|
||||||
def get_decrypt_dict(self, data):
|
|
||||||
decrypt_str = self.decrypt(data)
|
|
||||||
try:
|
|
||||||
return json.loads(decrypt_str)
|
|
||||||
except Exception as e:
|
|
||||||
abort(400, str(e))
|
|
||||||
|
|
||||||
def get_decrypt_data(self) -> dict:
|
|
||||||
self.record = self.get_record()
|
|
||||||
if not self.record:
|
|
||||||
return self.get_from_config()
|
|
||||||
return self.get_decrypt_dict(self.record.data)
|
|
||||||
|
|
||||||
def get_from_config(self):
|
|
||||||
return current_app.config.get(self._type, {})
|
|
||||||
|
|
||||||
def check_by_type(self) -> None:
|
|
||||||
existed = self.get_record()
|
|
||||||
if existed:
|
|
||||||
abort(400, ErrFormat.common_data_already_existed.format(self._type))
|
|
||||||
|
|
||||||
def create(self, data) -> CommonData:
|
|
||||||
self.check_by_type()
|
|
||||||
encrypt = data.pop('encrypt', None)
|
|
||||||
if encrypt is False:
|
|
||||||
return CommonData.create(data_type=self._type, data=data)
|
|
||||||
encrypted_data = self.encrypt(data)
|
|
||||||
try:
|
|
||||||
return CommonData.create(data_type=self._type, data=encrypted_data)
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
abort(400, str(e))
|
|
||||||
|
|
||||||
def update_by_record(self, record, data) -> CommonData:
|
|
||||||
encrypt = data.pop('encrypt', None)
|
|
||||||
if encrypt is False:
|
|
||||||
return record.update(data=data)
|
|
||||||
encrypted_data = self.encrypt(data)
|
|
||||||
try:
|
|
||||||
return record.update(data=encrypted_data)
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
abort(400, str(e))
|
|
||||||
|
|
||||||
def update(self, _id, data) -> CommonData:
|
|
||||||
existed = CommonData.get_by(first=True, to_dict=False, id=_id)
|
|
||||||
if not existed:
|
|
||||||
abort(404, ErrFormat.common_data_not_found.format(_id))
|
|
||||||
|
|
||||||
return self.update_by_record(existed, data)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def delete(_id) -> None:
|
|
||||||
existed = CommonData.get_by(first=True, to_dict=False, id=_id)
|
|
||||||
if not existed:
|
|
||||||
abort(404, ErrFormat.common_data_not_found.format(_id))
|
|
||||||
try:
|
|
||||||
existed.soft_delete()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
abort(400, str(e))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def encrypt(data) -> str:
|
|
||||||
if type(data) is dict:
|
|
||||||
try:
|
|
||||||
data = json.dumps(data)
|
|
||||||
except Exception as e:
|
|
||||||
abort(400, str(e))
|
|
||||||
return AESCrypto().encrypt(data)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def decrypt(data) -> str:
|
|
||||||
return AESCrypto().decrypt(data)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_enable_list():
|
|
||||||
all_records = CommonData.query.filter(
|
|
||||||
CommonData.data_type.in_(AuthenticateType.all()),
|
|
||||||
CommonData.deleted == 0
|
|
||||||
).all()
|
|
||||||
enable_list = []
|
|
||||||
for auth_type in AuthenticateType.all():
|
|
||||||
record = list(filter(lambda x: x.data_type == auth_type, all_records))
|
|
||||||
if not record:
|
|
||||||
config = current_app.config.get(auth_type, None)
|
|
||||||
if not config:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if config.get('enable', False):
|
|
||||||
enable_list.append(dict(
|
|
||||||
auth_type=auth_type,
|
|
||||||
))
|
|
||||||
|
|
||||||
continue
|
|
||||||
|
|
||||||
try:
|
|
||||||
decrypt_data = json.loads(AuthenticateDataCRUD.decrypt(record[0].data))
|
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.error(e)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if decrypt_data.get('enable', 0) == 1:
|
|
||||||
enable_list.append(dict(
|
|
||||||
auth_type=auth_type,
|
|
||||||
))
|
|
||||||
|
|
||||||
auth_auto_redirect = CommonDataCRUD.get_auth_auto_redirect_enable()
|
|
||||||
|
|
||||||
return dict(
|
|
||||||
enable_list=enable_list,
|
|
||||||
auth_auto_redirect=auth_auto_redirect,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test(self, test_type, data):
|
|
||||||
type_lower = self._type.lower()
|
|
||||||
func_name = f'test_{type_lower}'
|
|
||||||
if hasattr(self, func_name):
|
|
||||||
try:
|
|
||||||
return getattr(self, f'test_{type_lower}')(test_type, data)
|
|
||||||
except Exception as e:
|
|
||||||
abort(400, str(e))
|
|
||||||
abort(400, ErrFormat.not_support_test.format(self._type))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def test_ldap(test_type, data):
|
|
||||||
ldap_server = data.get('ldap_server')
|
|
||||||
ldap_user_dn = data.get('ldap_user_dn', '{}')
|
|
||||||
|
|
||||||
server = Server(ldap_server, connect_timeout=2)
|
|
||||||
if not server.check_availability():
|
|
||||||
raise Exception(ErrFormat.ldap_server_connect_not_available)
|
|
||||||
else:
|
|
||||||
if test_type == TestType.Connect:
|
|
||||||
return True
|
|
||||||
|
|
||||||
username = data.get('username', None)
|
|
||||||
if not username:
|
|
||||||
raise Exception(ErrFormat.ldap_test_username_required)
|
|
||||||
user = ldap_user_dn.format(username)
|
|
||||||
password = data.get('password', None)
|
|
||||||
|
|
||||||
try:
|
|
||||||
Connection(server, user=user, password=password, auto_bind=AUTO_BIND_NO_TLS)
|
|
||||||
except LDAPBindError:
|
|
||||||
ldap_domain = data.get('ldap_domain')
|
|
||||||
user_with_domain = f"{username}@{ldap_domain}"
|
|
||||||
try:
|
|
||||||
Connection(server, user=user_with_domain, password=password, auto_bind=AUTO_BIND_NO_TLS)
|
|
||||||
except Exception as e:
|
|
||||||
raise Exception(ErrFormat.ldap_test_unknown_error.format(str(e)))
|
|
||||||
|
|
||||||
except LDAPSocketOpenError:
|
|
||||||
raise Exception(ErrFormat.ldap_server_connect_timeout)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
raise Exception(ErrFormat.ldap_test_unknown_error.format(str(e)))
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
from api.extensions import cache
|
from api.extensions import cache
|
||||||
from api.models.common_setting import CompanyInfo
|
from api.models.common_setting import CompanyInfo
|
||||||
|
|
||||||
@@ -13,7 +11,6 @@ class CompanyInfoCRUD(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(**kwargs):
|
def create(**kwargs):
|
||||||
CompanyInfoCRUD.check_data(**kwargs)
|
|
||||||
res = CompanyInfo.create(**kwargs)
|
res = CompanyInfo.create(**kwargs)
|
||||||
CompanyInfoCache.refresh(res.info)
|
CompanyInfoCache.refresh(res.info)
|
||||||
return res
|
return res
|
||||||
@@ -25,26 +22,10 @@ class CompanyInfoCRUD(object):
|
|||||||
if not existed:
|
if not existed:
|
||||||
existed = CompanyInfoCRUD.create(**kwargs)
|
existed = CompanyInfoCRUD.create(**kwargs)
|
||||||
else:
|
else:
|
||||||
CompanyInfoCRUD.check_data(**kwargs)
|
|
||||||
existed = existed.update(**kwargs)
|
existed = existed.update(**kwargs)
|
||||||
CompanyInfoCache.refresh(existed.info)
|
CompanyInfoCache.refresh(existed.info)
|
||||||
return existed
|
return existed
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def check_data(**kwargs):
|
|
||||||
info = kwargs.get('info', {})
|
|
||||||
info['messenger'] = CompanyInfoCRUD.check_messenger(info.get('messenger', None))
|
|
||||||
|
|
||||||
kwargs['info'] = info
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def check_messenger(messenger):
|
|
||||||
if not messenger:
|
|
||||||
return messenger
|
|
||||||
|
|
||||||
parsed_url = urlparse(messenger)
|
|
||||||
return f"{parsed_url.scheme}://{parsed_url.netloc}"
|
|
||||||
|
|
||||||
|
|
||||||
class CompanyInfoCache(object):
|
class CompanyInfoCache(object):
|
||||||
key = 'CompanyInfoCache::'
|
key = 'CompanyInfoCache::'
|
||||||
@@ -60,4 +41,4 @@ class CompanyInfoCache(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def refresh(cls, info):
|
def refresh(cls, info):
|
||||||
cache.set(cls.key, info)
|
cache.set(cls.key, info)
|
@@ -19,19 +19,3 @@ BotNameMap = {
|
|||||||
'feishuApp': 'feishuBot',
|
'feishuApp': 'feishuBot',
|
||||||
'dingdingApp': 'dingdingBot',
|
'dingdingApp': 'dingdingBot',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class AuthenticateType(BaseEnum):
|
|
||||||
CAS = 'CAS'
|
|
||||||
OAUTH2 = 'OAUTH2'
|
|
||||||
OIDC = 'OIDC'
|
|
||||||
LDAP = 'LDAP'
|
|
||||||
|
|
||||||
|
|
||||||
AuthCommonConfig = 'AuthCommonConfig'
|
|
||||||
AuthCommonConfigAutoRedirect = 'auto_redirect'
|
|
||||||
|
|
||||||
|
|
||||||
class TestType(BaseEnum):
|
|
||||||
Connect = 'connect'
|
|
||||||
Login = 'login'
|
|
||||||
|
@@ -15,13 +15,10 @@ from wtforms import validators
|
|||||||
|
|
||||||
from api.extensions import db
|
from api.extensions import db
|
||||||
from api.lib.common_setting.acl import ACLManager
|
from api.lib.common_setting.acl import ACLManager
|
||||||
from api.lib.common_setting.const import OperatorType
|
from api.lib.common_setting.const import COMMON_SETTING_QUEUE, OperatorType
|
||||||
from api.lib.cmdb.const import CMDB_QUEUE
|
|
||||||
from api.lib.common_setting.resp_format import ErrFormat
|
from api.lib.common_setting.resp_format import ErrFormat
|
||||||
from api.models.common_setting import Employee, Department
|
from api.models.common_setting import Employee, Department
|
||||||
|
|
||||||
from api.tasks.common_setting import refresh_employee_acl_info, edit_employee_department_in_acl
|
|
||||||
|
|
||||||
acl_user_columns = [
|
acl_user_columns = [
|
||||||
'email',
|
'email',
|
||||||
'mobile',
|
'mobile',
|
||||||
@@ -140,9 +137,7 @@ class EmployeeCRUD(object):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def add(**kwargs):
|
def add(**kwargs):
|
||||||
try:
|
try:
|
||||||
res = CreateEmployee().create_single(**kwargs)
|
return CreateEmployee().create_single(**kwargs)
|
||||||
refresh_employee_acl_info.apply_async(args=(), queue=CMDB_QUEUE)
|
|
||||||
return res
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
abort(400, str(e))
|
abort(400, str(e))
|
||||||
|
|
||||||
@@ -169,9 +164,10 @@ class EmployeeCRUD(object):
|
|||||||
existed.update(**kwargs)
|
existed.update(**kwargs)
|
||||||
|
|
||||||
if len(e_list) > 0:
|
if len(e_list) > 0:
|
||||||
|
from api.tasks.common_setting import edit_employee_department_in_acl
|
||||||
edit_employee_department_in_acl.apply_async(
|
edit_employee_department_in_acl.apply_async(
|
||||||
args=(e_list, new_department_id, current_user.uid),
|
args=(e_list, new_department_id, current_user.uid),
|
||||||
queue=CMDB_QUEUE
|
queue=COMMON_SETTING_QUEUE
|
||||||
)
|
)
|
||||||
|
|
||||||
return existed
|
return existed
|
||||||
@@ -295,7 +291,7 @@ class EmployeeCRUD(object):
|
|||||||
employees = []
|
employees = []
|
||||||
for r in pagination.items:
|
for r in pagination.items:
|
||||||
d = r.Employee.to_dict()
|
d = r.Employee.to_dict()
|
||||||
d['department_name'] = r.Department.department_name if r.Department else ''
|
d['department_name'] = r.Department.department_name
|
||||||
employees.append(d)
|
employees.append(d)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -441,7 +437,7 @@ class EmployeeCRUD(object):
|
|||||||
employees = []
|
employees = []
|
||||||
for r in pagination.items:
|
for r in pagination.items:
|
||||||
d = r.Employee.to_dict()
|
d = r.Employee.to_dict()
|
||||||
d['department_name'] = r.Department.department_name if r.Department else ''
|
d['department_name'] = r.Department.department_name
|
||||||
employees.append(d)
|
employees.append(d)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -567,7 +563,6 @@ class EmployeeCRUD(object):
|
|||||||
for column in direct_columns:
|
for column in direct_columns:
|
||||||
tmp[column] = d.get(column, '')
|
tmp[column] = d.get(column, '')
|
||||||
notice_info = d.get('notice_info', {})
|
notice_info = d.get('notice_info', {})
|
||||||
notice_info = copy.deepcopy(notice_info) if notice_info else {}
|
|
||||||
tmp.update(**notice_info)
|
tmp.update(**notice_info)
|
||||||
results.append(tmp)
|
results.append(tmp)
|
||||||
return results
|
return results
|
||||||
@@ -575,7 +570,6 @@ class EmployeeCRUD(object):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def import_employee(employee_list):
|
def import_employee(employee_list):
|
||||||
res = CreateEmployee().batch_create(employee_list)
|
res = CreateEmployee().batch_create(employee_list)
|
||||||
refresh_employee_acl_info.apply_async(args=(), queue=CMDB_QUEUE)
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -692,27 +686,6 @@ class EmployeeCRUD(object):
|
|||||||
else:
|
else:
|
||||||
abort(400, ErrFormat.column_name_not_support)
|
abort(400, ErrFormat.column_name_not_support)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def update_last_login_by_uid(uid, last_login=None):
|
|
||||||
employee = Employee.get_by(acl_uid=uid, first=True, to_dict=False)
|
|
||||||
if not employee:
|
|
||||||
return
|
|
||||||
if last_login:
|
|
||||||
try:
|
|
||||||
last_login = datetime.strptime(last_login, '%Y-%m-%d %H:%M:%S')
|
|
||||||
except Exception as e:
|
|
||||||
last_login = datetime.now()
|
|
||||||
else:
|
|
||||||
last_login = datetime.now()
|
|
||||||
|
|
||||||
try:
|
|
||||||
employee.update(
|
|
||||||
last_login=last_login
|
|
||||||
)
|
|
||||||
return last_login
|
|
||||||
except Exception as e:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_map(key='uid', acl=None):
|
def get_user_map(key='uid', acl=None):
|
||||||
"""
|
"""
|
||||||
@@ -753,7 +726,6 @@ class CreateEmployee(object):
|
|||||||
try:
|
try:
|
||||||
existed = self.check_acl_user(user_data)
|
existed = self.check_acl_user(user_data)
|
||||||
if not existed:
|
if not existed:
|
||||||
user_data['add_from'] = 'common'
|
|
||||||
return self.acl.create_user(user_data)
|
return self.acl.create_user(user_data)
|
||||||
return existed
|
return existed
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@@ -8,9 +8,6 @@ class ErrFormat(CommonErrFormat):
|
|||||||
|
|
||||||
no_file_part = "没有文件部分"
|
no_file_part = "没有文件部分"
|
||||||
file_is_required = "文件是必须的"
|
file_is_required = "文件是必须的"
|
||||||
file_not_found = "文件不存在"
|
|
||||||
file_type_not_allowed = "文件类型不允许"
|
|
||||||
upload_failed = "上传失败: {}"
|
|
||||||
|
|
||||||
direct_supervisor_is_not_self = "直属上级不能是自己"
|
direct_supervisor_is_not_self = "直属上级不能是自己"
|
||||||
parent_department_is_not_self = "上级部门不能是自己"
|
parent_department_is_not_self = "上级部门不能是自己"
|
||||||
@@ -59,7 +56,6 @@ class ErrFormat(CommonErrFormat):
|
|||||||
email_send_timeout = "邮件发送超时"
|
email_send_timeout = "邮件发送超时"
|
||||||
|
|
||||||
common_data_not_found = "ID {} 找不到记录"
|
common_data_not_found = "ID {} 找不到记录"
|
||||||
common_data_already_existed = "{} 已存在"
|
|
||||||
notice_platform_existed = "{} 已存在"
|
notice_platform_existed = "{} 已存在"
|
||||||
notice_not_existed = "{} 配置项不存在"
|
notice_not_existed = "{} 配置项不存在"
|
||||||
notice_please_config_messenger_first = "请先配置 messenger"
|
notice_please_config_messenger_first = "请先配置 messenger"
|
||||||
@@ -67,11 +63,3 @@ class ErrFormat(CommonErrFormat):
|
|||||||
notice_bind_failed = "绑定失败: {}"
|
notice_bind_failed = "绑定失败: {}"
|
||||||
notice_bind_success = "绑定成功"
|
notice_bind_success = "绑定成功"
|
||||||
notice_remove_bind_success = "解绑成功"
|
notice_remove_bind_success = "解绑成功"
|
||||||
|
|
||||||
not_support_test = "不支持的测试类型: {}"
|
|
||||||
not_support_auth_type = "不支持的认证类型: {}"
|
|
||||||
ldap_server_connect_timeout = "LDAP服务器连接超时"
|
|
||||||
ldap_server_connect_not_available = "LDAP服务器连接不可用"
|
|
||||||
ldap_test_unknown_error = "LDAP测试未知错误: {}"
|
|
||||||
common_data_not_support_auth_type = "通用数据不支持auth类型: {}"
|
|
||||||
ldap_test_username_required = "LDAP测试用户名必填"
|
|
||||||
|
@@ -1,14 +1,6 @@
|
|||||||
import base64
|
|
||||||
import uuid
|
import uuid
|
||||||
import os
|
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
from flask import abort, current_app
|
|
||||||
import lz4.frame
|
|
||||||
|
|
||||||
from api.lib.common_setting.utils import get_cur_time_str
|
from api.lib.common_setting.utils import get_cur_time_str
|
||||||
from api.models.common_setting import CommonFile
|
|
||||||
from api.lib.common_setting.resp_format import ErrFormat
|
|
||||||
|
|
||||||
|
|
||||||
def allowed_file(filename, allowed_extensions):
|
def allowed_file(filename, allowed_extensions):
|
||||||
@@ -22,73 +14,3 @@ def generate_new_file_name(name):
|
|||||||
cur_str = get_cur_time_str('_')
|
cur_str = get_cur_time_str('_')
|
||||||
|
|
||||||
return f"{prev_name}_{cur_str}_{uid}.{ext}"
|
return f"{prev_name}_{cur_str}_{uid}.{ext}"
|
||||||
|
|
||||||
|
|
||||||
class CommonFileCRUD:
|
|
||||||
@staticmethod
|
|
||||||
def add_file(**kwargs):
|
|
||||||
return CommonFile.create(**kwargs)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_file(file_name, to_str=False):
|
|
||||||
existed = CommonFile.get_by(file_name=file_name, first=True, to_dict=False)
|
|
||||||
if not existed:
|
|
||||||
abort(400, ErrFormat.file_not_found)
|
|
||||||
|
|
||||||
uncompressed_data = lz4.frame.decompress(existed.binary)
|
|
||||||
|
|
||||||
return base64.b64encode(uncompressed_data).decode('utf-8') if to_str else BytesIO(uncompressed_data)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def sync_file_to_db():
|
|
||||||
for p in ['UPLOAD_DIRECTORY_FULL']:
|
|
||||||
upload_path = current_app.config.get(p, None)
|
|
||||||
if not upload_path:
|
|
||||||
continue
|
|
||||||
for root, dirs, files in os.walk(upload_path):
|
|
||||||
for file in files:
|
|
||||||
file_path = os.path.join(root, file)
|
|
||||||
if not os.path.isfile(file_path):
|
|
||||||
continue
|
|
||||||
|
|
||||||
existed = CommonFile.get_by(file_name=file, first=True, to_dict=False)
|
|
||||||
if existed:
|
|
||||||
continue
|
|
||||||
with open(file_path, 'rb') as f:
|
|
||||||
data = f.read()
|
|
||||||
compressed_data = lz4.frame.compress(data)
|
|
||||||
try:
|
|
||||||
CommonFileCRUD.add_file(
|
|
||||||
origin_name=file,
|
|
||||||
file_name=file,
|
|
||||||
binary=compressed_data
|
|
||||||
)
|
|
||||||
|
|
||||||
current_app.logger.info(f'sync file {file} to db')
|
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.error(f'sync file {file} to db error: {e}')
|
|
||||||
|
|
||||||
def get_file_binary_str(self, file_name):
|
|
||||||
return self.get_file(file_name, True)
|
|
||||||
|
|
||||||
def save_str_to_file(self, file_name, str_data):
|
|
||||||
try:
|
|
||||||
self.get_file(file_name)
|
|
||||||
current_app.logger.info(f'file {file_name} already exists')
|
|
||||||
return
|
|
||||||
except Exception as e:
|
|
||||||
# file not found
|
|
||||||
pass
|
|
||||||
|
|
||||||
bytes_data = base64.b64decode(str_data)
|
|
||||||
compressed_data = lz4.frame.compress(bytes_data)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.add_file(
|
|
||||||
origin_name=file_name,
|
|
||||||
file_name=file_name,
|
|
||||||
binary=compressed_data
|
|
||||||
)
|
|
||||||
current_app.logger.info(f'save_str_to_file {file_name} success')
|
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.error(f"save_str_to_file error: {e}")
|
|
||||||
|
@@ -94,7 +94,7 @@ class CRUDMixin(FormatMixin):
|
|||||||
if any((isinstance(_id, six.string_types) and _id.isdigit(),
|
if any((isinstance(_id, six.string_types) and _id.isdigit(),
|
||||||
isinstance(_id, (six.integer_types, float))), ):
|
isinstance(_id, (six.integer_types, float))), ):
|
||||||
obj = getattr(cls, "query").get(int(_id))
|
obj = getattr(cls, "query").get(int(_id))
|
||||||
if obj and not getattr(obj, 'deleted', False):
|
if obj and not obj.deleted:
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@@ -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, rebuild=True):
|
def grant_resource_to_role_by_rid(self, name, rid, resource_type_name=None, permissions=None):
|
||||||
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, rebuild=rebuild)
|
PermissionCRUD.grant(rid, permissions, resource_id=resource.id)
|
||||||
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, rebuild=rebuild)
|
PermissionCRUD.grant(rid, permissions, group_id=group.id)
|
||||||
|
|
||||||
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,20 +138,20 @@ 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, rebuild=True):
|
def revoke_resource_from_role_by_rid(self, name, rid, resource_type_name=None, permissions=None):
|
||||||
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, rebuild=rebuild)
|
PermissionCRUD.revoke(rid, permissions, resource_id=resource.id)
|
||||||
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, rebuild=rebuild)
|
PermissionCRUD.revoke(rid, permissions, group_id=group.id)
|
||||||
|
|
||||||
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:
|
||||||
return ResourceCRUD.delete(resource.id)
|
ResourceCRUD.delete(resource.id)
|
||||||
|
|
||||||
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):
|
||||||
|
@@ -1,19 +1,14 @@
|
|||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
import datetime
|
|
||||||
import itertools
|
import itertools
|
||||||
import json
|
import json
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from flask import has_request_context
|
from flask import has_request_context, request
|
||||||
from flask import request
|
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
|
|
||||||
from api.extensions import db
|
|
||||||
from api.lib.perm.acl import AppCache
|
from api.lib.perm.acl import AppCache
|
||||||
from api.models.acl import AuditLoginLog
|
|
||||||
from api.models.acl import AuditPermissionLog
|
from api.models.acl import AuditPermissionLog
|
||||||
from api.models.acl import AuditResourceLog
|
from api.models.acl import AuditResourceLog
|
||||||
from api.models.acl import AuditRoleLog
|
from api.models.acl import AuditRoleLog
|
||||||
@@ -288,27 +283,6 @@ class AuditCRUD(object):
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def search_login(_, q=None, page=1, page_size=10, start=None, end=None):
|
|
||||||
query = db.session.query(AuditLoginLog)
|
|
||||||
|
|
||||||
if start:
|
|
||||||
query = query.filter(AuditLoginLog.login_at >= start)
|
|
||||||
if end:
|
|
||||||
query = query.filter(AuditLoginLog.login_at <= end)
|
|
||||||
|
|
||||||
if q:
|
|
||||||
query = query.filter(AuditLoginLog.username == q)
|
|
||||||
|
|
||||||
records = query.order_by(
|
|
||||||
AuditLoginLog.id.desc()).offset((page - 1) * page_size).limit(page_size).all()
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'data': [r.to_dict() for r in records],
|
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_role_log(cls, app_id, operate_type: AuditOperateType,
|
def add_role_log(cls, app_id, operate_type: AuditOperateType,
|
||||||
scope: AuditScope, link_id: int, origin: dict, current: dict, extra: dict,
|
scope: AuditScope, link_id: int, origin: dict, current: dict, extra: dict,
|
||||||
@@ -374,30 +348,3 @@ class AuditCRUD(object):
|
|||||||
AuditTriggerLog.create(app_id=app_id, trigger_id=trigger_id, operate_uid=user_id,
|
AuditTriggerLog.create(app_id=app_id, trigger_id=trigger_id, operate_uid=user_id,
|
||||||
operate_type=operate_type.value,
|
operate_type=operate_type.value,
|
||||||
origin=origin, current=current, extra=extra, source=source.value)
|
origin=origin, current=current, extra=extra, source=source.value)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def add_login_log(cls, username, is_ok, description, _id=None, logout_at=None):
|
|
||||||
if _id is not None:
|
|
||||||
existed = AuditLoginLog.get_by_id(_id)
|
|
||||||
if existed is not None:
|
|
||||||
existed.update(logout_at=logout_at)
|
|
||||||
return
|
|
||||||
|
|
||||||
payload = dict(username=username,
|
|
||||||
is_ok=is_ok,
|
|
||||||
description=description,
|
|
||||||
logout_at=logout_at,
|
|
||||||
ip=request.headers.get('X-Real-IP') or request.remote_addr,
|
|
||||||
browser=request.headers.get('User-Agent'),
|
|
||||||
)
|
|
||||||
|
|
||||||
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
|
|
||||||
|
@@ -328,8 +328,6 @@ class ResourceCRUD(object):
|
|||||||
AuditCRUD.add_resource_log(resource.app_id, AuditOperateType.delete,
|
AuditCRUD.add_resource_log(resource.app_id, AuditOperateType.delete,
|
||||||
AuditScope.resource, resource.id, origin, {}, {})
|
AuditScope.resource, resource.id, origin, {}, {})
|
||||||
|
|
||||||
return rebuilds
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def delete_by_name(cls, name, type_id, app_id):
|
def delete_by_name(cls, name, type_id, app_id):
|
||||||
resource = Resource.get_by(name=name, resource_type_id=type_id, app_id=app_id) or abort(
|
resource = Resource.get_by(name=name, resource_type_id=type_id, app_id=app_id) or abort(
|
||||||
|
@@ -4,9 +4,6 @@ from api.lib.resp_format import CommonErrFormat
|
|||||||
|
|
||||||
|
|
||||||
class ErrFormat(CommonErrFormat):
|
class ErrFormat(CommonErrFormat):
|
||||||
login_succeed = "登录成功"
|
|
||||||
ldap_connection_failed = "连接LDAP服务失败"
|
|
||||||
invalid_password = "密码验证失败"
|
|
||||||
auth_only_with_app_token_failed = "应用 Token验证失败"
|
auth_only_with_app_token_failed = "应用 Token验证失败"
|
||||||
session_invalid = "您不是应用管理员 或者 session失效(尝试一下退出重新登录)"
|
session_invalid = "您不是应用管理员 或者 session失效(尝试一下退出重新登录)"
|
||||||
|
|
||||||
@@ -20,11 +17,11 @@ class ErrFormat(CommonErrFormat):
|
|||||||
role_exists = "角色 {} 已经存在!"
|
role_exists = "角色 {} 已经存在!"
|
||||||
global_role_not_found = "全局角色 {} 不存在!"
|
global_role_not_found = "全局角色 {} 不存在!"
|
||||||
global_role_exists = "全局角色 {} 已经存在!"
|
global_role_exists = "全局角色 {} 已经存在!"
|
||||||
|
user_role_delete_invalid = "删除用户角色, 请在 用户管理 页面操作!"
|
||||||
|
|
||||||
resource_no_permission = "您没有资源: {} 的 {} 权限"
|
resource_no_permission = "您没有资源: {} 的 {} 权限"
|
||||||
admin_required = "需要管理员权限"
|
admin_required = "需要管理员权限"
|
||||||
role_required = "需要角色: {}"
|
role_required = "需要角色: {}"
|
||||||
user_role_delete_invalid = "删除用户角色, 请在 用户管理 页面操作!"
|
|
||||||
|
|
||||||
app_is_ready_existed = "应用 {} 已经存在"
|
app_is_ready_existed = "应用 {} 已经存在"
|
||||||
app_not_found = "应用 {} 不存在!"
|
app_not_found = "应用 {} 不存在!"
|
||||||
|
@@ -41,7 +41,6 @@ class UserCRUD(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add(cls, **kwargs):
|
def add(cls, **kwargs):
|
||||||
add_from = kwargs.pop('add_from', None)
|
|
||||||
existed = User.get_by(username=kwargs['username'])
|
existed = User.get_by(username=kwargs['username'])
|
||||||
existed and abort(400, ErrFormat.user_exists.format(kwargs['username']))
|
existed and abort(400, ErrFormat.user_exists.format(kwargs['username']))
|
||||||
|
|
||||||
@@ -63,12 +62,10 @@ 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(), {}, {}
|
||||||
)
|
)
|
||||||
|
from api.lib.common_setting.employee import EmployeeCRUD
|
||||||
if add_from != 'common':
|
payload = {column: getattr(user, column) for column in ['uid', 'username', 'nickname', 'email', 'block']}
|
||||||
from api.lib.common_setting.employee import EmployeeCRUD
|
payload['rid'] = role.id
|
||||||
payload = {column: getattr(user, column) for column in ['uid', 'username', 'nickname', 'email', 'block']}
|
EmployeeCRUD.add_employee_from_acl_created(**payload)
|
||||||
payload['rid'] = role.id
|
|
||||||
EmployeeCRUD.add_employee_from_acl_created(**payload)
|
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
@@ -93,9 +93,6 @@ def _auth_with_token():
|
|||||||
|
|
||||||
|
|
||||||
def _auth_with_ip_white_list():
|
def _auth_with_ip_white_list():
|
||||||
if request.url.endswith("acl/users/info"):
|
|
||||||
return False
|
|
||||||
|
|
||||||
ip = request.headers.get('X-Real-IP') or request.remote_addr
|
ip = request.headers.get('X-Real-IP') or request.remote_addr
|
||||||
key = request.values.get('_key')
|
key = request.values.get('_key')
|
||||||
secret = request.values.get('_secret')
|
secret = request.values.get('_secret')
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
# -*- coding:utf-8 -*-
|
|
@@ -1,67 +0,0 @@
|
|||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from flask import abort
|
|
||||||
from flask import current_app
|
|
||||||
from flask import session
|
|
||||||
from ldap3 import ALL
|
|
||||||
from ldap3 import AUTO_BIND_NO_TLS
|
|
||||||
from ldap3 import Connection
|
|
||||||
from ldap3 import Server
|
|
||||||
from ldap3.core.exceptions import LDAPBindError
|
|
||||||
from ldap3.core.exceptions import LDAPCertificateError
|
|
||||||
from ldap3.core.exceptions import LDAPSocketOpenError
|
|
||||||
|
|
||||||
from api.lib.common_setting.common_data import AuthenticateDataCRUD
|
|
||||||
from api.lib.common_setting.const import AuthenticateType
|
|
||||||
from api.lib.perm.acl.audit import AuditCRUD
|
|
||||||
from api.lib.perm.acl.resp_format import ErrFormat
|
|
||||||
from api.models.acl import User
|
|
||||||
|
|
||||||
|
|
||||||
def authenticate_with_ldap(username, password):
|
|
||||||
config = AuthenticateDataCRUD(AuthenticateType.LDAP).get()
|
|
||||||
|
|
||||||
server = Server(config.get('ldap_server'), get_info=ALL, connect_timeout=3)
|
|
||||||
if '@' in username:
|
|
||||||
email = username
|
|
||||||
who = config.get('ldap_user_dn').format(username.split('@')[0])
|
|
||||||
else:
|
|
||||||
who = config.get('ldap_user_dn').format(username)
|
|
||||||
email = "{}@{}".format(who, config.get('ldap_domain'))
|
|
||||||
|
|
||||||
username = username.split('@')[0]
|
|
||||||
user = User.query.get_by_username(username)
|
|
||||||
try:
|
|
||||||
if not password:
|
|
||||||
raise LDAPCertificateError
|
|
||||||
|
|
||||||
try:
|
|
||||||
conn = Connection(server, user=who, password=password, auto_bind=AUTO_BIND_NO_TLS)
|
|
||||||
except LDAPBindError:
|
|
||||||
conn = Connection(server,
|
|
||||||
user=f"{username}@{config.get('ldap_domain')}",
|
|
||||||
password=password,
|
|
||||||
auto_bind=AUTO_BIND_NO_TLS)
|
|
||||||
|
|
||||||
if conn.result['result'] != 0:
|
|
||||||
AuditCRUD.add_login_log(username, False, ErrFormat.invalid_password)
|
|
||||||
raise LDAPBindError
|
|
||||||
else:
|
|
||||||
_id = AuditCRUD.add_login_log(username, True, ErrFormat.login_succeed)
|
|
||||||
session['LOGIN_ID'] = _id
|
|
||||||
|
|
||||||
if not user:
|
|
||||||
from api.lib.perm.acl.user import UserCRUD
|
|
||||||
user = UserCRUD.add(username=username, email=email, password=uuid.uuid4().hex)
|
|
||||||
|
|
||||||
return user, True
|
|
||||||
|
|
||||||
except LDAPBindError as e:
|
|
||||||
current_app.logger.info(e)
|
|
||||||
return user, False
|
|
||||||
|
|
||||||
except LDAPSocketOpenError as e:
|
|
||||||
current_app.logger.info(e)
|
|
||||||
return abort(403, ErrFormat.ldap_connection_failed)
|
|
@@ -1,30 +0,0 @@
|
|||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
from flask import current_app
|
|
||||||
|
|
||||||
from . import routing
|
|
||||||
|
|
||||||
|
|
||||||
class OAuth2(object):
|
|
||||||
def __init__(self, app=None, url_prefix=None):
|
|
||||||
self._app = app
|
|
||||||
if app is not None:
|
|
||||||
self.init_app(app, url_prefix)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def init_app(app, url_prefix=None):
|
|
||||||
# Configuration defaults
|
|
||||||
app.config.setdefault('OAUTH2_GRANT_TYPE', 'authorization_code')
|
|
||||||
app.config.setdefault('OAUTH2_RESPONSE_TYPE', 'code')
|
|
||||||
app.config.setdefault('OAUTH2_AFTER_LOGIN', '/')
|
|
||||||
|
|
||||||
app.config.setdefault('OIDC_GRANT_TYPE', 'authorization_code')
|
|
||||||
app.config.setdefault('OIDC_RESPONSE_TYPE', 'code')
|
|
||||||
app.config.setdefault('OIDC_AFTER_LOGIN', '/')
|
|
||||||
|
|
||||||
# Register Blueprint
|
|
||||||
app.register_blueprint(routing.blueprint, url_prefix=url_prefix)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def app(self):
|
|
||||||
return self._app or current_app
|
|
@@ -1,139 +0,0 @@
|
|||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
import secrets
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
import requests
|
|
||||||
from flask import Blueprint
|
|
||||||
from flask import abort
|
|
||||||
from flask import current_app
|
|
||||||
from flask import redirect
|
|
||||||
from flask import request
|
|
||||||
from flask import session
|
|
||||||
from flask import url_for
|
|
||||||
from flask_login import login_user
|
|
||||||
from flask_login import logout_user
|
|
||||||
from six.moves.urllib.parse import urlencode
|
|
||||||
from six.moves.urllib.parse import urlparse
|
|
||||||
|
|
||||||
from api.lib.common_setting.common_data import AuthenticateDataCRUD
|
|
||||||
from api.lib.perm.acl.audit import AuditCRUD
|
|
||||||
from api.lib.perm.acl.cache import UserCache
|
|
||||||
from api.lib.perm.acl.resp_format import ErrFormat
|
|
||||||
|
|
||||||
blueprint = Blueprint('oauth2', __name__)
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/api/<string:auth_type>/login')
|
|
||||||
def login(auth_type):
|
|
||||||
config = AuthenticateDataCRUD(auth_type.upper()).get()
|
|
||||||
|
|
||||||
if request.values.get("next"):
|
|
||||||
session["next"] = request.values.get("next")
|
|
||||||
|
|
||||||
session[f'{auth_type}_state'] = secrets.token_urlsafe(16)
|
|
||||||
|
|
||||||
auth_type = auth_type.upper()
|
|
||||||
|
|
||||||
redirect_uri = "{}://{}{}".format(urlparse(request.referrer).scheme,
|
|
||||||
urlparse(request.referrer).netloc,
|
|
||||||
url_for('oauth2.callback', auth_type=auth_type.lower()))
|
|
||||||
qs = urlencode({
|
|
||||||
'client_id': config['client_id'],
|
|
||||||
'redirect_uri': redirect_uri,
|
|
||||||
'response_type': current_app.config[f'{auth_type}_RESPONSE_TYPE'],
|
|
||||||
'scope': ' '.join(config['scopes'] or []),
|
|
||||||
'state': session[f'{auth_type.lower()}_state'],
|
|
||||||
})
|
|
||||||
|
|
||||||
return redirect("{}?{}".format(config['authorize_url'].split('?')[0], qs))
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/api/<string:auth_type>/callback')
|
|
||||||
def callback(auth_type):
|
|
||||||
auth_type = auth_type.upper()
|
|
||||||
config = AuthenticateDataCRUD(auth_type).get()
|
|
||||||
|
|
||||||
redirect_url = session.get("next") or config.get('after_login') or '/'
|
|
||||||
|
|
||||||
if request.values['state'] != session.get(f'{auth_type.lower()}_state'):
|
|
||||||
return abort(401, "state is invalid")
|
|
||||||
|
|
||||||
if 'code' not in request.values:
|
|
||||||
return abort(401, 'code is invalid')
|
|
||||||
|
|
||||||
response = requests.post(config['token_url'], data={
|
|
||||||
'client_id': config['client_id'],
|
|
||||||
'client_secret': config['client_secret'],
|
|
||||||
'code': request.values['code'],
|
|
||||||
'grant_type': current_app.config[f'{auth_type}_GRANT_TYPE'],
|
|
||||||
'redirect_uri': url_for('oauth2.callback', auth_type=auth_type.lower(), _external=True),
|
|
||||||
}, headers={'Accept': 'application/json'})
|
|
||||||
if response.status_code != 200:
|
|
||||||
current_app.logger.error(response.text)
|
|
||||||
return abort(401)
|
|
||||||
access_token = response.json().get('access_token')
|
|
||||||
if not access_token:
|
|
||||||
return abort(401)
|
|
||||||
|
|
||||||
response = requests.get(config['user_info']['url'], headers={
|
|
||||||
'Authorization': 'Bearer {}'.format(access_token),
|
|
||||||
'Accept': 'application/json',
|
|
||||||
})
|
|
||||||
if response.status_code != 200:
|
|
||||||
return abort(401)
|
|
||||||
|
|
||||||
res = response.json()
|
|
||||||
email = res.get(config['user_info']['email'])
|
|
||||||
username = res.get(config['user_info']['username'])
|
|
||||||
avatar = res.get(config['user_info'].get('avatar'))
|
|
||||||
user = UserCache.get(username)
|
|
||||||
if user is None:
|
|
||||||
current_app.logger.info("create user: {}".format(username))
|
|
||||||
from api.lib.perm.acl.user import UserCRUD
|
|
||||||
|
|
||||||
user_dict = dict(username=username, email=email, avatar=avatar)
|
|
||||||
user_dict['password'] = uuid.uuid4().hex
|
|
||||||
|
|
||||||
user = UserCRUD.add(**user_dict)
|
|
||||||
|
|
||||||
# log the user in
|
|
||||||
login_user(user)
|
|
||||||
|
|
||||||
from api.lib.perm.acl.acl import ACLManager
|
|
||||||
user_info = ACLManager.get_user_info(username)
|
|
||||||
|
|
||||||
session["acl"] = dict(uid=user_info.get("uid"),
|
|
||||||
avatar=user.avatar if user else user_info.get("avatar"),
|
|
||||||
userId=user_info.get("uid"),
|
|
||||||
rid=user_info.get("rid"),
|
|
||||||
userName=user_info.get("username"),
|
|
||||||
nickName=user_info.get("nickname") or user_info.get("username"),
|
|
||||||
parentRoles=user_info.get("parents"),
|
|
||||||
childRoles=user_info.get("children"),
|
|
||||||
roleName=user_info.get("role"))
|
|
||||||
session["uid"] = user_info.get("uid")
|
|
||||||
|
|
||||||
_id = AuditCRUD.add_login_log(username, True, ErrFormat.login_succeed)
|
|
||||||
session['LOGIN_ID'] = _id
|
|
||||||
|
|
||||||
return redirect(redirect_url)
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/api/<string:auth_type>/logout')
|
|
||||||
def logout(auth_type):
|
|
||||||
"acl" in session and session.pop("acl")
|
|
||||||
"uid" in session and session.pop("uid")
|
|
||||||
f'{auth_type}_state' in session and session.pop(f'{auth_type}_state')
|
|
||||||
"next" in session and session.pop("next")
|
|
||||||
|
|
||||||
redirect_url = url_for('oauth2.login', auth_type=auth_type, _external=True, next=request.referrer)
|
|
||||||
|
|
||||||
logout_user()
|
|
||||||
|
|
||||||
current_app.logger.debug('Redirecting to: {0}'.format(redirect_url))
|
|
||||||
|
|
||||||
AuditCRUD.add_login_log(None, None, None, _id=session.get('LOGIN_ID'), logout_at=datetime.datetime.now())
|
|
||||||
|
|
||||||
return redirect(redirect_url)
|
|
@@ -5,18 +5,17 @@ import copy
|
|||||||
import hashlib
|
import hashlib
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from ldap3 import Server, Connection, ALL
|
||||||
|
from ldap3.core.exceptions import LDAPBindError, LDAPCertificateError
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from flask import session
|
|
||||||
from flask_sqlalchemy import BaseQuery
|
from flask_sqlalchemy import BaseQuery
|
||||||
|
|
||||||
from api.extensions import db
|
from api.extensions import db
|
||||||
from api.lib.database import CRUDModel
|
from api.lib.database import CRUDModel
|
||||||
from api.lib.database import Model
|
from api.lib.database import Model
|
||||||
from api.lib.database import Model2
|
|
||||||
from api.lib.database import SoftDeleteMixin
|
from api.lib.database import SoftDeleteMixin
|
||||||
from api.lib.perm.acl.const import ACL_QUEUE
|
from api.lib.perm.acl.const import ACL_QUEUE
|
||||||
from api.lib.perm.acl.const import OperateType
|
from api.lib.perm.acl.const import OperateType
|
||||||
from api.lib.perm.acl.resp_format import ErrFormat
|
|
||||||
|
|
||||||
|
|
||||||
class App(Model):
|
class App(Model):
|
||||||
@@ -29,26 +28,21 @@ class App(Model):
|
|||||||
|
|
||||||
|
|
||||||
class UserQuery(BaseQuery):
|
class UserQuery(BaseQuery):
|
||||||
|
def _join(self, *args, **kwargs):
|
||||||
|
super(UserQuery, self)._join(*args, **kwargs)
|
||||||
|
|
||||||
def authenticate(self, login, password):
|
def authenticate(self, login, password):
|
||||||
from api.lib.perm.acl.audit import AuditCRUD
|
|
||||||
|
|
||||||
user = self.filter(db.or_(User.username == login,
|
user = self.filter(db.or_(User.username == login,
|
||||||
User.email == login)).filter(User.deleted.is_(False)).filter(User.block == 0).first()
|
User.email == login)).filter(User.deleted.is_(False)).filter(User.block == 0).first()
|
||||||
if user:
|
if user:
|
||||||
|
current_app.logger.info(user)
|
||||||
authenticated = user.check_password(password)
|
authenticated = user.check_password(password)
|
||||||
if authenticated:
|
if authenticated:
|
||||||
_id = AuditCRUD.add_login_log(login, True, ErrFormat.login_succeed)
|
from api.tasks.acl import op_record
|
||||||
session['LOGIN_ID'] = _id
|
op_record.apply_async(args=(None, login, OperateType.LOGIN, ["ACL"]), queue=ACL_QUEUE)
|
||||||
else:
|
|
||||||
AuditCRUD.add_login_log(login, False, ErrFormat.invalid_password)
|
|
||||||
else:
|
else:
|
||||||
authenticated = False
|
authenticated = False
|
||||||
|
|
||||||
AuditCRUD.add_login_log(login, False, ErrFormat.user_not_found.format(login))
|
|
||||||
|
|
||||||
current_app.logger.info(("login", login, user, authenticated))
|
|
||||||
|
|
||||||
return user, authenticated
|
return user, authenticated
|
||||||
|
|
||||||
def authenticate_with_key(self, key, secret, args, path):
|
def authenticate_with_key(self, key, secret, args, path):
|
||||||
@@ -63,6 +57,38 @@ class UserQuery(BaseQuery):
|
|||||||
|
|
||||||
return user, authenticated
|
return user, authenticated
|
||||||
|
|
||||||
|
def authenticate_with_ldap(self, username, password):
|
||||||
|
server = Server(current_app.config.get('LDAP_SERVER'), get_info=ALL)
|
||||||
|
if '@' in username:
|
||||||
|
email = username
|
||||||
|
who = current_app.config.get('LDAP_USER_DN').format(username.split('@')[0])
|
||||||
|
else:
|
||||||
|
who = current_app.config.get('LDAP_USER_DN').format(username)
|
||||||
|
email = "{}@{}".format(who, current_app.config.get('LDAP_DOMAIN'))
|
||||||
|
|
||||||
|
username = username.split('@')[0]
|
||||||
|
user = self.get_by_username(username)
|
||||||
|
try:
|
||||||
|
if not password:
|
||||||
|
raise LDAPCertificateError
|
||||||
|
|
||||||
|
conn = Connection(server, user=who, password=password)
|
||||||
|
conn.bind()
|
||||||
|
if conn.result['result'] != 0:
|
||||||
|
raise LDAPBindError
|
||||||
|
conn.unbind()
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
from api.lib.perm.acl.user import UserCRUD
|
||||||
|
user = UserCRUD.add(username=username, email=email)
|
||||||
|
|
||||||
|
from api.tasks.acl import op_record
|
||||||
|
op_record.apply_async(args=(None, username, OperateType.LOGIN, ["ACL"]), queue=ACL_QUEUE)
|
||||||
|
|
||||||
|
return user, True
|
||||||
|
except LDAPBindError:
|
||||||
|
return user, False
|
||||||
|
|
||||||
def search(self, key):
|
def search(self, key):
|
||||||
query = self.filter(db.or_(User.email == key,
|
query = self.filter(db.or_(User.email == key,
|
||||||
User.nickname.ilike('%' + key + '%'),
|
User.nickname.ilike('%' + key + '%'),
|
||||||
@@ -112,7 +138,6 @@ class User(CRUDModel, SoftDeleteMixin):
|
|||||||
wx_id = db.Column(db.String(32))
|
wx_id = db.Column(db.String(32))
|
||||||
employee_id = db.Column(db.String(16), index=True)
|
employee_id = db.Column(db.String(16), index=True)
|
||||||
avatar = db.Column(db.String(128))
|
avatar = db.Column(db.String(128))
|
||||||
|
|
||||||
# apps = db.Column(db.JSON)
|
# apps = db.Column(db.JSON)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -143,9 +168,11 @@ class User(CRUDModel, SoftDeleteMixin):
|
|||||||
|
|
||||||
|
|
||||||
class RoleQuery(BaseQuery):
|
class RoleQuery(BaseQuery):
|
||||||
|
def _join(self, *args, **kwargs):
|
||||||
|
super(RoleQuery, self)._join(*args, **kwargs)
|
||||||
|
|
||||||
def authenticate(self, login, password):
|
def authenticate(self, login, password):
|
||||||
role = self.filter(Role.name == login).filter(Role.deleted.is_(False)).first()
|
role = self.filter(Role.name == login).first()
|
||||||
if role:
|
if role:
|
||||||
authenticated = role.check_password(password)
|
authenticated = role.check_password(password)
|
||||||
|
|
||||||
@@ -350,16 +377,3 @@ class AuditTriggerLog(Model):
|
|||||||
current = db.Column(db.JSON, default=dict(), comment='当前数据')
|
current = db.Column(db.JSON, default=dict(), comment='当前数据')
|
||||||
extra = db.Column(db.JSON, default=dict(), comment='权限名')
|
extra = db.Column(db.JSON, default=dict(), comment='权限名')
|
||||||
source = db.Column(db.String(16), default='', comment='来源')
|
source = db.Column(db.String(16), default='', comment='来源')
|
||||||
|
|
||||||
|
|
||||||
class AuditLoginLog(Model2):
|
|
||||||
__tablename__ = "acl_audit_login_logs"
|
|
||||||
|
|
||||||
username = db.Column(db.String(64), index=True)
|
|
||||||
channel = db.Column(db.Enum('web', 'api'), default="web")
|
|
||||||
ip = db.Column(db.String(15))
|
|
||||||
browser = db.Column(db.String(256))
|
|
||||||
description = db.Column(db.String(128))
|
|
||||||
is_ok = db.Column(db.Boolean)
|
|
||||||
login_at = db.Column(db.DateTime)
|
|
||||||
logout_at = db.Column(db.DateTime)
|
|
||||||
|
@@ -96,11 +96,3 @@ class NoticeConfig(Model):
|
|||||||
|
|
||||||
platform = db.Column(db.VARCHAR(255), nullable=False)
|
platform = db.Column(db.VARCHAR(255), nullable=False)
|
||||||
info = db.Column(db.JSON)
|
info = db.Column(db.JSON)
|
||||||
|
|
||||||
|
|
||||||
class CommonFile(Model):
|
|
||||||
__tablename__ = 'common_file'
|
|
||||||
|
|
||||||
file_name = db.Column(db.VARCHAR(512), nullable=False, index=True)
|
|
||||||
origin_name = db.Column(db.VARCHAR(512), nullable=False)
|
|
||||||
binary = db.Column(db.LargeBinary(16777216), nullable=False)
|
|
||||||
|
@@ -25,9 +25,10 @@ from api.models.acl import Role
|
|||||||
from api.models.acl import Trigger
|
from api.models.acl import Trigger
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name="acl.role_rebuild",
|
@celery.task(base=QueueOnce,
|
||||||
queue=ACL_QUEUE,)
|
name="acl.role_rebuild",
|
||||||
@flush_db
|
queue=ACL_QUEUE,
|
||||||
|
once={"graceful": True, "unlock_before_run": True})
|
||||||
@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]
|
||||||
@@ -189,18 +190,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, role_name, operate_type, obj):
|
def op_record(app, rolename, 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(role_name, int):
|
if isinstance(rolename, int):
|
||||||
u = UserCache.get(role_name)
|
u = UserCache.get(rolename)
|
||||||
if u:
|
if u:
|
||||||
role_name = u.username
|
rolename = u.username
|
||||||
if not u:
|
if not u:
|
||||||
r = RoleCache.get(role_name)
|
r = RoleCache.get(rolename)
|
||||||
if r:
|
if r:
|
||||||
role_name = r.name
|
rolename = r.name
|
||||||
|
|
||||||
OperateRecordCRUD.add(app, role_name, operate_type, obj)
|
OperateRecordCRUD.add(app, rolename, operate_type, obj)
|
||||||
|
@@ -1,24 +1,24 @@
|
|||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
import requests
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from api.extensions import celery
|
from api.extensions import celery
|
||||||
|
from api.extensions import db
|
||||||
from api.lib.common_setting.acl import ACLManager
|
from api.lib.common_setting.acl import ACLManager
|
||||||
from api.lib.cmdb.const import CMDB_QUEUE
|
from api.lib.common_setting.const import COMMON_SETTING_QUEUE
|
||||||
from api.lib.common_setting.resp_format import ErrFormat
|
from api.lib.common_setting.resp_format import ErrFormat
|
||||||
from api.models.common_setting import Department, Employee
|
from api.models.common_setting import Department
|
||||||
from api.lib.decorator import flush_db
|
|
||||||
from api.lib.decorator import reconnect_db
|
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name="common_setting.edit_employee_department_in_acl", queue=CMDB_QUEUE)
|
@celery.task(name="common_setting.edit_employee_department_in_acl", queue=COMMON_SETTING_QUEUE)
|
||||||
@flush_db
|
|
||||||
@reconnect_db
|
|
||||||
def edit_employee_department_in_acl(e_list, new_d_id, op_uid):
|
def edit_employee_department_in_acl(e_list, new_d_id, op_uid):
|
||||||
"""
|
"""
|
||||||
:param e_list:{acl_rid: 11, department_id: 22}
|
:param e_list:{acl_rid: 11, department_id: 22}
|
||||||
:param new_d_id
|
:param new_d_id
|
||||||
:param op_uid
|
:param op_uid
|
||||||
"""
|
"""
|
||||||
|
db.session.remove()
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
new_department = Department.get_by(
|
new_department = Department.get_by(
|
||||||
first=True, department_id=new_d_id, to_dict=False)
|
first=True, department_id=new_d_id, to_dict=False)
|
||||||
@@ -75,41 +75,3 @@ def edit_employee_department_in_acl(e_list, new_d_id, op_uid):
|
|||||||
result.append(ErrFormat.acl_add_user_to_role_failed.format(str(e)))
|
result.append(ErrFormat.acl_add_user_to_role_failed.format(str(e)))
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name="common_setting.refresh_employee_acl_info", queue=CMDB_QUEUE)
|
|
||||||
@flush_db
|
|
||||||
@reconnect_db
|
|
||||||
def refresh_employee_acl_info():
|
|
||||||
acl = ACLManager('acl')
|
|
||||||
role_map = {role['name']: role for role in acl.get_all_roles()}
|
|
||||||
|
|
||||||
criterion = [
|
|
||||||
Employee.deleted == 0
|
|
||||||
]
|
|
||||||
query = Employee.query.filter(*criterion).order_by(
|
|
||||||
Employee.created_at.desc()
|
|
||||||
)
|
|
||||||
|
|
||||||
for em in query.all():
|
|
||||||
if em.acl_uid and em.acl_rid:
|
|
||||||
continue
|
|
||||||
role = role_map.get(em.username, None)
|
|
||||||
if not role:
|
|
||||||
continue
|
|
||||||
|
|
||||||
params = dict()
|
|
||||||
if not em.acl_uid:
|
|
||||||
params['acl_uid'] = role.get('uid', 0)
|
|
||||||
|
|
||||||
if not em.acl_rid:
|
|
||||||
params['acl_rid'] = role.get('id', 0)
|
|
||||||
|
|
||||||
try:
|
|
||||||
em.update(**params)
|
|
||||||
current_app.logger.info(
|
|
||||||
f"refresh_employee_acl_info success, employee_id: {em.employee_id}, uid: {em.acl_uid}, "
|
|
||||||
f"rid: {em.acl_rid}")
|
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.error(str(e))
|
|
||||||
continue
|
|
||||||
|
@@ -24,7 +24,6 @@ class AuditLogView(APIView):
|
|||||||
'role': AuditCRUD.search_role,
|
'role': AuditCRUD.search_role,
|
||||||
'trigger': AuditCRUD.search_trigger,
|
'trigger': AuditCRUD.search_trigger,
|
||||||
'resource': AuditCRUD.search_resource,
|
'resource': AuditCRUD.search_resource,
|
||||||
'login': AuditCRUD.search_login,
|
|
||||||
}
|
}
|
||||||
if name not in func_map:
|
if name not in func_map:
|
||||||
abort(400, f'wrong {name}, please use {func_map.keys()}')
|
abort(400, f'wrong {name}, please use {func_map.keys()}')
|
||||||
|
@@ -8,15 +8,11 @@ from flask import abort
|
|||||||
from flask import current_app
|
from flask import current_app
|
||||||
from flask import request
|
from flask import request
|
||||||
from flask import session
|
from flask import session
|
||||||
from flask_login import login_user
|
from flask_login import login_user, logout_user
|
||||||
from flask_login import logout_user
|
|
||||||
|
|
||||||
from api.lib.common_setting.common_data import AuthenticateDataCRUD
|
|
||||||
from api.lib.common_setting.const import AuthenticateType
|
|
||||||
from api.lib.decorator import args_required
|
from api.lib.decorator import args_required
|
||||||
from api.lib.decorator import args_validate
|
from api.lib.decorator import args_validate
|
||||||
from api.lib.perm.acl.acl import ACLManager
|
from api.lib.perm.acl.acl import ACLManager
|
||||||
from api.lib.perm.acl.audit import AuditCRUD
|
|
||||||
from api.lib.perm.acl.cache import RoleCache
|
from api.lib.perm.acl.cache import RoleCache
|
||||||
from api.lib.perm.acl.cache import User
|
from api.lib.perm.acl.cache import User
|
||||||
from api.lib.perm.acl.cache import UserCache
|
from api.lib.perm.acl.cache import UserCache
|
||||||
@@ -38,10 +34,8 @@ class LoginView(APIView):
|
|||||||
username = request.values.get("username") or request.values.get("email")
|
username = request.values.get("username") or request.values.get("email")
|
||||||
password = request.values.get("password")
|
password = request.values.get("password")
|
||||||
_role = None
|
_role = None
|
||||||
config = AuthenticateDataCRUD(AuthenticateType.LDAP).get()
|
if current_app.config.get('AUTH_WITH_LDAP'):
|
||||||
if config.get('enabled') or config.get('enable'):
|
user, authenticated = User.query.authenticate_with_ldap(username, password)
|
||||||
from api.lib.perm.authentication.ldap import authenticate_with_ldap
|
|
||||||
user, authenticated = authenticate_with_ldap(username, password)
|
|
||||||
else:
|
else:
|
||||||
user, authenticated = User.query.authenticate(username, password)
|
user, authenticated = User.query.authenticate(username, password)
|
||||||
if not user:
|
if not user:
|
||||||
@@ -182,7 +176,4 @@ class LogoutView(APIView):
|
|||||||
@auth_abandoned
|
@auth_abandoned
|
||||||
def post(self):
|
def post(self):
|
||||||
logout_user()
|
logout_user()
|
||||||
|
|
||||||
AuditCRUD.add_login_log(None, None, None, _id=session.get('LOGIN_ID'), logout_at=datetime.datetime.now())
|
|
||||||
|
|
||||||
self.jsonify(code=200)
|
self.jsonify(code=200)
|
||||||
|
@@ -43,11 +43,9 @@ class CIRelationSearchView(APIView):
|
|||||||
facet = handle_arg_list(request.values.get("facet", ""))
|
facet = handle_arg_list(request.values.get("facet", ""))
|
||||||
sort = request.values.get("sort")
|
sort = request.values.get("sort")
|
||||||
reverse = request.values.get("reverse") in current_app.config.get('BOOL_TRUE')
|
reverse = request.values.get("reverse") in current_app.config.get('BOOL_TRUE')
|
||||||
has_m2m = request.values.get("has_m2m") in current_app.config.get('BOOL_TRUE')
|
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
s = Search(root_id, level, query, fl, facet, page, count, sort, reverse,
|
s = Search(root_id, level, query, fl, facet, page, count, sort, reverse, ancestor_ids=ancestor_ids)
|
||||||
ancestor_ids=ancestor_ids, has_m2m=has_m2m)
|
|
||||||
try:
|
try:
|
||||||
response, counter, total, page, numfound, facet = s.search()
|
response, counter, total, page, numfound, facet = s.search()
|
||||||
except SearchError as e:
|
except SearchError as e:
|
||||||
@@ -71,10 +69,9 @@ class CIRelationStatisticsView(APIView):
|
|||||||
level = request.values.get('level', 1)
|
level = request.values.get('level', 1)
|
||||||
type_ids = set(map(int, handle_arg_list(request.values.get('type_ids', []))))
|
type_ids = set(map(int, handle_arg_list(request.values.get('type_ids', []))))
|
||||||
ancestor_ids = request.values.get('ancestor_ids') or None # only for many to many
|
ancestor_ids = request.values.get('ancestor_ids') or None # only for many to many
|
||||||
has_m2m = request.values.get("has_m2m") in current_app.config.get('BOOL_TRUE')
|
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
s = Search(root_ids, level, ancestor_ids=ancestor_ids, has_m2m=has_m2m)
|
s = Search(root_ids, level, ancestor_ids=ancestor_ids)
|
||||||
try:
|
try:
|
||||||
result = s.statistics(type_ids)
|
result = s.statistics(type_ids)
|
||||||
except SearchError as e:
|
except SearchError as e:
|
||||||
|
@@ -166,8 +166,7 @@ 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)
|
unique = AttributeCache.get(unique_id).name
|
||||||
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,14 +318,12 @@ class CITypeAttributeGroupView(APIView):
|
|||||||
|
|
||||||
|
|
||||||
class CITypeTemplateView(APIView):
|
class CITypeTemplateView(APIView):
|
||||||
url_prefix = ("/ci_types/template/import", "/ci_types/template/export", "/ci_types/<int:type_id>/template/export")
|
url_prefix = ("/ci_types/template/import", "/ci_types/template/export")
|
||||||
|
|
||||||
@role_required(RoleEnum.CONFIG)
|
@role_required(RoleEnum.CONFIG)
|
||||||
def get(self, type_id=None): # export
|
def get(self): # export
|
||||||
if type_id is not None:
|
return self.jsonify(
|
||||||
return self.jsonify(dict(ci_type_template=CITypeTemplateManager.export_template_by_type(type_id)))
|
dict(ci_type_template=CITypeTemplateManager.export_template()))
|
||||||
|
|
||||||
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
|
||||||
@@ -460,21 +457,13 @@ 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 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))
|
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, rebuild=False)
|
acl.grant_resource_to_role_by_rid(type_name, rid, ResourceTypeEnum.CI_TYPE, perms)
|
||||||
|
|
||||||
if request.values.get('ci_filter') or request.values.get('attr_filter'):
|
CIFilterPermsCRUD().add(type_id=type_id, rid=rid, **request.values)
|
||||||
CIFilterPermsCRUD().add(type_id=type_id, rid=rid, **request.values)
|
|
||||||
else:
|
|
||||||
from api.tasks.acl import role_rebuild
|
|
||||||
from api.lib.perm.acl.const import ACL_QUEUE
|
|
||||||
|
|
||||||
app_id = AppCache.get('cmdb').id
|
|
||||||
current_app.logger.info((rid, app_id))
|
|
||||||
role_rebuild.apply_async(args=(rid, app_id), queue=ACL_QUEUE)
|
|
||||||
current_app.logger.info('done')
|
|
||||||
|
|
||||||
return self.jsonify(code=200)
|
return self.jsonify(code=200)
|
||||||
|
|
||||||
@@ -492,27 +481,21 @@ 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 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))
|
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, rebuild=False)
|
acl.revoke_resource_from_role_by_rid(type_name, rid, ResourceTypeEnum.CI_TYPE, perms)
|
||||||
|
|
||||||
app_id = AppCache.get('cmdb').id
|
if PermEnum.READ in perms:
|
||||||
resource = None
|
CIFilterPermsCRUD().delete(type_id=type_id, rid=rid)
|
||||||
if PermEnum.READ in perms or not perms:
|
|
||||||
resource = CIFilterPermsCRUD().delete(type_id=type_id, rid=rid)
|
|
||||||
|
|
||||||
if not resource:
|
app_id = AppCache.get('cmdb').id
|
||||||
from api.tasks.acl import role_rebuild
|
users = RoleRelationCRUD.get_users_by_rid(rid, app_id)
|
||||||
from api.lib.perm.acl.const import ACL_QUEUE
|
for i in (users or []):
|
||||||
|
if i.get('role', {}).get('id') and not RoleCRUD.has_permission(
|
||||||
role_rebuild.apply_async(args=(rid, app_id), queue=ACL_QUEUE)
|
i.get('role').get('id'), type_name, ResourceTypeEnum.CI_TYPE, app_id, PermEnum.READ):
|
||||||
|
PreferenceManager.delete_by_type_id(type_id, i.get('uid'))
|
||||||
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'))
|
|
||||||
|
|
||||||
return self.jsonify(type_id=type_id, rid=rid)
|
return self.jsonify(type_id=type_id, rid=rid)
|
||||||
|
|
||||||
|
@@ -1,88 +0,0 @@
|
|||||||
from flask import abort, request
|
|
||||||
|
|
||||||
from api.lib.common_setting.common_data import AuthenticateDataCRUD
|
|
||||||
from api.lib.common_setting.const import TestType
|
|
||||||
from api.lib.common_setting.resp_format import ErrFormat
|
|
||||||
from api.lib.perm.acl.acl import role_required
|
|
||||||
from api.resource import APIView
|
|
||||||
|
|
||||||
prefix = '/auth_config'
|
|
||||||
|
|
||||||
|
|
||||||
class AuthConfigView(APIView):
|
|
||||||
url_prefix = (f'{prefix}/<string:auth_type>',)
|
|
||||||
|
|
||||||
@role_required("acl_admin")
|
|
||||||
def get(self, auth_type):
|
|
||||||
cli = AuthenticateDataCRUD(auth_type)
|
|
||||||
|
|
||||||
if auth_type not in cli.get_support_type_list():
|
|
||||||
abort(400, ErrFormat.not_support_auth_type.format(auth_type))
|
|
||||||
|
|
||||||
if auth_type in cli.common_type_list:
|
|
||||||
data = cli.get_record(True)
|
|
||||||
else:
|
|
||||||
data = cli.get_record_with_decrypt()
|
|
||||||
return self.jsonify(data)
|
|
||||||
|
|
||||||
@role_required("acl_admin")
|
|
||||||
def post(self, auth_type):
|
|
||||||
cli = AuthenticateDataCRUD(auth_type)
|
|
||||||
|
|
||||||
if auth_type not in cli.get_support_type_list():
|
|
||||||
abort(400, ErrFormat.not_support_auth_type.format(auth_type))
|
|
||||||
|
|
||||||
params = request.json
|
|
||||||
data = params.get('data', {})
|
|
||||||
if auth_type in cli.common_type_list:
|
|
||||||
data['encrypt'] = False
|
|
||||||
cli.create(data)
|
|
||||||
|
|
||||||
return self.jsonify(params)
|
|
||||||
|
|
||||||
|
|
||||||
class AuthConfigViewWithId(APIView):
|
|
||||||
url_prefix = (f'{prefix}/<string:auth_type>/<int:_id>',)
|
|
||||||
|
|
||||||
@role_required("acl_admin")
|
|
||||||
def put(self, auth_type, _id):
|
|
||||||
cli = AuthenticateDataCRUD(auth_type)
|
|
||||||
|
|
||||||
if auth_type not in cli.get_support_type_list():
|
|
||||||
abort(400, ErrFormat.not_support_auth_type.format(auth_type))
|
|
||||||
|
|
||||||
params = request.json
|
|
||||||
data = params.get('data', {})
|
|
||||||
if auth_type in cli.common_type_list:
|
|
||||||
data['encrypt'] = False
|
|
||||||
|
|
||||||
res = cli.update(_id, data)
|
|
||||||
|
|
||||||
return self.jsonify(res.to_dict())
|
|
||||||
|
|
||||||
@role_required("acl_admin")
|
|
||||||
def delete(self, auth_type, _id):
|
|
||||||
cli = AuthenticateDataCRUD(auth_type)
|
|
||||||
|
|
||||||
if auth_type not in cli.get_support_type_list():
|
|
||||||
abort(400, ErrFormat.not_support_auth_type.format(auth_type))
|
|
||||||
cli.delete(_id)
|
|
||||||
return self.jsonify({})
|
|
||||||
|
|
||||||
|
|
||||||
class AuthEnableListView(APIView):
|
|
||||||
url_prefix = (f'{prefix}/enable_list',)
|
|
||||||
|
|
||||||
method_decorators = []
|
|
||||||
|
|
||||||
def get(self):
|
|
||||||
return self.jsonify(AuthenticateDataCRUD.get_enable_list())
|
|
||||||
|
|
||||||
|
|
||||||
class AuthConfigTestView(APIView):
|
|
||||||
url_prefix = (f'{prefix}/<string:auth_type>/test',)
|
|
||||||
|
|
||||||
def post(self, auth_type):
|
|
||||||
test_type = request.values.get('test_type', TestType.Connect)
|
|
||||||
params = request.json
|
|
||||||
return self.jsonify(AuthenticateDataCRUD(auth_type).test(test_type, params.get('data')))
|
|
@@ -24,12 +24,12 @@ class DataView(APIView):
|
|||||||
class DataViewWithId(APIView):
|
class DataViewWithId(APIView):
|
||||||
url_prefix = (f'{prefix}/<string:data_type>/<int:_id>',)
|
url_prefix = (f'{prefix}/<string:data_type>/<int:_id>',)
|
||||||
|
|
||||||
def put(self, data_type, _id):
|
def put(self, _id):
|
||||||
params = request.json
|
params = request.json
|
||||||
res = CommonDataCRUD.update_data(_id, **params)
|
res = CommonDataCRUD.update_data(_id, **params)
|
||||||
|
|
||||||
return self.jsonify(res.to_dict())
|
return self.jsonify(res.to_dict())
|
||||||
|
|
||||||
def delete(self, data_type, _id):
|
def delete(self, _id):
|
||||||
CommonDataCRUD.delete(_id)
|
CommonDataCRUD.delete(_id)
|
||||||
return self.jsonify({})
|
return self.jsonify({})
|
||||||
|
@@ -3,10 +3,9 @@ import os
|
|||||||
|
|
||||||
from flask import request, abort, current_app, send_from_directory
|
from flask import request, abort, current_app, send_from_directory
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
import lz4.frame
|
|
||||||
|
|
||||||
from api.lib.common_setting.resp_format import ErrFormat
|
from api.lib.common_setting.resp_format import ErrFormat
|
||||||
from api.lib.common_setting.upload_file import allowed_file, generate_new_file_name, CommonFileCRUD
|
from api.lib.common_setting.upload_file import allowed_file, generate_new_file_name
|
||||||
from api.resource import APIView
|
from api.resource import APIView
|
||||||
|
|
||||||
prefix = '/file'
|
prefix = '/file'
|
||||||
@@ -29,8 +28,7 @@ class GetFileView(APIView):
|
|||||||
url_prefix = (f'{prefix}/<string:_filename>',)
|
url_prefix = (f'{prefix}/<string:_filename>',)
|
||||||
|
|
||||||
def get(self, _filename):
|
def get(self, _filename):
|
||||||
file_stream = CommonFileCRUD.get_file(_filename)
|
return send_from_directory(current_app.config['UPLOAD_DIRECTORY_FULL'], _filename, as_attachment=True)
|
||||||
return self.send_file(file_stream, as_attachment=True, download_name=_filename)
|
|
||||||
|
|
||||||
|
|
||||||
class PostFileView(APIView):
|
class PostFileView(APIView):
|
||||||
@@ -46,8 +44,6 @@ class PostFileView(APIView):
|
|||||||
if not file:
|
if not file:
|
||||||
abort(400, ErrFormat.file_is_required)
|
abort(400, ErrFormat.file_is_required)
|
||||||
extension = file.mimetype.split('/')[-1]
|
extension = file.mimetype.split('/')[-1]
|
||||||
if '+' in extension:
|
|
||||||
extension = file.filename.split('.')[-1]
|
|
||||||
if file.filename == '':
|
if file.filename == '':
|
||||||
filename = f'.{extension}'
|
filename = f'.{extension}'
|
||||||
else:
|
else:
|
||||||
@@ -57,20 +53,11 @@ class PostFileView(APIView):
|
|||||||
filename = file.filename
|
filename = file.filename
|
||||||
|
|
||||||
if allowed_file(filename, current_app.config.get('ALLOWED_EXTENSIONS', ALLOWED_EXTENSIONS)):
|
if allowed_file(filename, current_app.config.get('ALLOWED_EXTENSIONS', ALLOWED_EXTENSIONS)):
|
||||||
new_filename = generate_new_file_name(filename)
|
filename = generate_new_file_name(filename)
|
||||||
new_filename = secure_filename(new_filename)
|
filename = secure_filename(filename)
|
||||||
file_content = file.read()
|
file.save(os.path.join(
|
||||||
compressed_data = lz4.frame.compress(file_content)
|
current_app.config['UPLOAD_DIRECTORY_FULL'], filename))
|
||||||
try:
|
|
||||||
CommonFileCRUD.add_file(
|
|
||||||
origin_name=filename,
|
|
||||||
file_name=new_filename,
|
|
||||||
binary=compressed_data,
|
|
||||||
)
|
|
||||||
|
|
||||||
return self.jsonify(file_name=new_filename)
|
return self.jsonify(file_name=filename)
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.error(e)
|
|
||||||
abort(400, ErrFormat.upload_failed.format(e))
|
|
||||||
|
|
||||||
abort(400, ErrFormat.file_type_not_allowed.format(filename))
|
abort(400, 'Extension not allow')
|
||||||
|
@@ -34,7 +34,7 @@ cryptography>=41.0.2
|
|||||||
PyJWT==2.4.0
|
PyJWT==2.4.0
|
||||||
PyMySQL==1.1.0
|
PyMySQL==1.1.0
|
||||||
ldap3==2.9.1
|
ldap3==2.9.1
|
||||||
PyYAML==6.0.1
|
PyYAML==6.0
|
||||||
redis==4.6.0
|
redis==4.6.0
|
||||||
requests==2.31.0
|
requests==2.31.0
|
||||||
requests_oauthlib==1.3.1
|
requests_oauthlib==1.3.1
|
||||||
@@ -48,6 +48,6 @@ treelib==1.6.1
|
|||||||
Werkzeug>=2.3.6
|
Werkzeug>=2.3.6
|
||||||
WTForms==3.0.0
|
WTForms==3.0.0
|
||||||
shamir~=17.12.0
|
shamir~=17.12.0
|
||||||
|
hvac~=2.0.0
|
||||||
pycryptodomex>=3.19.0
|
pycryptodomex>=3.19.0
|
||||||
colorama>=0.4.6
|
colorama>=0.4.6
|
||||||
lz4>=4.3.2
|
|
@@ -11,10 +11,10 @@ from environs import Env
|
|||||||
env = Env()
|
env = Env()
|
||||||
env.read_env()
|
env.read_env()
|
||||||
|
|
||||||
ENV = env.str('FLASK_ENV', default='production')
|
ENV = env.str("FLASK_ENV", default="production")
|
||||||
DEBUG = ENV == 'development'
|
DEBUG = ENV == "development"
|
||||||
SECRET_KEY = env.str('SECRET_KEY')
|
SECRET_KEY = env.str("SECRET_KEY")
|
||||||
BCRYPT_LOG_ROUNDS = env.int('BCRYPT_LOG_ROUNDS', default=13)
|
BCRYPT_LOG_ROUNDS = env.int("BCRYPT_LOG_ROUNDS", default=13)
|
||||||
DEBUG_TB_ENABLED = DEBUG
|
DEBUG_TB_ENABLED = DEBUG
|
||||||
DEBUG_TB_INTERCEPT_REDIRECTS = False
|
DEBUG_TB_INTERCEPT_REDIRECTS = False
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ ERROR_CODES = [400, 401, 403, 404, 405, 500, 502]
|
|||||||
# # database
|
# # database
|
||||||
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://{user}:{password}@127.0.0.1:3306/{db}?charset=utf8'
|
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://{user}:{password}@127.0.0.1:3306/{db}?charset=utf8'
|
||||||
SQLALCHEMY_BINDS = {
|
SQLALCHEMY_BINDS = {
|
||||||
'user': 'mysql+pymysql://{user}:{password}@127.0.0.1:3306/{db}?charset=utf8'
|
"user": 'mysql+pymysql://{user}:{password}@127.0.0.1:3306/{db}?charset=utf8'
|
||||||
}
|
}
|
||||||
SQLALCHEMY_ECHO = False
|
SQLALCHEMY_ECHO = False
|
||||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||||
@@ -32,11 +32,11 @@ SQLALCHEMY_ENGINE_OPTIONS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# # cache
|
# # cache
|
||||||
CACHE_TYPE = 'redis'
|
CACHE_TYPE = "redis"
|
||||||
CACHE_REDIS_HOST = '127.0.0.1'
|
CACHE_REDIS_HOST = "127.0.0.1"
|
||||||
CACHE_REDIS_PORT = 6379
|
CACHE_REDIS_PORT = 6379
|
||||||
CACHE_REDIS_PASSWORD = ''
|
CACHE_REDIS_PASSWORD = ""
|
||||||
CACHE_KEY_PREFIX = 'CMDB::'
|
CACHE_KEY_PREFIX = "CMDB::"
|
||||||
CACHE_DEFAULT_TIMEOUT = 3000
|
CACHE_DEFAULT_TIMEOUT = 3000
|
||||||
|
|
||||||
# # log
|
# # log
|
||||||
@@ -55,10 +55,10 @@ DEFAULT_MAIL_SENDER = ''
|
|||||||
|
|
||||||
# # queue
|
# # queue
|
||||||
CELERY = {
|
CELERY = {
|
||||||
'broker_url': 'redis://127.0.0.1:6379/2',
|
"broker_url": 'redis://127.0.0.1:6379/2',
|
||||||
'result_backend': 'redis://127.0.0.1:6379/2',
|
"result_backend": "redis://127.0.0.1:6379/2",
|
||||||
'broker_vhost': '/',
|
"broker_vhost": "/",
|
||||||
'broker_connection_retry_on_startup': True
|
"broker_connection_retry_on_startup": True
|
||||||
}
|
}
|
||||||
ONCE = {
|
ONCE = {
|
||||||
'backend': 'celery_once.backends.Redis',
|
'backend': 'celery_once.backends.Redis',
|
||||||
@@ -67,81 +67,33 @@ ONCE = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# =============================== Authentication ===========================================================
|
# # SSO
|
||||||
|
CAS_SERVER = "http://sso.xxx.com"
|
||||||
|
CAS_VALIDATE_SERVER = "http://sso.xxx.com"
|
||||||
|
CAS_LOGIN_ROUTE = "/cas/login"
|
||||||
|
CAS_LOGOUT_ROUTE = "/cas/logout"
|
||||||
|
CAS_VALIDATE_ROUTE = "/cas/serviceValidate"
|
||||||
|
CAS_AFTER_LOGIN = "/"
|
||||||
|
DEFAULT_SERVICE = "http://127.0.0.1:8000"
|
||||||
|
|
||||||
# # CAS
|
# # ldap
|
||||||
CAS = dict(
|
AUTH_WITH_LDAP = False
|
||||||
enabled=False,
|
LDAP_SERVER = ''
|
||||||
cas_server='https://{your-CASServer-hostname}',
|
LDAP_DOMAIN = ''
|
||||||
cas_validate_server='https://{your-CASServer-hostname}',
|
LDAP_USER_DN = 'cn={},ou=users,dc=xxx,dc=com'
|
||||||
cas_login_route='/cas/built-in/cas/login',
|
|
||||||
cas_logout_route='/cas/built-in/cas/logout',
|
|
||||||
cas_validate_route='/cas/built-in/cas/serviceValidate',
|
|
||||||
cas_after_login='/',
|
|
||||||
cas_user_map={
|
|
||||||
'username': {'tag': 'cas:user'},
|
|
||||||
'nickname': {'tag': 'cas:attribute', 'attrs': {'name': 'displayName'}},
|
|
||||||
'email': {'tag': 'cas:attribute', 'attrs': {'name': 'email'}},
|
|
||||||
'mobile': {'tag': 'cas:attribute', 'attrs': {'name': 'phone'}},
|
|
||||||
'avatar': {'tag': 'cas:attribute', 'attrs': {'name': 'avatar'}},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# # OAuth2.0
|
|
||||||
OAUTH2 = dict(
|
|
||||||
enabled=False,
|
|
||||||
client_id='',
|
|
||||||
client_secret='',
|
|
||||||
authorize_url='https://{your-OAuth2Server-hostname}/login/oauth/authorize',
|
|
||||||
token_url='https://{your-OAuth2Server-hostname}/api/login/oauth/access_token',
|
|
||||||
scopes=['profile', 'email'],
|
|
||||||
user_info={
|
|
||||||
'url': 'https://{your-OAuth2Server-hostname}/api/userinfo',
|
|
||||||
'email': 'email',
|
|
||||||
'username': 'name',
|
|
||||||
'avatar': 'picture'
|
|
||||||
},
|
|
||||||
after_login='/'
|
|
||||||
)
|
|
||||||
|
|
||||||
# # OIDC
|
|
||||||
OIDC = dict(
|
|
||||||
enabled=False,
|
|
||||||
client_id='',
|
|
||||||
client_secret='',
|
|
||||||
authorize_url='https://{your-OIDCServer-hostname}/login/oauth/authorize',
|
|
||||||
token_url='https://{your-OIDCServer-hostname}/api/login/oauth/access_token',
|
|
||||||
scopes=['openid', 'profile', 'email'],
|
|
||||||
user_info={
|
|
||||||
'url': 'https://{your-OIDCServer-hostname}/api/userinfo',
|
|
||||||
'email': 'email',
|
|
||||||
'username': 'name',
|
|
||||||
'avatar': 'picture'
|
|
||||||
},
|
|
||||||
after_login='/'
|
|
||||||
)
|
|
||||||
|
|
||||||
# # LDAP
|
|
||||||
LDAP = dict(
|
|
||||||
enabled=False,
|
|
||||||
ldap_server='',
|
|
||||||
ldap_domain='',
|
|
||||||
ldap_user_dn='cn={},ou=users,dc=xxx,dc=com'
|
|
||||||
)
|
|
||||||
# ==========================================================================================================
|
|
||||||
|
|
||||||
# # pagination
|
# # pagination
|
||||||
DEFAULT_PAGE_COUNT = 50
|
DEFAULT_PAGE_COUNT = 50
|
||||||
|
|
||||||
# # permission
|
# # permission
|
||||||
WHITE_LIST = ['127.0.0.1']
|
WHITE_LIST = ["127.0.0.1"]
|
||||||
USE_ACL = True
|
USE_ACL = True
|
||||||
|
|
||||||
# # elastic search
|
# # elastic search
|
||||||
ES_HOST = '127.0.0.1'
|
ES_HOST = '127.0.0.1'
|
||||||
USE_ES = False
|
USE_ES = False
|
||||||
|
|
||||||
BOOL_TRUE = ['true', 'TRUE', 'True', True, '1', 1, 'Yes', 'YES', 'yes', 'Y', 'y']
|
BOOL_TRUE = ['true', 'TRUE', 'True', True, '1', 1, "Yes", "YES", "yes", 'Y', 'y']
|
||||||
|
|
||||||
# # messenger
|
# # messenger
|
||||||
USE_MESSENGER = True
|
USE_MESSENGER = True
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: "iconfont"; /* Project id 3857903 */
|
font-family: "iconfont"; /* Project id 3857903 */
|
||||||
src: url('iconfont.woff2?t=1702544951995') format('woff2'),
|
src: url('iconfont.woff2?t=1698273699449') format('woff2'),
|
||||||
url('iconfont.woff?t=1702544951995') format('woff'),
|
url('iconfont.woff?t=1698273699449') format('woff'),
|
||||||
url('iconfont.ttf?t=1702544951995') format('truetype');
|
url('iconfont.ttf?t=1698273699449') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
@@ -13,274 +13,6 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
.OAUTH2:before {
|
|
||||||
content: "\e8d8";
|
|
||||||
}
|
|
||||||
|
|
||||||
.OIDC:before {
|
|
||||||
content: "\e8d6";
|
|
||||||
}
|
|
||||||
|
|
||||||
.CAS:before {
|
|
||||||
content: "\e8d7";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-setting-auth:before {
|
|
||||||
content: "\e8d5";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-setting-auth-selected:before {
|
|
||||||
content: "\e8d4";
|
|
||||||
}
|
|
||||||
|
|
||||||
.a-itsm-knowledge2:before {
|
|
||||||
content: "\e8d2";
|
|
||||||
}
|
|
||||||
|
|
||||||
.itsm-qrdownload:before {
|
|
||||||
content: "\e8d3";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-playback:before {
|
|
||||||
content: "\e8d1";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-disconnect:before {
|
|
||||||
content: "\e8d0";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-publickey-selected:before {
|
|
||||||
content: "\e8cf";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-publickey:before {
|
|
||||||
content: "\e8ce";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-gateway:before {
|
|
||||||
content: "\e8b9";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-gateway-selected:before {
|
|
||||||
content: "\e8bf";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-account:before {
|
|
||||||
content: "\e8c0";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-account-selected:before {
|
|
||||||
content: "\e8c1";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-command:before {
|
|
||||||
content: "\e8c2";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-command-selected:before {
|
|
||||||
content: "\e8c3";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-assetlist:before {
|
|
||||||
content: "\e8c4";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-assetlist-selected:before {
|
|
||||||
content: "\e8c5";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-sessiononline:before {
|
|
||||||
content: "\e8c6";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-sessiononline-selected:before {
|
|
||||||
content: "\e8c7";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-sessionhistory-selected:before {
|
|
||||||
content: "\e8c8";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-sessionhistory:before {
|
|
||||||
content: "\e8c9";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-login:before {
|
|
||||||
content: "\e8ca";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-login-selected:before {
|
|
||||||
content: "\e8cb";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-operation:before {
|
|
||||||
content: "\e8cc";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-operation-selected:before {
|
|
||||||
content: "\e8cd";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-workstation-selected:before {
|
|
||||||
content: "\e8b7";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-workstation:before {
|
|
||||||
content: "\e8b8";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-file-selected:before {
|
|
||||||
content: "\e8be";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-file:before {
|
|
||||||
content: "\e8bc";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-time:before {
|
|
||||||
content: "\e8bd";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-download:before {
|
|
||||||
content: "\e8bb";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-commandrecord:before {
|
|
||||||
content: "\e8ba";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-asset:before {
|
|
||||||
content: "\e8b6";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-total_asset:before {
|
|
||||||
content: "\e8b5";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-switch:before {
|
|
||||||
content: "\e8b4";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-session:before {
|
|
||||||
content: "\e8b3";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-connect:before {
|
|
||||||
content: "\e8b2";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-login:before {
|
|
||||||
content: "\e8b1";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-dashboard:before {
|
|
||||||
content: "\e8af";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-dashboard-selected:before {
|
|
||||||
content: "\e8b0";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-recentsession:before {
|
|
||||||
content: "\e8ae";
|
|
||||||
}
|
|
||||||
|
|
||||||
.oneterm-myassets:before {
|
|
||||||
content: "\e8ad";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-log:before {
|
|
||||||
content: "\e8aa";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-session-selected:before {
|
|
||||||
content: "\e8ab";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-session:before {
|
|
||||||
content: "\e8ac";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-log-selected:before {
|
|
||||||
content: "\e8a9";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-assets:before {
|
|
||||||
content: "\e8a7";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-oneterm-assets-selected:before {
|
|
||||||
content: "\e8a8";
|
|
||||||
}
|
|
||||||
|
|
||||||
.itsm-down:before {
|
|
||||||
content: "\e8a5";
|
|
||||||
}
|
|
||||||
|
|
||||||
.itsm-up:before {
|
|
||||||
content: "\e8a6";
|
|
||||||
}
|
|
||||||
|
|
||||||
.itsm-download:before {
|
|
||||||
content: "\e8a4";
|
|
||||||
}
|
|
||||||
|
|
||||||
.itsm-print:before {
|
|
||||||
content: "\e8a3";
|
|
||||||
}
|
|
||||||
|
|
||||||
.itsm-view:before {
|
|
||||||
content: "\e8a2";
|
|
||||||
}
|
|
||||||
|
|
||||||
.itsm-word:before {
|
|
||||||
content: "\e8a1";
|
|
||||||
}
|
|
||||||
|
|
||||||
.datainsight-custom:before {
|
|
||||||
content: "\e89e";
|
|
||||||
}
|
|
||||||
|
|
||||||
.datainsight-prometheus:before {
|
|
||||||
content: "\e89f";
|
|
||||||
}
|
|
||||||
|
|
||||||
.datainsight-zabbix:before {
|
|
||||||
content: "\e8a0";
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-mainpeople:before {
|
|
||||||
content: "\e89a";
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-deputypeople:before {
|
|
||||||
content: "\e89d";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-setting-duty:before {
|
|
||||||
content: "\e89c";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-setting-duty-selected:before {
|
|
||||||
content: "\e89b";
|
|
||||||
}
|
|
||||||
|
|
||||||
.datainsight-sequential:before {
|
|
||||||
content: "\e899";
|
|
||||||
}
|
|
||||||
|
|
||||||
.datainsight-close:before {
|
|
||||||
content: "\e898";
|
|
||||||
}
|
|
||||||
|
|
||||||
.datainsight-handle:before {
|
|
||||||
content: "\e897";
|
|
||||||
}
|
|
||||||
|
|
||||||
.datainsight-table:before {
|
|
||||||
content: "\e896";
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-xianxing-password:before {
|
.icon-xianxing-password:before {
|
||||||
content: "\e894";
|
content: "\e894";
|
||||||
}
|
}
|
||||||
@@ -289,11 +21,11 @@
|
|||||||
content: "\e895";
|
content: "\e895";
|
||||||
}
|
}
|
||||||
|
|
||||||
.itsm-download-all:before {
|
.a-itsm-oneclickdownload:before {
|
||||||
content: "\e892";
|
content: "\e892";
|
||||||
}
|
}
|
||||||
|
|
||||||
.itsm-download-package:before {
|
.a-itsm-packagedownload:before {
|
||||||
content: "\e893";
|
content: "\e893";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -5,475 +5,6 @@
|
|||||||
"css_prefix_text": "",
|
"css_prefix_text": "",
|
||||||
"description": "",
|
"description": "",
|
||||||
"glyphs": [
|
"glyphs": [
|
||||||
{
|
|
||||||
"icon_id": "38566548",
|
|
||||||
"name": "OAuth2.0",
|
|
||||||
"font_class": "OAUTH2",
|
|
||||||
"unicode": "e8d8",
|
|
||||||
"unicode_decimal": 59608
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38566584",
|
|
||||||
"name": "OIDC",
|
|
||||||
"font_class": "OIDC",
|
|
||||||
"unicode": "e8d6",
|
|
||||||
"unicode_decimal": 59606
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38566578",
|
|
||||||
"name": "cas",
|
|
||||||
"font_class": "CAS",
|
|
||||||
"unicode": "e8d7",
|
|
||||||
"unicode_decimal": 59607
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38547395",
|
|
||||||
"name": "setting-authentication",
|
|
||||||
"font_class": "ops-setting-auth",
|
|
||||||
"unicode": "e8d5",
|
|
||||||
"unicode_decimal": 59605
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38547389",
|
|
||||||
"name": "setting-authentication-selected",
|
|
||||||
"font_class": "ops-setting-auth-selected",
|
|
||||||
"unicode": "e8d4",
|
|
||||||
"unicode_decimal": 59604
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38533133",
|
|
||||||
"name": "itsm-knowledge (2)",
|
|
||||||
"font_class": "a-itsm-knowledge2",
|
|
||||||
"unicode": "e8d2",
|
|
||||||
"unicode_decimal": 59602
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38531868",
|
|
||||||
"name": "itsm-QRcode",
|
|
||||||
"font_class": "itsm-qrdownload",
|
|
||||||
"unicode": "e8d3",
|
|
||||||
"unicode_decimal": 59603
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38413515",
|
|
||||||
"name": "oneterm-playback",
|
|
||||||
"font_class": "oneterm-playback",
|
|
||||||
"unicode": "e8d1",
|
|
||||||
"unicode_decimal": 59601
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38413481",
|
|
||||||
"name": "oneterm-disconnect",
|
|
||||||
"font_class": "oneterm-disconnect",
|
|
||||||
"unicode": "e8d0",
|
|
||||||
"unicode_decimal": 59600
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38407867",
|
|
||||||
"name": "oneterm-key-selected",
|
|
||||||
"font_class": "ops-oneterm-publickey-selected",
|
|
||||||
"unicode": "e8cf",
|
|
||||||
"unicode_decimal": 59599
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38407915",
|
|
||||||
"name": "oneterm-key",
|
|
||||||
"font_class": "ops-oneterm-publickey",
|
|
||||||
"unicode": "e8ce",
|
|
||||||
"unicode_decimal": 59598
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38311855",
|
|
||||||
"name": "oneterm-gateway",
|
|
||||||
"font_class": "ops-oneterm-gateway",
|
|
||||||
"unicode": "e8b9",
|
|
||||||
"unicode_decimal": 59577
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38311938",
|
|
||||||
"name": "oneterm-gateway-selected",
|
|
||||||
"font_class": "ops-oneterm-gateway-selected",
|
|
||||||
"unicode": "e8bf",
|
|
||||||
"unicode_decimal": 59583
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38311957",
|
|
||||||
"name": "oneterm-account",
|
|
||||||
"font_class": "ops-oneterm-account",
|
|
||||||
"unicode": "e8c0",
|
|
||||||
"unicode_decimal": 59584
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38311961",
|
|
||||||
"name": "oneterm-account-selected",
|
|
||||||
"font_class": "ops-oneterm-account-selected",
|
|
||||||
"unicode": "e8c1",
|
|
||||||
"unicode_decimal": 59585
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38311974",
|
|
||||||
"name": "oneterm-command",
|
|
||||||
"font_class": "ops-oneterm-command",
|
|
||||||
"unicode": "e8c2",
|
|
||||||
"unicode_decimal": 59586
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38311976",
|
|
||||||
"name": "oneterm-command-selected",
|
|
||||||
"font_class": "ops-oneterm-command-selected",
|
|
||||||
"unicode": "e8c3",
|
|
||||||
"unicode_decimal": 59587
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38311979",
|
|
||||||
"name": "oneterm-asset_list",
|
|
||||||
"font_class": "ops-oneterm-assetlist",
|
|
||||||
"unicode": "e8c4",
|
|
||||||
"unicode_decimal": 59588
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38311985",
|
|
||||||
"name": "oneterm-asset_list-selected",
|
|
||||||
"font_class": "ops-oneterm-assetlist-selected",
|
|
||||||
"unicode": "e8c5",
|
|
||||||
"unicode_decimal": 59589
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38312030",
|
|
||||||
"name": "oneterm-online",
|
|
||||||
"font_class": "ops-oneterm-sessiononline",
|
|
||||||
"unicode": "e8c6",
|
|
||||||
"unicode_decimal": 59590
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38312152",
|
|
||||||
"name": "oneterm-online-selected",
|
|
||||||
"font_class": "ops-oneterm-sessiononline-selected",
|
|
||||||
"unicode": "e8c7",
|
|
||||||
"unicode_decimal": 59591
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38312154",
|
|
||||||
"name": "oneterm-history-selected",
|
|
||||||
"font_class": "ops-oneterm-sessionhistory-selected",
|
|
||||||
"unicode": "e8c8",
|
|
||||||
"unicode_decimal": 59592
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38312155",
|
|
||||||
"name": "oneterm-history",
|
|
||||||
"font_class": "ops-oneterm-sessionhistory",
|
|
||||||
"unicode": "e8c9",
|
|
||||||
"unicode_decimal": 59593
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38312404",
|
|
||||||
"name": "oneterm-entry_log",
|
|
||||||
"font_class": "ops-oneterm-login",
|
|
||||||
"unicode": "e8ca",
|
|
||||||
"unicode_decimal": 59594
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38312423",
|
|
||||||
"name": "oneterm-entry_log-selected",
|
|
||||||
"font_class": "ops-oneterm-login-selected",
|
|
||||||
"unicode": "e8cb",
|
|
||||||
"unicode_decimal": 59595
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38312426",
|
|
||||||
"name": "oneterm-operation_log",
|
|
||||||
"font_class": "ops-oneterm-operation",
|
|
||||||
"unicode": "e8cc",
|
|
||||||
"unicode_decimal": 59596
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38312445",
|
|
||||||
"name": "oneterm-operation_log-selected",
|
|
||||||
"font_class": "ops-oneterm-operation-selected",
|
|
||||||
"unicode": "e8cd",
|
|
||||||
"unicode_decimal": 59597
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38307876",
|
|
||||||
"name": "oneterm-workstation-selected",
|
|
||||||
"font_class": "ops-oneterm-workstation-selected",
|
|
||||||
"unicode": "e8b7",
|
|
||||||
"unicode_decimal": 59575
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38307871",
|
|
||||||
"name": "oneterm-workstation",
|
|
||||||
"font_class": "ops-oneterm-workstation",
|
|
||||||
"unicode": "e8b8",
|
|
||||||
"unicode_decimal": 59576
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38302246",
|
|
||||||
"name": "oneterm-file-selected",
|
|
||||||
"font_class": "oneterm-file-selected",
|
|
||||||
"unicode": "e8be",
|
|
||||||
"unicode_decimal": 59582
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38302255",
|
|
||||||
"name": "oneterm-file",
|
|
||||||
"font_class": "oneterm-file",
|
|
||||||
"unicode": "e8bc",
|
|
||||||
"unicode_decimal": 59580
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38203528",
|
|
||||||
"name": "oneterm-time",
|
|
||||||
"font_class": "oneterm-time",
|
|
||||||
"unicode": "e8bd",
|
|
||||||
"unicode_decimal": 59581
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38203331",
|
|
||||||
"name": "oneterm-download",
|
|
||||||
"font_class": "oneterm-download",
|
|
||||||
"unicode": "e8bb",
|
|
||||||
"unicode_decimal": 59579
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38201351",
|
|
||||||
"name": "oneterm-command record",
|
|
||||||
"font_class": "oneterm-commandrecord",
|
|
||||||
"unicode": "e8ba",
|
|
||||||
"unicode_decimal": 59578
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38199341",
|
|
||||||
"name": "oneterm-connected assets",
|
|
||||||
"font_class": "oneterm-asset",
|
|
||||||
"unicode": "e8b6",
|
|
||||||
"unicode_decimal": 59574
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38199350",
|
|
||||||
"name": "oneterm-total assets",
|
|
||||||
"font_class": "oneterm-total_asset",
|
|
||||||
"unicode": "e8b5",
|
|
||||||
"unicode_decimal": 59573
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38199303",
|
|
||||||
"name": "oneterm-switch (3)",
|
|
||||||
"font_class": "oneterm-switch",
|
|
||||||
"unicode": "e8b4",
|
|
||||||
"unicode_decimal": 59572
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38199317",
|
|
||||||
"name": "oneterm-session",
|
|
||||||
"font_class": "oneterm-session",
|
|
||||||
"unicode": "e8b3",
|
|
||||||
"unicode_decimal": 59571
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38199339",
|
|
||||||
"name": "oneterm-connection",
|
|
||||||
"font_class": "oneterm-connect",
|
|
||||||
"unicode": "e8b2",
|
|
||||||
"unicode_decimal": 59570
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38198321",
|
|
||||||
"name": "oneterm-log in",
|
|
||||||
"font_class": "oneterm-login",
|
|
||||||
"unicode": "e8b1",
|
|
||||||
"unicode_decimal": 59569
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194554",
|
|
||||||
"name": "oneterm-dashboard",
|
|
||||||
"font_class": "ops-oneterm-dashboard",
|
|
||||||
"unicode": "e8af",
|
|
||||||
"unicode_decimal": 59567
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194525",
|
|
||||||
"name": "oneterm-dashboard-selected",
|
|
||||||
"font_class": "ops-oneterm-dashboard-selected",
|
|
||||||
"unicode": "e8b0",
|
|
||||||
"unicode_decimal": 59568
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194352",
|
|
||||||
"name": "oneterm-recent session",
|
|
||||||
"font_class": "oneterm-recentsession",
|
|
||||||
"unicode": "e8ae",
|
|
||||||
"unicode_decimal": 59566
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194383",
|
|
||||||
"name": "oneterm-my assets",
|
|
||||||
"font_class": "oneterm-myassets",
|
|
||||||
"unicode": "e8ad",
|
|
||||||
"unicode_decimal": 59565
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194089",
|
|
||||||
"name": "oneterm-log",
|
|
||||||
"font_class": "ops-oneterm-log",
|
|
||||||
"unicode": "e8aa",
|
|
||||||
"unicode_decimal": 59562
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194088",
|
|
||||||
"name": "oneterm-conversation-selected",
|
|
||||||
"font_class": "ops-oneterm-session-selected",
|
|
||||||
"unicode": "e8ab",
|
|
||||||
"unicode_decimal": 59563
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194065",
|
|
||||||
"name": "oneterm-conversation",
|
|
||||||
"font_class": "ops-oneterm-session",
|
|
||||||
"unicode": "e8ac",
|
|
||||||
"unicode_decimal": 59564
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194105",
|
|
||||||
"name": "oneterm-log-selected",
|
|
||||||
"font_class": "ops-oneterm-log-selected",
|
|
||||||
"unicode": "e8a9",
|
|
||||||
"unicode_decimal": 59561
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194054",
|
|
||||||
"name": "oneterm-assets",
|
|
||||||
"font_class": "ops-oneterm-assets",
|
|
||||||
"unicode": "e8a7",
|
|
||||||
"unicode_decimal": 59559
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38194055",
|
|
||||||
"name": "oneterm-assets-selected",
|
|
||||||
"font_class": "ops-oneterm-assets-selected",
|
|
||||||
"unicode": "e8a8",
|
|
||||||
"unicode_decimal": 59560
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38123087",
|
|
||||||
"name": "itsm-down",
|
|
||||||
"font_class": "itsm-down",
|
|
||||||
"unicode": "e8a5",
|
|
||||||
"unicode_decimal": 59557
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38123084",
|
|
||||||
"name": "itsm-up",
|
|
||||||
"font_class": "itsm-up",
|
|
||||||
"unicode": "e8a6",
|
|
||||||
"unicode_decimal": 59558
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38105374",
|
|
||||||
"name": "itsm-download",
|
|
||||||
"font_class": "itsm-download",
|
|
||||||
"unicode": "e8a4",
|
|
||||||
"unicode_decimal": 59556
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38105235",
|
|
||||||
"name": "itsm-print",
|
|
||||||
"font_class": "itsm-print",
|
|
||||||
"unicode": "e8a3",
|
|
||||||
"unicode_decimal": 59555
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38104997",
|
|
||||||
"name": "itsm-view",
|
|
||||||
"font_class": "itsm-view",
|
|
||||||
"unicode": "e8a2",
|
|
||||||
"unicode_decimal": 59554
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38105129",
|
|
||||||
"name": "itsm-word",
|
|
||||||
"font_class": "itsm-word",
|
|
||||||
"unicode": "e8a1",
|
|
||||||
"unicode_decimal": 59553
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38095730",
|
|
||||||
"name": "datainsight-custom",
|
|
||||||
"font_class": "datainsight-custom",
|
|
||||||
"unicode": "e89e",
|
|
||||||
"unicode_decimal": 59550
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38095729",
|
|
||||||
"name": "datainsight-prometheus",
|
|
||||||
"font_class": "datainsight-prometheus",
|
|
||||||
"unicode": "e89f",
|
|
||||||
"unicode_decimal": 59551
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "38095728",
|
|
||||||
"name": "datainsight-zabbix",
|
|
||||||
"font_class": "datainsight-zabbix",
|
|
||||||
"unicode": "e8a0",
|
|
||||||
"unicode_decimal": 59552
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "37944507",
|
|
||||||
"name": "setting-main people",
|
|
||||||
"font_class": "setting-mainpeople",
|
|
||||||
"unicode": "e89a",
|
|
||||||
"unicode_decimal": 59546
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "37944503",
|
|
||||||
"name": "setting-deputy people",
|
|
||||||
"font_class": "setting-deputypeople",
|
|
||||||
"unicode": "e89d",
|
|
||||||
"unicode_decimal": 59549
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "37940080",
|
|
||||||
"name": "ops-setting-duty",
|
|
||||||
"font_class": "ops-setting-duty",
|
|
||||||
"unicode": "e89c",
|
|
||||||
"unicode_decimal": 59548
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "37940033",
|
|
||||||
"name": "ops-setting-duty-selected",
|
|
||||||
"font_class": "ops-setting-duty-selected",
|
|
||||||
"unicode": "e89b",
|
|
||||||
"unicode_decimal": 59547
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "37841524",
|
|
||||||
"name": "datainsight-sequential",
|
|
||||||
"font_class": "datainsight-sequential",
|
|
||||||
"unicode": "e899",
|
|
||||||
"unicode_decimal": 59545
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "37841535",
|
|
||||||
"name": "datainsight-close",
|
|
||||||
"font_class": "datainsight-close",
|
|
||||||
"unicode": "e898",
|
|
||||||
"unicode_decimal": 59544
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "37841537",
|
|
||||||
"name": "datainsight-handle",
|
|
||||||
"font_class": "datainsight-handle",
|
|
||||||
"unicode": "e897",
|
|
||||||
"unicode_decimal": 59543
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"icon_id": "37841515",
|
|
||||||
"name": "datainsight-table",
|
|
||||||
"font_class": "datainsight-table",
|
|
||||||
"unicode": "e896",
|
|
||||||
"unicode_decimal": 59542
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"icon_id": "37830610",
|
"icon_id": "37830610",
|
||||||
"name": "icon-xianxing-password",
|
"name": "icon-xianxing-password",
|
||||||
@@ -491,14 +22,14 @@
|
|||||||
{
|
{
|
||||||
"icon_id": "37822199",
|
"icon_id": "37822199",
|
||||||
"name": "itsm-oneclick download",
|
"name": "itsm-oneclick download",
|
||||||
"font_class": "itsm-download-all",
|
"font_class": "a-itsm-oneclickdownload",
|
||||||
"unicode": "e892",
|
"unicode": "e892",
|
||||||
"unicode_decimal": 59538
|
"unicode_decimal": 59538
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "37822198",
|
"icon_id": "37822198",
|
||||||
"name": "itsm-package download",
|
"name": "itsm-package download",
|
||||||
"font_class": "itsm-download-package",
|
"font_class": "a-itsm-packagedownload",
|
||||||
"unicode": "e893",
|
"unicode": "e893",
|
||||||
"unicode_decimal": 59539
|
"unicode_decimal": 59539
|
||||||
},
|
},
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,39 +0,0 @@
|
|||||||
import { axios } from '@/utils/request'
|
|
||||||
|
|
||||||
export function getAuthData(data_type) {
|
|
||||||
return axios({
|
|
||||||
url: `/common-setting/v1/auth_config/${data_type}`,
|
|
||||||
method: 'get',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function postAuthData(data_type, data) {
|
|
||||||
return axios({
|
|
||||||
url: `/common-setting/v1/auth_config/${data_type}`,
|
|
||||||
method: 'post',
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function putAuthData(data_type, id, data) {
|
|
||||||
return axios({
|
|
||||||
url: `/common-setting/v1/auth_config/${data_type}/${id}`,
|
|
||||||
method: 'put',
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAuthDataEnable() {
|
|
||||||
return axios({
|
|
||||||
url: `/common-setting/v1/auth_config/enable_list`,
|
|
||||||
method: 'get',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function testLDAP(test_type, data) {
|
|
||||||
return axios({
|
|
||||||
url: `/common-setting/v1/auth_config/LDAP/test?test_type=${test_type}`,
|
|
||||||
method: 'post',
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
}
|
|
@@ -1,6 +1,8 @@
|
|||||||
|
import config from '@/config/setting'
|
||||||
|
|
||||||
const api = {
|
const api = {
|
||||||
Login: '/v1/acl/login',
|
Login: config.useSSO ? '/api/sso/login' : '/v1/acl/login',
|
||||||
Logout: '/v1/acl/logout',
|
Logout: config.useSSO ? '/api/sso/logout' : '/v1/acl/logout',
|
||||||
ForgePassword: '/auth/forge-password',
|
ForgePassword: '/auth/forge-password',
|
||||||
Register: '/auth/register',
|
Register: '/auth/register',
|
||||||
twoStepCode: '/auth/2step-code',
|
twoStepCode: '/auth/2step-code',
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import api from './index'
|
import api from './index'
|
||||||
import { axios } from '@/utils/request'
|
import { axios } from '@/utils/request'
|
||||||
|
import config from '@/config/setting'
|
||||||
/**
|
/**
|
||||||
* login func
|
* login func
|
||||||
* parameter: {
|
* parameter: {
|
||||||
@@ -11,10 +12,9 @@ import { axios } from '@/utils/request'
|
|||||||
* @param parameter
|
* @param parameter
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
export function login(data, auth_type) {
|
export function login(data) {
|
||||||
if (auth_type) {
|
if (config.useSSO) {
|
||||||
localStorage.setItem('ops_auth_type', auth_type)
|
window.location.href = config.ssoLoginUrl
|
||||||
window.location.href = `/api/${auth_type.toLowerCase()}/login`
|
|
||||||
} else {
|
} else {
|
||||||
return axios({
|
return axios({
|
||||||
url: api.Login,
|
url: api.Login,
|
||||||
@@ -43,15 +43,17 @@ export function getInfo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function logout() {
|
export function logout() {
|
||||||
const auth_type = localStorage.getItem('ops_auth_type')
|
if (config.useSSO) {
|
||||||
localStorage.clear()
|
window.location.replace(api.Logout)
|
||||||
return axios({
|
} else {
|
||||||
url: auth_type ? `/${auth_type.toLowerCase()}/logout` : api.Logout,
|
return axios({
|
||||||
method: auth_type ? 'get' : 'post',
|
url: api.Logout,
|
||||||
headers: {
|
method: 'post',
|
||||||
'Content-Type': 'application/json;charset=UTF-8'
|
headers: {
|
||||||
}
|
'Content-Type': 'application/json;charset=UTF-8'
|
||||||
})
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 78 KiB |
@@ -95,10 +95,6 @@ export default {
|
|||||||
const unsubTree = subscribeTreeView(citypeId, '')
|
const unsubTree = subscribeTreeView(citypeId, '')
|
||||||
Promise.all([unsubCIType, unsubTree]).then(() => {
|
Promise.all([unsubCIType, unsubTree]).then(() => {
|
||||||
that.$message.success('取消订阅成功')
|
that.$message.success('取消订阅成功')
|
||||||
const lastTypeId = window.localStorage.getItem('ops_ci_typeid') || undefined
|
|
||||||
if (Number(citypeId) === Number(lastTypeId)) {
|
|
||||||
localStorage.setItem('ops_ci_typeid', '')
|
|
||||||
}
|
|
||||||
// 删除路由
|
// 删除路由
|
||||||
const href = window.location.href
|
const href = window.location.href
|
||||||
const hrefSplit = href.split('/')
|
const hrefSplit = href.split('/')
|
||||||
|
@@ -66,8 +66,10 @@ export default {
|
|||||||
|
|
||||||
this.$confirm({
|
this.$confirm({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: '确认注销登录 ?',
|
content: '真的要注销登录吗 ?',
|
||||||
onOk() {
|
onOk() {
|
||||||
|
// localStorage.removeItem('ops_cityps_currentId')
|
||||||
|
localStorage.clear()
|
||||||
return that.Logout()
|
return that.Logout()
|
||||||
},
|
},
|
||||||
onCancel() {},
|
onCancel() {},
|
||||||
|
@@ -2,6 +2,7 @@ const appConfig = {
|
|||||||
buildModules: ['cmdb', 'acl'], // 需要编译的模块
|
buildModules: ['cmdb', 'acl'], // 需要编译的模块
|
||||||
redirectTo: '/cmdb', // 首页的重定向路径
|
redirectTo: '/cmdb', // 首页的重定向路径
|
||||||
buildAclToModules: true, // 是否在各个应用下 内联权限管理
|
buildAclToModules: true, // 是否在各个应用下 内联权限管理
|
||||||
|
ssoLogoutURL: '/api/sso/logout',
|
||||||
showDocs: false,
|
showDocs: false,
|
||||||
useEncryption: false,
|
useEncryption: false,
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* 项目默认配置项
|
* 项目默认配置项
|
||||||
|
* useSSO - 是否启用单点登录, 默认为否, 可以根据需要接入到公司的单点登录系统
|
||||||
* primaryColor - 默认主题色, 如果修改颜色不生效,请清理 localStorage
|
* primaryColor - 默认主题色, 如果修改颜色不生效,请清理 localStorage
|
||||||
* navTheme - sidebar theme ['dark', 'light'] 两种主题
|
* navTheme - sidebar theme ['dark', 'light'] 两种主题
|
||||||
* colorWeak - 色盲模式
|
* colorWeak - 色盲模式
|
||||||
@@ -14,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
useSSO: false,
|
||||||
|
ssoLoginUrl: '/api/sso/login',
|
||||||
primaryColor: '#1890ff', // primary color of ant design
|
primaryColor: '#1890ff', // primary color of ant design
|
||||||
navTheme: 'dark', // theme for nav menu
|
navTheme: 'dark', // theme for nav menu
|
||||||
layout: 'sidemenu', // nav menu position: sidemenu or topmenu
|
layout: 'sidemenu', // nav menu position: sidemenu or topmenu
|
||||||
|
@@ -6,6 +6,7 @@ import store from './store'
|
|||||||
import NProgress from 'nprogress'
|
import NProgress from 'nprogress'
|
||||||
import 'nprogress/nprogress.css'
|
import 'nprogress/nprogress.css'
|
||||||
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
|
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
|
||||||
|
import config from '@/config/setting'
|
||||||
import { ACCESS_TOKEN } from './store/global/mutation-types'
|
import { ACCESS_TOKEN } from './store/global/mutation-types'
|
||||||
|
|
||||||
NProgress.configure({ showSpinner: false })
|
NProgress.configure({ showSpinner: false })
|
||||||
@@ -15,16 +16,16 @@ const whitePath = ['/user/login', '/user/logout', '/user/register', '/api/sso/lo
|
|||||||
|
|
||||||
// 此处不处理登录, 只处理 是否有用户信息的认证 前端permission的处理 axios处理401 -> 登录
|
// 此处不处理登录, 只处理 是否有用户信息的认证 前端permission的处理 axios处理401 -> 登录
|
||||||
// 登录页面处理处理 是否使用单点登录
|
// 登录页面处理处理 是否使用单点登录
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
NProgress.start() // start progress bar
|
NProgress.start() // start progress bar
|
||||||
to.meta && (!!to.meta.title && setDocumentTitle(`${to.meta.title} - ${domTitle}`))
|
to.meta && (!!to.meta.title && setDocumentTitle(`${to.meta.title} - ${domTitle}`))
|
||||||
|
|
||||||
const authed = store.state.authed
|
const authed = store.state.authed
|
||||||
const auth_type = localStorage.getItem('ops_auth_type')
|
|
||||||
|
|
||||||
if (whitePath.includes(to.path)) {
|
if (whitePath.includes(to.path)) {
|
||||||
next()
|
next()
|
||||||
} else if ((auth_type || (!auth_type && Vue.ls.get(ACCESS_TOKEN))) && store.getters.roles.length === 0) {
|
} else if ((config.useSSO || (!config.useSSO && Vue.ls.get(ACCESS_TOKEN))) && store.getters.roles.length === 0) {
|
||||||
store.dispatch('GetAuthDataEnable')
|
|
||||||
store.dispatch('GetInfo').then(res => {
|
store.dispatch('GetInfo').then(res => {
|
||||||
const roles = res.result && res.result.role
|
const roles = res.result && res.result.role
|
||||||
store.dispatch("loadAllUsers")
|
store.dispatch("loadAllUsers")
|
||||||
@@ -45,17 +46,10 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
setTimeout(() => { store.dispatch('Logout') }, 3000)
|
setTimeout(() => { store.dispatch('Logout') }, 3000)
|
||||||
})
|
})
|
||||||
} else if (to.path === '/user/login' && !auth_type && store.getters.roles.length !== 0) {
|
} else if (to.path === '/user/login' && !config.useSSO && store.getters.roles.length !== 0) {
|
||||||
next({ path: '/' })
|
next({ path: '/' })
|
||||||
} else if (!auth_type && !Vue.ls.get(ACCESS_TOKEN) && to.path !== '/user/login') {
|
} else if (!config.useSSO && !Vue.ls.get(ACCESS_TOKEN) && to.path !== '/user/login') {
|
||||||
await store.dispatch('GetAuthDataEnable')
|
next({ path: '/user/login', query: { redirect: to.fullPath } })
|
||||||
const { enable_list = [] } = store?.state?.user?.auth_enable ?? {}
|
|
||||||
const _enable_list = enable_list.filter(en => en.auth_type !== 'LDAP')
|
|
||||||
if (_enable_list.length === 1) {
|
|
||||||
next({ path: '/user/logout', query: { redirect: to.fullPath } })
|
|
||||||
} else {
|
|
||||||
next({ path: '/user/login', query: { redirect: to.fullPath } })
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
|
@@ -65,7 +65,8 @@ const genAclRoutes = async () => {
|
|||||||
path: `/acl/operate_history`,
|
path: `/acl/operate_history`,
|
||||||
name: 'acl_operate_history',
|
name: 'acl_operate_history',
|
||||||
component: () => import('../views/operation_history/index.vue'),
|
component: () => import('../views/operation_history/index.vue'),
|
||||||
meta: { title: '操作审计', icon: 'search', permission: ['acl_admin'] },
|
// meta: { title: '操作审计', icon: 'search', permission: ['acl_admin'] },
|
||||||
|
meta: { title: '操作审计', icon: 'search' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `/acl/user`,
|
path: `/acl/user`,
|
||||||
|
@@ -133,8 +133,8 @@ export default {
|
|||||||
if (newVal) {
|
if (newVal) {
|
||||||
this.tableData = this.allUsers.filter(
|
this.tableData = this.allUsers.filter(
|
||||||
(item) =>
|
(item) =>
|
||||||
(item.username && item.username.toLowerCase().includes(newVal.toLowerCase())) ||
|
item.username.toLowerCase().includes(newVal.toLowerCase()) ||
|
||||||
(item.nickname && item.nickname.toLowerCase().includes(newVal.toLowerCase()))
|
item.nickname.toLowerCase().includes(newVal.toLowerCase())
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
this.tableData = this.allUsers
|
this.tableData = this.allUsers
|
||||||
|
@@ -16,14 +16,12 @@ export function processFile(fileObj) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function uploadData(ciId, data) {
|
export function uploadData(ciId, data) {
|
||||||
|
data.ci_type = ciId
|
||||||
|
data.exist_policy = 'replace'
|
||||||
return axios({
|
return axios({
|
||||||
url: '/v0.1/ci',
|
url: '/v0.1/ci',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data,
|
||||||
...data,
|
|
||||||
ci_type: ciId,
|
|
||||||
exist_policy: 'replace'
|
|
||||||
},
|
|
||||||
isShowMessage: false
|
isShowMessage: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -111,14 +111,12 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations('cmdbStore', ['SET_IS_TABLE_LOADING']),
|
...mapMutations('cmdbStore', ['SET_IS_TABLE_LOADING']),
|
||||||
open({ preferenceAttrList, ciTypeName = undefined }) {
|
open({ preferenceAttrList }) {
|
||||||
this.preferenceAttrList = preferenceAttrList
|
this.preferenceAttrList = preferenceAttrList
|
||||||
this.visible = true
|
this.visible = true
|
||||||
this.$nextTick((res) => {
|
this.$nextTick((res) => {
|
||||||
this.form.setFieldsValue({
|
this.form.setFieldsValue({
|
||||||
filename: ciTypeName
|
filename: `cmdb-${moment().format('YYYYMMDDHHmmss')}`,
|
||||||
? `cmdb-${ciTypeName}-${moment().format('YYYYMMDDHHmmss')}`
|
|
||||||
: `cmdb-${moment().format('YYYYMMDDHHmmss')}`,
|
|
||||||
})
|
})
|
||||||
if (this.treeType === 'tree') {
|
if (this.treeType === 'tree') {
|
||||||
const _check = ['ci_type_alias']
|
const _check = ['ci_type_alias']
|
||||||
|
@@ -77,7 +77,7 @@ const genCmdbRoutes = async () => {
|
|||||||
path: '/cmdb/ci_types',
|
path: '/cmdb/ci_types',
|
||||||
name: 'ci_type',
|
name: 'ci_type',
|
||||||
component: () => import('../views/ci_types/index'),
|
component: () => import('../views/ci_types/index'),
|
||||||
meta: { title: '模型配置', icon: 'ops-cmdb-citype', selectedIcon: 'ops-cmdb-citype-selected', keepAlive: false, permission: ['cmdb_admin', 'admin'] }
|
meta: { title: '模型配置', icon: 'ops-cmdb-citype', selectedIcon: 'ops-cmdb-citype-selected', keepAlive: false }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/cmdb/disabled3',
|
path: '/cmdb/disabled3',
|
||||||
|
@@ -1,16 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="cmdb-batch-upload" :style="{ height: `${windowHeight - 64}px` }">
|
<div class="cmdb-batch-upload" :style="{ height: `${windowHeight - 64}px` }">
|
||||||
<div id="title">
|
<div id="title">
|
||||||
<ci-type-choice ref="ciTypeChoice" @getCiTypeAttr="showCiType" />
|
<ci-type-choice @getCiTypeAttr="showCiType" />
|
||||||
</div>
|
</div>
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<upload-file-form
|
<upload-file-form :ciType="ciType" ref="uploadFileForm" @uploadDone="uploadDone"></upload-file-form>
|
||||||
:isUploading="isUploading"
|
|
||||||
:ciType="ciType"
|
|
||||||
ref="uploadFileForm"
|
|
||||||
@uploadDone="uploadDone"
|
|
||||||
></upload-file-form>
|
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24" v-if="ciType && uploadData.length">
|
<a-col :span="24" v-if="ciType && uploadData.length">
|
||||||
<CiUploadTable :ciTypeAttrs="ciTypeAttrs" ref="ciUploadTable" :uploadData="uploadData"></CiUploadTable>
|
<CiUploadTable :ciTypeAttrs="ciTypeAttrs" ref="ciUploadTable" :uploadData="uploadData"></CiUploadTable>
|
||||||
@@ -18,19 +13,15 @@
|
|||||||
<a-space size="large">
|
<a-space size="large">
|
||||||
<a-button type="primary" ghost @click="handleCancel">取消</a-button>
|
<a-button type="primary" ghost @click="handleCancel">取消</a-button>
|
||||||
<a-button @click="handleUpload" type="primary">上传</a-button>
|
<a-button @click="handleUpload" type="primary">上传</a-button>
|
||||||
<a-button v-if="hasError && !isUploading" @click="downloadError" type="primary">失败下载</a-button>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="24" v-if="ciType">
|
<a-col :span="24">
|
||||||
<upload-result
|
<upload-result
|
||||||
ref="uploadResult"
|
ref="uploadResult"
|
||||||
:upLoadData="uploadData"
|
:upLoadData="uploadData"
|
||||||
:ciType="ciType"
|
:ciType="ciType"
|
||||||
:unique-field="uniqueField"
|
:unique-field="uniqueField"
|
||||||
:isUploading="isUploading"
|
|
||||||
@uploadResultDone="uploadResultDone"
|
|
||||||
@uploadResultError="uploadResultError"
|
|
||||||
></upload-result>
|
></upload-result>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
@@ -38,7 +29,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import moment from 'moment'
|
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import CiTypeChoice from './modules/CiTypeChoice'
|
import CiTypeChoice from './modules/CiTypeChoice'
|
||||||
import CiUploadTable from './modules/CiUploadTable'
|
import CiUploadTable from './modules/CiUploadTable'
|
||||||
@@ -61,8 +51,7 @@ export default {
|
|||||||
ciType: 0,
|
ciType: 0,
|
||||||
uniqueField: '',
|
uniqueField: '',
|
||||||
uniqueId: 0,
|
uniqueId: 0,
|
||||||
isUploading: false,
|
displayUpload: true,
|
||||||
hasError: false,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -70,12 +59,13 @@ export default {
|
|||||||
windowHeight: (state) => state.windowHeight,
|
windowHeight: (state) => state.windowHeight,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
inject: ['reload'],
|
||||||
methods: {
|
methods: {
|
||||||
showCiType(message) {
|
showCiType(message) {
|
||||||
this.ciTypeAttrs = message ?? {}
|
this.ciTypeAttrs = message
|
||||||
this.ciType = message?.type_id ?? 0
|
this.ciType = message.type_id
|
||||||
this.uniqueField = message?.unique ?? ''
|
this.uniqueField = message.unique
|
||||||
this.uniqueId = message?.unique_id ?? 0
|
this.uniqueId = message.unique_id
|
||||||
},
|
},
|
||||||
uploadDone(dataList) {
|
uploadDone(dataList) {
|
||||||
const _uploadData = filterNull(dataList).map((item, i) => {
|
const _uploadData = filterNull(dataList).map((item, i) => {
|
||||||
@@ -83,20 +73,7 @@ export default {
|
|||||||
const _ele = {}
|
const _ele = {}
|
||||||
item.forEach((ele, j) => {
|
item.forEach((ele, j) => {
|
||||||
if (ele !== undefined && ele !== null) {
|
if (ele !== undefined && ele !== null) {
|
||||||
const _find = this.ciTypeAttrs.attributes.find(
|
_ele[dataList[0][j]] = ele
|
||||||
(attr) => attr.alias === dataList[0][j] || attr.name === dataList[0][j]
|
|
||||||
)
|
|
||||||
if (_find?.value_type === '4' && typeof ele === 'number') {
|
|
||||||
_ele[dataList[0][j]] = moment(Math.round((ele - 25569) * 86400 * 1000 - 28800000)).format('YYYY-MM-DD')
|
|
||||||
} else if (_find?.value_type === '3' && typeof ele === 'number') {
|
|
||||||
_ele[dataList[0][j]] = moment(Math.round((ele - 25569) * 86400 * 1000 - 28800000)).format(
|
|
||||||
'YYYY-MM-DD HH:mm:ss'
|
|
||||||
)
|
|
||||||
} else if (_find?.value_type === '5' && typeof ele === 'number') {
|
|
||||||
_ele[dataList[0][j]] = moment(Math.round(ele * 86400 * 1000 - 28800000)).format('HH:mm:ss')
|
|
||||||
} else {
|
|
||||||
_ele[dataList[0][j]] = ele
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return _ele
|
return _ele
|
||||||
@@ -104,8 +81,6 @@ export default {
|
|||||||
return item
|
return item
|
||||||
})
|
})
|
||||||
this.uploadData = _uploadData.slice(1)
|
this.uploadData = _uploadData.slice(1)
|
||||||
this.hasError = false
|
|
||||||
this.isUploading = false
|
|
||||||
},
|
},
|
||||||
handleUpload() {
|
handleUpload() {
|
||||||
if (!this.ciType) {
|
if (!this.ciType) {
|
||||||
@@ -113,7 +88,6 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (this.uploadData && this.uploadData.length > 0) {
|
if (this.uploadData && this.uploadData.length > 0) {
|
||||||
this.isUploading = true
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs.uploadResult.upload2Server()
|
this.$refs.uploadResult.upload2Server()
|
||||||
})
|
})
|
||||||
@@ -122,24 +96,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleCancel() {
|
handleCancel() {
|
||||||
if (!this.isUploading) {
|
this.reload()
|
||||||
this.showCiType(null)
|
|
||||||
this.$refs.ciTypeChoice.selectNum = null
|
|
||||||
this.hasError = false
|
|
||||||
} else {
|
|
||||||
this.$message.warning('批量上传已取消')
|
|
||||||
this.isUploading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
uploadResultDone() {
|
|
||||||
this.isUploading = false
|
|
||||||
},
|
|
||||||
uploadResultError(index) {
|
|
||||||
this.hasError = true
|
|
||||||
this.$refs.ciUploadTable.uploadResultError(index)
|
|
||||||
},
|
|
||||||
downloadError() {
|
|
||||||
this.$refs.ciUploadTable.downloadError()
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,6 @@
|
|||||||
:style="{ width: '300px' }"
|
:style="{ width: '300px' }"
|
||||||
class="ops-select"
|
class="ops-select"
|
||||||
:filter-option="filterOption"
|
:filter-option="filterOption"
|
||||||
v-model="selectNum"
|
|
||||||
>
|
>
|
||||||
<a-select-option v-for="ciType in ciTypeList" :key="ciType.name" :value="ciType.id">{{
|
<a-select-option v-for="ciType in ciTypeList" :key="ciType.name" :value="ciType.id">{{
|
||||||
ciType.alias
|
ciType.alias
|
||||||
@@ -89,12 +88,10 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { mapState } from 'vuex'
|
|
||||||
import { downloadExcel } from '../../../utils/helper'
|
import { downloadExcel } from '../../../utils/helper'
|
||||||
import { getCITypes } from '@/modules/cmdb/api/CIType'
|
import { getCITypes } from '@/modules/cmdb/api/CIType'
|
||||||
import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
|
import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
|
||||||
import { getCITypeParent, getCanEditByParentIdChildId } from '@/modules/cmdb/api/CITypeRelation'
|
import { getCITypeParent, getCanEditByParentIdChildId } from '@/modules/cmdb/api/CITypeRelation'
|
||||||
import { searchPermResourceByRoleId } from '@/modules/acl/api/permission'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CiTypeChoice',
|
name: 'CiTypeChoice',
|
||||||
@@ -102,7 +99,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
ciTypeList: [],
|
ciTypeList: [],
|
||||||
ciTypeName: '',
|
ciTypeName: '',
|
||||||
selectNum: null,
|
selectNum: 0,
|
||||||
selectCiTypeAttrList: [],
|
selectCiTypeAttrList: [],
|
||||||
visible: false,
|
visible: false,
|
||||||
checkedAttrs: [],
|
checkedAttrs: [],
|
||||||
@@ -114,21 +111,9 @@ export default {
|
|||||||
canEdit: {},
|
canEdit: {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
created: function() {
|
||||||
...mapState({
|
|
||||||
rid: (state) => state.user.rid,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
async created() {
|
|
||||||
const { resources } = await searchPermResourceByRoleId(this.rid, {
|
|
||||||
resource_type_id: 'CIType',
|
|
||||||
app_id: 'cmdb',
|
|
||||||
})
|
|
||||||
getCITypes().then((res) => {
|
getCITypes().then((res) => {
|
||||||
this.ciTypeList = res.ci_types.filter((type) => {
|
this.ciTypeList = res.ci_types
|
||||||
const _findRe = resources.find((re) => re.name === type.name)
|
|
||||||
return _findRe?.permissions.includes('create') ?? false
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -146,6 +131,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
selectCiType(el) {
|
selectCiType(el) {
|
||||||
// 当选择好模板类型时的回调函数
|
// 当选择好模板类型时的回调函数
|
||||||
|
this.selectNum = el
|
||||||
getCITypeAttributesById(el).then((res) => {
|
getCITypeAttributesById(el).then((res) => {
|
||||||
this.$emit('getCiTypeAttr', res)
|
this.$emit('getCiTypeAttr', res)
|
||||||
this.selectCiTypeAttrList = res
|
this.selectCiTypeAttrList = res
|
||||||
@@ -169,6 +155,7 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.parentsType = res.parents.filter((parent) => this.canEdit[parent.id])
|
this.parentsType = res.parents.filter((parent) => this.canEdit[parent.id])
|
||||||
|
|
||||||
const _parentsForm = {}
|
const _parentsForm = {}
|
||||||
res.parents.forEach((item) => {
|
res.parents.forEach((item) => {
|
||||||
const _find = item.attributes.find((attr) => attr.id === item.unique_id)
|
const _find = item.attributes.find((attr) => attr.id === item.unique_id)
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="cmdb-batch-upload-table">
|
<div class="cmdb-batch-upload-table">
|
||||||
<vxe-table
|
<vxe-table
|
||||||
ref="xTable"
|
|
||||||
stripe
|
stripe
|
||||||
show-header-overflow
|
show-header-overflow
|
||||||
show-overflow=""
|
show-overflow=""
|
||||||
@@ -9,8 +8,6 @@
|
|||||||
class="ops-stripe-table"
|
class="ops-stripe-table"
|
||||||
:max-height="200"
|
:max-height="200"
|
||||||
:data="dataSource"
|
:data="dataSource"
|
||||||
resizable
|
|
||||||
:row-style="rowStyle"
|
|
||||||
>
|
>
|
||||||
<vxe-column type="seq" width="40" />
|
<vxe-column type="seq" width="40" />
|
||||||
<vxe-column
|
<vxe-column
|
||||||
@@ -39,9 +36,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {}
|
||||||
errorIndexList: [],
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
columns() {
|
columns() {
|
||||||
@@ -69,33 +64,7 @@ export default {
|
|||||||
return _.cloneDeep(this.uploadData)
|
return _.cloneDeep(this.uploadData)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
methods: {},
|
||||||
uploadData() {
|
|
||||||
this.errorIndexList = []
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
uploadResultError(index) {
|
|
||||||
const _errorIndexList = _.cloneDeep(this.errorIndexList)
|
|
||||||
_errorIndexList.push(index)
|
|
||||||
this.errorIndexList = _errorIndexList
|
|
||||||
},
|
|
||||||
rowStyle({ rowIndex }) {
|
|
||||||
if (this.errorIndexList.includes(rowIndex)) {
|
|
||||||
return 'color:red;'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
downloadError() {
|
|
||||||
const data = this.uploadData.filter((item, index) => this.errorIndexList.includes(index))
|
|
||||||
this.$refs.xTable.exportData({
|
|
||||||
data,
|
|
||||||
type: 'xlsx',
|
|
||||||
columnFilterMethod({ column }) {
|
|
||||||
return column.property
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
accept=".xls,.xlsx"
|
accept=".xls,.xlsx"
|
||||||
:showUploadList="false"
|
:showUploadList="false"
|
||||||
:fileList="fileList"
|
:fileList="fileList"
|
||||||
:disabled="!ciType || isUploading"
|
:disabled="!ciType"
|
||||||
>
|
>
|
||||||
<img :style="{ width: '80px', height: '80px' }" src="@/assets/file_upload.png" />
|
<img :style="{ width: '80px', height: '80px' }" src="@/assets/file_upload.png" />
|
||||||
<p class="ant-upload-text">点击或拖拽文件至此上传!</p>
|
<p class="ant-upload-text">点击或拖拽文件至此上传!</p>
|
||||||
@@ -29,11 +29,7 @@ export default {
|
|||||||
ciType: {
|
ciType: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
}
|
||||||
isUploading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -44,20 +40,7 @@ export default {
|
|||||||
percent: 0,
|
percent: 0,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
ciType: {
|
|
||||||
handler(newValue) {
|
|
||||||
if (!newValue) {
|
|
||||||
this.ciItemNum = 0
|
|
||||||
this.fileList = []
|
|
||||||
this.dataList = []
|
|
||||||
this.progressStatus = 'active'
|
|
||||||
this.percent = 0
|
|
||||||
this.$emit('uploadDone', this.dataList)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
customRequest(data) {
|
customRequest(data) {
|
||||||
this.fileList = [data.file]
|
this.fileList = [data.file]
|
||||||
|
@@ -34,10 +34,6 @@ export default {
|
|||||||
required: true,
|
required: true,
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
isUploading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
@@ -55,38 +51,33 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
async sleep(n) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve()
|
||||||
|
}, n || 5)
|
||||||
|
})
|
||||||
|
},
|
||||||
async upload2Server() {
|
async upload2Server() {
|
||||||
this.visible = true
|
this.visible = true
|
||||||
this.success = 0
|
this.success = 0
|
||||||
this.errorNum = 0
|
this.errorNum = 0
|
||||||
this.errorItems = []
|
this.errorItems = []
|
||||||
const floor = Math.ceil(this.total / 6)
|
for (let i = 0; i < this.total; i++) {
|
||||||
for (let i = 0; i < floor; i++) {
|
// await this.sleep(20)
|
||||||
if (this.isUploading) {
|
const item = this.upLoadData[i]
|
||||||
const itemList = this.upLoadData.slice(6 * i, 6 * i + 6)
|
await uploadData(this.ciType, item)
|
||||||
const promises = itemList.map((x) => uploadData(this.ciType, x))
|
.then((res) => {
|
||||||
await Promise.allSettled(promises)
|
console.log(res)
|
||||||
.then((res) => {
|
this.success += 1
|
||||||
res.forEach((r, j) => {
|
})
|
||||||
if (r.status === 'fulfilled') {
|
.catch((err) => {
|
||||||
this.success += 1
|
this.errorNum += 1
|
||||||
} else {
|
this.errorItems.push(((err.response || {}).data || {}).message || '请求出现错误,请稍后再试')
|
||||||
this.errorItems.push(r?.reason?.response?.data.message ?? '请求出现错误,请稍后再试')
|
})
|
||||||
this.errorNum += 1
|
.finally(() => {
|
||||||
this.$emit('uploadResultError', 6 * i + j)
|
this.complete += 1
|
||||||
}
|
})
|
||||||
})
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.complete += 6
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.isUploading) {
|
|
||||||
this.$emit('uploadResultDone')
|
|
||||||
this.$message.success('批量上传已完成')
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@@ -108,7 +108,7 @@
|
|||||||
<span>{{ col.title }}</span>
|
<span>{{ col.title }}</span>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="col.is_choice || col.is_password || col.is_list" #edit="{ row }">
|
<template v-if="col.is_choice || col.is_password" #edit="{ row }">
|
||||||
<vxe-input v-if="col.is_password" v-model="passwordValue[col.field]" />
|
<vxe-input v-if="col.is_password" v-model="passwordValue[col.field]" />
|
||||||
<a-select
|
<a-select
|
||||||
:getPopupContainer="(trigger) => trigger.parentElement"
|
:getPopupContainer="(trigger) => trigger.parentElement"
|
||||||
@@ -145,18 +145,6 @@
|
|||||||
</span>
|
</span>
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
<a-select
|
|
||||||
:getPopupContainer="(trigger) => trigger.parentElement"
|
|
||||||
:style="{ width: '100%', height: '32px' }"
|
|
||||||
v-model="row[col.field]"
|
|
||||||
placeholder="请选择"
|
|
||||||
v-else-if="col.is_list"
|
|
||||||
:showArrow="false"
|
|
||||||
mode="tags"
|
|
||||||
class="ci-table-edit-select"
|
|
||||||
allowClear
|
|
||||||
>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
</template>
|
||||||
<template
|
<template
|
||||||
v-if="col.value_type === '6' || col.is_link || col.is_password || col.is_choice"
|
v-if="col.value_type === '6' || col.is_link || col.is_password || col.is_choice"
|
||||||
@@ -576,10 +564,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async openBatchDownload() {
|
async openBatchDownload() {
|
||||||
this.$refs.batchDownload.open({
|
this.$refs.batchDownload.open({ preferenceAttrList: this.preferenceAttrList })
|
||||||
preferenceAttrList: this.preferenceAttrList,
|
|
||||||
ciTypeName: this.$route.meta.title || this.$route.meta.name,
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
batchDownload({ filename, type, checkedKeys }) {
|
batchDownload({ filename, type, checkedKeys }) {
|
||||||
const jsonAttrList = []
|
const jsonAttrList = []
|
||||||
@@ -669,19 +654,13 @@ export default {
|
|||||||
let errorNum = 0
|
let errorNum = 0
|
||||||
this.loading = true
|
this.loading = true
|
||||||
this.loadTip = `正在删除...`
|
this.loadTip = `正在删除...`
|
||||||
const floor = Math.ceil(this.selectedRowKeys.length / 6)
|
for (let i = 0; i < this.selectedRowKeys.length; i++) {
|
||||||
for (let i = 0; i < floor; i++) {
|
await deleteCI(this.selectedRowKeys[i], false)
|
||||||
const itemList = this.selectedRowKeys.slice(6 * i, 6 * i + 6)
|
.then(() => {
|
||||||
const promises = itemList.map((x) => deleteCI(x, false))
|
successNum += 1
|
||||||
await Promise.allSettled(promises)
|
})
|
||||||
.then((res) => {
|
.catch(() => {
|
||||||
res.forEach((r) => {
|
errorNum += 1
|
||||||
if (r.status === 'fulfilled') {
|
|
||||||
successNum += 1
|
|
||||||
} else {
|
|
||||||
errorNum += 1
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.loadTip = `正在删除,共${this.selectedRowKeys.length}个,成功${successNum}个,失败${errorNum}个`
|
this.loadTip = `正在删除,共${this.selectedRowKeys.length}个,成功${successNum}个,失败${errorNum}个`
|
||||||
@@ -894,10 +873,6 @@ export default {
|
|||||||
unsubscribe(ciType, type = 'all') {
|
unsubscribe(ciType, type = 'all') {
|
||||||
const promises = [subscribeCIType(this.typeId, ''), subscribeTreeView(this.typeId, '')]
|
const promises = [subscribeCIType(this.typeId, ''), subscribeTreeView(this.typeId, '')]
|
||||||
Promise.all(promises).then(() => {
|
Promise.all(promises).then(() => {
|
||||||
const lastTypeId = window.localStorage.getItem('ops_ci_typeid') || undefined
|
|
||||||
if (Number(ciType) === Number(lastTypeId)) {
|
|
||||||
localStorage.setItem('ops_ci_typeid', '')
|
|
||||||
}
|
|
||||||
this.$message.success('取消订阅成功')
|
this.$message.success('取消订阅成功')
|
||||||
this.resetRoute()
|
this.resetRoute()
|
||||||
this.$router.push('/cmdb/preference')
|
this.$router.push('/cmdb/preference')
|
||||||
|
@@ -276,16 +276,15 @@ export default {
|
|||||||
},
|
},
|
||||||
mergeRowMethod({ row, _rowIndex, column, visibleData }) {
|
mergeRowMethod({ row, _rowIndex, column, visibleData }) {
|
||||||
const fields = ['created_at', 'username']
|
const fields = ['created_at', 'username']
|
||||||
const cellValue1 = row['created_at']
|
const cellValue = row[column.property]
|
||||||
const cellValue2 = row['username']
|
if (cellValue && fields.includes(column.property)) {
|
||||||
if (cellValue1 && cellValue2 && fields.includes(column.property)) {
|
|
||||||
const prevRow = visibleData[_rowIndex - 1]
|
const prevRow = visibleData[_rowIndex - 1]
|
||||||
let nextRow = visibleData[_rowIndex + 1]
|
let nextRow = visibleData[_rowIndex + 1]
|
||||||
if (prevRow && prevRow['created_at'] === cellValue1 && prevRow['username'] === cellValue2) {
|
if (prevRow && prevRow[column.property] === cellValue) {
|
||||||
return { rowspan: 0, colspan: 0 }
|
return { rowspan: 0, colspan: 0 }
|
||||||
} else {
|
} else {
|
||||||
let countRowspan = 1
|
let countRowspan = 1
|
||||||
while (nextRow && nextRow['created_at'] === cellValue1 && nextRow['username'] === cellValue2) {
|
while (nextRow && nextRow[column.property] === cellValue) {
|
||||||
nextRow = visibleData[++countRowspan + _rowIndex]
|
nextRow = visibleData[++countRowspan + _rowIndex]
|
||||||
}
|
}
|
||||||
if (countRowspan > 1) {
|
if (countRowspan > 1) {
|
||||||
|
@@ -59,9 +59,6 @@
|
|||||||
{{ ci[attr.name] }}
|
{{ ci[attr.name] }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="attr.is_list">
|
|
||||||
<span> {{ ci[attr.name].join(',') }}</span>
|
|
||||||
</template>
|
|
||||||
<template v-else>{{ getName(ci[attr.name]) }}</template>
|
<template v-else>{{ getName(ci[attr.name]) }}</template>
|
||||||
</span>
|
</span>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
@@ -78,6 +75,7 @@
|
|||||||
placeholder="请选择"
|
placeholder="请选择"
|
||||||
v-if="attr.is_choice"
|
v-if="attr.is_choice"
|
||||||
:mode="attr.is_list ? 'multiple' : 'default'"
|
:mode="attr.is_list ? 'multiple' : 'default'"
|
||||||
|
:multiple="attr.is_list"
|
||||||
showSearch
|
showSearch
|
||||||
allowClear
|
allowClear
|
||||||
size="small"
|
size="small"
|
||||||
@@ -105,23 +103,6 @@
|
|||||||
</span>
|
</span>
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
<a-select
|
|
||||||
:style="{ width: '100%' }"
|
|
||||||
v-decorator="[
|
|
||||||
attr.name,
|
|
||||||
{
|
|
||||||
rules: [{ required: attr.is_required }],
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
placeholder="请选择"
|
|
||||||
v-else-if="attr.is_list"
|
|
||||||
mode="tags"
|
|
||||||
showSearch
|
|
||||||
allowClear
|
|
||||||
size="small"
|
|
||||||
:getPopupContainer="(trigger) => trigger.parentElement"
|
|
||||||
>
|
|
||||||
</a-select>
|
|
||||||
<a-input-number
|
<a-input-number
|
||||||
size="small"
|
size="small"
|
||||||
v-decorator="[
|
v-decorator="[
|
||||||
@@ -241,7 +222,7 @@ export default {
|
|||||||
this.$nextTick(async () => {
|
this.$nextTick(async () => {
|
||||||
if (this.attr.is_list && !this.attr.is_choice) {
|
if (this.attr.is_list && !this.attr.is_choice) {
|
||||||
this.form.setFieldsValue({
|
this.form.setFieldsValue({
|
||||||
[`${this.attr.name}`]: this.ci[this.attr.name] || null,
|
[`${this.attr.name}`]: this.ci[this.attr.name].join(',') || null,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
placeholder="请选择"
|
placeholder="请选择"
|
||||||
v-if="attr.is_choice"
|
v-if="attr.is_choice"
|
||||||
:mode="attr.is_list ? 'multiple' : 'default'"
|
:mode="attr.is_list ? 'multiple' : 'default'"
|
||||||
|
:multiple="attr.is_list"
|
||||||
showSearch
|
showSearch
|
||||||
allowClear
|
allowClear
|
||||||
>
|
>
|
||||||
|
@@ -53,8 +53,8 @@ export default {
|
|||||||
return postCITypeDiscovery(this.CITypeId, { adr_id: id, interval: type === 'agent' ? 300 : 3600 })
|
return postCITypeDiscovery(this.CITypeId, { adr_id: id, interval: type === 'agent' ? 300 : 3600 })
|
||||||
})
|
})
|
||||||
await Promise.all(promises)
|
await Promise.all(promises)
|
||||||
.then((res) => {
|
.then(() => {
|
||||||
this.getCITypeDiscovery(res[0].id)
|
this.getCITypeDiscovery(this.selectedIds[0].id)
|
||||||
this.$message.success('添加成功')
|
this.$message.success('添加成功')
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
@@ -2,22 +2,20 @@
|
|||||||
<div class="attr-ad" :style="{ height: `${windowHeight - 104}px` }">
|
<div class="attr-ad" :style="{ height: `${windowHeight - 104}px` }">
|
||||||
<div v-if="adCITypeList && adCITypeList.length">
|
<div v-if="adCITypeList && adCITypeList.length">
|
||||||
<a-tabs size="small" v-model="currentTab">
|
<a-tabs size="small" v-model="currentTab">
|
||||||
<a-tab-pane v-for="item in adCITypeList" :key="item.id">
|
<a-tab-pane v-for="item in adCITypeList" :key="item.adr_id">
|
||||||
<a-space slot="tab">
|
<a-space slot="tab">
|
||||||
<span v-if="item.extra_option && item.extra_option.alias">{{ item.extra_option.alias }}</span>
|
<span>{{ getADCITypeParam(item.adr_id) }}</span>
|
||||||
<span v-else>{{ getADCITypeParam(item.adr_id) }}</span>
|
|
||||||
<a-icon type="close-circle" @click="(e) => deleteADT(e, item)" />
|
<a-icon type="close-circle" @click="(e) => deleteADT(e, item)" />
|
||||||
</a-space>
|
</a-space>
|
||||||
<AttrADTabpane
|
<AttrADTabpane
|
||||||
:ref="`attrAdTabpane_${item.id}`"
|
:ref="`attrAdTabpane_${item.adr_id}`"
|
||||||
:adr_id="item.adr_id"
|
:currentTab="item.adr_id"
|
||||||
:adrList="adrList"
|
:adrList="adrList"
|
||||||
:adCITypeList="adCITypeList"
|
:adCITypeList="adCITypeList"
|
||||||
:currentAdt="item"
|
:currentAdt="item"
|
||||||
:ciTypeAttributes="ciTypeAttributes"
|
:ciTypeAttributes="ciTypeAttributes"
|
||||||
:currentAdr="getADCITypeParam(item.adr_id, undefined, true)"
|
:currentAdr="getADCITypeParam(item.adr_id, undefined, true)"
|
||||||
@openEditDrawer="(data, type, adType) => openEditDrawer(data, type, adType)"
|
@openEditDrawer="(data, type, adType) => openEditDrawer(data, type, adType)"
|
||||||
@handleSave="getCITypeDiscovery"
|
|
||||||
/>
|
/>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-space
|
<a-space
|
||||||
@@ -136,8 +134,8 @@ export default {
|
|||||||
async getCITypeDiscovery(currentTab) {
|
async getCITypeDiscovery(currentTab) {
|
||||||
await getCITypeDiscovery(this.CITypeId).then((res) => {
|
await getCITypeDiscovery(this.CITypeId).then((res) => {
|
||||||
this.adCITypeList = res.filter((item) => item.adr_id)
|
this.adCITypeList = res.filter((item) => item.adr_id)
|
||||||
if (this.adCITypeList && this.adCITypeList.length && !this.currentTab) {
|
if (res && res.length && !this.currentTab) {
|
||||||
this.currentTab = this.adCITypeList[0].id
|
this.currentTab = res[0].adr_id
|
||||||
}
|
}
|
||||||
if (currentTab) {
|
if (currentTab) {
|
||||||
this.currentTab = currentTab
|
this.currentTab = currentTab
|
||||||
@@ -158,7 +156,7 @@ export default {
|
|||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
const that = this
|
const that = this
|
||||||
this.$confirm({
|
this.$confirm({
|
||||||
title: `确认删除 【${item?.extra_option?.alias || this.getADCITypeParam(item.adr_id)}】`,
|
title: `确认删除 【${this.getADCITypeParam(item.adr_id)}】`,
|
||||||
content: (h) => (
|
content: (h) => (
|
||||||
<div>
|
<div>
|
||||||
<a-checkbox v-model={that.deletePlugin}>删除插件</a-checkbox>
|
<a-checkbox v-model={that.deletePlugin}>删除插件</a-checkbox>
|
||||||
@@ -166,22 +164,18 @@ export default {
|
|||||||
),
|
),
|
||||||
onOk() {
|
onOk() {
|
||||||
deleteCITypeDiscovery(item.id).then(async () => {
|
deleteCITypeDiscovery(item.id).then(async () => {
|
||||||
if (that.currentTab === item.id) {
|
if (that.currentTab === item.adr_id) {
|
||||||
that.currentTab = ''
|
that.currentTab = ''
|
||||||
}
|
}
|
||||||
|
that.deletePlugin = false
|
||||||
that.$message.success('删除成功!')
|
that.$message.success('删除成功!')
|
||||||
that.getCITypeDiscovery()
|
that.getCITypeDiscovery()
|
||||||
if (that.deletePlugin) {
|
if (that.deletePlugin) {
|
||||||
await deleteDiscovery(item.adr_id).finally(() => {
|
await deleteDiscovery(item.adr_id)
|
||||||
that.deletePlugin = false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
that.deletePlugin = false
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onCancel() {
|
onCancel() {},
|
||||||
that.deletePlugin = false
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
openEditDrawer(data, type, adType) {
|
openEditDrawer(data, type, adType) {
|
||||||
@@ -189,12 +183,12 @@ export default {
|
|||||||
},
|
},
|
||||||
async updateNotInner(adr) {
|
async updateNotInner(adr) {
|
||||||
const _idx = this.adCITypeList.findIndex((item) => item.adr_id === adr.id)
|
const _idx = this.adCITypeList.findIndex((item) => item.adr_id === adr.id)
|
||||||
let res
|
|
||||||
if (_idx < 0) {
|
if (_idx < 0) {
|
||||||
res = await postCITypeDiscovery(this.CITypeId, { adr_id: adr.id, interval: 300 })
|
await postCITypeDiscovery(this.CITypeId, { adr_id: adr.id, interval: 300 })
|
||||||
}
|
}
|
||||||
await this.getDiscovery()
|
await this.getDiscovery()
|
||||||
await this.getCITypeDiscovery(res?.id ?? undefined)
|
await this.getCITypeDiscovery()
|
||||||
|
this.currentTab = adr.id
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs[`attrAdTabpane_${this.currentTab}`][0].init()
|
this.$refs[`attrAdTabpane_${this.currentTab}`][0].init()
|
||||||
})
|
})
|
||||||
|
@@ -14,7 +14,6 @@
|
|||||||
<span>编辑</span>
|
<span>编辑</span>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a>
|
</a>
|
||||||
<div>别名:<a-input v-model="alias" style="width:200px;" /></div>
|
|
||||||
<div class="attr-ad-header">字段映射</div>
|
<div class="attr-ad-header">字段映射</div>
|
||||||
<vxe-table
|
<vxe-table
|
||||||
v-if="adrType === 'agent'"
|
v-if="adrType === 'agent'"
|
||||||
@@ -57,7 +56,7 @@
|
|||||||
:ruleName="adrName"
|
:ruleName="adrName"
|
||||||
:ciTypeAttributes="ciTypeAttributes"
|
:ciTypeAttributes="ciTypeAttributes"
|
||||||
:adCITypeList="adCITypeList"
|
:adCITypeList="adCITypeList"
|
||||||
:currentTab="adr_id"
|
:currentTab="currentTab"
|
||||||
:style="{ marginBottom: '20px' }"
|
:style="{ marginBottom: '20px' }"
|
||||||
/>
|
/>
|
||||||
<a-form-model
|
<a-form-model
|
||||||
@@ -134,7 +133,7 @@ export default {
|
|||||||
name: 'AttrADTabpane',
|
name: 'AttrADTabpane',
|
||||||
components: { Vcrontab, HttpSnmpAD, CMDBExprDrawer, MonitorNodeSetting },
|
components: { Vcrontab, HttpSnmpAD, CMDBExprDrawer, MonitorNodeSetting },
|
||||||
props: {
|
props: {
|
||||||
adr_id: {
|
currentTab: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
@@ -188,7 +187,6 @@ export default {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
form3: this.$form.createForm(this, { name: 'snmp_form' }),
|
form3: this.$form.createForm(this, { name: 'snmp_form' }),
|
||||||
alias: '',
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -207,7 +205,7 @@ export default {
|
|||||||
},
|
},
|
||||||
agentTypeRadioList() {
|
agentTypeRadioList() {
|
||||||
const { permissions = [] } = this.userRoles
|
const { permissions = [] } = this.userRoles
|
||||||
if ((permissions.includes('cmdb_admin') || permissions.includes('admin')) && this.adrType !== 'http') {
|
if (permissions.includes('cmdb_admin') || permissions.includes('admin')) {
|
||||||
return [
|
return [
|
||||||
{ value: 'all', label: '所有节点' },
|
{ value: 'all', label: '所有节点' },
|
||||||
{ value: 'agent_id', label: '指定节点' },
|
{ value: 'agent_id', label: '指定节点' },
|
||||||
@@ -223,9 +221,8 @@ export default {
|
|||||||
mounted() {},
|
mounted() {},
|
||||||
methods: {
|
methods: {
|
||||||
init() {
|
init() {
|
||||||
const _find = this.adrList.find((item) => Number(item.id) === Number(this.adr_id))
|
const _find = this.adrList.find((item) => Number(item.id) === Number(this.currentTab))
|
||||||
const _findADT = this.adCITypeList.find((item) => Number(item.id) === Number(this.currentAdt.id))
|
const _findADT = this.adCITypeList.find((item) => Number(item.adr_id) === Number(this.currentTab))
|
||||||
this.alias = _findADT?.extra_option?.alias ?? ''
|
|
||||||
if (this.adrType === 'http') {
|
if (this.adrType === 'http') {
|
||||||
const { category = undefined, key = '', secret = '' } = _findADT?.extra_option ?? {}
|
const { category = undefined, key = '', secret = '' } = _findADT?.extra_option ?? {}
|
||||||
this.form2 = {
|
this.form2 = {
|
||||||
@@ -297,7 +294,7 @@ export default {
|
|||||||
this.cron = cron
|
this.cron = cron
|
||||||
},
|
},
|
||||||
handleSave() {
|
handleSave() {
|
||||||
const { currentAdt, alias } = this
|
const { currentAdt } = this
|
||||||
let params
|
let params
|
||||||
if (this.adrType === 'http') {
|
if (this.adrType === 'http') {
|
||||||
params = {
|
params = {
|
||||||
@@ -363,15 +360,9 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (params.extra_option) {
|
|
||||||
params.extra_option.alias = alias
|
|
||||||
} else {
|
|
||||||
params.extra_option = {}
|
|
||||||
params.extra_option.alias = alias
|
|
||||||
}
|
|
||||||
putCITypeDiscovery(currentAdt.id, params).then((res) => {
|
putCITypeDiscovery(currentAdt.id, params).then((res) => {
|
||||||
this.$message.success('保存成功')
|
this.$message.success('保存成功')
|
||||||
this.$emit('handleSave')
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleOpenCmdb() {
|
handleOpenCmdb() {
|
||||||
|
@@ -104,10 +104,6 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
attributes: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const propertyList = [
|
const propertyList = [
|
||||||
@@ -164,7 +160,7 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
openTrigger() {
|
openTrigger() {
|
||||||
this.$refs.triggerForm.open(this.property, this.attributes)
|
this.$refs.triggerForm.open(this.property)
|
||||||
},
|
},
|
||||||
handleCalcComputed() {
|
handleCalcComputed() {
|
||||||
const that = this
|
const that = this
|
||||||
|
@@ -98,7 +98,6 @@
|
|||||||
:property="item"
|
:property="item"
|
||||||
@ok="handleOk"
|
@ok="handleOk"
|
||||||
:CITypeId="CITypeId"
|
:CITypeId="CITypeId"
|
||||||
:attributes="attributes"
|
|
||||||
/>
|
/>
|
||||||
<i></i> <i></i> <i></i> <i></i> <i></i>
|
<i></i> <i></i> <i></i> <i></i> <i></i>
|
||||||
</draggable>
|
</draggable>
|
||||||
@@ -138,7 +137,6 @@
|
|||||||
:property="item"
|
:property="item"
|
||||||
@ok="handleOk"
|
@ok="handleOk"
|
||||||
:CITypeId="CITypeId"
|
:CITypeId="CITypeId"
|
||||||
:attributes="attributes"
|
|
||||||
/>
|
/>
|
||||||
<i></i> <i></i> <i></i> <i></i> <i></i>
|
<i></i> <i></i> <i></i> <i></i> <i></i>
|
||||||
</draggable>
|
</draggable>
|
||||||
|
@@ -40,18 +40,20 @@
|
|||||||
<a-menu-item key="0">
|
<a-menu-item key="0">
|
||||||
<a-upload
|
<a-upload
|
||||||
name="file"
|
name="file"
|
||||||
accept=".json"
|
accept="json"
|
||||||
:showUploadList="false"
|
:showUploadList="false"
|
||||||
style="display: inline-block"
|
style="display: inline-block"
|
||||||
action="/api/v0.1/ci_types/template/import/file"
|
action="/api/v0.1/ci_types/template/import/file "
|
||||||
@change="changeUploadFile"
|
|
||||||
>
|
>
|
||||||
<a><a-icon type="upload"/></a><a> 导入</a>
|
<a-space
|
||||||
|
><a><a-icon type="upload"/></a><a>导入</a></a-space
|
||||||
|
>
|
||||||
</a-upload>
|
</a-upload>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item key="1">
|
<a-menu-item key="1">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a href="/api/v0.1/ci_types/template/export/file"><a-icon type="download" /> 导出</a>
|
<a><a-icon type="download"/></a>
|
||||||
|
<a href="/api/v0.1/ci_types/template/export/file">导出</a>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
@@ -132,12 +134,6 @@
|
|||||||
<a-space class="ci-types-left-detail-action">
|
<a-space class="ci-types-left-detail-action">
|
||||||
<a><a-icon type="user-add" @click="(e) => handlePerm(e, ci)"/></a>
|
<a><a-icon type="user-add" @click="(e) => handlePerm(e, ci)"/></a>
|
||||||
<a><a-icon type="edit" @click="(e) => handleEdit(e, ci)"/></a>
|
<a><a-icon type="edit" @click="(e) => handleEdit(e, ci)"/></a>
|
||||||
<a
|
|
||||||
v-if="permissions.includes('admin') || permissions.includes('cmdb_admin')"
|
|
||||||
@click="(e) => handleDownloadCiType(e, ci)"
|
|
||||||
><a-icon
|
|
||||||
type="download"
|
|
||||||
/></a>
|
|
||||||
<a style="color: red" @click="(e) => handleDelete(e, ci)"><a-icon type="delete"/></a>
|
<a style="color: red" @click="(e) => handleDelete(e, ci)"><a-icon type="delete"/></a>
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
@@ -297,7 +293,6 @@ import SplitPane from '@/components/SplitPane'
|
|||||||
import CMDBGrant from '../../components/cmdbGrant'
|
import CMDBGrant from '../../components/cmdbGrant'
|
||||||
import { ops_move_icon as OpsMoveIcon } from '@/core/icons'
|
import { ops_move_icon as OpsMoveIcon } from '@/core/icons'
|
||||||
import AttributeStore from './attributeStore.vue'
|
import AttributeStore from './attributeStore.vue'
|
||||||
import { getAllDepAndEmployee } from '@/api/company'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CITypes',
|
name: 'CITypes',
|
||||||
@@ -313,7 +308,6 @@ export default {
|
|||||||
OpsMoveIcon,
|
OpsMoveIcon,
|
||||||
AttributeStore,
|
AttributeStore,
|
||||||
},
|
},
|
||||||
inject: ['reload'],
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
emptyImage,
|
emptyImage,
|
||||||
@@ -348,8 +342,6 @@ export default {
|
|||||||
|
|
||||||
orderSelectionOptions: [],
|
orderSelectionOptions: [],
|
||||||
default_order_asc: '1',
|
default_order_asc: '1',
|
||||||
|
|
||||||
allTreeDepAndEmp: [],
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -413,13 +405,9 @@ export default {
|
|||||||
resource_type: () => {
|
resource_type: () => {
|
||||||
return this.resource_type
|
return this.resource_type
|
||||||
},
|
},
|
||||||
provide_allTreeDepAndEmp: () => {
|
|
||||||
return this.allTreeDepAndEmp
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.getAllDepAndEmployee()
|
|
||||||
const _currentId = localStorage.getItem('ops_cityps_currentId')
|
const _currentId = localStorage.getItem('ops_cityps_currentId')
|
||||||
if (_currentId) {
|
if (_currentId) {
|
||||||
this.currentId = _currentId
|
this.currentId = _currentId
|
||||||
@@ -431,11 +419,6 @@ export default {
|
|||||||
this.getAttributes()
|
this.getAttributes()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getAllDepAndEmployee() {
|
|
||||||
getAllDepAndEmployee({ block: 0 }).then((res) => {
|
|
||||||
this.allTreeDepAndEmp = res
|
|
||||||
})
|
|
||||||
},
|
|
||||||
async loadCITypes(isResetCurrentId = false) {
|
async loadCITypes(isResetCurrentId = false) {
|
||||||
const groups = await getCITypeGroupsConfig({ need_other: true })
|
const groups = await getCITypeGroupsConfig({ need_other: true })
|
||||||
let alreadyReset = false
|
let alreadyReset = false
|
||||||
@@ -721,21 +704,6 @@ export default {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleDownloadCiType(e, ci) {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
const x = new XMLHttpRequest()
|
|
||||||
x.open('GET', `/api/v0.1/ci_types/${ci.id}/template/export`, true)
|
|
||||||
x.responseType = 'blob'
|
|
||||||
x.onload = function(e) {
|
|
||||||
const url = window.URL.createObjectURL(x.response)
|
|
||||||
const a = document.createElement('a')
|
|
||||||
a.href = url
|
|
||||||
a.download = `${ci.alias || ci.name}.json`
|
|
||||||
a.click()
|
|
||||||
}
|
|
||||||
x.send()
|
|
||||||
},
|
|
||||||
resetRoute() {
|
resetRoute() {
|
||||||
resetRouter()
|
resetRouter()
|
||||||
const roles = store.getters.roles
|
const roles = store.getters.roles
|
||||||
@@ -800,19 +768,6 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
changeUploadFile({ file, fileList, event }) {
|
|
||||||
const key = 'upload'
|
|
||||||
if (file.status === 'uploading') {
|
|
||||||
this.$message.loading({ content: '正在导入中', key, duration: 0 })
|
|
||||||
}
|
|
||||||
if (file.status === 'done') {
|
|
||||||
this.$message.success({ content: '导入成功', key, duration: 2 })
|
|
||||||
this.reload()
|
|
||||||
}
|
|
||||||
if (file.status === 'error') {
|
|
||||||
this.$message.error({ content: '导入失败,请稍后重试', key, duration: 2 })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@@ -372,7 +372,7 @@ export default {
|
|||||||
},
|
},
|
||||||
async open(property, attrList) {
|
async open(property, attrList) {
|
||||||
this.visible = true
|
this.visible = true
|
||||||
await this.getNoticeConfigAppBot()
|
this.getNoticeConfigAppBot()
|
||||||
this.attrList = attrList
|
this.attrList = attrList
|
||||||
if (property.has_trigger) {
|
if (property.has_trigger) {
|
||||||
this.triggerId = property.trigger.id
|
this.triggerId = property.trigger.id
|
||||||
|
@@ -71,6 +71,7 @@ import _ from 'lodash'
|
|||||||
import { getTriggerList, deleteTrigger, updateTrigger } from '../../api/CIType'
|
import { getTriggerList, deleteTrigger, updateTrigger } from '../../api/CIType'
|
||||||
import { getCITypeAttributesById } from '../../api/CITypeAttr'
|
import { getCITypeAttributesById } from '../../api/CITypeAttr'
|
||||||
import TriggerForm from './triggerForm.vue'
|
import TriggerForm from './triggerForm.vue'
|
||||||
|
import { getAllDepAndEmployee } from '@/api/company'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TriggerTable',
|
name: 'TriggerTable',
|
||||||
@@ -85,6 +86,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
tableData: [],
|
tableData: [],
|
||||||
attrList: [],
|
attrList: [],
|
||||||
|
allTreeDepAndEmp: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -95,9 +97,20 @@ export default {
|
|||||||
provide() {
|
provide() {
|
||||||
return {
|
return {
|
||||||
refresh: this.getTableData,
|
refresh: this.getTableData,
|
||||||
|
provide_allTreeDepAndEmp: () => {
|
||||||
|
return this.allTreeDepAndEmp
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getAllDepAndEmployee()
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
getAllDepAndEmployee() {
|
||||||
|
getAllDepAndEmployee({ block: 0 }).then((res) => {
|
||||||
|
this.allTreeDepAndEmp = res
|
||||||
|
})
|
||||||
|
},
|
||||||
async getTableData() {
|
async getTableData() {
|
||||||
const [triggerList, attrList] = await Promise.all([
|
const [triggerList, attrList] = await Promise.all([
|
||||||
getTriggerList(this.CITypeId),
|
getTriggerList(this.CITypeId),
|
||||||
|
@@ -61,7 +61,7 @@
|
|||||||
</vxe-column>
|
</vxe-column>
|
||||||
<vxe-column field="type_id" title="模型" width="150px">
|
<vxe-column field="type_id" title="模型" width="150px">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
{{ row.operate_type === '删除模型' ? row.change.alias : row.type_id }}
|
{{ row.operate_type === '删除模型' ? row.change.alias : row.type_id}}
|
||||||
</template>
|
</template>
|
||||||
</vxe-column>
|
</vxe-column>
|
||||||
<vxe-column field="changeDescription" title="描述">
|
<vxe-column field="changeDescription" title="描述">
|
||||||
|
@@ -314,12 +314,6 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Promise.all(promises).then(() => {
|
Promise.all(promises).then(() => {
|
||||||
if (type === 'all' || type === 'ci') {
|
|
||||||
const lastTypeId = window.localStorage.getItem('ops_ci_typeid') || undefined
|
|
||||||
if (Number(ciType.id) === Number(lastTypeId)) {
|
|
||||||
localStorage.setItem('ops_ci_typeid', '')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
that.$message.success('取消订阅成功')
|
that.$message.success('取消订阅成功')
|
||||||
that.resetRoute()
|
that.resetRoute()
|
||||||
})
|
})
|
||||||
|
@@ -25,7 +25,7 @@
|
|||||||
:expandedKeys="expandedKeys"
|
:expandedKeys="expandedKeys"
|
||||||
>
|
>
|
||||||
<a-icon slot="switcherIcon" type="down" />
|
<a-icon slot="switcherIcon" type="down" />
|
||||||
<template #title="{ key: treeKey, title, isLeaf }">
|
<template #title="{ key: treeKey, title,isLeaf }">
|
||||||
<ContextMenu
|
<ContextMenu
|
||||||
:title="title"
|
:title="title"
|
||||||
:treeKey="treeKey"
|
:treeKey="treeKey"
|
||||||
@@ -135,7 +135,7 @@
|
|||||||
{{ col.title }}</span
|
{{ col.title }}</span
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="col.is_choice || col.is_password || col.is_list" #edit="{ row }">
|
<template v-if="col.is_choice || col.is_password" #edit="{ row }">
|
||||||
<vxe-input v-if="col.is_password" v-model="passwordValue[col.field]" />
|
<vxe-input v-if="col.is_password" v-model="passwordValue[col.field]" />
|
||||||
<a-select
|
<a-select
|
||||||
:getPopupContainer="(trigger) => trigger.parentElement"
|
:getPopupContainer="(trigger) => trigger.parentElement"
|
||||||
@@ -172,18 +172,6 @@
|
|||||||
</span>
|
</span>
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
<a-select
|
|
||||||
:getPopupContainer="(trigger) => trigger.parentElement"
|
|
||||||
:style="{ width: '100%', height: '32px' }"
|
|
||||||
v-model="row[col.field]"
|
|
||||||
placeholder="请选择"
|
|
||||||
v-else-if="col.is_list"
|
|
||||||
:showArrow="false"
|
|
||||||
mode="tags"
|
|
||||||
class="ci-table-edit-select"
|
|
||||||
allowClear
|
|
||||||
>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
</template>
|
||||||
<template
|
<template
|
||||||
v-if="col.value_type === '6' || col.is_link || col.is_password || col.is_choice"
|
v-if="col.value_type === '6' || col.is_link || col.is_password || col.is_choice"
|
||||||
@@ -322,7 +310,6 @@
|
|||||||
v-else-if="relationViews.name2id && !relationViews.name2id.length"
|
v-else-if="relationViews.name2id && !relationViews.name2id.length"
|
||||||
></a-alert>
|
></a-alert>
|
||||||
<AddTableModal ref="addTableModal" @reload="reload" />
|
<AddTableModal ref="addTableModal" @reload="reload" />
|
||||||
<!-- <GrantDrawer ref="grantDrawer" resourceTypeName="RelationView" app_id="cmdb" /> -->
|
|
||||||
<CMDBGrant ref="cmdbGrant" resourceType="RelationView" app_id="cmdb" />
|
<CMDBGrant ref="cmdbGrant" resourceType="RelationView" app_id="cmdb" />
|
||||||
|
|
||||||
<ci-detail ref="detail" :typeId="Number(currentTypeId[0])" />
|
<ci-detail ref="detail" :typeId="Number(currentTypeId[0])" />
|
||||||
@@ -379,7 +366,6 @@ export default {
|
|||||||
SearchForm,
|
SearchForm,
|
||||||
AddTableModal,
|
AddTableModal,
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
// GrantDrawer,
|
|
||||||
CMDBGrant,
|
CMDBGrant,
|
||||||
SplitPane,
|
SplitPane,
|
||||||
ElTree: Tree,
|
ElTree: Tree,
|
||||||
@@ -497,11 +483,11 @@ export default {
|
|||||||
},
|
},
|
||||||
inject: ['reload'],
|
inject: ['reload'],
|
||||||
watch: {
|
watch: {
|
||||||
'$route.path': function (newPath, oldPath) {
|
'$route.path': function(newPath, oldPath) {
|
||||||
this.viewId = this.$route.params.viewId
|
this.viewId = this.$route.params.viewId
|
||||||
this.reload()
|
this.reload()
|
||||||
},
|
},
|
||||||
pageNo: function (newPage, oldPage) {
|
pageNo: function(newPage, oldPage) {
|
||||||
this.loadData({ pageNo: newPage }, undefined, this.sortByTable)
|
this.loadData({ pageNo: newPage }, undefined, this.sortByTable)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -653,9 +639,6 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
q = `q=_type:${this.currentTypeId[0]},` + q
|
q = `q=_type:${this.currentTypeId[0]},` + q
|
||||||
}
|
}
|
||||||
if (Object.values(this.level2constraint).includes('2')) {
|
|
||||||
q = q + `&&has_m2m=1`
|
|
||||||
}
|
|
||||||
if (this.currentTypeId[0]) {
|
if (this.currentTypeId[0]) {
|
||||||
const res = await searchCIRelation(q)
|
const res = await searchCIRelation(q)
|
||||||
|
|
||||||
@@ -694,7 +677,6 @@ export default {
|
|||||||
root_ids: key.split('%')[0],
|
root_ids: key.split('%')[0],
|
||||||
level: this.treeKeys.length - index,
|
level: this.treeKeys.length - index,
|
||||||
type_ids: this.showTypes.map((type) => type.id).join(','),
|
type_ids: this.showTypes.map((type) => type.id).join(','),
|
||||||
has_m2m: Number(Object.values(this.level2constraint).includes('2')),
|
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
let result
|
let result
|
||||||
const getTreeItem = (data, id) => {
|
const getTreeItem = (data, id) => {
|
||||||
@@ -748,10 +730,7 @@ export default {
|
|||||||
_showTypes = JSON.parse(JSON.stringify(this.origShowTypes))
|
_showTypes = JSON.parse(JSON.stringify(this.origShowTypes))
|
||||||
}
|
}
|
||||||
const promises = _showTypeIds.map((typeId) => {
|
const promises = _showTypeIds.map((typeId) => {
|
||||||
let _q = (`q=_type:${typeId},` + q).replace(/count=\d*/, 'count=1')
|
const _q = (`q=_type:${typeId},` + q).replace(/count=\d*/, 'count=1')
|
||||||
if (Object.values(this.level2constraint).includes('2')) {
|
|
||||||
_q = _q + `&&has_m2m=1`
|
|
||||||
}
|
|
||||||
console.log(_q)
|
console.log(_q)
|
||||||
if (this.treeKeys.length === 0) {
|
if (this.treeKeys.length === 0) {
|
||||||
return searchCI2(_q).then((res) => {
|
return searchCI2(_q).then((res) => {
|
||||||
@@ -807,7 +786,6 @@ export default {
|
|||||||
root_ids: ciIds.join(','),
|
root_ids: ciIds.join(','),
|
||||||
level: level,
|
level: level,
|
||||||
type_ids: this.showTypes.map((type) => type.id).join(','),
|
type_ids: this.showTypes.map((type) => type.id).join(','),
|
||||||
has_m2m: Number(Object.values(this.level2constraint).includes('2')),
|
|
||||||
}).then((num) => {
|
}).then((num) => {
|
||||||
facet.forEach((item, idx) => {
|
facet.forEach((item, idx) => {
|
||||||
item[1] += num[ciIds[idx] + '']
|
item[1] += num[ciIds[idx] + '']
|
||||||
@@ -837,9 +815,6 @@ export default {
|
|||||||
.map((item) => item.split('%')[0])
|
.map((item) => item.split('%')[0])
|
||||||
.join(',')}`
|
.join(',')}`
|
||||||
}
|
}
|
||||||
if (Object.values(this.level2constraint).includes('2')) {
|
|
||||||
q = q + `&&has_m2m=1`
|
|
||||||
}
|
|
||||||
searchCIRelation(q).then(async (res) => {
|
searchCIRelation(q).then(async (res) => {
|
||||||
const facet = []
|
const facet = []
|
||||||
const ciIds = []
|
const ciIds = []
|
||||||
@@ -862,7 +837,6 @@ export default {
|
|||||||
root_ids: ciIds.join(','),
|
root_ids: ciIds.join(','),
|
||||||
level: _level - 1,
|
level: _level - 1,
|
||||||
type_ids: this.showTypes.map((type) => type.id).join(','),
|
type_ids: this.showTypes.map((type) => type.id).join(','),
|
||||||
has_m2m: Number(Object.values(this.level2constraint).includes('2')),
|
|
||||||
}).then((num) => {
|
}).then((num) => {
|
||||||
facet.forEach((item, idx) => {
|
facet.forEach((item, idx) => {
|
||||||
item[1] += num[ciIds[idx] + '']
|
item[1] += num[ciIds[idx] + '']
|
||||||
@@ -1336,10 +1310,7 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
async openBatchDownload() {
|
async openBatchDownload() {
|
||||||
this.$refs.batchDownload.open({
|
this.$refs.batchDownload.open({ preferenceAttrList: this.preferenceAttrList })
|
||||||
preferenceAttrList: this.preferenceAttrList,
|
|
||||||
ciTypeName: this.$route.meta.name,
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
batchDownload({ filename, type, checkedKeys }) {
|
batchDownload({ filename, type, checkedKeys }) {
|
||||||
const jsonAttrList = []
|
const jsonAttrList = []
|
||||||
|
@@ -130,27 +130,22 @@ export default {
|
|||||||
exp = expression.match(regQ) ? expression.match(regQ)[0] : null
|
exp = expression.match(regQ) ? expression.match(regQ)[0] : null
|
||||||
}
|
}
|
||||||
|
|
||||||
await searchCI({
|
const res = await searchCI({
|
||||||
q: `_type:${this.addTypeId}${exp ? `,${exp}` : ''}${fuzzySearch ? `,*${fuzzySearch}*` : ''}`,
|
q: `_type:${this.addTypeId}${exp ? `,${exp}` : ''}${fuzzySearch ? `,*${fuzzySearch}*` : ''}`,
|
||||||
count: 50,
|
count: 50,
|
||||||
page: this.currentPage,
|
page: this.currentPage,
|
||||||
sort,
|
sort,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
this.tableData = res.result
|
||||||
this.tableData = res.result
|
this.totalNumber = res.numfound
|
||||||
this.totalNumber = res.numfound
|
this.columns = this.getColumns(res.result, this.preferenceAttrList)
|
||||||
this.columns = this.getColumns(res.result, this.preferenceAttrList)
|
this.$nextTick(() => {
|
||||||
this.$nextTick(() => {
|
const _table = this.$refs.xTable
|
||||||
const _table = this.$refs.xTable
|
if (_table) {
|
||||||
if (_table) {
|
_table.refreshColumn()
|
||||||
_table.refreshColumn()
|
}
|
||||||
}
|
this.loading = false
|
||||||
this.loading = false
|
})
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
getColumns(data, attrList) {
|
getColumns(data, attrList) {
|
||||||
const modalDom = document.getElementById('add-table-modal')
|
const modalDom = document.getElementById('add-table-modal')
|
||||||
|
@@ -193,7 +193,7 @@
|
|||||||
{{ col.title }}</span
|
{{ col.title }}</span
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="col.is_choice || col.is_password || col.is_list" #edit="{ row }">
|
<template v-if="col.is_choice || col.is_password" #edit="{ row }">
|
||||||
<vxe-input v-if="col.is_password" v-model="passwordValue[col.field]" />
|
<vxe-input v-if="col.is_password" v-model="passwordValue[col.field]" />
|
||||||
<a-select
|
<a-select
|
||||||
:getPopupContainer="(trigger) => trigger.parentElement"
|
:getPopupContainer="(trigger) => trigger.parentElement"
|
||||||
@@ -230,18 +230,6 @@
|
|||||||
</span>
|
</span>
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
<a-select
|
|
||||||
:getPopupContainer="(trigger) => trigger.parentElement"
|
|
||||||
:style="{ width: '100%', height: '32px' }"
|
|
||||||
v-model="row[col.field]"
|
|
||||||
placeholder="请选择"
|
|
||||||
v-else-if="col.is_list"
|
|
||||||
:showArrow="false"
|
|
||||||
mode="tags"
|
|
||||||
class="ci-table-edit-select"
|
|
||||||
allowClear
|
|
||||||
>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
</template>
|
||||||
<template
|
<template
|
||||||
v-if="col.value_type === '6' || col.is_link || col.is_password || col.is_choice"
|
v-if="col.value_type === '6' || col.is_link || col.is_password || col.is_choice"
|
||||||
@@ -516,7 +504,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'$route.path': function (newPath, oldPath) {
|
'$route.path': function(newPath, oldPath) {
|
||||||
this.newLoad = true
|
this.newLoad = true
|
||||||
this.typeId = this.$route.params.typeId
|
this.typeId = this.$route.params.typeId
|
||||||
this.initPage()
|
this.initPage()
|
||||||
@@ -984,10 +972,7 @@ export default {
|
|||||||
const $table = this.$refs['xTable'].getVxetableRef()
|
const $table = this.$refs['xTable'].getVxetableRef()
|
||||||
const data = {}
|
const data = {}
|
||||||
this.columns.forEach((item) => {
|
this.columns.forEach((item) => {
|
||||||
if (
|
if (!(item.field in this.initialPasswordValue) && !_.isEqual(row[item.field], this.initialInstanceList[rowIndex][item.field])) {
|
||||||
!(item.field in this.initialPasswordValue) &&
|
|
||||||
!_.isEqual(row[item.field], this.initialInstanceList[rowIndex][item.field])
|
|
||||||
) {
|
|
||||||
data[item.field] = row[item.field] ?? null
|
data[item.field] = row[item.field] ?? null
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -1054,10 +1039,7 @@ export default {
|
|||||||
this.$refs.jsonEditor.open(column, row)
|
this.$refs.jsonEditor.open(column, row)
|
||||||
},
|
},
|
||||||
async openBatchDownload() {
|
async openBatchDownload() {
|
||||||
this.$refs.batchDownload.open({
|
this.$refs.batchDownload.open({ preferenceAttrList: this.currentAttrList })
|
||||||
preferenceAttrList: this.currentAttrList,
|
|
||||||
ciTypeName: this.$route.meta.title || this.$route.meta.name,
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
batchDownload({ filename, type, checkedKeys }) {
|
batchDownload({ filename, type, checkedKeys }) {
|
||||||
console.log(filename, type)
|
console.log(filename, type)
|
||||||
@@ -1102,19 +1084,13 @@ export default {
|
|||||||
let errorNum = 0
|
let errorNum = 0
|
||||||
this.loading = true
|
this.loading = true
|
||||||
this.loadTip = `正在删除...`
|
this.loadTip = `正在删除...`
|
||||||
const floor = Math.ceil(this.selectedRowKeys.length / 6)
|
for (let i = 0; i < this.selectedRowKeys.length; i++) {
|
||||||
for (let i = 0; i < floor; i++) {
|
await deleteCI(this.selectedRowKeys[i], false)
|
||||||
const itemList = this.selectedRowKeys.slice(6 * i, 6 * i + 6)
|
.then(() => {
|
||||||
const promises = itemList.map((x) => deleteCI(x, false))
|
successNum += 1
|
||||||
await Promise.allSettled(promises)
|
})
|
||||||
.then((res) => {
|
.catch(() => {
|
||||||
res.forEach((r) => {
|
errorNum += 1
|
||||||
if (r.status === 'fulfilled') {
|
|
||||||
successNum += 1
|
|
||||||
} else {
|
|
||||||
errorNum += 1
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.loadTip = `正在删除,共${this.selectedRowKeys.length}个,成功${successNum}个,失败${errorNum}个`
|
this.loadTip = `正在删除,共${this.selectedRowKeys.length}个,成功${successNum}个,失败${errorNum}个`
|
||||||
|
@@ -92,13 +92,7 @@ export const generatorDynamicRouter = async () => {
|
|||||||
meta: { title: '飞书', icon: 'ops-setting-notice-feishu', selectedIcon: 'ops-setting-notice-feishu-selected' },
|
meta: { title: '飞书', icon: 'ops-setting-notice-feishu', selectedIcon: 'ops-setting-notice-feishu-selected' },
|
||||||
component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/notice/feishu')
|
component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/notice/feishu')
|
||||||
}]
|
}]
|
||||||
},
|
}
|
||||||
{
|
|
||||||
path: '/setting/auth',
|
|
||||||
name: 'company_auth',
|
|
||||||
meta: { title: '认证设置', appName: 'backend', icon: 'ops-setting-auth', selectedIcon: 'ops-setting-auth-selected', permission: ['acl_admin'] },
|
|
||||||
component: () => import(/* webpackChunkName: "setting" */ '@/views/setting/auth/index')
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
},])
|
},])
|
||||||
return routes
|
return routes
|
||||||
@@ -118,11 +112,6 @@ export const constantRouterMap = [
|
|||||||
name: 'login',
|
name: 'login',
|
||||||
component: () => import(/* webpackChunkName: "user" */ '@/views/user/Login'),
|
component: () => import(/* webpackChunkName: "user" */ '@/views/user/Login'),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/user/logout',
|
|
||||||
name: 'logout',
|
|
||||||
component: () => import(/* webpackChunkName: "user" */ '@/views/user/Logout'),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/user',
|
path: '/user',
|
||||||
component: UserLayout,
|
component: UserLayout,
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
import { generatorDynamicRouter, constantRouterMap } from '@/router/config'
|
import { generatorDynamicRouter, constantRouterMap } from '@/router/config'
|
||||||
import { searchPermResourceByRoleId } from '@/modules/acl/api/permission'
|
|
||||||
import store from '@/store'
|
|
||||||
import user from './user'
|
import user from './user'
|
||||||
/**
|
/**
|
||||||
* 过滤账户是否拥有某一个权限,并将菜单从加载列表移除
|
* 过滤账户是否拥有某一个权限,并将菜单从加载列表移除
|
||||||
@@ -9,47 +7,37 @@ import user from './user'
|
|||||||
* @param route
|
* @param route
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
async function hasPermission(permission, route) {
|
function hasPermission(permission, route) {
|
||||||
return new Promise(async (resolve, reject) => {
|
const { detailPermissions } = user.state
|
||||||
const { detailPermissions } = user.state
|
if (route.meta && route.meta.permission) {
|
||||||
if (route.meta && route.meta.permission) {
|
// let flag = false
|
||||||
const totalPer = [...route.meta.appName && detailPermissions[`${route.meta.appName}`] ? detailPermissions[`${route.meta.appName}`].map(item => item.name) : [], ...permission]
|
// for (let i = 0, len = permission.length; i < len; i++) {
|
||||||
let flag = false
|
// flag = (route.meta.permission || []).includes(permission[i])
|
||||||
if (route.name === 'ci_type') {
|
// if (flag) {
|
||||||
await searchPermResourceByRoleId(store.state.user.rid, {
|
// return true
|
||||||
resource_type_id: 'page',
|
// }
|
||||||
app_id: 'cmdb',
|
// }
|
||||||
}).then(res => {
|
// return false
|
||||||
const { resources } = res
|
const totalPer = [...route.meta.appName && detailPermissions[`${route.meta.appName}`] ? detailPermissions[`${route.meta.appName}`].map(item => item.name) : [], ...permission]
|
||||||
const _idx = resources.findIndex(item => item.name === '模型配置')
|
return route.meta.permission.some(item => totalPer.includes(item))
|
||||||
flag = flag || (_idx > -1)
|
}
|
||||||
})
|
return true
|
||||||
}
|
|
||||||
resolve(route.meta.permission.some(item => totalPer.includes(item)) || flag)
|
|
||||||
}
|
|
||||||
resolve(true)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function filterAsyncRouter(routerMap, roles) {
|
function filterAsyncRouter(routerMap, roles) {
|
||||||
const filteredRoutes = []
|
return routerMap.filter(route => {
|
||||||
for (let i = 0; i < routerMap.length; i++) {
|
|
||||||
const route = routerMap[i]
|
|
||||||
const default_route = ['company_info', 'company_structure', 'company_group']
|
const default_route = ['company_info', 'company_structure', 'company_group']
|
||||||
if (default_route.includes(route.name)) {
|
if (default_route.includes(route.name)) {
|
||||||
filteredRoutes.push(route)
|
return true
|
||||||
} else {
|
|
||||||
await hasPermission(roles.permissions, route).then(async flag => {
|
|
||||||
if (flag) {
|
|
||||||
if (route.children && route.children.length) {
|
|
||||||
route.children = await filterAsyncRouter(route.children, roles)
|
|
||||||
}
|
|
||||||
filteredRoutes.push(route)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
if (hasPermission(roles.permissions, route)) {
|
||||||
return filteredRoutes
|
if (route.children && route.children.length) {
|
||||||
|
route.children = filterAsyncRouter(route.children, roles)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const routes = {
|
const routes = {
|
||||||
@@ -67,8 +55,8 @@ const routes = {
|
|||||||
GenerateRoutes({ commit }, data) {
|
GenerateRoutes({ commit }, data) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const { roles } = data
|
const { roles } = data
|
||||||
generatorDynamicRouter().then(async routers => {
|
generatorDynamicRouter().then(routers => {
|
||||||
const accessedRouters = await filterAsyncRouter(routers, roles)
|
const accessedRouters = filterAsyncRouter(routers, roles)
|
||||||
commit('SET_ROUTERS', accessedRouters)
|
commit('SET_ROUTERS', accessedRouters)
|
||||||
resolve(accessedRouters)
|
resolve(accessedRouters)
|
||||||
})
|
})
|
||||||
|
@@ -6,7 +6,6 @@ import { getAllUsers } from '../../api/login'
|
|||||||
import { searchPermResourceByRoleId } from '@/modules/acl/api/permission'
|
import { searchPermResourceByRoleId } from '@/modules/acl/api/permission'
|
||||||
import { getEmployeeByUid, getEmployeeList } from '@/api/employee'
|
import { getEmployeeByUid, getEmployeeList } from '@/api/employee'
|
||||||
import { getAllDepartmentList } from '@/api/company'
|
import { getAllDepartmentList } from '@/api/company'
|
||||||
import { getAuthDataEnable } from '@/api/auth'
|
|
||||||
|
|
||||||
const user = {
|
const user = {
|
||||||
state: {
|
state: {
|
||||||
@@ -45,8 +44,7 @@ const user = {
|
|||||||
nickname: '',
|
nickname: '',
|
||||||
sex: '',
|
sex: '',
|
||||||
position_name: '',
|
position_name: '',
|
||||||
direct_supervisor_id: null,
|
direct_supervisor_id: null
|
||||||
auth_enable: {}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
mutations: {
|
mutations: {
|
||||||
@@ -54,14 +52,13 @@ const user = {
|
|||||||
state.token = token
|
state.token = token
|
||||||
},
|
},
|
||||||
|
|
||||||
SET_USER_INFO: (state, { name, welcome, avatar, roles, info, uid, rid, username, mobile, department_id, employee_id, email, nickname, sex, position_name, direct_supervisor_id, annual_leave }) => {
|
SET_USER_INFO: (state, { name, welcome, avatar, roles, info, uid, username, mobile, department_id, employee_id, email, nickname, sex, position_name, direct_supervisor_id, annual_leave }) => {
|
||||||
state.name = name
|
state.name = name
|
||||||
state.welcome = welcome
|
state.welcome = welcome
|
||||||
state.avatar = avatar
|
state.avatar = avatar
|
||||||
state.roles = roles
|
state.roles = roles
|
||||||
state.info = info
|
state.info = info
|
||||||
state.uid = uid
|
state.uid = uid
|
||||||
state.rid = rid
|
|
||||||
state.authed = true
|
state.authed = true
|
||||||
state.username = username
|
state.username = username
|
||||||
state.mobile = mobile
|
state.mobile = mobile
|
||||||
@@ -90,27 +87,13 @@ const user = {
|
|||||||
...data
|
...data
|
||||||
} : state.detailPermissions
|
} : state.detailPermissions
|
||||||
},
|
},
|
||||||
SET_AUTH_ENABLE: (state, data) => {
|
|
||||||
state.auth_enable = data
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
// 获取enable_list
|
|
||||||
GetAuthDataEnable({ commit }, userInfo) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
getAuthDataEnable().then(res => {
|
|
||||||
commit('SET_AUTH_ENABLE', res)
|
|
||||||
resolve()
|
|
||||||
}).catch(error => {
|
|
||||||
reject(error)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 登录
|
// 登录
|
||||||
Login({ commit }, { userInfo, auth_type = undefined }) {
|
Login({ commit }, userInfo) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
login(userInfo, auth_type).then(response => {
|
login(userInfo).then(response => {
|
||||||
Vue.ls.set(ACCESS_TOKEN, response.token, 7 * 24 * 60 * 60 * 1000)
|
Vue.ls.set(ACCESS_TOKEN, response.token, 7 * 24 * 60 * 60 * 1000)
|
||||||
commit('SET_TOKEN', response.token)
|
commit('SET_TOKEN', response.token)
|
||||||
resolve()
|
resolve()
|
||||||
@@ -150,7 +133,6 @@ const user = {
|
|||||||
welcome: welcome(),
|
welcome: welcome(),
|
||||||
avatar: result.avatar,
|
avatar: result.avatar,
|
||||||
uid: result.uid,
|
uid: result.uid,
|
||||||
rid: result.rid,
|
|
||||||
username: result.username,
|
username: result.username,
|
||||||
mobile: res.mobile,
|
mobile: res.mobile,
|
||||||
department_id: res.department_id,
|
department_id: res.department_id,
|
||||||
@@ -177,11 +159,10 @@ const user = {
|
|||||||
Vue.ls.remove(ACCESS_TOKEN)
|
Vue.ls.remove(ACCESS_TOKEN)
|
||||||
|
|
||||||
logout(state.token).then(() => {
|
logout(state.token).then(() => {
|
||||||
|
window.location.reload()
|
||||||
resolve()
|
resolve()
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
resolve()
|
resolve()
|
||||||
}).finally(() => {
|
|
||||||
window.location.href = '/user/logout'
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@@ -9,6 +9,7 @@ import logo from './global/logo'
|
|||||||
import notice from './global/notice'
|
import notice from './global/notice'
|
||||||
import getters from './global/getters'
|
import getters from './global/getters'
|
||||||
import appConfig from '@/config/app'
|
import appConfig from '@/config/app'
|
||||||
|
console.log(appConfig)
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
/* eslint-dsiable */
|
/* eslint-dsiable */
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import store from '@/store'
|
||||||
import { VueAxios } from './axios'
|
import { VueAxios } from './axios'
|
||||||
|
import config from '@/config/setting'
|
||||||
import message from 'ant-design-vue/es/message'
|
import message from 'ant-design-vue/es/message'
|
||||||
import notification from 'ant-design-vue/es/notification'
|
import notification from 'ant-design-vue/es/notification'
|
||||||
import { ACCESS_TOKEN } from '@/store/global/mutation-types'
|
import { ACCESS_TOKEN } from '@/store/global/mutation-types'
|
||||||
import router from '@/router'
|
|
||||||
|
|
||||||
// 创建 axios 实例
|
// 创建 axios 实例
|
||||||
const service = axios.create({
|
const service = axios.create({
|
||||||
@@ -51,8 +52,8 @@ const err = (error) => {
|
|||||||
}
|
}
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
console.log(error.config.url)
|
console.log(error.config.url)
|
||||||
if (error.response.status === 401 && router.path === '/user/login') {
|
if (error.response.status === 401 && config.useSSO) {
|
||||||
window.location.href = '/user/logout'
|
store.dispatch('Login')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Promise.reject(error)
|
return Promise.reject(error)
|
||||||
|
@@ -1,111 +0,0 @@
|
|||||||
<template>
|
|
||||||
<a-form-model ref="form" :model="form" :label-col="labelCol" :wrapper-col="wrapperCol" :rules="rules">
|
|
||||||
<SpanTitle>基本</SpanTitle>
|
|
||||||
<a-form-model-item label="是否启用" prop="enable">
|
|
||||||
<a-switch
|
|
||||||
:checked="Boolean(form.enable)"
|
|
||||||
@change="
|
|
||||||
() => {
|
|
||||||
$set(form, 'enable', Number(!form.enable))
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</a-form-model-item>
|
|
||||||
<a-form-model-item label="服务端地址" prop="cas_server" help="不包括url path,例如https://xxx.com">
|
|
||||||
<a-input v-model="form.cas_server" placeholder="请输入服务端地址" />
|
|
||||||
</a-form-model-item>
|
|
||||||
<a-form-model-item label="验证服务端地址" prop="cas_validate_server" help="不包括url path,例如https://xxx.com">
|
|
||||||
<a-input v-model="form.cas_validate_server" placeholder="请输入验证服务端地址" />
|
|
||||||
</a-form-model-item>
|
|
||||||
<SpanTitle>其他</SpanTitle>
|
|
||||||
<a-form-model-item label="登录路由" prop="cas_login_route">
|
|
||||||
<a-input v-model="form.cas_login_route" placeholder="/cas/built-in/cas/login" />
|
|
||||||
</a-form-model-item>
|
|
||||||
<a-form-model-item label="注销路由" prop="cas_logout_route">
|
|
||||||
<a-input v-model="form.cas_logout_route" placeholder="/cas/built-in/cas/logout" />
|
|
||||||
</a-form-model-item>
|
|
||||||
<a-form-model-item label="验证路由" prop="cas_validate_route">
|
|
||||||
<a-input v-model="form.cas_validate_route" placeholder="/cas/built-in/cas/serviceValidate" />
|
|
||||||
</a-form-model-item>
|
|
||||||
<a-form-model-item label="重定向路由" prop="cas_after_login">
|
|
||||||
<a-input v-model="form.cas_after_login" placeholder="请输入重定向路由" />
|
|
||||||
</a-form-model-item>
|
|
||||||
<a-form-model-item label="用户属性映射" prop="cas_user_map" :wrapper-col="{ span: 15 }">
|
|
||||||
<vue-json-editor
|
|
||||||
:style="{ '--custom-height': `${200}px` }"
|
|
||||||
v-model="form.cas_user_map"
|
|
||||||
:showBtns="false"
|
|
||||||
mode="code"
|
|
||||||
lang="zh"
|
|
||||||
@json-change="onJsonChange"
|
|
||||||
@has-error="onJsonError"
|
|
||||||
/>
|
|
||||||
</a-form-model-item>
|
|
||||||
</a-form-model>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import _ from 'lodash'
|
|
||||||
import vueJsonEditor from 'vue-json-editor'
|
|
||||||
import SpanTitle from '../components/spanTitle.vue'
|
|
||||||
export default {
|
|
||||||
name: 'CAS',
|
|
||||||
components: { SpanTitle, vueJsonEditor },
|
|
||||||
data() {
|
|
||||||
const defaultForm = {
|
|
||||||
enable: 0,
|
|
||||||
cas_server: '',
|
|
||||||
cas_validate_server: '',
|
|
||||||
cas_login_route: '',
|
|
||||||
cas_logout_route: '',
|
|
||||||
cas_validate_route: '',
|
|
||||||
cas_after_login: '/',
|
|
||||||
cas_user_map: {
|
|
||||||
username: { tag: 'cas:user' },
|
|
||||||
nickname: { tag: 'cas:attribute', attrs: { name: 'displayName' } },
|
|
||||||
email: { tag: 'cas:attribute', attrs: { name: 'email' } },
|
|
||||||
mobile: { tag: 'cas:attribute', attrs: { name: 'phone' } },
|
|
||||||
avatar: { tag: 'cas:attribute', attrs: { name: 'avatar' } },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
defaultForm,
|
|
||||||
labelCol: { span: 3 },
|
|
||||||
wrapperCol: { span: 10 },
|
|
||||||
form: _.cloneDeep(defaultForm),
|
|
||||||
rules: {
|
|
||||||
enable: [{ required: true }],
|
|
||||||
cas_server: [{ required: true, message: '请输入服务端地址' }],
|
|
||||||
cas_login_route: [{ required: true, message: '请输入登录路由' }],
|
|
||||||
cas_logout_route: [{ required: true, message: '请输入注销路由' }],
|
|
||||||
cas_validate_route: [{ required: true, message: '请输入验证路由' }],
|
|
||||||
},
|
|
||||||
isJsonRight: true,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
setData(data) {
|
|
||||||
if (data) {
|
|
||||||
this.form = data
|
|
||||||
} else {
|
|
||||||
this.form = _.cloneDeep(this.defaultForm)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getData(callback) {
|
|
||||||
this.$refs.form.validate((valid) => {
|
|
||||||
if (valid && this.isJsonRight) {
|
|
||||||
callback(this.form)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onJsonChange(value) {
|
|
||||||
this.isJsonRight = true
|
|
||||||
},
|
|
||||||
onJsonError() {
|
|
||||||
this.isJsonRight = false
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style></style>
|
|
@@ -1,65 +0,0 @@
|
|||||||
<template>
|
|
||||||
<a-form-model ref="form" :model="form" :label-col="labelCol" :wrapper-col="wrapperCol" :rules="rules">
|
|
||||||
<SpanTitle>基本</SpanTitle>
|
|
||||||
<a-form-model-item
|
|
||||||
label="自动跳转到第三方登录页"
|
|
||||||
prop="auto_redirect"
|
|
||||||
help="如果关闭,则会弹出跳转到第三方登录页的确认,点取消按钮会进入系统内置的登录页"
|
|
||||||
>
|
|
||||||
<a-switch
|
|
||||||
:checked="Boolean(form.auto_redirect)"
|
|
||||||
@change="
|
|
||||||
() => {
|
|
||||||
$set(form, 'auto_redirect', Number(!form.auto_redirect))
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</a-form-model-item>
|
|
||||||
<!-- <a-form-model-item
|
|
||||||
label="API服务地址"
|
|
||||||
prop="api_host"
|
|
||||||
help="如果服务的部署没使用DNS, 如果要启用CAS、OAuth2.0、OIDC的,则须填API服务地址"
|
|
||||||
>
|
|
||||||
<a-input v-model="form.api_host" placeholder="http://127.0.0.1:5000" />
|
|
||||||
</a-form-model-item> -->
|
|
||||||
</a-form-model>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import SpanTitle from '../components/spanTitle.vue'
|
|
||||||
export default {
|
|
||||||
name: 'AuthCommonConfig',
|
|
||||||
components: { SpanTitle },
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
labelCol: { span: 5 },
|
|
||||||
wrapperCol: { span: 10 },
|
|
||||||
form: {
|
|
||||||
auto_redirect: 0,
|
|
||||||
api_host: '',
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
auto_redirect: [{ required: true }],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
setData(data) {
|
|
||||||
if (data) {
|
|
||||||
this.form = data
|
|
||||||
} else {
|
|
||||||
this.form = { auto_redirect: 0 }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getData(callback) {
|
|
||||||
this.$refs.form.validate((valid) => {
|
|
||||||
if (valid) {
|
|
||||||
callback(this.form)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style></style>
|
|
@@ -1,166 +0,0 @@
|
|||||||
<template>
|
|
||||||
<a-tabs type="card" class="ops-tab" v-model="activeKey" @change="changeActiveKey">
|
|
||||||
<a-tab-pane v-for="item in authList" :key="item.value">
|
|
||||||
<span slot="tab">
|
|
||||||
{{ item.label }}
|
|
||||||
<a-icon
|
|
||||||
v-if="enable_list.find((en) => en.auth_type === item.value)"
|
|
||||||
type="check-circle"
|
|
||||||
theme="filled"
|
|
||||||
style="color:#2f54eb"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<div class="setting-auth">
|
|
||||||
<components :ref="item.value" :is="item.value === 'OIDC' ? 'OAUTH2' : item.value" :data_type="item.value" />
|
|
||||||
<a-row>
|
|
||||||
<a-col :offset="item.value === 'AuthCommonConfig' ? 5 : 3">
|
|
||||||
<a-space>
|
|
||||||
<a-button :loading="loading" type="primary" @click="handleSave">保存</a-button>
|
|
||||||
<template v-if="item.value === 'LDAP'">
|
|
||||||
<a-button :loading="loading" ghost type="primary" @click="handleTest('connect')">测试连接</a-button>
|
|
||||||
<a-button :loading="loading" ghost type="primary" @click="handleTest('login')">测试登录</a-button>
|
|
||||||
</template>
|
|
||||||
<a-button :loading="loading" @click="handleReset">重置</a-button>
|
|
||||||
</a-space>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</div>
|
|
||||||
<LoginModal v-if="item.value === 'LDAP'" ref="loginModal" @handleOK="(values) => handleTest('login', values)" />
|
|
||||||
</a-tab-pane>
|
|
||||||
</a-tabs>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import _ from 'lodash'
|
|
||||||
import LDAP from './ldap.vue'
|
|
||||||
import CAS from './cas.vue'
|
|
||||||
import AuthCommonConfig from './common.vue'
|
|
||||||
import OAUTH2 from './oauth2.vue'
|
|
||||||
import LoginModal from './loginModal.vue'
|
|
||||||
import { getAuthData, postAuthData, putAuthData, getAuthDataEnable, testLDAP } from '@/api/auth'
|
|
||||||
export default {
|
|
||||||
name: 'Auth',
|
|
||||||
components: { LDAP, CAS, AuthCommonConfig, OAUTH2, LoginModal },
|
|
||||||
data() {
|
|
||||||
const authList = [
|
|
||||||
{
|
|
||||||
value: 'LDAP',
|
|
||||||
label: 'LDAP',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'CAS',
|
|
||||||
label: 'CAS',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'OAUTH2',
|
|
||||||
label: 'OAUTH2',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'OIDC',
|
|
||||||
label: 'OIDC',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'AuthCommonConfig',
|
|
||||||
label: '通用',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
return {
|
|
||||||
authList,
|
|
||||||
activeKey: 'LDAP',
|
|
||||||
dataTypeId: null,
|
|
||||||
loading: false,
|
|
||||||
enable_list: [],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.changeActiveKey()
|
|
||||||
this.getAuthDataEnable()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getAuthDataEnable() {
|
|
||||||
getAuthDataEnable().then((res) => {
|
|
||||||
this.enable_list = res.enable_list
|
|
||||||
})
|
|
||||||
},
|
|
||||||
changeActiveKey() {
|
|
||||||
getAuthData(this.activeKey).then((res) => {
|
|
||||||
const _res = _.cloneDeep(res)
|
|
||||||
this.$refs[this.activeKey][0].setData(_res?.data ?? null)
|
|
||||||
if (_res && JSON.stringify(_res) !== '{}') {
|
|
||||||
this.dataTypeId = _res.id
|
|
||||||
} else {
|
|
||||||
this.dataTypeId = null
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
handleSave() {
|
|
||||||
this.$refs[this.activeKey][0].getData(async (data) => {
|
|
||||||
this.loading = true
|
|
||||||
if (this.dataTypeId) {
|
|
||||||
await putAuthData(this.activeKey, this.dataTypeId, { data }).finally(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
await postAuthData(this.activeKey, { data }).finally(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.$message.success('保存成功')
|
|
||||||
this.changeActiveKey()
|
|
||||||
this.getAuthDataEnable()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
handleReset() {
|
|
||||||
this.changeActiveKey()
|
|
||||||
},
|
|
||||||
handleTest(type, values = null) {
|
|
||||||
this.$refs[this.activeKey][0].getData(async (data) => {
|
|
||||||
if (type === 'login' && !values) {
|
|
||||||
this.$refs.loginModal[0].open()
|
|
||||||
} else {
|
|
||||||
this.loading = true
|
|
||||||
let _data = _.cloneDeep(data)
|
|
||||||
if (values) {
|
|
||||||
_data = {
|
|
||||||
..._data,
|
|
||||||
...values,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
testLDAP(type, { data: _data })
|
|
||||||
.then((res) => {
|
|
||||||
this.$message.success('测试成功')
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.setting-auth {
|
|
||||||
background-color: #fff;
|
|
||||||
height: calc(100vh - 128px);
|
|
||||||
overflow: auto;
|
|
||||||
border-radius: 0 5px 5px 5px;
|
|
||||||
padding-top: 24px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
.setting-auth {
|
|
||||||
.jsoneditor-outer {
|
|
||||||
height: var(--custom-height) !important;
|
|
||||||
border: 1px solid #2f54eb;
|
|
||||||
}
|
|
||||||
div.jsoneditor-menu {
|
|
||||||
background-color: #2f54eb;
|
|
||||||
}
|
|
||||||
.jsoneditor-modes {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,80 +0,0 @@
|
|||||||
<template>
|
|
||||||
<a-form-model ref="form" :model="form" :label-col="labelCol" :wrapper-col="wrapperCol" :rules="rules">
|
|
||||||
<SpanTitle>基本</SpanTitle>
|
|
||||||
<a-form-model-item label="是否启用" prop="enable">
|
|
||||||
<a-switch
|
|
||||||
:checked="Boolean(form.enable)"
|
|
||||||
@change="
|
|
||||||
() => {
|
|
||||||
$set(form, 'enable', Number(!form.enable))
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</a-form-model-item>
|
|
||||||
<a-form-model-item
|
|
||||||
label="服务器地址"
|
|
||||||
prop="ldap_server"
|
|
||||||
help="例如: 192.168.1.6 或者 ldap://192.168.1.6 或者 ldap://192.168.1.6:389"
|
|
||||||
>
|
|
||||||
<a-input v-model="form.ldap_server" placeholder="请输入服务器地址" />
|
|
||||||
</a-form-model-item>
|
|
||||||
<a-form-model-item label="域" prop="ldap_domain">
|
|
||||||
<a-input v-model="form.ldap_domain" placeholder="请输入域" />
|
|
||||||
</a-form-model-item>
|
|
||||||
<SpanTitle>用户</SpanTitle>
|
|
||||||
<a-form-model-item
|
|
||||||
label="用户名称"
|
|
||||||
prop="ldap_user_dn"
|
|
||||||
help="用户dn: cn={},ou=users,dc=xxx,dc=com {}会替换成用户名"
|
|
||||||
>
|
|
||||||
<a-input v-model="form.ldap_user_dn" placeholder="请输入用户名称" />
|
|
||||||
</a-form-model-item>
|
|
||||||
</a-form-model>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import SpanTitle from '../components/spanTitle.vue'
|
|
||||||
export default {
|
|
||||||
name: 'LDAP',
|
|
||||||
components: { SpanTitle },
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
labelCol: { span: 3 },
|
|
||||||
wrapperCol: { span: 10 },
|
|
||||||
form: {
|
|
||||||
enable: 0,
|
|
||||||
ldap_server: '',
|
|
||||||
ldap_domain: '',
|
|
||||||
ldap_user_dn: 'cn={},ou=users,dc=xxx,dc=com',
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
enable: [{ required: true }],
|
|
||||||
ldap_server: [{ required: true, message: '请输入服务器地址' }],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
setData(data) {
|
|
||||||
if (data) {
|
|
||||||
this.form = { ...data }
|
|
||||||
} else {
|
|
||||||
this.form = {
|
|
||||||
enable: 0,
|
|
||||||
ldap_server: '',
|
|
||||||
ldap_domain: '',
|
|
||||||
ldap_user_dn: 'cn={},ou=users,dc=xxx,dc=com',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getData(callback) {
|
|
||||||
this.$refs.form.validate((valid) => {
|
|
||||||
if (valid) {
|
|
||||||
callback(this.form)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style></style>
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user