mirror of
https://github.com/veops/cmdb.git
synced 2025-08-07 17:07:46 +08:00
前后端全面升级
This commit is contained in:
@@ -1,29 +1,2 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import os
|
||||
|
||||
from flask import Blueprint
|
||||
from flask_restful import Api
|
||||
|
||||
from api.resource import register_resources
|
||||
from api.views.account import LoginView, LogoutView
|
||||
|
||||
HERE = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
# account
|
||||
blueprint_account = Blueprint('account_api', __name__, url_prefix='/api')
|
||||
account_rest = Api(blueprint_account)
|
||||
account_rest.add_resource(LoginView, LoginView.url_prefix)
|
||||
account_rest.add_resource(LogoutView, LogoutView.url_prefix)
|
||||
|
||||
|
||||
# cmdb
|
||||
blueprint_cmdb_v01 = Blueprint('cmdb_api_v01', __name__, url_prefix='/api/v0.1')
|
||||
rest = Api(blueprint_cmdb_v01)
|
||||
register_resources(os.path.join(HERE, "cmdb"), rest)
|
||||
|
||||
|
||||
# acl
|
||||
blueprint_acl_v1 = Blueprint('acl_api_v1', __name__, url_prefix='/api/v1/acl')
|
||||
rest = Api(blueprint_acl_v1)
|
||||
register_resources(os.path.join(HERE, "acl"), rest)
|
||||
|
@@ -2,19 +2,17 @@
|
||||
|
||||
import datetime
|
||||
|
||||
import six
|
||||
import jwt
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
from flask import request
|
||||
from flask import session
|
||||
from flask_login import login_user, logout_user
|
||||
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.perm.acl.cache import User
|
||||
from api.lib.perm.auth import auth_abandoned
|
||||
from api.models.acl import User, Role
|
||||
from api.resource import APIView
|
||||
from api.lib.perm.acl.role import RoleRelationCRUD
|
||||
from api.lib.perm.acl.cache import RoleCache
|
||||
|
||||
|
||||
class LoginView(APIView):
|
||||
@@ -26,14 +24,9 @@ class LoginView(APIView):
|
||||
def post(self):
|
||||
username = request.values.get("username") or request.values.get("email")
|
||||
password = request.values.get("password")
|
||||
if current_app.config.get('AUTH_WITH_LDAP'):
|
||||
user, authenticated = User.query.authenticate_with_ldap(username, password)
|
||||
else:
|
||||
user, authenticated = User.query.authenticate(username, password)
|
||||
if not user:
|
||||
return abort(403, "User <{0}> does not exist".format(username))
|
||||
user, authenticated = User.query.authenticate(username, password)
|
||||
if not authenticated:
|
||||
return abort(403, "invalid username or password")
|
||||
return abort(401, "invalid username or password")
|
||||
|
||||
login_user(user)
|
||||
|
||||
@@ -43,19 +36,30 @@ class LoginView(APIView):
|
||||
'exp': datetime.datetime.now() + datetime.timedelta(minutes=24 * 60 * 7)},
|
||||
current_app.config['SECRET_KEY'])
|
||||
|
||||
role = Role.get_by(uid=user.uid, first=True, to_dict=False)
|
||||
if role:
|
||||
parent_ids = RoleRelationCRUD.recursive_parent_ids(role.id)
|
||||
parent_roles = [RoleCache.get(i).name for i in parent_ids]
|
||||
else:
|
||||
parent_roles = []
|
||||
session["acl"] = dict(uid=user.uid,
|
||||
avatar=user.avatar,
|
||||
userName=user.username,
|
||||
nickName=user.nickname,
|
||||
parentRoles=parent_roles)
|
||||
return self.jsonify(token=token.decode() if six.PY2 else token, username=username)
|
||||
|
||||
return self.jsonify(token=token.decode())
|
||||
|
||||
class AuthWithKeyView(APIView):
|
||||
url_prefix = "/auth_with_key"
|
||||
|
||||
@args_required("key")
|
||||
@args_required("secret")
|
||||
@args_required("path")
|
||||
@auth_abandoned
|
||||
def post(self):
|
||||
key = request.values.get('key')
|
||||
secret = request.values.get('secret')
|
||||
path = six.moves.urllib.parse.urlparse(request.values.get('path')).path
|
||||
payload = request.values.get('payload') or {}
|
||||
|
||||
payload.pop('_key', None)
|
||||
payload.pop('_secret', None)
|
||||
|
||||
req_args = [str(payload[k]) for k in sorted(payload.keys())]
|
||||
user, authenticated = User.query.authenticate_with_key(key, secret, req_args, path)
|
||||
|
||||
return self.jsonify(user=user.to_dict() if user else {},
|
||||
authenticated=authenticated)
|
||||
|
||||
|
||||
class LogoutView(APIView):
|
||||
|
78
cmdb-api/api/views/acl/app.py
Normal file
78
cmdb-api/api/views/acl/app.py
Normal file
@@ -0,0 +1,78 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from flask import abort
|
||||
from flask import request
|
||||
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.decorator import args_validate
|
||||
from api.lib.perm.acl.acl import is_app_admin
|
||||
from api.lib.perm.acl.app import AppCRUD
|
||||
from api.lib.perm.acl.resp_format import ErrFormat
|
||||
from api.lib.perm.auth import auth_abandoned
|
||||
from api.lib.utils import get_page
|
||||
from api.lib.utils import get_page_size
|
||||
from api.resource import APIView
|
||||
|
||||
|
||||
class AppView(APIView):
|
||||
url_prefix = ('/apps', '/apps/<int:_id>')
|
||||
|
||||
def get(self, _id=None):
|
||||
if _id is not None:
|
||||
if not is_app_admin('acl'):
|
||||
return abort(403, ErrFormat.no_permission)
|
||||
|
||||
app = AppCRUD.get(_id)
|
||||
app = app and app.to_dict() or {}
|
||||
|
||||
return self.jsonify(**app)
|
||||
|
||||
page = get_page(request.values.get('page', 1))
|
||||
page_size = get_page_size(request.values.get('page_size'))
|
||||
q = request.values.get('q')
|
||||
|
||||
numfound, res = AppCRUD.search(q, page, page_size)
|
||||
|
||||
res = [i.to_dict() for i in res]
|
||||
for i in res:
|
||||
i.pop('app_id', None)
|
||||
i.pop('secret_key', None)
|
||||
|
||||
return self.jsonify(page=page,
|
||||
page_size=page_size,
|
||||
numfound=numfound,
|
||||
total=len(res),
|
||||
apps=res)
|
||||
|
||||
@args_required('name')
|
||||
@args_validate(AppCRUD.cls)
|
||||
def post(self):
|
||||
name = request.values.get('name')
|
||||
description = request.values.get('description')
|
||||
|
||||
app = AppCRUD.add(name, description)
|
||||
|
||||
return self.jsonify(app.to_dict())
|
||||
|
||||
@args_validate(AppCRUD.cls)
|
||||
def put(self, _id):
|
||||
app = AppCRUD.update(_id, **request.values)
|
||||
|
||||
return self.jsonify(app.to_dict())
|
||||
|
||||
def delete(self, _id):
|
||||
AppCRUD.delete(_id)
|
||||
|
||||
return self.jsonify(id=_id)
|
||||
|
||||
|
||||
class AppAccessTokenView(APIView):
|
||||
url_prefix = '/apps/token'
|
||||
|
||||
@args_required('app_id')
|
||||
@args_required('secret_key')
|
||||
@auth_abandoned
|
||||
def post(self):
|
||||
token = AppCRUD.gen_token(request.values.get('app_id'), request.values.get('secret_key'))
|
||||
|
||||
return self.jsonify(token=token)
|
39
cmdb-api/api/views/acl/audit.py
Normal file
39
cmdb-api/api/views/acl/audit.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from flask import request, abort
|
||||
|
||||
from api.lib.perm.acl.audit import AuditCRUD
|
||||
from api.lib.utils import get_page
|
||||
from api.lib.utils import get_page_size
|
||||
from api.resource import APIView
|
||||
|
||||
|
||||
class AuditLogView(APIView):
|
||||
url_prefix = ("/audit_log/<string:name>",)
|
||||
|
||||
def get(self, name):
|
||||
page = get_page(request.values.get("page", 1))
|
||||
page_size = get_page_size(request.values.get("page_size"))
|
||||
app_id = request.values.get('app_id')
|
||||
q = request.values.get('q')
|
||||
start = request.values.get('start')
|
||||
end = request.values.get('end')
|
||||
|
||||
func_map = {
|
||||
'permission': AuditCRUD.search_permission,
|
||||
'role': AuditCRUD.search_role,
|
||||
'trigger': AuditCRUD.search_trigger,
|
||||
'resource': AuditCRUD.search_resource,
|
||||
}
|
||||
if name not in func_map:
|
||||
abort(400, f'wrong {name}, please use {func_map.keys()}')
|
||||
|
||||
_func = func_map[name]
|
||||
|
||||
data = _func(app_id, q, page, page_size, start, end)
|
||||
|
||||
return self.jsonify(
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
**data,
|
||||
)
|
179
cmdb-api/api/views/acl/login.py
Normal file
179
cmdb-api/api/views/acl/login.py
Normal file
@@ -0,0 +1,179 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import datetime
|
||||
|
||||
import jwt
|
||||
import six
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
from flask import request
|
||||
from flask import session
|
||||
from flask_login import login_user, logout_user
|
||||
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.decorator import args_validate
|
||||
from api.lib.perm.acl.acl import ACLManager
|
||||
from api.lib.perm.acl.cache import RoleCache
|
||||
from api.lib.perm.acl.cache import User
|
||||
from api.lib.perm.acl.cache import UserCache
|
||||
from api.lib.perm.acl.resp_format import ErrFormat
|
||||
from api.lib.perm.auth import auth_abandoned
|
||||
from api.lib.perm.auth import auth_with_app_token
|
||||
from api.models.acl import Role
|
||||
from api.resource import APIView
|
||||
|
||||
|
||||
class LoginView(APIView):
|
||||
url_prefix = "/login"
|
||||
|
||||
@args_required("username")
|
||||
@args_required("password")
|
||||
@auth_abandoned
|
||||
@args_validate(User)
|
||||
def post(self):
|
||||
username = request.values.get("username") or request.values.get("email")
|
||||
password = request.values.get("password")
|
||||
_role = None
|
||||
if current_app.config.get('AUTH_WITH_LDAP'):
|
||||
user, authenticated = User.query.authenticate_with_ldap(username, password)
|
||||
else:
|
||||
user, authenticated = User.query.authenticate(username, password)
|
||||
if not user:
|
||||
_role, authenticated = Role.query.authenticate(username, password)
|
||||
|
||||
if not user and not _role:
|
||||
return abort(401, ErrFormat.user_not_found.format(username))
|
||||
|
||||
if not authenticated:
|
||||
return abort(401, ErrFormat.invalid_password)
|
||||
|
||||
if user:
|
||||
login_user(user)
|
||||
user.update(has_logined=True, last_login=datetime.datetime.now())
|
||||
|
||||
token = jwt.encode({
|
||||
'sub': user.email,
|
||||
'iat': datetime.datetime.now(),
|
||||
'exp': datetime.datetime.now() + datetime.timedelta(minutes=24 * 60 * 7)},
|
||||
current_app.config['SECRET_KEY'])
|
||||
|
||||
username = username.split("@")[0]
|
||||
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"),
|
||||
parentRoles=user_info.get("parents"),
|
||||
childRoles=user_info.get("children"),
|
||||
roleName=user_info.get("role"))
|
||||
session["uid"] = user_info.get("uid")
|
||||
|
||||
return self.jsonify(token=token.decode() if six.PY2 else token, username=username)
|
||||
else:
|
||||
return self.jsonify(username=username)
|
||||
|
||||
|
||||
class AuthWithKeyView(APIView):
|
||||
url_prefix = "/auth_with_key"
|
||||
|
||||
@args_required("key")
|
||||
@args_required("secret")
|
||||
@args_required("path")
|
||||
@auth_abandoned
|
||||
def post(self):
|
||||
key = request.values.get('key')
|
||||
secret = request.values.get('secret')
|
||||
path = six.moves.urllib.parse.urlparse(request.values.get('path')).path
|
||||
payload = request.values.get('payload') or {}
|
||||
|
||||
payload.pop('_key', None)
|
||||
payload.pop('_secret', None)
|
||||
|
||||
req_args = [str(payload[k]) for k in sorted(payload.keys())]
|
||||
user, authenticated = User.query.authenticate_with_key(key, secret, req_args, path)
|
||||
if user:
|
||||
role = RoleCache.get_by_name(None, user.username)
|
||||
role or abort(404, ErrFormat.role_not_found.format(user.username))
|
||||
user = user.to_dict()
|
||||
else:
|
||||
role, authenticated = Role.query.authenticate_with_key(key, secret, req_args, path)
|
||||
user = role and role.to_dict() or {}
|
||||
|
||||
can_proxy = True if role and role.is_app_admin else False
|
||||
|
||||
if can_proxy and request.values.get('proxy'):
|
||||
role = RoleCache.get_by_name(None, request.values.get('proxy'))
|
||||
role or abort(404, ErrFormat.role_not_found.format(request.values.get('proxy')))
|
||||
user = role and role.to_dict() or {}
|
||||
|
||||
user['rid'] = role and role.id
|
||||
user.pop('password', None)
|
||||
user.pop('key', None)
|
||||
user.pop('secret', None)
|
||||
|
||||
if not user.get('username'):
|
||||
user['username'] = user.get('name')
|
||||
|
||||
return self.jsonify(user=user,
|
||||
authenticated=authenticated,
|
||||
rid=role and role.id,
|
||||
can_proxy=can_proxy)
|
||||
|
||||
|
||||
class AuthWithTokenView(APIView):
|
||||
url_prefix = ("/auth_with_token", "/req_token")
|
||||
|
||||
@auth_with_app_token
|
||||
def get(self):
|
||||
username = request.values.get('username')
|
||||
|
||||
token = jwt.encode({
|
||||
'sub': username,
|
||||
'iat': datetime.datetime.now(),
|
||||
'exp': datetime.datetime.now() + datetime.timedelta(minutes=2 * 60)},
|
||||
current_app.config['SECRET_KEY'])
|
||||
|
||||
return self.jsonify(token=token)
|
||||
|
||||
@args_required("token")
|
||||
@auth_with_app_token
|
||||
def post(self):
|
||||
token = request.values.get('token')
|
||||
|
||||
user = None
|
||||
try:
|
||||
data = jwt.decode(token, current_app.config['SECRET_KEY'], algorithms=['HS256'])
|
||||
authenticated = True
|
||||
user = UserCache.get(data.get('sub'))
|
||||
if not user:
|
||||
authenticated = False
|
||||
except jwt.ExpiredSignatureError:
|
||||
authenticated = False
|
||||
except (jwt.InvalidTokenError, Exception) as e:
|
||||
current_app.logger.error(str(e))
|
||||
authenticated = False
|
||||
|
||||
if user is not None:
|
||||
role = RoleCache.get_by_name(None, user.username)
|
||||
role or abort(404, ErrFormat.role_not_found.format(user.username))
|
||||
user = user.to_dict()
|
||||
|
||||
user['rid'] = role and role.id
|
||||
user.pop('password', None)
|
||||
user.pop('key', None)
|
||||
user.pop('secret', None)
|
||||
|
||||
return self.jsonify(user=user,
|
||||
authenticated=authenticated)
|
||||
|
||||
|
||||
class LogoutView(APIView):
|
||||
url_prefix = "/logout"
|
||||
|
||||
@auth_abandoned
|
||||
def post(self):
|
||||
logout_user()
|
||||
self.jsonify(code=200)
|
@@ -1,10 +1,17 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
from flask import request
|
||||
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.perm.acl.acl import ACLManager
|
||||
from api.lib.perm.acl.cache import AppCache
|
||||
from api.lib.perm.acl.permission import PermissionCRUD
|
||||
from api.lib.perm.acl.resp_format import ErrFormat
|
||||
from api.lib.perm.auth import auth_only_for_acl
|
||||
from api.lib.perm.auth import auth_with_app_token
|
||||
from api.lib.utils import handle_arg_list
|
||||
from api.resource import APIView
|
||||
|
||||
@@ -12,29 +19,149 @@ from api.resource import APIView
|
||||
class ResourcePermissionView(APIView):
|
||||
url_prefix = ("/resources/<int:resource_id>/permissions", "/resource_groups/<int:group_id>/permissions")
|
||||
|
||||
@auth_with_app_token
|
||||
def get(self, resource_id=None, group_id=None):
|
||||
return self.jsonify(PermissionCRUD.get_all(resource_id, group_id))
|
||||
need_users = request.values.get('need_users', 1) in current_app.config.get('BOOL_TRUE')
|
||||
return self.jsonify(PermissionCRUD.get_all(resource_id, group_id, need_users=need_users))
|
||||
|
||||
|
||||
class ResourcePermission2View(APIView):
|
||||
url_prefix = "/resource/permissions"
|
||||
|
||||
@args_required('resource_name')
|
||||
@args_required('resource_type_name')
|
||||
@auth_with_app_token
|
||||
def get(self):
|
||||
resource_name = request.values.get('resource_name')
|
||||
resource_type_name = request.values.get('resource_type_name')
|
||||
app_id = request.values.get('app_id')
|
||||
|
||||
return self.jsonify(PermissionCRUD.get_all2(resource_name, resource_type_name, app_id))
|
||||
|
||||
|
||||
class RolePermissionGrantView(APIView):
|
||||
url_prefix = ('/roles/<int:rid>/resources/<int:resource_id>/grant',
|
||||
'/roles/<int:rid>/resource_groups/<int:group_id>/grant')
|
||||
'/roles/<int:rid>/resource_groups/<int:group_id>/grant',
|
||||
'/roles/<int:rid>/resources/batch/grant2', # by names
|
||||
)
|
||||
|
||||
@args_required('perms')
|
||||
@auth_only_for_acl
|
||||
def post(self, rid, resource_id=None, group_id=None):
|
||||
perms = handle_arg_list(request.values.get("perms"))
|
||||
|
||||
if "batch" in request.url:
|
||||
resource_ids = request.values.get('resource_ids')
|
||||
perm_map = request.values.get('perm_map')
|
||||
resource_names = request.values.get('resource_names')
|
||||
resource_type_id = request.values.get('resource_type_id')
|
||||
app = AppCache.get(request.values.get('app_id'))
|
||||
PermissionCRUD.batch_grant_by_resource_names(rid, perms, resource_type_id, resource_names,
|
||||
resource_ids, perm_map, app_id=app and app.id)
|
||||
|
||||
return self.jsonify(rid=rid, resource_names=resource_names, resource_type_id=resource_type_id, perms=perms)
|
||||
|
||||
PermissionCRUD.grant(rid, perms, resource_id=resource_id, group_id=group_id)
|
||||
|
||||
return self.jsonify(rid=rid, resource_id=resource_id, group_id=group_id, perms=perms)
|
||||
|
||||
|
||||
class RolePermissionGrant2View(APIView):
|
||||
url_prefix = ('/roles/<int:rid>/resources/<int:resource_id>/grant2',)
|
||||
|
||||
def post(self, rid, resource_id):
|
||||
if not ACLManager(request.values.get('app_id')).has_permission(None, None, 'grant', resource_id):
|
||||
return abort(403, ErrFormat.no_permission2)
|
||||
|
||||
perms = handle_arg_list(request.values.get("perms"))
|
||||
|
||||
PermissionCRUD.grant(rid, perms, resource_id=resource_id)
|
||||
|
||||
return self.jsonify(rid=rid, resource_id=resource_id, perms=perms)
|
||||
|
||||
|
||||
class RolePermissionBatchGrantView(APIView):
|
||||
url_prefix = ('/roles/<int:rid>/resources/batch/grant',
|
||||
'/roles/<int:rid>/resource_groups/batch/grant')
|
||||
|
||||
@auth_only_for_acl
|
||||
def post(self, rid):
|
||||
resource_ids = request.values.get('resource_ids')
|
||||
group_ids = request.values.get('group_ids')
|
||||
|
||||
perms = handle_arg_list(request.values.get("perms"))
|
||||
|
||||
if resource_ids and isinstance(resource_ids, list):
|
||||
for resource_id in resource_ids[:-1]:
|
||||
PermissionCRUD.grant(rid, perms, resource_id=resource_id, group_id=None, rebuild=False)
|
||||
PermissionCRUD.grant(rid, perms, resource_id=resource_ids[-1], group_id=None, rebuild=True)
|
||||
|
||||
if group_ids and isinstance(group_ids, list):
|
||||
for group_id in group_ids[:-1]:
|
||||
PermissionCRUD.grant(rid, perms, resource_id=None, group_id=group_id, rebuild=False)
|
||||
PermissionCRUD.grant(rid, perms, resource_id=None, group_id=group_ids[-1], rebuild=True)
|
||||
|
||||
return self.jsonify(rid=rid, resource_ids=resource_ids, group_ids=group_ids, perms=perms)
|
||||
|
||||
|
||||
class RolePermissionRevokeView(APIView):
|
||||
url_prefix = ('/roles/<int:rid>/resources/<int:resource_id>/revoke',
|
||||
'/roles/<int:rid>/resource_groups/<int:group_id>/revoke')
|
||||
'/roles/<int:rid>/resource_groups/<int:group_id>/revoke',
|
||||
'/roles/<int:rid>/resources/batch/revoke2', # by names
|
||||
)
|
||||
|
||||
@args_required('perms')
|
||||
@auth_only_for_acl
|
||||
def post(self, rid, resource_id=None, group_id=None):
|
||||
perms = handle_arg_list(request.values.get("perms"))
|
||||
if "batch" in request.url:
|
||||
resource_names = request.values.get('resource_names')
|
||||
resource_type_id = request.values.get('resource_type_id')
|
||||
resource_ids = request.values.get('resource_ids')
|
||||
perm_map = request.values.get('perm_map')
|
||||
app = AppCache.get(request.values.get('app_id'))
|
||||
PermissionCRUD.batch_revoke_by_resource_names(rid, perms, resource_type_id, resource_names,
|
||||
resource_ids, perm_map, app_id=app and app.id)
|
||||
|
||||
return self.jsonify(rid=rid, resource_names=resource_names, resource_type_id=resource_type_id, perms=perms)
|
||||
|
||||
PermissionCRUD.revoke(rid, perms, resource_id=resource_id, group_id=group_id)
|
||||
|
||||
return self.jsonify(rid=rid, resource_id=resource_id, group_id=group_id, perms=perms)
|
||||
|
||||
|
||||
class RolePermissionRevoke2View(APIView):
|
||||
url_prefix = ('/roles/<int:rid>/resources/<int:resource_id>/revoke2',
|
||||
'/roles/<int:rid>/resource_groups/<int:group_id>/revoke2',)
|
||||
|
||||
def post(self, rid, resource_id=None, group_id=None):
|
||||
if not ACLManager(request.values.get('app_id')).has_permission(None, None, 'grant', resource_id):
|
||||
return abort(403, ErrFormat.no_permission2)
|
||||
|
||||
perms = handle_arg_list(request.values.get("perms"))
|
||||
|
||||
PermissionCRUD.revoke(rid, perms, resource_id=resource_id, group_id=group_id)
|
||||
|
||||
return self.jsonify(rid=rid, resource_id=resource_id, perms=perms)
|
||||
|
||||
|
||||
class RolePermissionBatchRevokeView(APIView):
|
||||
url_prefix = ('/roles/<int:rid>/resources/batch/revoke',
|
||||
'/roles/<int:rid>/resource_groups/batch/revoke')
|
||||
|
||||
@auth_only_for_acl
|
||||
def post(self, rid):
|
||||
resource_ids = request.values.get('resource_ids')
|
||||
group_ids = request.values.get('group_ids')
|
||||
|
||||
perms = handle_arg_list(request.values.get("perms"))
|
||||
|
||||
if resource_ids and isinstance(resource_ids, list):
|
||||
for resource_id in resource_ids[:-1]:
|
||||
PermissionCRUD.revoke(rid, perms, resource_id=resource_id, group_id=None, rebuild=False)
|
||||
PermissionCRUD.revoke(rid, perms, resource_id=resource_ids[-1], group_id=None, rebuild=True)
|
||||
|
||||
if group_ids and isinstance(group_ids, list):
|
||||
for group_id in group_ids[:-1]:
|
||||
PermissionCRUD.revoke(rid, perms, resource_id=None, group_id=group_id, rebuild=False)
|
||||
PermissionCRUD.revoke(rid, perms, resource_id=None, group_id=group_ids[-1], rebuild=True)
|
||||
|
||||
return self.jsonify(rid=rid, resource_ids=resource_ids, group_ids=group_ids, perms=perms)
|
||||
|
@@ -1,12 +1,16 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from flask import g
|
||||
from flask import request
|
||||
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.decorator import args_validate
|
||||
from api.lib.perm.acl import validate_app
|
||||
from api.lib.perm.acl.resource import ResourceCRUD
|
||||
from api.lib.perm.acl.resource import ResourceGroupCRUD
|
||||
from api.lib.perm.acl.resource import ResourceTypeCRUD
|
||||
from api.lib.perm.auth import auth_only_for_acl
|
||||
from api.lib.perm.auth import auth_with_app_token
|
||||
from api.lib.utils import get_page
|
||||
from api.lib.utils import get_page_size
|
||||
from api.lib.utils import handle_arg_list
|
||||
@@ -16,8 +20,8 @@ from api.resource import APIView
|
||||
class ResourceTypeView(APIView):
|
||||
url_prefix = ("/resource_types", "/resource_types/<int:type_id>")
|
||||
|
||||
@args_required('app_id')
|
||||
@validate_app
|
||||
@auth_with_app_token
|
||||
def get(self):
|
||||
page = get_page(request.values.get("page", 1))
|
||||
page_size = get_page_size(request.values.get("page_size"))
|
||||
@@ -33,9 +37,10 @@ class ResourceTypeView(APIView):
|
||||
id2perms=id2perms)
|
||||
|
||||
@args_required('name')
|
||||
@args_required('app_id')
|
||||
@args_required('perms')
|
||||
@validate_app
|
||||
@auth_only_for_acl
|
||||
@args_validate(ResourceTypeCRUD.cls, exclude_args=['app_id'])
|
||||
def post(self):
|
||||
name = request.values.get('name')
|
||||
app_id = request.values.get('app_id')
|
||||
@@ -46,11 +51,14 @@ class ResourceTypeView(APIView):
|
||||
|
||||
return self.jsonify(rt.to_dict())
|
||||
|
||||
@auth_only_for_acl
|
||||
@args_validate(ResourceTypeCRUD.cls, exclude_args=['app_id'])
|
||||
def put(self, type_id):
|
||||
rt = ResourceTypeCRUD.update(type_id, **request.values)
|
||||
|
||||
return self.jsonify(rt.to_dict())
|
||||
|
||||
@auth_only_for_acl
|
||||
def delete(self, type_id):
|
||||
ResourceTypeCRUD.delete(type_id)
|
||||
|
||||
@@ -60,6 +68,7 @@ class ResourceTypeView(APIView):
|
||||
class ResourceTypePermsView(APIView):
|
||||
url_prefix = "/resource_types/<int:type_id>/perms"
|
||||
|
||||
@auth_with_app_token
|
||||
def get(self, type_id):
|
||||
return self.jsonify(ResourceTypeCRUD.get_perms(type_id))
|
||||
|
||||
@@ -67,36 +76,43 @@ class ResourceTypePermsView(APIView):
|
||||
class ResourceView(APIView):
|
||||
url_prefix = ("/resources", "/resources/<int:resource_id>")
|
||||
|
||||
@args_required('app_id')
|
||||
@validate_app
|
||||
@auth_with_app_token
|
||||
def get(self):
|
||||
page = get_page(request.values.get("page", 1))
|
||||
page_size = get_page_size(request.values.get("page_size"))
|
||||
q = request.values.get('q')
|
||||
u = request.values.get('u')
|
||||
resource_type_id = request.values.get('resource_type_id')
|
||||
app_id = request.values.get('app_id')
|
||||
|
||||
numfound, res = ResourceCRUD.search(q, app_id, resource_type_id, page, page_size)
|
||||
numfound, res = ResourceCRUD.search(q, u, app_id, resource_type_id, page, page_size)
|
||||
|
||||
return self.jsonify(numfound=numfound,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
resources=[i.to_dict() for i in res])
|
||||
resources=res)
|
||||
|
||||
@args_required('name')
|
||||
@args_required('type_id')
|
||||
@args_required('app_id')
|
||||
@validate_app
|
||||
@auth_only_for_acl
|
||||
@args_validate(ResourceCRUD.cls, exclude_args=['app_id'])
|
||||
def post(self):
|
||||
name = request.values.get('name')
|
||||
type_id = request.values.get('type_id')
|
||||
app_id = request.values.get('app_id')
|
||||
uid = request.values.get('uid')
|
||||
if not uid and hasattr(g, "user") and hasattr(g.user, "uid"):
|
||||
uid = g.user.uid
|
||||
|
||||
resource = ResourceCRUD.add(name, type_id, app_id)
|
||||
resource = ResourceCRUD.add(name, type_id, app_id, uid)
|
||||
|
||||
return self.jsonify(resource.to_dict())
|
||||
|
||||
@args_required('name')
|
||||
@auth_only_for_acl
|
||||
@args_validate(ResourceCRUD.cls, exclude_args=['app_id'])
|
||||
def put(self, resource_id):
|
||||
name = request.values.get('name')
|
||||
|
||||
@@ -104,6 +120,7 @@ class ResourceView(APIView):
|
||||
|
||||
return self.jsonify(resource.to_dict())
|
||||
|
||||
@auth_only_for_acl
|
||||
def delete(self, resource_id):
|
||||
ResourceCRUD.delete(resource_id)
|
||||
|
||||
@@ -113,15 +130,16 @@ class ResourceView(APIView):
|
||||
class ResourceGroupView(APIView):
|
||||
url_prefix = ("/resource_groups", "/resource_groups/<int:group_id>")
|
||||
|
||||
@args_required('app_id')
|
||||
@validate_app
|
||||
@auth_with_app_token
|
||||
def get(self):
|
||||
page = get_page(request.values.get("page", 1))
|
||||
page_size = get_page_size(request.values.get("page_size"))
|
||||
q = request.values.get('q')
|
||||
app_id = request.values.get('app_id')
|
||||
resource_type_id = request.values.get('resource_type_id')
|
||||
|
||||
numfound, res = ResourceGroupCRUD.search(q, app_id, page, page_size)
|
||||
numfound, res = ResourceGroupCRUD.search(q, app_id, resource_type_id, page, page_size)
|
||||
|
||||
return self.jsonify(numfound=numfound,
|
||||
page=page,
|
||||
@@ -130,8 +148,9 @@ class ResourceGroupView(APIView):
|
||||
|
||||
@args_required('name')
|
||||
@args_required('type_id')
|
||||
@args_required('app_id')
|
||||
@validate_app
|
||||
@auth_only_for_acl
|
||||
@args_validate(ResourceGroupCRUD.cls, exclude_args=['app_id'])
|
||||
def post(self):
|
||||
name = request.values.get('name')
|
||||
type_id = request.values.get('type_id')
|
||||
@@ -142,6 +161,8 @@ class ResourceGroupView(APIView):
|
||||
return self.jsonify(group.to_dict())
|
||||
|
||||
@args_required('items')
|
||||
@auth_only_for_acl
|
||||
@args_validate(ResourceGroupCRUD.cls, exclude_args=['app_id'])
|
||||
def put(self, group_id):
|
||||
items = handle_arg_list(request.values.get("items"))
|
||||
|
||||
@@ -151,6 +172,7 @@ class ResourceGroupView(APIView):
|
||||
|
||||
return self.jsonify(items)
|
||||
|
||||
@auth_only_for_acl
|
||||
def delete(self, group_id):
|
||||
ResourceGroupCRUD.delete(group_id)
|
||||
|
||||
@@ -160,6 +182,7 @@ class ResourceGroupView(APIView):
|
||||
class ResourceGroupItemsView(APIView):
|
||||
url_prefix = "/resource_groups/<int:group_id>/items"
|
||||
|
||||
@auth_with_app_token
|
||||
def get(self, group_id):
|
||||
items = ResourceGroupCRUD.get_items(group_id)
|
||||
|
||||
|
@@ -1,12 +1,21 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
from flask import g
|
||||
from flask import request
|
||||
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.decorator import args_validate
|
||||
from api.lib.perm.acl import validate_app
|
||||
from api.lib.perm.acl.acl import is_app_admin
|
||||
from api.lib.perm.acl.cache import AppCache
|
||||
from api.lib.perm.acl.cache import RoleCache
|
||||
from api.lib.perm.acl.resp_format import ErrFormat
|
||||
from api.lib.perm.acl.role import RoleCRUD
|
||||
from api.lib.perm.acl.role import RoleRelationCRUD
|
||||
from api.lib.perm.auth import auth_only_for_acl
|
||||
from api.lib.perm.auth import auth_with_app_token
|
||||
from api.lib.utils import get_page
|
||||
from api.lib.utils import get_page_size
|
||||
from api.resource import APIView
|
||||
@@ -15,43 +24,56 @@ from api.resource import APIView
|
||||
class RoleView(APIView):
|
||||
url_prefix = ("/roles", "/roles/<int:rid>")
|
||||
|
||||
@args_required('app_id')
|
||||
@validate_app
|
||||
@auth_with_app_token
|
||||
def get(self):
|
||||
page = get_page(request.values.get("page", 1))
|
||||
page_size = get_page_size(request.values.get("page_size"))
|
||||
q = request.values.get('q')
|
||||
app_id = request.values.get('app_id')
|
||||
is_all = request.values.get('is_all', True)
|
||||
is_all = True if is_all in current_app.config.get("BOOL_TRUE") else False
|
||||
user_role = request.values.get('user_role', True)
|
||||
user_only = request.values.get('user_only', False)
|
||||
user_role = True if user_role in current_app.config.get("BOOL_TRUE") else False
|
||||
user_only = True if user_only in current_app.config.get("BOOL_TRUE") else False
|
||||
|
||||
numfound, roles = RoleCRUD.search(q, app_id, page, page_size, user_role)
|
||||
numfound, roles = RoleCRUD.search(q, app_id, page, page_size, user_role, is_all, user_only)
|
||||
|
||||
id2parents = RoleRelationCRUD.get_parents([i.id for i in roles])
|
||||
id2parents = RoleRelationCRUD.get_parents([i.id for i in roles], app_id=app_id)
|
||||
|
||||
roles = [i.to_dict() for i in roles]
|
||||
for i in roles:
|
||||
i.pop('password', None)
|
||||
|
||||
return self.jsonify(numfound=numfound,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
id2parents=id2parents,
|
||||
roles=[i.to_dict() for i in roles])
|
||||
roles=roles)
|
||||
|
||||
@args_required('name')
|
||||
@args_required('app_id')
|
||||
@validate_app
|
||||
@auth_with_app_token
|
||||
@args_validate(RoleCRUD.cls, exclude_args=['app_id'])
|
||||
def post(self):
|
||||
name = request.values.get('name')
|
||||
app_id = request.values.get('app_id')
|
||||
is_app_admin = request.values.get('is_app_admin', False)
|
||||
password = request.values.get('password')
|
||||
_is_app_admin = request.values.get('is_app_admin', False)
|
||||
|
||||
role = RoleCRUD.add_role(name, app_id, is_app_admin=is_app_admin)
|
||||
role = RoleCRUD.add_role(name, app_id, password=password, is_app_admin=_is_app_admin)
|
||||
|
||||
return self.jsonify(role.to_dict())
|
||||
|
||||
@auth_only_for_acl
|
||||
@args_validate(RoleCRUD.cls, exclude_args=['app_id'])
|
||||
def put(self, rid):
|
||||
role = RoleCRUD.update_role(rid, **request.values)
|
||||
|
||||
return self.jsonify(role.to_dict())
|
||||
|
||||
@auth_only_for_acl
|
||||
def delete(self, rid):
|
||||
RoleCRUD.delete_role(rid)
|
||||
|
||||
@@ -59,19 +81,97 @@ class RoleView(APIView):
|
||||
|
||||
|
||||
class RoleRelationView(APIView):
|
||||
url_prefix = "/roles/<int:child_id>/parents"
|
||||
url_prefix = ("/roles/<int:rid>/parents", "/roles/<int:rid>/users", "/roles/<int:rid>/children")
|
||||
|
||||
@auth_with_app_token
|
||||
@validate_app
|
||||
def get(self, rid):
|
||||
app_id = request.values.get('app_id')
|
||||
app = AppCache.get(app_id)
|
||||
if app and app.name == "acl":
|
||||
app_id = None # global
|
||||
|
||||
users = RoleRelationCRUD.get_users_by_rid(rid, app_id)
|
||||
|
||||
return self.jsonify(users=users)
|
||||
|
||||
@auth_only_for_acl
|
||||
@validate_app
|
||||
@args_validate(RoleRelationCRUD.cls, exclude_args=['app_id'])
|
||||
def post(self, rid):
|
||||
|
||||
app_id = request.values.get('app_id')
|
||||
app = AppCache.get(app_id)
|
||||
if app and app.name == "acl":
|
||||
app_id = None # global
|
||||
|
||||
role = RoleCache.get(rid) or abort(400, ErrFormat.role_not_found.format("id={}".format(rid)))
|
||||
|
||||
if request.values.get('parent_id'):
|
||||
parent_id = request.values.get('parent_id')
|
||||
|
||||
res = RoleRelationCRUD.add(role, parent_id, [rid], app_id)
|
||||
|
||||
return self.jsonify(res)
|
||||
elif request.values.get("child_ids") and isinstance(request.values['child_ids'], list):
|
||||
res = RoleRelationCRUD.add(role, rid, request.values['child_ids'], app_id)
|
||||
|
||||
return self.jsonify(res)
|
||||
|
||||
else:
|
||||
return abort(400, ErrFormat.invalid_request)
|
||||
|
||||
@args_required('parent_id')
|
||||
def post(self, child_id):
|
||||
parent_id = request.values.get('parent_id')
|
||||
res = RoleRelationCRUD.add(parent_id, child_id)
|
||||
|
||||
return self.jsonify(res.to_dict())
|
||||
|
||||
@args_required('parent_id')
|
||||
def delete(self, child_id):
|
||||
@auth_only_for_acl
|
||||
@validate_app
|
||||
def delete(self, rid):
|
||||
parent_id = request.values.get('parent_id')
|
||||
|
||||
RoleRelationCRUD.delete2(parent_id, child_id)
|
||||
app_id = request.values.get('app_id')
|
||||
app = AppCache.get(app_id)
|
||||
if app and app.name == "acl":
|
||||
app_id = None # global
|
||||
|
||||
return self.jsonify(parent_id=parent_id, child_id=child_id)
|
||||
RoleRelationCRUD.delete2(parent_id, rid, app_id)
|
||||
|
||||
return self.jsonify(parent_id=parent_id, child_id=rid)
|
||||
|
||||
|
||||
class RoleResourcesView(APIView):
|
||||
url_prefix = "/roles/<int:rid>/resources"
|
||||
|
||||
@auth_with_app_token
|
||||
@validate_app
|
||||
def get(self, rid):
|
||||
resource_type_id = request.values.get('resource_type_id')
|
||||
group_flat = request.values.get('group_flat', True)
|
||||
res = RoleCRUD.recursive_resources(rid, request.values['app_id'], resource_type_id, group_flat, to_record=True)
|
||||
|
||||
return self.jsonify(res)
|
||||
|
||||
|
||||
class RoleHasPermissionView(APIView):
|
||||
url_prefix = "/roles/has_perm"
|
||||
|
||||
@args_required('resource_name')
|
||||
@args_required('resource_type_name')
|
||||
@args_required('perm')
|
||||
@validate_app
|
||||
@auth_with_app_token
|
||||
def get(self):
|
||||
if not request.values.get('rid'):
|
||||
role = RoleCache.get_by_name(None, g.user.username)
|
||||
role or abort(404, ErrFormat.role_not_found.format(g.user.username))
|
||||
else:
|
||||
role = RoleCache.get(int(request.values.get('rid')))
|
||||
|
||||
app_id = request.values.get('app_id')
|
||||
if is_app_admin(app_id):
|
||||
return self.jsonify(result=True)
|
||||
|
||||
resource_name = request.values.get('resource_name')
|
||||
resource_type_name = request.values.get('resource_type_name')
|
||||
perm = request.values.get('perm')
|
||||
result = RoleCRUD.has_permission(role.id, resource_name, resource_type_name, app_id, perm)
|
||||
|
||||
return self.jsonify(result=result)
|
||||
|
92
cmdb-api/api/views/acl/trigger.py
Normal file
92
cmdb-api/api/views/acl/trigger.py
Normal file
@@ -0,0 +1,92 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from flask import request
|
||||
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.decorator import args_validate
|
||||
from api.lib.perm.acl import validate_app
|
||||
from api.lib.perm.acl.trigger import TriggerCRUD
|
||||
from api.lib.perm.auth import auth_only_for_acl
|
||||
from api.lib.perm.auth import auth_with_app_token
|
||||
from api.resource import APIView
|
||||
|
||||
|
||||
class TriggerView(APIView):
|
||||
url_prefix = ("/triggers", "/triggers/<int:_id>")
|
||||
|
||||
@validate_app
|
||||
@auth_with_app_token
|
||||
def get(self):
|
||||
return self.jsonify(TriggerCRUD.get(request.values.get('app_id')))
|
||||
|
||||
@args_required('name')
|
||||
@args_required('resource_type_id')
|
||||
@args_required('roles')
|
||||
@args_required('permissions')
|
||||
@validate_app
|
||||
@auth_only_for_acl
|
||||
@args_validate(TriggerCRUD.cls, exclude_args=['app_id'])
|
||||
def post(self):
|
||||
request.values.pop('_key', None)
|
||||
request.values.pop('_secret', None)
|
||||
trigger = TriggerCRUD.add(request.values.pop('app_id', None), **request.values)
|
||||
|
||||
return self.jsonify(trigger.to_dict())
|
||||
|
||||
@args_required('resource_type_id')
|
||||
@args_required('roles')
|
||||
@args_required('permissions')
|
||||
@validate_app
|
||||
@auth_only_for_acl
|
||||
@args_validate(TriggerCRUD.cls, exclude_args=['app_id'])
|
||||
def put(self, _id):
|
||||
request.values.pop('_key', None)
|
||||
request.values.pop('_secret', None)
|
||||
|
||||
trigger = TriggerCRUD.update(_id, **request.values)
|
||||
|
||||
return self.jsonify(trigger.to_dict())
|
||||
|
||||
@auth_only_for_acl
|
||||
def delete(self, _id):
|
||||
TriggerCRUD.delete(_id)
|
||||
|
||||
return self.jsonify(id=_id)
|
||||
|
||||
|
||||
class TriggerResourceView(APIView):
|
||||
url_prefix = "/triggers/resources"
|
||||
|
||||
@validate_app
|
||||
@auth_with_app_token
|
||||
@args_required("resource_type_id")
|
||||
def post(self):
|
||||
app_id = request.values.get('app_id')
|
||||
resource_type_id = request.values.get('resource_type_id')
|
||||
wildcard = request.values.get('pattern')
|
||||
uid = request.values.get('owner')
|
||||
|
||||
resources = TriggerCRUD.get_resources(app_id, resource_type_id, wildcard, uid)
|
||||
resources = [i.to_dict() for i in resources]
|
||||
|
||||
return self.jsonify(resources)
|
||||
|
||||
|
||||
class TriggerApplyView(APIView):
|
||||
url_prefix = "/triggers/<int:_id>/apply"
|
||||
|
||||
@auth_only_for_acl
|
||||
def post(self, _id):
|
||||
TriggerCRUD.apply(_id)
|
||||
|
||||
return self.jsonify(id=_id)
|
||||
|
||||
|
||||
class TriggerCancelView(APIView):
|
||||
url_prefix = "/triggers/<int:_id>/cancel"
|
||||
|
||||
@auth_only_for_acl
|
||||
def post(self, _id):
|
||||
TriggerCRUD.cancel(_id)
|
||||
|
||||
return self.jsonify(id=_id)
|
@@ -1,13 +1,25 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
|
||||
import requests
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
from flask import g
|
||||
from flask import request
|
||||
from flask import session
|
||||
from flask_login import current_user
|
||||
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.decorator import args_validate
|
||||
from api.lib.perm.acl.acl import ACLManager
|
||||
from api.lib.perm.acl.acl import role_required
|
||||
from api.lib.perm.acl.audit import AuditCRUD, AuditOperateType
|
||||
from api.lib.perm.acl.cache import AppCache
|
||||
from api.lib.perm.acl.cache import UserCache
|
||||
from api.lib.perm.acl.resp_format import ErrFormat
|
||||
from api.lib.perm.acl.role import RoleRelationCRUD
|
||||
from api.lib.perm.acl.user import UserCRUD
|
||||
from api.lib.perm.auth import auth_with_app_token
|
||||
from api.lib.utils import get_page
|
||||
from api.lib.utils import get_page_size
|
||||
from api.resource import APIView
|
||||
@@ -16,25 +28,57 @@ from api.resource import APIView
|
||||
class GetUserInfoView(APIView):
|
||||
url_prefix = "/users/info"
|
||||
|
||||
@auth_with_app_token
|
||||
def get(self):
|
||||
name = session.get("CAS_USERNAME") or current_user.nickname
|
||||
role = dict(permissions=session.get("acl", {}).get("parentRoles", []))
|
||||
avatar = current_user.avatar
|
||||
app_id = request.values.get('app_id')
|
||||
if not app_id:
|
||||
name = session.get("acl", {}).get("userName") or session.get("CAS_USERNAME") or \
|
||||
current_user.username or request.values.get('username')
|
||||
else:
|
||||
|
||||
return self.jsonify(result=dict(name=name,
|
||||
role=role,
|
||||
avatar=avatar))
|
||||
name = request.values.get('username')
|
||||
|
||||
current_app.logger.info("get user info for1: app_id: {0}, name: {1}".format(request.values.get('app_id'), name))
|
||||
user_info = ACLManager().get_user_info(name, request.values.get('app_id'))
|
||||
current_app.logger.info("get user info for2: {}".format(user_info))
|
||||
|
||||
result = dict(name=user_info.get('nickname') or name,
|
||||
username=user_info.get('username') or name,
|
||||
email=user_info.get('email'),
|
||||
uid=user_info.get('uid'),
|
||||
rid=user_info.get('rid'),
|
||||
role=dict(permissions=user_info.get('parents')),
|
||||
avatar=user_info.get('avatar'))
|
||||
|
||||
current_app.logger.info("get user info for3: {}".format(result))
|
||||
return self.jsonify(result=result)
|
||||
|
||||
|
||||
class GetUserKeySecretView(APIView):
|
||||
url_prefix = "/users/secret"
|
||||
|
||||
@auth_with_app_token
|
||||
def get(self):
|
||||
if not request.values.get('app_id'):
|
||||
name = session.get("acl", {}).get("userName") or session.get("CAS_USERNAME") or current_user.username
|
||||
else:
|
||||
name = request.values.get('username')
|
||||
|
||||
user = UserCache.get(name) or abort(404, ErrFormat.user_not_found.format(name))
|
||||
|
||||
return self.jsonify(key=user.key, secret=user.secret)
|
||||
|
||||
|
||||
class UserView(APIView):
|
||||
url_prefix = ("/users", "/users/<int:uid>")
|
||||
|
||||
@auth_with_app_token
|
||||
def get(self):
|
||||
page = get_page(request.values.get('page', 1))
|
||||
page_size = get_page_size(request.values.get('page_size'))
|
||||
q = request.values.get("q")
|
||||
numfound, users = UserCRUD.search(q, page, page_size)
|
||||
id2parents = RoleRelationCRUD.get_parents(uids=[i.uid for i in users])
|
||||
id2parents = RoleRelationCRUD.get_parents(uids=[i.uid for i in users], all_app=True)
|
||||
|
||||
users = [i.to_dict() for i in users]
|
||||
for u in users:
|
||||
@@ -50,22 +94,49 @@ class UserView(APIView):
|
||||
|
||||
@args_required('username')
|
||||
@args_required('email')
|
||||
@role_required("acl_admin")
|
||||
@args_validate(UserCRUD.cls)
|
||||
def post(self):
|
||||
request.values.pop('_key', None)
|
||||
request.values.pop('_secret', None)
|
||||
|
||||
user = UserCRUD.add(**request.values)
|
||||
|
||||
return self.jsonify(user.to_dict())
|
||||
|
||||
@role_required("acl_admin")
|
||||
@args_validate(UserCRUD.cls)
|
||||
def put(self, uid):
|
||||
request.values.pop('_key', None)
|
||||
request.values.pop('_secret', None)
|
||||
|
||||
user = UserCRUD.update(uid, **request.values)
|
||||
|
||||
return self.jsonify(user.to_dict())
|
||||
|
||||
@role_required("acl_admin")
|
||||
def delete(self, uid):
|
||||
if g.user.uid == uid:
|
||||
return abort(400, ErrFormat.invalid_operation)
|
||||
UserCRUD.delete(uid)
|
||||
|
||||
return self.jsonify(uid=uid)
|
||||
|
||||
|
||||
class UserOnTheJobView(APIView):
|
||||
url_prefix = ("/users/employee",)
|
||||
|
||||
@auth_with_app_token
|
||||
def get(self):
|
||||
if current_app.config.get('HR_URI'):
|
||||
try:
|
||||
return self.jsonify(requests.get(current_app.config["HR_URI"]).json())
|
||||
except:
|
||||
return abort(400, ErrFormat.invalid_request)
|
||||
else:
|
||||
return self.jsonify(UserCRUD.get_employees())
|
||||
|
||||
|
||||
class UserResetKeySecretView(APIView):
|
||||
url_prefix = "/users/reset_key_secret"
|
||||
|
||||
@@ -76,3 +147,31 @@ class UserResetKeySecretView(APIView):
|
||||
|
||||
def put(self):
|
||||
return self.post()
|
||||
|
||||
|
||||
class UserResetPasswordView(APIView):
|
||||
url_prefix = "/users/reset_password"
|
||||
|
||||
@auth_with_app_token
|
||||
@args_required('username')
|
||||
@args_required('password')
|
||||
@args_validate(UserCRUD.cls, exclude_args=['app_id'])
|
||||
def post(self):
|
||||
if request.values.get('app_id'):
|
||||
app = AppCache.get(request.values['app_id'])
|
||||
if app.name not in ('cas-server', 'acl'):
|
||||
return abort(403, ErrFormat.invalid_request)
|
||||
|
||||
elif hasattr(g, 'user'):
|
||||
if g.user.username != request.values['username']:
|
||||
return abort(403, ErrFormat.invalid_request)
|
||||
|
||||
else:
|
||||
return abort(400, ErrFormat.invalid_operation)
|
||||
|
||||
user = UserCache.get(request.values['username'])
|
||||
user or abort(404, ErrFormat.user_not_found.format(request.values['username']))
|
||||
|
||||
UserCRUD.update(user.uid, password=request.values['password'])
|
||||
|
||||
return self.jsonify(code=200)
|
||||
|
@@ -6,9 +6,9 @@ from flask import current_app
|
||||
from flask import request
|
||||
|
||||
from api.lib.cmdb.attribute import AttributeManager
|
||||
from api.lib.cmdb.const import RoleEnum
|
||||
from api.lib.cmdb.resp_format import ErrFormat
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.perm.acl.acl import role_required
|
||||
from api.lib.decorator import args_validate
|
||||
from api.lib.utils import get_page
|
||||
from api.lib.utils import get_page_size
|
||||
from api.lib.utils import handle_arg_list
|
||||
@@ -42,24 +42,30 @@ class AttributeView(APIView):
|
||||
attr_dict = attr_manager.get_attribute_by_name(attr_name)
|
||||
if attr_dict is None:
|
||||
attr_dict = attr_manager.get_attribute_by_alias(attr_name)
|
||||
|
||||
if not attr_dict:
|
||||
return abort(404, ErrFormat.attribute_not_found.format("name={}".format(attr_name)))
|
||||
elif attr_id is not None:
|
||||
attr_dict = attr_manager.get_attribute_by_id(attr_id)
|
||||
if attr_dict is not None:
|
||||
return self.jsonify(attribute=attr_dict)
|
||||
abort(404, "Attribute is not found")
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
if not attr_dict:
|
||||
return abort(404, ErrFormat.attribute_not_found.format("name={}".format(attr_name)))
|
||||
|
||||
return self.jsonify(attribute=attr_dict)
|
||||
|
||||
@args_required("name")
|
||||
@args_validate(AttributeManager.cls)
|
||||
def post(self):
|
||||
choice_value = handle_arg_list(request.values.get("choice_value"))
|
||||
params = request.values
|
||||
params["choice_value"] = choice_value
|
||||
|
||||
current_app.logger.debug(params)
|
||||
|
||||
attr_id = AttributeManager.add(**params)
|
||||
return self.jsonify(attr_id=attr_id)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@args_validate(AttributeManager.cls)
|
||||
def put(self, attr_id):
|
||||
choice_value = handle_arg_list(request.values.get("choice_value"))
|
||||
params = request.values
|
||||
@@ -68,7 +74,6 @@ class AttributeView(APIView):
|
||||
AttributeManager().update(attr_id, **params)
|
||||
return self.jsonify(attr_id=attr_id)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
def delete(self, attr_id):
|
||||
attr_name = AttributeManager.delete(attr_id)
|
||||
return self.jsonify(message="attribute {0} deleted".format(attr_name))
|
||||
|
236
cmdb-api/api/views/cmdb/auto_discovery.py
Normal file
236
cmdb-api/api/views/cmdb/auto_discovery.py
Normal file
@@ -0,0 +1,236 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import json
|
||||
from io import BytesIO
|
||||
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
from flask import g
|
||||
from flask import request
|
||||
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCICRUD
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryCITypeCRUD
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryHTTPManager
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoveryRuleCRUD
|
||||
from api.lib.cmdb.auto_discovery.auto_discovery import AutoDiscoverySNMPManager
|
||||
from api.lib.cmdb.auto_discovery.const import DEFAULT_HTTP
|
||||
from api.lib.cmdb.const import PermEnum
|
||||
from api.lib.cmdb.const import ResourceTypeEnum
|
||||
from api.lib.cmdb.resp_format import ErrFormat
|
||||
from api.lib.cmdb.search import SearchError
|
||||
from api.lib.cmdb.search.ci import search
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.decorator import args_validate
|
||||
from api.lib.perm.acl.acl import has_perm_from_args
|
||||
from api.lib.utils import AESCrypto
|
||||
from api.lib.utils import get_page
|
||||
from api.lib.utils import get_page_size
|
||||
from api.lib.utils import handle_arg_list
|
||||
from api.resource import APIView
|
||||
|
||||
|
||||
class AutoDiscoveryRuleView(APIView):
|
||||
url_prefix = ("/adr", "/adr/<int:adr_id>")
|
||||
|
||||
def get(self):
|
||||
_, res = AutoDiscoveryRuleCRUD.search(page=1, page_size=100000, **request.values)
|
||||
|
||||
rebuild = False
|
||||
exists = {i['name'] for i in res}
|
||||
for i in DEFAULT_HTTP:
|
||||
if i['name'] not in exists:
|
||||
AutoDiscoveryRuleCRUD().add(**i)
|
||||
rebuild = True
|
||||
|
||||
if rebuild:
|
||||
_, res = AutoDiscoveryRuleCRUD.search(page=1, page_size=100000, **request.values)
|
||||
|
||||
return self.jsonify(res)
|
||||
|
||||
@args_required("name", value_required=True)
|
||||
@args_validate(AutoDiscoveryRuleCRUD.cls)
|
||||
def post(self):
|
||||
return self.jsonify(AutoDiscoveryRuleCRUD().add(**request.values).to_dict())
|
||||
|
||||
@args_validate(AutoDiscoveryRuleCRUD.cls)
|
||||
def put(self, adr_id):
|
||||
return self.jsonify(AutoDiscoveryRuleCRUD().update(adr_id, **request.values).to_dict())
|
||||
|
||||
def delete(self, adr_id):
|
||||
AutoDiscoveryRuleCRUD().delete(adr_id)
|
||||
|
||||
return self.jsonify(adr_id=adr_id)
|
||||
|
||||
|
||||
class AutoDiscoveryRuleTemplateFileView(APIView):
|
||||
url_prefix = ("/adr/template/import/file", "/adr/template/export/file")
|
||||
|
||||
def get(self): # export
|
||||
adr_tpt = AutoDiscoveryRuleCRUD().get_by_inner()
|
||||
adr_tpt = dict(auto_discovery_rules=adr_tpt)
|
||||
|
||||
bf = BytesIO()
|
||||
bf.write(bytes(json.dumps(adr_tpt).encode('utf-8')))
|
||||
bf.seek(0)
|
||||
|
||||
return self.send_file(bf,
|
||||
as_attachment=True,
|
||||
attachment_filename="cmdb_auto_discovery.json",
|
||||
mimetype='application/json',
|
||||
cache_timeout=0)
|
||||
|
||||
def post(self):
|
||||
f = request.files.get('file')
|
||||
|
||||
if f is None:
|
||||
return abort(400, ErrFormat.argument_file_not_found)
|
||||
|
||||
content = f.read()
|
||||
try:
|
||||
content = json.loads(content)
|
||||
except:
|
||||
return abort(400, ErrFormat.invalid_json)
|
||||
tpt = content.get('auto_discovery_rules')
|
||||
|
||||
AutoDiscoveryRuleCRUD().import_template(tpt)
|
||||
|
||||
return self.jsonify(code=200)
|
||||
|
||||
|
||||
class AutoDiscoveryRuleHTTPView(APIView):
|
||||
url_prefix = ("/adr/http/<string:name>/categories", "/adr/http/<string:name>/attributes",
|
||||
"/adr/snmp/<string:name>/attributes")
|
||||
|
||||
def get(self, name):
|
||||
if "snmp" in request.url:
|
||||
return self.jsonify(AutoDiscoverySNMPManager.get_attributes())
|
||||
|
||||
if "attributes" in request.url:
|
||||
category = request.values.get('category')
|
||||
return self.jsonify(AutoDiscoveryHTTPManager.get_attributes(name, category))
|
||||
|
||||
return self.jsonify(AutoDiscoveryHTTPManager.get_categories(name))
|
||||
|
||||
|
||||
class AutoDiscoveryCITypeView(APIView):
|
||||
url_prefix = ("/adt/ci_types/<int:type_id>", "/adt/<int:adt_id>")
|
||||
|
||||
def get(self, type_id):
|
||||
_, res = AutoDiscoveryCITypeCRUD.search(page=1, page_size=100000, type_id=type_id, **request.values)
|
||||
for i in res:
|
||||
if isinstance(i.get("extra_option"), dict) and i['extra_option'].get('secret'):
|
||||
if not (g.user.username == "cmdb_agent" or g.user.uid == i['uid']):
|
||||
i['extra_option'].pop('secret', None)
|
||||
else:
|
||||
i['extra_option']['secret'] = AESCrypto.decrypt(i['extra_option']['secret'])
|
||||
|
||||
return self.jsonify(res)
|
||||
|
||||
@args_validate(AutoDiscoveryCITypeCRUD.cls)
|
||||
def post(self, type_id):
|
||||
if not request.values.get('interval'):
|
||||
request.values.pop('interval', None)
|
||||
|
||||
return self.jsonify(AutoDiscoveryCITypeCRUD().add(type_id=type_id, **request.values).to_dict())
|
||||
|
||||
@args_validate(AutoDiscoveryCITypeCRUD.cls)
|
||||
def put(self, adt_id):
|
||||
if not request.values.get('interval'):
|
||||
request.values.pop('interval', None)
|
||||
|
||||
return self.jsonify(AutoDiscoveryCITypeCRUD().update(adt_id, **request.values).to_dict())
|
||||
|
||||
def delete(self, adt_id):
|
||||
AutoDiscoveryCITypeCRUD().delete(adt_id)
|
||||
|
||||
return self.jsonify(adt_id=adt_id)
|
||||
|
||||
|
||||
class AutoDiscoveryCIView(APIView):
|
||||
url_prefix = ("/adc", "/adc/<int:adc_id>", "/adc/ci_types/<int:type_id>/attributes", "/adc/ci_types")
|
||||
|
||||
def get(self, type_id=None):
|
||||
if "attributes" in request.url:
|
||||
return self.jsonify(AutoDiscoveryCICRUD.get_attributes_by_type_id(type_id))
|
||||
elif "ci_types" in request.url:
|
||||
need_other = request.values.get("need_other")
|
||||
return self.jsonify(AutoDiscoveryCICRUD.get_ci_types(need_other))
|
||||
|
||||
page = get_page(request.values.pop('page', 1))
|
||||
page_size = get_page_size(request.values.pop('page_size', None))
|
||||
fl = handle_arg_list(request.values.get('fl'))
|
||||
numfound, res = AutoDiscoveryCICRUD.search(page=page, page_size=page_size, fl=fl, **request.values)
|
||||
|
||||
return self.jsonify(page=page,
|
||||
page_size=page_size,
|
||||
numfound=numfound,
|
||||
total=len(res),
|
||||
result=res)
|
||||
|
||||
@args_validate(AutoDiscoveryCICRUD.cls)
|
||||
@args_required("type_id")
|
||||
@args_required("adt_id")
|
||||
@args_required("instance")
|
||||
def post(self):
|
||||
request.values.pop("_key", None)
|
||||
request.values.pop("_secret", None)
|
||||
|
||||
return self.jsonify(AutoDiscoveryCICRUD().upsert(**request.values).to_dict())
|
||||
|
||||
def put(self):
|
||||
return self.post()
|
||||
|
||||
@has_perm_from_args("adc_id", ResourceTypeEnum.CI, PermEnum.DELETE, AutoDiscoveryCICRUD.get_type_name)
|
||||
def delete(self, adc_id):
|
||||
AutoDiscoveryCICRUD().delete(adc_id)
|
||||
|
||||
return self.jsonify(adc_id=adc_id)
|
||||
|
||||
|
||||
class AutoDiscoveryCIDelete2View(APIView):
|
||||
url_prefix = ("/adc",)
|
||||
|
||||
def delete(self):
|
||||
type_id = request.values.get('type_id')
|
||||
unique_value = request.values.get('unique_value')
|
||||
|
||||
AutoDiscoveryCICRUD.delete2(type_id, unique_value)
|
||||
|
||||
return self.jsonify(type_id=type_id, unique_value=unique_value)
|
||||
|
||||
|
||||
class AutoDiscoveryCIAcceptView(APIView):
|
||||
url_prefix = ("/adc/<int:adc_id>/accept",)
|
||||
|
||||
@has_perm_from_args("adc_id", ResourceTypeEnum.CI, PermEnum.ADD, AutoDiscoveryCICRUD.get_type_name)
|
||||
def put(self, adc_id):
|
||||
AutoDiscoveryCICRUD.accept(None, adc_id=adc_id)
|
||||
|
||||
return self.jsonify(adc_id=adc_id)
|
||||
|
||||
|
||||
class AutoDiscoveryRuleSyncView(APIView):
|
||||
url_prefix = ("/adt/sync",)
|
||||
|
||||
def get(self):
|
||||
if g.user.username not in ("cmdb_agent", "worker", "admin"):
|
||||
return abort(403)
|
||||
|
||||
oneagent_name = request.values.get('oneagent_name')
|
||||
oneagent_id = request.values.get('oneagent_id')
|
||||
last_update_at = request.values.get('last_update_at')
|
||||
|
||||
query = "{},oneagent_id:{}".format(oneagent_name, oneagent_id)
|
||||
current_app.logger.info(query)
|
||||
s = search(query)
|
||||
try:
|
||||
response, _, _, _, _, _ = s.search()
|
||||
except SearchError as e:
|
||||
import traceback
|
||||
current_app.logger.error(traceback.format_exc())
|
||||
return abort(400, str(e))
|
||||
|
||||
ci_id = response and response[0]["_id"]
|
||||
rules, last_update_at = AutoDiscoveryCITypeCRUD.get(ci_id, oneagent_id, last_update_at)
|
||||
|
||||
return self.jsonify(rules=rules, last_update_at=last_update_at)
|
12
cmdb-api/api/views/cmdb/c_stats.py
Normal file
12
cmdb-api/api/views/cmdb/c_stats.py
Normal file
@@ -0,0 +1,12 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
|
||||
from api.lib.cmdb.cache import CMDBCounterCache
|
||||
from api.resource import APIView
|
||||
|
||||
|
||||
class CMDBStatisticsView(APIView):
|
||||
url_prefix = "/statistics"
|
||||
|
||||
def get(self):
|
||||
return self.jsonify(CMDBCounterCache.get())
|
@@ -9,14 +9,14 @@ from flask import request
|
||||
|
||||
from api.lib.cmdb.cache import CITypeCache
|
||||
from api.lib.cmdb.ci import CIManager
|
||||
from api.lib.cmdb.ci import CIRelationManager
|
||||
from api.lib.cmdb.const import ExistPolicy
|
||||
from api.lib.cmdb.const import ResourceTypeEnum, PermEnum
|
||||
from api.lib.cmdb.const import RetKey
|
||||
from api.lib.cmdb.perms import has_perm_for_ci
|
||||
from api.lib.cmdb.search import SearchError
|
||||
from api.lib.cmdb.search.ci.db.search import Search as SearchFromDB
|
||||
from api.lib.cmdb.search.ci.es.search import Search as SearchFromES
|
||||
from api.lib.cmdb.search.ci import search
|
||||
from api.lib.perm.acl.acl import has_perm_from_args
|
||||
from api.lib.perm.auth import auth_abandoned
|
||||
from api.lib.utils import get_page
|
||||
from api.lib.utils import get_page_size
|
||||
from api.lib.utils import handle_arg_list
|
||||
@@ -62,52 +62,63 @@ class CIView(APIView):
|
||||
ret_key = RetKey.NAME
|
||||
|
||||
manager = CIManager()
|
||||
ci = manager.get_ci_by_id_from_db(ci_id, ret_key=ret_key, fields=fields)
|
||||
ci = manager.get_ci_by_id_from_db(ci_id, ret_key=ret_key, fields=fields, valid=True)
|
||||
|
||||
return self.jsonify(ci_id=ci_id, ci=ci)
|
||||
|
||||
@staticmethod
|
||||
def _wrap_ci_dict():
|
||||
ci_dict = dict()
|
||||
for k, v in request.values.items():
|
||||
if k != "ci_type" and not k.startswith("_"):
|
||||
ci_dict[k] = v.strip() if isinstance(v, six.string_types) else v
|
||||
ci_dict = {k: v.strip() if isinstance(v, six.string_types) else v for k, v in request.values.items()
|
||||
if k != "ci_type" and not k.startswith("_")}
|
||||
|
||||
return ci_dict
|
||||
|
||||
@has_perm_from_args("ci_type", ResourceTypeEnum.CI, PermEnum.ADD, lambda x: CITypeCache.get(x).name)
|
||||
@has_perm_for_ci("ci_type", ResourceTypeEnum.CI, PermEnum.ADD, lambda x: CITypeCache.get(x))
|
||||
def post(self):
|
||||
ci_type = request.values.get("ci_type")
|
||||
_no_attribute_policy = request.values.get("_no_attribute_policy", ExistPolicy.IGNORE)
|
||||
_no_attribute_policy = request.values.get("no_attribute_policy", ExistPolicy.IGNORE)
|
||||
|
||||
exist_policy = request.values.pop('exist_policy', None)
|
||||
|
||||
ci_dict = self._wrap_ci_dict()
|
||||
|
||||
manager = CIManager()
|
||||
current_app.logger.debug(ci_dict)
|
||||
ci_id = manager.add(ci_type,
|
||||
exist_policy=ExistPolicy.REJECT,
|
||||
_no_attribute_policy=_no_attribute_policy, **ci_dict)
|
||||
exist_policy=exist_policy or ExistPolicy.REJECT,
|
||||
_no_attribute_policy=_no_attribute_policy,
|
||||
_is_admin=request.values.pop('__is_admin', False),
|
||||
**ci_dict)
|
||||
|
||||
return self.jsonify(ci_id=ci_id)
|
||||
|
||||
@has_perm_from_args("ci_id", ResourceTypeEnum.CI, PermEnum.UPDATE, CIManager.get_type_name)
|
||||
@has_perm_for_ci("ci_id", ResourceTypeEnum.CI, PermEnum.UPDATE, CIManager.get_type)
|
||||
def put(self, ci_id=None):
|
||||
args = request.values
|
||||
current_app.logger.info(args)
|
||||
ci_type = args.get("ci_type")
|
||||
_no_attribute_policy = args.get("_no_attribute_policy", ExistPolicy.IGNORE)
|
||||
_no_attribute_policy = args.get("no_attribute_policy", ExistPolicy.IGNORE)
|
||||
|
||||
ci_dict = self._wrap_ci_dict()
|
||||
manager = CIManager()
|
||||
if ci_id is not None:
|
||||
manager.update(ci_id, **ci_dict)
|
||||
manager.update(ci_id,
|
||||
_is_admin=request.values.pop('__is_admin', False),
|
||||
**ci_dict)
|
||||
else:
|
||||
ci_id = manager.add(ci_type,
|
||||
exist_policy=ExistPolicy.REPLACE,
|
||||
_no_attribute_policy=_no_attribute_policy,
|
||||
_is_admin=request.values.pop('__is_admin', False),
|
||||
**ci_dict)
|
||||
|
||||
return self.jsonify(ci_id=ci_id)
|
||||
|
||||
@has_perm_from_args("ci_id", ResourceTypeEnum.CI, PermEnum.DELETE, CIManager.get_type_name)
|
||||
@has_perm_for_ci("ci_id", ResourceTypeEnum.CI, PermEnum.DELETE, CIManager.get_type)
|
||||
def delete(self, ci_id):
|
||||
manager = CIManager()
|
||||
manager.delete(ci_id)
|
||||
|
||||
return self.jsonify(message="ok")
|
||||
|
||||
|
||||
@@ -116,13 +127,13 @@ class CIDetailView(APIView):
|
||||
|
||||
def get(self, ci_id):
|
||||
_ci = CI.get_by_id(ci_id).to_dict()
|
||||
|
||||
return self.jsonify(**_ci)
|
||||
|
||||
|
||||
class CISearchView(APIView):
|
||||
url_prefix = ("/ci/s", "/ci/search")
|
||||
|
||||
@auth_abandoned
|
||||
def get(self):
|
||||
"""@params: q: query statement
|
||||
fl: filter by column
|
||||
@@ -130,12 +141,12 @@ class CISearchView(APIView):
|
||||
ret_key: id, name, alias
|
||||
facet: statistic
|
||||
"""
|
||||
|
||||
page = get_page(request.values.get("page", 1))
|
||||
count = get_page_size(request.values.get("count") or request.values.get("page_size"))
|
||||
|
||||
query = request.values.get('q', "")
|
||||
fl = handle_arg_list(request.values.get('fl', ""))
|
||||
excludes = handle_arg_list(request.values.get('excludes', ""))
|
||||
ret_key = request.values.get('ret_key', RetKey.NAME)
|
||||
if ret_key not in (RetKey.NAME, RetKey.ALIAS, RetKey.ID):
|
||||
ret_key = RetKey.NAME
|
||||
@@ -143,15 +154,18 @@ class CISearchView(APIView):
|
||||
sort = request.values.get("sort")
|
||||
|
||||
start = time.time()
|
||||
if current_app.config.get("USE_ES"):
|
||||
s = SearchFromES(query, fl, facet, page, ret_key, count, sort)
|
||||
else:
|
||||
s = SearchFromDB(query, fl, facet, page, ret_key, count, sort)
|
||||
s = search(query, fl, facet, page, ret_key, count, sort, excludes)
|
||||
try:
|
||||
response, counter, total, page, numfound, facet = s.search()
|
||||
except SearchError as e:
|
||||
return abort(400, str(e))
|
||||
current_app.logger.debug("search time is :{0}".format(time.time() - start))
|
||||
|
||||
if request.values.get('need_children') in current_app.config.get('BOOL_TRUE') and len(response) == 1:
|
||||
children = CIRelationManager.get_children(response[0]['_id'], ret_key=ret_key) # one floor
|
||||
response[0].update(children)
|
||||
|
||||
current_app.logger.debug("search time is: {0}".format(time.time() - start))
|
||||
|
||||
return self.jsonify(numfound=numfound,
|
||||
total=total,
|
||||
page=page,
|
||||
@@ -159,6 +173,9 @@ class CISearchView(APIView):
|
||||
counter=counter,
|
||||
result=response)
|
||||
|
||||
def post(self):
|
||||
return self.get()
|
||||
|
||||
|
||||
class CIUnique(APIView):
|
||||
url_prefix = "/ci/<int:ci_id>/unique"
|
||||
@@ -204,7 +221,7 @@ class CIHeartbeatView(APIView):
|
||||
class CIFlushView(APIView):
|
||||
url_prefix = ("/ci/flush", "/ci/<int:ci_id>/flush")
|
||||
|
||||
@auth_abandoned
|
||||
# @auth_abandoned
|
||||
def get(self, ci_id=None):
|
||||
from api.tasks.cmdb import ci_cache
|
||||
from api.lib.cmdb.const import CMDB_QUEUE
|
||||
@@ -214,4 +231,12 @@ class CIFlushView(APIView):
|
||||
cis = CI.get_by(to_dict=False)
|
||||
for ci in cis:
|
||||
ci_cache.apply_async([ci.id], queue=CMDB_QUEUE)
|
||||
|
||||
return self.jsonify(code=200)
|
||||
|
||||
|
||||
class CIAutoDiscoveryStatisticsView(APIView):
|
||||
url_prefix = "/ci/adc/statistics"
|
||||
|
||||
def get(self):
|
||||
return self.jsonify(CIManager.get_ad_statistics())
|
||||
|
@@ -9,6 +9,7 @@ from flask import request
|
||||
|
||||
from api.lib.cmdb.cache import RelationTypeCache
|
||||
from api.lib.cmdb.ci import CIRelationManager
|
||||
from api.lib.cmdb.resp_format import ErrFormat
|
||||
from api.lib.cmdb.search import SearchError
|
||||
from api.lib.cmdb.search.ci_relation.search import Search
|
||||
from api.lib.decorator import args_required
|
||||
@@ -22,7 +23,6 @@ from api.resource import APIView
|
||||
class CIRelationSearchView(APIView):
|
||||
url_prefix = ("/ci_relations/s", "/ci_relations/search")
|
||||
|
||||
@auth_abandoned
|
||||
def get(self):
|
||||
"""@params: q: query statement
|
||||
fl: filter by column
|
||||
@@ -31,7 +31,6 @@ class CIRelationSearchView(APIView):
|
||||
level: default is 1
|
||||
facet: statistic
|
||||
"""
|
||||
|
||||
page = get_page(request.values.get("page", 1))
|
||||
count = get_page_size(request.values.get("count") or request.values.get("page_size"))
|
||||
|
||||
@@ -42,9 +41,10 @@ class CIRelationSearchView(APIView):
|
||||
fl = handle_arg_list(request.values.get('fl', ""))
|
||||
facet = handle_arg_list(request.values.get("facet", ""))
|
||||
sort = request.values.get("sort")
|
||||
reverse = request.values.get("reverse") in current_app.config.get('BOOL_TRUE')
|
||||
|
||||
start = time.time()
|
||||
s = Search(root_id, level, query, fl, facet, page, count, sort)
|
||||
s = Search(root_id, level, query, fl, facet, page, count, sort, reverse)
|
||||
try:
|
||||
response, counter, total, page, numfound, facet = s.search()
|
||||
except SearchError as e:
|
||||
@@ -89,7 +89,7 @@ class GetSecondCIsView(APIView):
|
||||
try:
|
||||
relation_type_id = RelationTypeCache.get(relation_type).id if relation_type else None
|
||||
except AttributeError:
|
||||
return abort(400, "invalid relation type <{0}>".format(relation_type))
|
||||
return abort(400, ErrFormat.invalid_relation_type.format(relation_type))
|
||||
|
||||
manager = CIRelationManager()
|
||||
numfound, total, second_cis = manager.get_second_cis(
|
||||
@@ -147,12 +147,12 @@ class BatchCreateOrUpdateCIRelationView(APIView):
|
||||
url_prefix = "/ci_relations/batch"
|
||||
|
||||
@args_required('ci_ids')
|
||||
@args_required('parents')
|
||||
def post(self):
|
||||
ci_ids = request.values.get('ci_ids')
|
||||
parents = request.values.get('parents')
|
||||
ci_ids = list(map(int, request.values.get('ci_ids')))
|
||||
parents = list(map(int, request.values.get('parents', [])))
|
||||
children = list(map(int, request.values.get('children', [])))
|
||||
|
||||
CIRelationManager.batch_update(ci_ids, parents)
|
||||
CIRelationManager.batch_update(ci_ids, parents, children)
|
||||
|
||||
return self.jsonify(code=200)
|
||||
|
||||
@@ -160,3 +160,13 @@ class BatchCreateOrUpdateCIRelationView(APIView):
|
||||
@args_required('parents')
|
||||
def put(self):
|
||||
return self.post()
|
||||
|
||||
@args_required('ci_ids')
|
||||
@args_required('parents')
|
||||
def delete(self):
|
||||
ci_ids = list(map(int, request.values.get('ci_ids')))
|
||||
parents = list(map(int, request.values.get('parents', [])))
|
||||
|
||||
CIRelationManager.batch_delete(ci_ids, parents)
|
||||
|
||||
return self.jsonify(code=200)
|
||||
|
@@ -1,9 +1,13 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
|
||||
import json
|
||||
from io import BytesIO
|
||||
|
||||
from flask import abort
|
||||
from flask import current_app
|
||||
from flask import request
|
||||
from flask import session
|
||||
|
||||
from api.lib.cmdb.cache import AttributeCache
|
||||
from api.lib.cmdb.cache import CITypeCache
|
||||
@@ -11,9 +15,23 @@ from api.lib.cmdb.ci_type import CITypeAttributeGroupManager
|
||||
from api.lib.cmdb.ci_type import CITypeAttributeManager
|
||||
from api.lib.cmdb.ci_type import CITypeGroupManager
|
||||
from api.lib.cmdb.ci_type import CITypeManager
|
||||
from api.lib.cmdb.const import RoleEnum
|
||||
from api.lib.cmdb.ci_type import CITypeTemplateManager
|
||||
from api.lib.cmdb.ci_type import CITypeTriggerManager
|
||||
from api.lib.cmdb.ci_type import CITypeUniqueConstraintManager
|
||||
from api.lib.cmdb.const import PermEnum, ResourceTypeEnum, RoleEnum
|
||||
from api.lib.cmdb.perms import CIFilterPermsCRUD
|
||||
from api.lib.cmdb.preference import PreferenceManager
|
||||
from api.lib.cmdb.resp_format import ErrFormat
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.decorator import args_validate
|
||||
from api.lib.perm.acl.acl import ACLManager
|
||||
from api.lib.perm.acl.acl import has_perm_from_args
|
||||
from api.lib.perm.acl.acl import is_app_admin
|
||||
from api.lib.perm.acl.acl import role_required
|
||||
from api.lib.perm.acl.cache import AppCache
|
||||
from api.lib.perm.acl.role import RoleCRUD
|
||||
from api.lib.perm.acl.role import RoleRelationCRUD
|
||||
from api.lib.perm.auth import auth_with_app_token
|
||||
from api.lib.utils import handle_arg_list
|
||||
from api.resource import APIView
|
||||
|
||||
@@ -34,8 +52,8 @@ class CITypeView(APIView):
|
||||
|
||||
return self.jsonify(numfound=count, ci_types=ci_types)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@args_required("name")
|
||||
@args_validate(CITypeManager.cls)
|
||||
def post(self):
|
||||
params = request.values
|
||||
|
||||
@@ -49,44 +67,67 @@ class CITypeView(APIView):
|
||||
|
||||
return self.jsonify(type_id=type_id)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
@args_validate(CITypeManager.cls)
|
||||
def put(self, type_id):
|
||||
params = request.values
|
||||
|
||||
manager = CITypeManager()
|
||||
manager.update(type_id, **params)
|
||||
|
||||
return self.jsonify(type_id=type_id)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
def delete(self, type_id):
|
||||
CITypeManager.delete(type_id)
|
||||
|
||||
return self.jsonify(type_id=type_id)
|
||||
|
||||
|
||||
class CITypeGroupView(APIView):
|
||||
url_prefix = ("/ci_types/groups", "/ci_types/groups/<int:gid>")
|
||||
url_prefix = ("/ci_types/groups",
|
||||
"/ci_types/groups/config",
|
||||
"/ci_types/groups/order",
|
||||
"/ci_types/groups/<int:gid>")
|
||||
|
||||
def get(self):
|
||||
config_required = True if "/config" in request.url else False
|
||||
need_other = request.values.get("need_other")
|
||||
return self.jsonify(CITypeGroupManager.get(need_other))
|
||||
|
||||
return self.jsonify(CITypeGroupManager.get(need_other, config_required))
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@args_required("name")
|
||||
@args_validate(CITypeGroupManager.cls)
|
||||
def post(self):
|
||||
name = request.values.get("name")
|
||||
group = CITypeGroupManager.add(name)
|
||||
|
||||
return self.jsonify(group.to_dict())
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
def put(self, gid):
|
||||
name = request.values.get('name')
|
||||
@args_validate(CITypeGroupManager.cls)
|
||||
def put(self, gid=None):
|
||||
if "/order" in request.url:
|
||||
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))
|
||||
|
||||
group_ids = request.values.get('group_ids')
|
||||
CITypeGroupManager.order(group_ids)
|
||||
|
||||
return self.jsonify(group_ids=group_ids)
|
||||
|
||||
name = request.values.get('name') or abort(400, ErrFormat.argument_value_required.format("name"))
|
||||
type_ids = request.values.get('type_ids')
|
||||
|
||||
CITypeGroupManager.update(gid, name, type_ids)
|
||||
|
||||
return self.jsonify(gid=gid)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
def delete(self, gid):
|
||||
CITypeGroupManager.delete(gid)
|
||||
type_ids = request.values.get("type_ids")
|
||||
CITypeGroupManager.delete(gid, type_ids)
|
||||
|
||||
return self.jsonify(gid=gid)
|
||||
|
||||
|
||||
@@ -97,16 +138,18 @@ class CITypeQueryView(APIView):
|
||||
def get(self):
|
||||
q = request.args.get("q")
|
||||
res = CITypeManager.query(q)
|
||||
|
||||
return self.jsonify(ci_type=res)
|
||||
|
||||
|
||||
class EnableCITypeView(APIView):
|
||||
url_prefix = "/ci_types/<int:type_id>/enable"
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
def post(self, type_id):
|
||||
enable = request.values.get("enable", True)
|
||||
CITypeManager.set_enabled(type_id, enabled=enable)
|
||||
|
||||
return self.jsonify(type_id=type_id, enable=enable)
|
||||
|
||||
|
||||
@@ -114,16 +157,22 @@ class CITypeAttributeView(APIView):
|
||||
url_prefix = ("/ci_types/<int:type_id>/attributes", "/ci_types/<string:type_name>/attributes")
|
||||
|
||||
def get(self, type_id=None, type_name=None):
|
||||
t = CITypeCache.get(type_id) or CITypeCache.get(type_name) or abort(404, "CIType does not exist")
|
||||
t = CITypeCache.get(type_id) or CITypeCache.get(type_name) or abort(404, ErrFormat.ci_type_not_found)
|
||||
type_id = t.id
|
||||
unique_id = t.unique_id
|
||||
unique = AttributeCache.get(unique_id).name
|
||||
return self.jsonify(attributes=CITypeAttributeManager.get_attributes_by_type_id(type_id),
|
||||
|
||||
attr_filter = CIFilterPermsCRUD.get_attr_filter(type_id)
|
||||
attributes = CITypeAttributeManager.get_attributes_by_type_id(type_id)
|
||||
if attr_filter:
|
||||
attributes = [i for i in attributes if i['name'] in attr_filter]
|
||||
|
||||
return self.jsonify(attributes=attributes,
|
||||
type_id=type_id,
|
||||
unique_id=unique_id,
|
||||
unique=unique)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
@args_required("attr_id")
|
||||
def post(self, type_id=None):
|
||||
attr_id_list = handle_arg_list(request.values.get("attr_id"))
|
||||
@@ -131,31 +180,34 @@ class CITypeAttributeView(APIView):
|
||||
params.pop("attr_id", "")
|
||||
|
||||
CITypeAttributeManager.add(type_id, attr_id_list, **params)
|
||||
|
||||
return self.jsonify(attributes=attr_id_list)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
@args_required("attributes")
|
||||
def put(self, type_id=None):
|
||||
"""
|
||||
attributes is list, only support raw data request
|
||||
:param type_id:
|
||||
:return:
|
||||
:param type_id:
|
||||
:return:
|
||||
"""
|
||||
attributes = request.values.get("attributes")
|
||||
current_app.logger.debug(attributes)
|
||||
if not isinstance(attributes, list):
|
||||
return abort(400, "attributes must be list")
|
||||
return abort(400, ErrFormat.argument_attributes_must_be_list)
|
||||
|
||||
CITypeAttributeManager.update(type_id, attributes)
|
||||
|
||||
return self.jsonify(attributes=attributes)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
@args_required("attr_id")
|
||||
def delete(self, type_id=None):
|
||||
"""
|
||||
Form request: attr_id is a string, separated by commas
|
||||
Raw data request: attr_id is a list
|
||||
:param type_id:
|
||||
:return:
|
||||
:param type_id:
|
||||
:return:
|
||||
"""
|
||||
attr_id_list = handle_arg_list(request.values.get("attr_id", ""))
|
||||
|
||||
@@ -164,6 +216,25 @@ class CITypeAttributeView(APIView):
|
||||
return self.jsonify(attributes=attr_id_list)
|
||||
|
||||
|
||||
class CITypesAttributeView(APIView):
|
||||
url_prefix = ("/ci_types/attributes",)
|
||||
|
||||
@args_required("type_ids", value_required=True)
|
||||
def get(self):
|
||||
type_ids = handle_arg_list(request.values.get('type_ids'))
|
||||
|
||||
attr_names = set()
|
||||
attributes = list()
|
||||
for type_id in type_ids:
|
||||
_attributes = CITypeAttributeManager.get_attributes_by_type_id(type_id)
|
||||
for _attr in _attributes:
|
||||
if _attr['name'] not in attr_names:
|
||||
attr_names.add(_attr['name'])
|
||||
attributes.append(_attr)
|
||||
|
||||
return self.jsonify(attributes=attributes)
|
||||
|
||||
|
||||
class CITypeAttributeTransferView(APIView):
|
||||
url_prefix = "/ci_types/<int:type_id>/attributes/transfer"
|
||||
|
||||
@@ -198,10 +269,18 @@ class CITypeAttributeGroupView(APIView):
|
||||
|
||||
def get(self, type_id):
|
||||
need_other = request.values.get("need_other")
|
||||
return self.jsonify(CITypeAttributeGroupManager.get_by_type_id(type_id, need_other))
|
||||
groups = CITypeAttributeGroupManager.get_by_type_id(type_id, need_other)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
attr_filter = CIFilterPermsCRUD.get_attr_filter(type_id)
|
||||
if attr_filter:
|
||||
for group in groups:
|
||||
group['attributes'] = [attr for attr in (group.get('attributes') or []) if attr['name'] in attr_filter]
|
||||
|
||||
return self.jsonify(groups)
|
||||
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
@args_required("name")
|
||||
@args_validate(CITypeAttributeGroupManager.cls)
|
||||
def post(self, type_id):
|
||||
name = request.values.get("name").strip()
|
||||
order = request.values.get("order") or 0
|
||||
@@ -213,7 +292,9 @@ class CITypeAttributeGroupView(APIView):
|
||||
current_app.logger.warning(group.id)
|
||||
return self.jsonify(group_id=group.id)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
@args_required("name")
|
||||
@args_validate(CITypeAttributeGroupManager.cls)
|
||||
def put(self, group_id):
|
||||
name = request.values.get("name")
|
||||
order = request.values.get("order") or 0
|
||||
@@ -224,7 +305,198 @@ class CITypeAttributeGroupView(APIView):
|
||||
CITypeAttributeGroupManager.update(group_id, name, attr_order, order)
|
||||
return self.jsonify(group_id=group_id)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
def delete(self, group_id):
|
||||
CITypeAttributeGroupManager.delete(group_id)
|
||||
return self.jsonify(group_id=group_id)
|
||||
|
||||
|
||||
class CITypeTemplateView(APIView):
|
||||
url_prefix = ("/ci_types/template/import", "/ci_types/template/export")
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
def get(self): # export
|
||||
return self.jsonify(
|
||||
dict(ci_type_template=CITypeTemplateManager.export_template()))
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
def post(self): # import
|
||||
tpt = request.values.get('ci_type_template') or {}
|
||||
|
||||
CITypeTemplateManager().import_template(tpt)
|
||||
|
||||
return self.jsonify(code=200)
|
||||
|
||||
|
||||
class CITypeCanDefineComputed(APIView):
|
||||
url_prefix = "/ci_types/can_define_computed"
|
||||
|
||||
@role_required(PermEnum.CONFIG)
|
||||
def get(self):
|
||||
return self.jsonify(code=200)
|
||||
|
||||
|
||||
class CITypeTemplateFileView(APIView):
|
||||
url_prefix = ("/ci_types/template/import/file", "/ci_types/template/export/file")
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
def get(self): # export
|
||||
tpt_json = CITypeTemplateManager.export_template()
|
||||
tpt_json = dict(ci_type_template=tpt_json)
|
||||
|
||||
bf = BytesIO()
|
||||
bf.write(bytes(json.dumps(tpt_json).encode('utf-8')))
|
||||
bf.seek(0)
|
||||
|
||||
return self.send_file(bf,
|
||||
as_attachment=True,
|
||||
attachment_filename="cmdb_template.json",
|
||||
mimetype='application/json',
|
||||
cache_timeout=0)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
def post(self): # import
|
||||
f = request.files.get('file')
|
||||
|
||||
if f is None:
|
||||
return abort(400, ErrFormat.argument_file_not_found)
|
||||
|
||||
content = f.read()
|
||||
try:
|
||||
content = json.loads(content)
|
||||
except:
|
||||
return abort(400, ErrFormat.invalid_json)
|
||||
tpt = content.get('ci_type_template')
|
||||
|
||||
CITypeTemplateManager().import_template(tpt)
|
||||
|
||||
return self.jsonify(code=200)
|
||||
|
||||
|
||||
class CITypeUniqueConstraintView(APIView):
|
||||
url_prefix = ("/ci_types/<int:type_id>/unique_constraint", "/ci_types/<int:type_id>/unique_constraint/<int:_id>")
|
||||
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
def get(self, type_id):
|
||||
return self.jsonify(CITypeUniqueConstraintManager.get_detail(type_id))
|
||||
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
@args_required("attr_ids")
|
||||
def post(self, type_id):
|
||||
attr_ids = request.values.get('attr_ids')
|
||||
|
||||
return self.jsonify(CITypeUniqueConstraintManager().add(type_id, attr_ids))
|
||||
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
@args_required("attr_ids")
|
||||
def put(self, type_id, _id):
|
||||
assert type_id is not None
|
||||
|
||||
attr_ids = request.values.get('attr_ids')
|
||||
|
||||
return self.jsonify(CITypeUniqueConstraintManager().update(_id, attr_ids))
|
||||
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
def delete(self, type_id, _id):
|
||||
assert type_id is not None
|
||||
|
||||
CITypeUniqueConstraintManager().delete(_id)
|
||||
|
||||
return self.jsonify(code=200)
|
||||
|
||||
|
||||
class CITypeTriggerView(APIView):
|
||||
url_prefix = ("/ci_types/<int:type_id>/triggers", "/ci_types/<int:type_id>/triggers/<int:_id>")
|
||||
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
def get(self, type_id):
|
||||
return self.jsonify(CITypeTriggerManager.get(type_id))
|
||||
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
@args_required("attr_id")
|
||||
@args_required("notify")
|
||||
def post(self, type_id):
|
||||
attr_id = request.values.get('attr_id')
|
||||
notify = request.values.get('notify')
|
||||
|
||||
return self.jsonify(CITypeTriggerManager().add(type_id, attr_id, notify))
|
||||
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
@args_required("notify")
|
||||
def put(self, type_id, _id):
|
||||
assert type_id is not None
|
||||
|
||||
notify = request.values.get('notify')
|
||||
|
||||
return self.jsonify(CITypeTriggerManager().update(_id, notify))
|
||||
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
def delete(self, type_id, _id):
|
||||
assert type_id is not None
|
||||
|
||||
CITypeTriggerManager().delete(_id)
|
||||
|
||||
return self.jsonify(code=200)
|
||||
|
||||
|
||||
class CITypeGrantView(APIView):
|
||||
url_prefix = "/ci_types/<int:type_id>/roles/<int:rid>/grant"
|
||||
|
||||
def post(self, type_id, rid):
|
||||
perms = request.values.pop('perms', None)
|
||||
|
||||
if request.values.get('attr_filter'):
|
||||
request.values['attr_filter'] = handle_arg_list(request.values.get('attr_filter', ''))
|
||||
|
||||
_type = CITypeCache.get(type_id)
|
||||
type_name = _type and _type.name or abort(404, ErrFormat.ci_type_not_found)
|
||||
acl = ACLManager('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))
|
||||
|
||||
acl.grant_resource_to_role_by_rid(type_name, rid, ResourceTypeEnum.CI_TYPE, perms)
|
||||
|
||||
CIFilterPermsCRUD().add(type_id=type_id, rid=rid, **request.values)
|
||||
|
||||
return self.jsonify(code=200)
|
||||
|
||||
|
||||
class CITypeRevokeView(APIView):
|
||||
url_prefix = "/ci_types/<int:type_id>/roles/<int:rid>/revoke"
|
||||
|
||||
@args_required('perms')
|
||||
def post(self, type_id, rid):
|
||||
perms = request.values.pop('perms', None)
|
||||
|
||||
if request.values.get('attr_filter'):
|
||||
request.values['attr_filter'] = handle_arg_list(request.values.get('attr_filter', ''))
|
||||
|
||||
_type = CITypeCache.get(type_id)
|
||||
type_name = _type and _type.name or abort(404, ErrFormat.ci_type_not_found)
|
||||
acl = ACLManager('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))
|
||||
|
||||
acl.revoke_resource_from_role_by_rid(type_name, rid, ResourceTypeEnum.CI_TYPE, perms)
|
||||
|
||||
if PermEnum.READ in perms:
|
||||
CIFilterPermsCRUD().delete(type_id=type_id, rid=rid)
|
||||
|
||||
app_id = AppCache.get('cmdb').id
|
||||
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)
|
||||
|
||||
|
||||
class CITypeFilterPermissionView(APIView):
|
||||
url_prefix = "/ci_types/<int:type_id>/filters/permissions"
|
||||
|
||||
@auth_with_app_token
|
||||
def get(self, type_id):
|
||||
return self.jsonify(CIFilterPermsCRUD().get(type_id))
|
||||
|
@@ -1,11 +1,17 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
|
||||
from flask import abort
|
||||
from flask import request
|
||||
|
||||
from api.lib.cmdb.ci_type import CITypeManager
|
||||
from api.lib.cmdb.ci_type import CITypeRelationManager
|
||||
from api.lib.cmdb.const import RoleEnum
|
||||
from api.lib.cmdb.const import PermEnum, ResourceTypeEnum, RoleEnum
|
||||
from api.lib.cmdb.resp_format import ErrFormat
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.perm.acl.acl import ACLManager
|
||||
from api.lib.perm.acl.acl import has_perm_from_args
|
||||
from api.lib.perm.acl.acl import is_app_admin
|
||||
from api.lib.perm.acl.acl import role_required
|
||||
from api.resource import APIView
|
||||
|
||||
@@ -33,15 +39,16 @@ class CITypeRelationView(APIView):
|
||||
|
||||
return self.jsonify(res)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@has_perm_from_args("parent_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
@args_required("relation_type_id")
|
||||
def post(self, parent_id, child_id):
|
||||
relation_type_id = request.values.get("relation_type_id")
|
||||
ctr_id = CITypeRelationManager.add(parent_id, child_id, relation_type_id)
|
||||
constraint = request.values.get("constraint")
|
||||
ctr_id = CITypeRelationManager.add(parent_id, child_id, relation_type_id, constraint)
|
||||
|
||||
return self.jsonify(ctr_id=ctr_id)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@has_perm_from_args("parent_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
|
||||
def delete(self, parent_id, child_id):
|
||||
CITypeRelationManager.delete_2(parent_id, child_id)
|
||||
|
||||
@@ -56,3 +63,42 @@ class CITypeRelationDelete2View(APIView):
|
||||
CITypeRelationManager.delete(ctr_id)
|
||||
|
||||
return self.jsonify(code=200, ctr_id=ctr_id)
|
||||
|
||||
|
||||
class CITypeRelationGrantView(APIView):
|
||||
url_prefix = "/ci_type_relations/<int:parent_id>/<int:child_id>/roles/<int:rid>/grant"
|
||||
|
||||
def post(self, parent_id, child_id, rid):
|
||||
p = CITypeManager.check_is_existed(parent_id)
|
||||
c = CITypeManager.check_is_existed(child_id)
|
||||
resource_name = CITypeRelationManager.acl_resource_name(p.name, c.name)
|
||||
|
||||
perms = request.values.get('perms')
|
||||
|
||||
acl = ACLManager('cmdb')
|
||||
if not acl.has_permission(resource_name, ResourceTypeEnum.CI_TYPE_RELATION, PermEnum.GRANT) and \
|
||||
not is_app_admin('cmdb'):
|
||||
return abort(403, ErrFormat.no_permission.format(resource_name, PermEnum.GRANT))
|
||||
|
||||
acl.grant_resource_to_role_by_rid(resource_name, rid, ResourceTypeEnum.CI_TYPE_RELATION, perms)
|
||||
|
||||
return self.jsonify(code=200)
|
||||
|
||||
|
||||
class CITypeRelationRevokeView(APIView):
|
||||
url_prefix = "/ci_type_relations/<int:parent_id>/<int:child_id>/roles/<int:rid>/revoke"
|
||||
|
||||
def post(self, parent_id, child_id, rid):
|
||||
p = CITypeManager.check_is_existed(parent_id)
|
||||
c = CITypeManager.check_is_existed(child_id)
|
||||
resource_name = CITypeRelationManager.acl_resource_name(p.name, c.name)
|
||||
|
||||
perms = request.values.get('perms')
|
||||
acl = ACLManager('cmdb')
|
||||
if not acl.has_permission(resource_name, ResourceTypeEnum.CI_TYPE_RELATION, PermEnum.GRANT) and \
|
||||
not is_app_admin('cmdb'):
|
||||
return abort(403, ErrFormat.no_permission.format(resource_name, PermEnum.GRANT))
|
||||
|
||||
acl.revoke_resource_from_role_by_rid(resource_name, rid, ResourceTypeEnum.CI_TYPE_RELATION, perms)
|
||||
|
||||
return self.jsonify(code=200)
|
||||
|
72
cmdb-api/api/views/cmdb/custom_dashboard.py
Normal file
72
cmdb-api/api/views/cmdb/custom_dashboard.py
Normal file
@@ -0,0 +1,72 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
|
||||
from flask import request
|
||||
|
||||
from api.lib.cmdb.const import RoleEnum
|
||||
from api.lib.cmdb.custom_dashboard import CustomDashboardManager
|
||||
from api.lib.cmdb.custom_dashboard import SystemConfigManager
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.decorator import args_validate
|
||||
from api.lib.perm.acl.acl import role_required
|
||||
from api.resource import APIView
|
||||
|
||||
|
||||
class CustomDashboardApiView(APIView):
|
||||
url_prefix = ("/custom_dashboard", "/custom_dashboard/<int:_id>", "/custom_dashboard/batch")
|
||||
|
||||
def get(self):
|
||||
return self.jsonify(CustomDashboardManager.get())
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@args_validate(CustomDashboardManager.cls)
|
||||
def post(self):
|
||||
cm = CustomDashboardManager.add(**request.values)
|
||||
|
||||
return self.jsonify(cm.to_dict())
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@args_validate(CustomDashboardManager.cls)
|
||||
def put(self, _id=None):
|
||||
if _id is not None:
|
||||
cm = CustomDashboardManager.update(_id, **request.values)
|
||||
|
||||
return self.jsonify(cm.to_dict())
|
||||
|
||||
CustomDashboardManager.batch_update(request.values.get("id2options"))
|
||||
|
||||
return self.jsonify(id2options=request.values.get('id2options'))
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
def delete(self, _id):
|
||||
CustomDashboardManager.delete(_id)
|
||||
|
||||
return self.jsonify(code=200)
|
||||
|
||||
|
||||
class SystemConfigApiView(APIView):
|
||||
url_prefix = ("/system_config",)
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@args_required("name", value_required=True)
|
||||
def get(self):
|
||||
return self.jsonify(SystemConfigManager.get(request.values['name']))
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@args_validate(SystemConfigManager.cls)
|
||||
@args_required("name", value_required=True)
|
||||
@args_required("option", value_required=True)
|
||||
def post(self):
|
||||
cm = SystemConfigManager.create_or_update(**request.values)
|
||||
|
||||
return self.jsonify(cm.to_dict())
|
||||
|
||||
def put(self, _id=None):
|
||||
return self.post()
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@args_required("name")
|
||||
def delete(self):
|
||||
CustomDashboardManager.delete(request.values['name'])
|
||||
|
||||
return self.jsonify(code=200)
|
@@ -6,58 +6,92 @@ import datetime
|
||||
from flask import abort
|
||||
from flask import request
|
||||
|
||||
from api.lib.cmdb.ci import CIManager
|
||||
from api.lib.cmdb.const import ResourceTypeEnum, PermEnum
|
||||
from api.lib.cmdb.const import RoleEnum
|
||||
from api.lib.cmdb.history import AttributeHistoryManger
|
||||
from api.lib.cmdb.history import CITypeHistoryManager
|
||||
from api.lib.cmdb.resp_format import ErrFormat
|
||||
from api.lib.perm.acl.acl import has_perm_from_args
|
||||
from api.lib.perm.acl.acl import role_required
|
||||
from api.lib.utils import get_page
|
||||
from api.lib.utils import get_page_size
|
||||
from api.resource import APIView
|
||||
|
||||
|
||||
class RecordView(APIView):
|
||||
url_prefix = "/history/records"
|
||||
url_prefix = ("/history/records/attribute", "/history/records/relation")
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
def get(self):
|
||||
page = get_page(request.values.get("page", 1))
|
||||
page_size = get_page_size(request.values.get("page_size"))
|
||||
_start = request.values.get("start")
|
||||
_end = request.values.get("end")
|
||||
username = request.values.get("username", "")
|
||||
operate_type = request.values.get("operate_type", "")
|
||||
type_id = request.values.get("type_id")
|
||||
start, end = None, None
|
||||
if _start:
|
||||
try:
|
||||
start = datetime.datetime.strptime(_start, '%Y-%m-%d %H:%M:%S')
|
||||
except ValueError:
|
||||
abort(400, 'incorrect start date time')
|
||||
return abort(400, ErrFormat.datetime_argument_invalid.format('start'))
|
||||
if _end:
|
||||
try:
|
||||
end = datetime.datetime.strptime(_end, '%Y-%m-%d %H:%M:%S')
|
||||
except ValueError:
|
||||
abort(400, 'incorrect end date time')
|
||||
return abort(400, ErrFormat.datetime_argument_invalid.format('start'))
|
||||
|
||||
numfound, total, res = AttributeHistoryManger.get_records(start, end, username, page, page_size)
|
||||
if "attribute" in request.url:
|
||||
total, res = AttributeHistoryManger.get_records_for_attributes(start, end, username, page, page_size,
|
||||
operate_type,
|
||||
type_id,
|
||||
request.values.get('ci_id'),
|
||||
request.values.get('attr_id'))
|
||||
return self.jsonify(records=res,
|
||||
total=total,
|
||||
**request.values)
|
||||
else:
|
||||
total, res, cis = AttributeHistoryManger.get_records_for_relation(start, end, username, page, page_size,
|
||||
operate_type,
|
||||
type_id,
|
||||
request.values.get('first_ci_id'),
|
||||
request.values.get('second_ci_id'))
|
||||
|
||||
return self.jsonify(numfound=numfound,
|
||||
records=res,
|
||||
page=page,
|
||||
total=total,
|
||||
start=_start,
|
||||
end=_end,
|
||||
username=username)
|
||||
return self.jsonify(records=res,
|
||||
total=total,
|
||||
cis=cis,
|
||||
**request.values)
|
||||
|
||||
|
||||
class CIHistoryView(APIView):
|
||||
url_prefix = "/history/ci/<int:ci_id>"
|
||||
|
||||
@has_perm_from_args("ci_id", ResourceTypeEnum.CI, PermEnum.READ, CIManager.get_type_name)
|
||||
def get(self, ci_id):
|
||||
result = AttributeHistoryManger.get_by_ci_id(ci_id)
|
||||
|
||||
return self.jsonify(result)
|
||||
|
||||
|
||||
class RecordDetailView(APIView):
|
||||
url_prefix = "/history/records/<int:record_id>"
|
||||
class CITypeHistoryView(APIView):
|
||||
url_prefix = "/history/ci_types"
|
||||
|
||||
def get(self, record_id):
|
||||
username, timestamp, attr_dict, rel_dict = AttributeHistoryManger.get_record_detail(record_id)
|
||||
return self.jsonify(username=username,
|
||||
timestamp=timestamp,
|
||||
attr_history=attr_dict,
|
||||
rel_history=rel_dict)
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
def get(self):
|
||||
type_id = request.values.get("type_id")
|
||||
username = request.values.get("username")
|
||||
operate_type = request.values.get("operate_type")
|
||||
|
||||
page = get_page(request.values.get('page', 1))
|
||||
page_size = get_page_size(request.values.get('page_size', 1))
|
||||
|
||||
numfound, result = CITypeHistoryManager.get(page, page_size, username,
|
||||
type_id=type_id, operate_type=operate_type)
|
||||
|
||||
return self.jsonify(page=page,
|
||||
page_size=page_size,
|
||||
numfound=numfound,
|
||||
total=len(result),
|
||||
result=result)
|
||||
|
@@ -1,25 +1,35 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
|
||||
from flask import abort
|
||||
from flask import request
|
||||
|
||||
from api.lib.cmdb.ci_type import CITypeManager
|
||||
from api.lib.cmdb.const import ResourceTypeEnum, PermEnum, RoleEnum
|
||||
from api.lib.cmdb.const import PermEnum, ResourceTypeEnum, RoleEnum
|
||||
from api.lib.cmdb.perms import CIFilterPermsCRUD
|
||||
from api.lib.cmdb.preference import PreferenceManager
|
||||
from api.lib.cmdb.resp_format import ErrFormat
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.decorator import args_validate
|
||||
from api.lib.perm.acl.acl import ACLManager
|
||||
from api.lib.perm.acl.acl import has_perm_from_args
|
||||
from api.lib.perm.acl.acl import is_app_admin
|
||||
from api.lib.perm.acl.acl import role_required
|
||||
from api.lib.perm.acl.acl import validate_permission
|
||||
from api.lib.utils import handle_arg_list
|
||||
from api.resource import APIView
|
||||
|
||||
|
||||
class PreferenceShowCITypesView(APIView):
|
||||
url_prefix = "/preference/ci_types"
|
||||
url_prefix = ("/preference/ci_types", "/preference/ci_types2")
|
||||
|
||||
def get(self):
|
||||
instance = request.values.get("instance")
|
||||
tree = request.values.get("tree")
|
||||
|
||||
if "ci_types2" in request.url:
|
||||
return self.jsonify(PreferenceManager.get_types2(instance, tree))
|
||||
|
||||
return self.jsonify(PreferenceManager.get_types(instance, tree))
|
||||
|
||||
|
||||
@@ -29,14 +39,26 @@ class PreferenceShowAttributesView(APIView):
|
||||
def get(self, id_or_name):
|
||||
is_subscribed, attributes = PreferenceManager.get_show_attributes(id_or_name)
|
||||
|
||||
attr_filter = CIFilterPermsCRUD.get_attr_filter(int(id_or_name)) if str(id_or_name).isdigit() else []
|
||||
|
||||
if attr_filter:
|
||||
attributes = [i for i in attributes if i['name'] in attr_filter]
|
||||
|
||||
return self.jsonify(attributes=attributes, is_subscribed=is_subscribed)
|
||||
|
||||
@has_perm_from_args("id_or_name", ResourceTypeEnum.CI, PermEnum.READ, CITypeManager.get_name_by_id)
|
||||
@args_required("attr")
|
||||
@args_required("attr", value_required=False)
|
||||
@args_validate(PreferenceManager.pref_attr_cls)
|
||||
def post(self, id_or_name):
|
||||
id_or_name = int(id_or_name)
|
||||
attr_list = handle_arg_list(request.values.get("attr", ""))
|
||||
attr_list = handle_arg_list(request.values.get("attr", "")) # [[attr, false], ]
|
||||
orders = list(range(len(attr_list)))
|
||||
|
||||
if attr_list and not is_app_admin('cmdb'):
|
||||
resource_name = CITypeManager.get_name_by_id(id_or_name)
|
||||
if not ACLManager('cmdb').has_permission(resource_name, ResourceTypeEnum.CI, PermEnum.READ):
|
||||
from api.lib.perm.acl.resp_format import ErrFormat
|
||||
return abort(403, ErrFormat.resource_no_permission.format(resource_name, PermEnum.READ))
|
||||
|
||||
PreferenceManager.create_or_update_show_attributes(id_or_name, list(zip(attr_list, orders)))
|
||||
|
||||
return self.jsonify(type_id=id_or_name,
|
||||
@@ -53,12 +75,16 @@ class PreferenceTreeApiView(APIView):
|
||||
def get(self):
|
||||
return self.jsonify(PreferenceManager.get_tree_view())
|
||||
|
||||
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.READ, CITypeManager.get_name_by_id)
|
||||
@args_required("type_id")
|
||||
@args_required("levels")
|
||||
@args_required("levels", value_required=False)
|
||||
@args_validate(PreferenceManager.pref_tree_cls)
|
||||
def post(self):
|
||||
type_id = request.values.get("type_id")
|
||||
levels = handle_arg_list(request.values.get("levels"))
|
||||
if levels:
|
||||
if not is_app_admin("cmdb"):
|
||||
validate_permission(CITypeManager.get_name_by_id(type_id), ResourceTypeEnum.CI, PermEnum.READ)
|
||||
|
||||
res = PreferenceManager.create_or_update_tree_view(type_id, levels)
|
||||
|
||||
return self.jsonify(res and res.to_dict() or {})
|
||||
@@ -78,6 +104,7 @@ class PreferenceRelationApiView(APIView):
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@args_required("name")
|
||||
@args_required("cr_ids")
|
||||
@args_validate(PreferenceManager.pref_rel_cls)
|
||||
def post(self):
|
||||
name = request.values.get("name")
|
||||
cr_ids = request.values.get("cr_ids")
|
||||
@@ -96,3 +123,65 @@ class PreferenceRelationApiView(APIView):
|
||||
PreferenceManager.delete_relation_view(name)
|
||||
|
||||
return self.jsonify(name=name)
|
||||
|
||||
|
||||
class PreferenceSearchOptionView(APIView):
|
||||
url_prefix = ("/preference/search/option", "/preference/search/option/<int:_id>")
|
||||
|
||||
def get(self):
|
||||
res = PreferenceManager.get_search_option(**request.values)
|
||||
|
||||
return self.jsonify(res)
|
||||
|
||||
@args_required("name", value_required=True)
|
||||
@args_required("option", value_required=True)
|
||||
@args_validate(PreferenceManager.pre_so_cls)
|
||||
def post(self):
|
||||
res = PreferenceManager.add_search_option(**request.values)
|
||||
|
||||
return self.jsonify(res.to_dict())
|
||||
|
||||
@args_validate(PreferenceManager.pre_so_cls)
|
||||
def put(self, _id):
|
||||
res = PreferenceManager.update_search_option(_id, **request.values)
|
||||
|
||||
return self.jsonify(res.to_dict())
|
||||
|
||||
def delete(self, _id):
|
||||
PreferenceManager.delete_search_option(_id)
|
||||
|
||||
return self.jsonify(id=_id)
|
||||
|
||||
|
||||
class PreferenceRelationGrantView(APIView):
|
||||
url_prefix = "/preference/relation/view/roles/<int:rid>/grant"
|
||||
|
||||
def post(self, rid):
|
||||
name = request.values.get("name")
|
||||
perms = request.values.get('perms')
|
||||
|
||||
acl = ACLManager('cmdb')
|
||||
if not acl.has_permission(name, ResourceTypeEnum.RELATION_VIEW, PermEnum.GRANT) and \
|
||||
not is_app_admin('cmdb'):
|
||||
return abort(403, ErrFormat.no_permission.format(name, PermEnum.GRANT))
|
||||
|
||||
acl.grant_resource_to_role_by_rid(name, rid, ResourceTypeEnum.RELATION_VIEW, perms)
|
||||
|
||||
return self.jsonify(code=200)
|
||||
|
||||
|
||||
class PreferenceRelationRevokeView(APIView):
|
||||
url_prefix = "/preference/relation/view/roles/<int:rid>/revoke"
|
||||
|
||||
def post(self, rid):
|
||||
name = request.values.get("name")
|
||||
perms = request.values.get('perms')
|
||||
|
||||
acl = ACLManager('cmdb')
|
||||
if not acl.has_permission(name, ResourceTypeEnum.RELATION_VIEW, PermEnum.GRANT) and \
|
||||
not is_app_admin('cmdb'):
|
||||
return abort(403, ErrFormat.no_permission.format(name, PermEnum.GRANT))
|
||||
|
||||
acl.revoke_resource_from_role_by_rid(name, rid, ResourceTypeEnum.RELATION_VIEW, perms)
|
||||
|
||||
return self.jsonify(code=200)
|
||||
|
@@ -6,7 +6,9 @@ from flask import request
|
||||
|
||||
from api.lib.cmdb.const import RoleEnum
|
||||
from api.lib.cmdb.relation_type import RelationTypeManager
|
||||
from api.lib.cmdb.resp_format import ErrFormat
|
||||
from api.lib.decorator import args_required
|
||||
from api.lib.decorator import args_validate
|
||||
from api.lib.perm.acl.acl import role_required
|
||||
from api.resource import APIView
|
||||
|
||||
@@ -19,19 +21,24 @@ class RelationTypeView(APIView):
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@args_required("name")
|
||||
@args_validate(RelationTypeManager.cls)
|
||||
def post(self):
|
||||
name = request.values.get("name") or abort(400, "Name cannot be empty")
|
||||
name = request.values.get("name") or abort(400, ErrFormat.argument_value_required.format("name"))
|
||||
rel = RelationTypeManager.add(name)
|
||||
|
||||
return self.jsonify(rel.to_dict())
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
@args_required("name")
|
||||
@args_validate(RelationTypeManager.cls)
|
||||
def put(self, rel_id):
|
||||
name = request.values.get("name") or abort(400, "Name cannot be empty")
|
||||
name = request.values.get("name") or abort(400, ErrFormat.argument_value_required.format("name"))
|
||||
rel = RelationTypeManager.update(rel_id, name)
|
||||
|
||||
return self.jsonify(rel.to_dict())
|
||||
|
||||
@role_required(RoleEnum.CONFIG)
|
||||
def delete(self, rel_id):
|
||||
RelationTypeManager.delete(rel_id)
|
||||
|
||||
return self.jsonify(rel_id=rel_id)
|
||||
|
0
cmdb-api/api/views/common_setting/__init__.py
Normal file
0
cmdb-api/api/views/common_setting/__init__.py
Normal file
43
cmdb-api/api/views/common_setting/company_info.py
Normal file
43
cmdb-api/api/views/common_setting/company_info.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
from flask import abort
|
||||
from flask import request
|
||||
|
||||
from api.lib.common_setting.company_info import CompanyInfoCRUD
|
||||
from api.lib.common_setting.resp_format import ErrFormat
|
||||
from api.resource import APIView
|
||||
|
||||
prefix = '/company'
|
||||
|
||||
|
||||
class CompanyInfoView(APIView):
|
||||
url_prefix = (f'{prefix}/info',)
|
||||
|
||||
def get(self):
|
||||
return self.jsonify(CompanyInfoCRUD.get())
|
||||
|
||||
def post(self):
|
||||
info = CompanyInfoCRUD.get()
|
||||
if info:
|
||||
abort(400, ErrFormat.company_info_is_already_existed)
|
||||
data = {
|
||||
'info': {
|
||||
**request.values
|
||||
}
|
||||
}
|
||||
d = CompanyInfoCRUD.create(**data)
|
||||
res = d.to_dict()
|
||||
return self.jsonify(res)
|
||||
|
||||
|
||||
class CompanyInfoViewWithId(APIView):
|
||||
url_prefix = (f'{prefix}/info/<int:_id>',)
|
||||
|
||||
def put(self, _id):
|
||||
data = {
|
||||
'info': {
|
||||
**request.values
|
||||
}
|
||||
}
|
||||
d = CompanyInfoCRUD.update(_id, **data)
|
||||
res = d.to_dict()
|
||||
return self.jsonify(res)
|
111
cmdb-api/api/views/common_setting/department.py
Normal file
111
cmdb-api/api/views/common_setting/department.py
Normal file
@@ -0,0 +1,111 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
from flask import abort
|
||||
from flask import request
|
||||
from werkzeug.datastructures import MultiDict
|
||||
|
||||
from api.lib.common_setting.department import DepartmentCRUD
|
||||
from api.lib.common_setting.department import DepartmentTree, DepartmentForm
|
||||
from api.lib.common_setting.employee import EmployeeCRUD
|
||||
from api.lib.common_setting.resp_format import ErrFormat
|
||||
from api.resource import APIView
|
||||
|
||||
prefix = '/department'
|
||||
|
||||
|
||||
class DepartmentAllView(APIView):
|
||||
url_prefix = (f'{prefix}/all',)
|
||||
|
||||
def get(self):
|
||||
is_tree = int(request.args.get('is_tree', 1))
|
||||
|
||||
res = DepartmentTree().get_all_departments(is_tree)
|
||||
return self.jsonify(res)
|
||||
|
||||
|
||||
class DepartmentAllViewWithEmployee(APIView):
|
||||
url_prefix = (f'{prefix}/all_with_employee',)
|
||||
|
||||
def get(self):
|
||||
block = int(request.args.get('block', -1))
|
||||
try:
|
||||
res = DepartmentCRUD.get_all_departments_with_employee(block)
|
||||
return self.jsonify(res)
|
||||
except Exception as e:
|
||||
abort(500, str(e))
|
||||
|
||||
|
||||
class DepartmentView(APIView):
|
||||
url_prefix = (f'{prefix}',)
|
||||
|
||||
def get(self):
|
||||
department_parent_id = request.args.get('department_parent_id', 0)
|
||||
block = int(request.args.get('block', 0))
|
||||
|
||||
departments, department_id_list = DepartmentCRUD.get_departments_and_ids(
|
||||
department_parent_id, block)
|
||||
employees = EmployeeCRUD.get_employees_by_department_id(
|
||||
department_parent_id, block)
|
||||
|
||||
return self.jsonify(departments=departments, employees=employees)
|
||||
|
||||
def post(self):
|
||||
form = DepartmentForm(MultiDict(request.json))
|
||||
if not form.validate():
|
||||
abort(400, ','.join(['{}: {}'.format(filed, ','.join(msg))
|
||||
for filed, msg in form.errors.items()]))
|
||||
|
||||
data = DepartmentCRUD.add(**form.data)
|
||||
|
||||
return self.jsonify(data.to_dict())
|
||||
|
||||
|
||||
class DepartmentIDView(APIView):
|
||||
url_prefix = (f'{prefix}/<int:_id>',)
|
||||
|
||||
def get(self, _id):
|
||||
form = DepartmentForm(MultiDict(request.json))
|
||||
if not form.validate():
|
||||
abort(400, ','.join(['{}: {}'.format(filed, ','.join(msg))
|
||||
for filed, msg in form.errors.items()]))
|
||||
|
||||
department_parent_id = form.data.get('department_parent_id')
|
||||
if int(_id) == int(department_parent_id):
|
||||
abort(400, ErrFormat.parent_department_is_not_self)
|
||||
|
||||
data = DepartmentCRUD.edit(_id, **form.data)
|
||||
return self.jsonify(data.to_dict())
|
||||
|
||||
def delete(self, _id):
|
||||
if _id in [-1, 0]:
|
||||
abort(400, ErrFormat.delete_reserved_department_name)
|
||||
DepartmentCRUD.delete(_id)
|
||||
return self.jsonify(status='success')
|
||||
|
||||
|
||||
class DepartmentParentView(APIView):
|
||||
url_prefix = (f'{prefix}/allow_parent',)
|
||||
|
||||
def get(self):
|
||||
department_id = request.args.get('department_id', None)
|
||||
if department_id is None:
|
||||
abort(400, ErrFormat.department_id_is_required)
|
||||
|
||||
p_department_list = DepartmentCRUD.get_allow_parent_d_id_by(
|
||||
int(department_id))
|
||||
return self.jsonify(p_department_list)
|
||||
|
||||
|
||||
class DepartmentSortView(APIView):
|
||||
url_prefix = (f'{prefix}/update_sort',)
|
||||
|
||||
def put(self):
|
||||
"""
|
||||
修改部门排序,只能在同一个上级内排序
|
||||
"""
|
||||
department_list = request.json.get('department_list', None)
|
||||
if department_list is None:
|
||||
abort(400, ErrFormat.department_list_is_required)
|
||||
|
||||
result = DepartmentCRUD.update_department_sort(department_list)
|
||||
|
||||
return self.jsonify(result)
|
187
cmdb-api/api/views/common_setting/employee.py
Normal file
187
cmdb-api/api/views/common_setting/employee.py
Normal file
@@ -0,0 +1,187 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import os
|
||||
|
||||
from flask import abort, current_app, send_from_directory
|
||||
from flask import request
|
||||
from werkzeug.datastructures import MultiDict
|
||||
|
||||
from api.lib.common_setting.employee import EmployeeCRUD, EmployeeAddForm, EmployeeUpdateByUidForm
|
||||
from api.lib.common_setting.resp_format import ErrFormat
|
||||
from api.resource import APIView
|
||||
|
||||
prefix = '/employee'
|
||||
|
||||
|
||||
class EmployeeView(APIView):
|
||||
url_prefix = (f'{prefix}',)
|
||||
|
||||
def get(self):
|
||||
department_id = int(request.args.get('department_id', 0))
|
||||
page = int(request.args.get('page', 1))
|
||||
page_size = int(request.args.get('page_size', 10))
|
||||
search = request.args.get('search', '')
|
||||
order = request.args.get('order', '')
|
||||
block_status = int(request.args.get('block_status', -1))
|
||||
|
||||
employee_list = EmployeeCRUD.get_employee_list_by(
|
||||
department_id, block_status, search, order, page, page_size)
|
||||
|
||||
return self.jsonify(employee_list)
|
||||
|
||||
def post(self):
|
||||
form = EmployeeAddForm(MultiDict(request.json))
|
||||
if not form.validate():
|
||||
abort(400, ','.join(['{}: {}'.format(filed, ','.join(msg))
|
||||
for filed, msg in form.errors.items()]))
|
||||
|
||||
data = EmployeeCRUD.add(**form.data)
|
||||
return self.jsonify(data.to_dict())
|
||||
|
||||
|
||||
class EmployeeFilterView(APIView):
|
||||
url_prefix = (f'{prefix}/filter',)
|
||||
|
||||
def post(self):
|
||||
params = request.json
|
||||
department_id = int(params.get('department_id', 0))
|
||||
page = int(params.get('page', 1))
|
||||
page_size = int(params.get('page_size', 10))
|
||||
search = params.get('search', '')
|
||||
order = params.get('order', '')
|
||||
block_status = int(params.get('block_status', -1))
|
||||
conditions = list(params.get("conditions", []))
|
||||
employee_list = EmployeeCRUD.get_employee_list_by_body(department_id, block_status, search, order, conditions,
|
||||
page, page_size)
|
||||
|
||||
return self.jsonify(employee_list)
|
||||
|
||||
|
||||
class EmployeeViewWithId(APIView):
|
||||
url_prefix = (f'{prefix}/<int:_id>',)
|
||||
|
||||
def get(self, _id):
|
||||
data = EmployeeCRUD.get_employee_by_id(_id)
|
||||
return self.jsonify(data.to_dict())
|
||||
|
||||
def put(self, _id):
|
||||
params = request.json
|
||||
direct_supervisor_id = params.get('direct_supervisor_id', None)
|
||||
if direct_supervisor_id and int(_id) == int(direct_supervisor_id):
|
||||
abort(400, ErrFormat.direct_supervisor_is_not_self)
|
||||
|
||||
data = EmployeeCRUD.update(_id, **params)
|
||||
return self.jsonify(data.to_dict())
|
||||
|
||||
|
||||
class EmployeeCountView(APIView):
|
||||
url_prefix = (f'{prefix}/count',)
|
||||
|
||||
def get(self):
|
||||
block_status = int(request.args.get('block_status', -1))
|
||||
employee_count = EmployeeCRUD.get_employee_count(block_status)
|
||||
return self.jsonify(employee_count=employee_count)
|
||||
|
||||
|
||||
class EmployeeImportView(APIView):
|
||||
url_prefix = (f'{prefix}/import',)
|
||||
|
||||
def post(self):
|
||||
employee_list = request.json.get('employee_list', [])
|
||||
if not employee_list:
|
||||
abort(400, ErrFormat.employee_list_is_empty)
|
||||
result = EmployeeCRUD.import_employee(employee_list)
|
||||
return self.jsonify(result)
|
||||
|
||||
|
||||
class EmployeeBatchView(APIView):
|
||||
url_prefix = (f'{prefix}/batch',)
|
||||
|
||||
def post(self):
|
||||
params = request.json
|
||||
column_name = params.get('column_name', None)
|
||||
employee_id_list = params.get('employee_id_list', None)
|
||||
column_value = params.get('column_value', None)
|
||||
if column_name not in ['department_id', 'direct_supervisor_id', 'position_name', 'password', 'block']:
|
||||
abort(400, ErrFormat.column_name_not_support)
|
||||
result = EmployeeCRUD.batch_employee(
|
||||
column_name, column_value, employee_id_list)
|
||||
return self.jsonify(result)
|
||||
|
||||
|
||||
class EmployeeViewWithACLID(APIView):
|
||||
url_prefix = (f'{prefix}/by_uid/<int:_uid>',)
|
||||
|
||||
def get(self, _uid):
|
||||
result = EmployeeCRUD.get_employee_by_uid_with_create(_uid)
|
||||
return self.jsonify(result)
|
||||
|
||||
def put(self, _uid):
|
||||
form = EmployeeUpdateByUidForm(MultiDict(request.json))
|
||||
if not form.validate():
|
||||
abort(400, ','.join(['{}: {}'.format(filed, ','.join(msg))
|
||||
for filed, msg in form.errors.items()]))
|
||||
|
||||
data = EmployeeCRUD.edit_employee_by_uid(_uid, **form.data)
|
||||
return self.jsonify(data.to_dict())
|
||||
|
||||
|
||||
class EmployeeChangePasswordWithACLID(APIView):
|
||||
url_prefix = (f'{prefix}/by_uid/change_password/<int:_uid>',)
|
||||
|
||||
def put(self, _uid):
|
||||
password = request.json.get('password', None)
|
||||
if not password:
|
||||
abort(400, ErrFormat.password_is_required)
|
||||
|
||||
data = EmployeeCRUD.change_password_by_uid(_uid, password)
|
||||
return self.jsonify(200)
|
||||
|
||||
|
||||
class EmployeePositionView(APIView):
|
||||
url_prefix = (f'{prefix}/position',)
|
||||
|
||||
def get(self):
|
||||
""""""
|
||||
result = EmployeeCRUD.get_all_position()
|
||||
return self.jsonify(result)
|
||||
|
||||
|
||||
class EmployeeViewExportExcel(APIView):
|
||||
url_prefix = (f'{prefix}/export_all',)
|
||||
|
||||
def get(self):
|
||||
col_desc_map = {
|
||||
'nickname': "姓名",
|
||||
'email': '邮箱',
|
||||
'sex': '性别',
|
||||
'mobile': '手机号',
|
||||
'department_name': '部门',
|
||||
'position_name': '岗位',
|
||||
'nickname_direct_supervisor': '直接上级',
|
||||
'last_login': '上次登录时间',
|
||||
}
|
||||
|
||||
# 规定了静态文件的存储位置
|
||||
excel_filename = 'all_employee_info.xlsx'
|
||||
excel_path = current_app.config['UPLOAD_DIRECTORY_FULL']
|
||||
excel_path_with_filename = os.path.join(excel_path, excel_filename)
|
||||
|
||||
# 根据parameter查表,自连接通过上级id获取上级名字列
|
||||
block_status = int(request.args.get('block_status', -1))
|
||||
df = EmployeeCRUD.get_export_employee_df(block_status)
|
||||
|
||||
# 改变列名为中文head
|
||||
try:
|
||||
df = df.rename(columns=col_desc_map)
|
||||
except Exception as e:
|
||||
abort(500, ErrFormat.rename_columns_failed.format(str(e)))
|
||||
|
||||
# 生成静态excel文件
|
||||
try:
|
||||
df.to_excel(excel_path_with_filename,
|
||||
sheet_name='Sheet1', index=False, encoding="utf-8")
|
||||
except Exception as e:
|
||||
current_app.logger.error(e)
|
||||
abort(500, ErrFormat.generate_excel_failed.format(str(e)))
|
||||
|
||||
return send_from_directory(excel_path, excel_filename, as_attachment=True)
|
63
cmdb-api/api/views/common_setting/file_manage.py
Normal file
63
cmdb-api/api/views/common_setting/file_manage.py
Normal file
@@ -0,0 +1,63 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import os
|
||||
|
||||
from flask import request, abort, current_app, send_from_directory
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from api.lib.common_setting.resp_format import ErrFormat
|
||||
from api.lib.common_setting.upload_file import allowed_file, generate_new_file_name
|
||||
from api.resource import APIView
|
||||
|
||||
prefix = '/file'
|
||||
|
||||
ALLOWED_EXTENSIONS = {
|
||||
'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'xls', 'xlsx', 'doc', 'docx', 'ppt', 'pptx', 'csv'
|
||||
}
|
||||
|
||||
|
||||
class FileExtensionAllowView(APIView):
|
||||
url_prefix = (f'{prefix}/allow_extensions',)
|
||||
|
||||
def get(self):
|
||||
extensions = current_app.config.get('ALLOWED_EXTENSIONS', ALLOWED_EXTENSIONS)
|
||||
extensions = list(extensions)
|
||||
return self.jsonify(extensions)
|
||||
|
||||
|
||||
class GetFileView(APIView):
|
||||
url_prefix = (f'{prefix}/<string:_filename>',)
|
||||
|
||||
def get(self, _filename):
|
||||
return send_from_directory(current_app.config['UPLOAD_DIRECTORY_FULL'], _filename, as_attachment=True)
|
||||
|
||||
|
||||
class PostFileView(APIView):
|
||||
url_prefix = (f'{prefix}',)
|
||||
|
||||
def post(self):
|
||||
# check if the post request has the file part
|
||||
if 'file' not in request.files:
|
||||
abort(400, ErrFormat.no_file_part)
|
||||
|
||||
file = request.files['file']
|
||||
|
||||
if not file:
|
||||
abort(400, ErrFormat.file_is_required)
|
||||
extension = file.mimetype.split('/')[-1]
|
||||
if file.filename == '':
|
||||
filename = f'.{extension}'
|
||||
else:
|
||||
if extension not in file.filename:
|
||||
filename = file.filename + f".{extension}"
|
||||
else:
|
||||
filename = file.filename
|
||||
|
||||
if allowed_file(filename, current_app.config.get('ALLOWED_EXTENSIONS', ALLOWED_EXTENSIONS)):
|
||||
filename = generate_new_file_name(filename)
|
||||
filename = secure_filename(filename)
|
||||
file.save(os.path.join(
|
||||
current_app.config['UPLOAD_DIRECTORY_FULL'], filename))
|
||||
|
||||
return self.jsonify(file_name=filename)
|
||||
|
||||
abort(400, 'Extension not allow')
|
33
cmdb-api/api/views/entry.py
Normal file
33
cmdb-api/api/views/entry.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import os
|
||||
|
||||
from flask import Blueprint
|
||||
from flask_restful import Api
|
||||
|
||||
from api.resource import register_resources
|
||||
from .account import LoginView, LogoutView, AuthWithKeyView
|
||||
|
||||
HERE = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
# account
|
||||
blueprint_account = Blueprint('account_api', __name__, url_prefix='/api')
|
||||
account_rest = Api(blueprint_account)
|
||||
account_rest.add_resource(LoginView, LoginView.url_prefix)
|
||||
account_rest.add_resource(LogoutView, LogoutView.url_prefix)
|
||||
account_rest.add_resource(AuthWithKeyView, AuthWithKeyView.url_prefix)
|
||||
|
||||
# cmdb
|
||||
blueprint_cmdb_v01 = Blueprint('cmdb_api_v01', __name__, url_prefix='/api/v0.1')
|
||||
rest = Api(blueprint_cmdb_v01)
|
||||
register_resources(os.path.join(HERE, "cmdb"), rest)
|
||||
|
||||
# acl
|
||||
blueprint_acl_v1 = Blueprint('acl_api_v1', __name__, url_prefix='/api/v1/acl')
|
||||
rest = Api(blueprint_acl_v1)
|
||||
register_resources(os.path.join(HERE, "acl"), rest)
|
||||
|
||||
# common_setting
|
||||
blueprint_cs_v1 = Blueprint('common_setting_api_v1', __name__, url_prefix='/api/common-setting/v1')
|
||||
rest = Api(blueprint_cs_v1)
|
||||
register_resources(os.path.join(HERE, "common_setting"), rest)
|
Reference in New Issue
Block a user