前后端全面升级

This commit is contained in:
pycook
2023-07-10 17:42:15 +08:00
parent c444fed436
commit db5ff60aff
629 changed files with 97789 additions and 23995 deletions

View File

@@ -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)

View File

@@ -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):

View 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)

View 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,
)

View 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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View 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)

View File

@@ -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)

View File

@@ -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))

View 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)

View 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())

View File

@@ -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())

View File

@@ -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)

View File

@@ -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))

View File

@@ -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)

View 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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View 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)

View 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)

View 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)

View 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')

View 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)