mirror of https://github.com/veops/cmdb.git
commit
6a7bb725cc
|
@ -17,6 +17,9 @@
|
||||||
- username: admin
|
- username: admin
|
||||||
- password: admin
|
- password: admin
|
||||||
|
|
||||||
|
> **重要提示**: `master` 分支在开发过程中可能处于 *不稳定的状态* 。
|
||||||
|
请通过[releases](https://github.com/pycook/cmdb/releases)获取
|
||||||
|
|
||||||
Overview
|
Overview
|
||||||
----
|
----
|
||||||

|

|
||||||
|
|
|
@ -10,9 +10,14 @@ from api.lib.exception import CommitException
|
||||||
|
|
||||||
class FormatMixin(object):
|
class FormatMixin(object):
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return dict([(k.name,
|
res = dict()
|
||||||
getattr(self, k.name) if not isinstance(getattr(self, k.name), datetime.datetime) else getattr(
|
for k in getattr(self, "__table__").columns:
|
||||||
self, k.name).strftime('%Y-%m-%d %H:%M:%S')) for k in getattr(self, "__table__").columns])
|
if not isinstance(getattr(self, k.name), datetime.datetime):
|
||||||
|
res[k.name] = getattr(self, k.name)
|
||||||
|
else:
|
||||||
|
res[k.name] = getattr(self, k.name).strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_columns(cls):
|
def get_columns(cls):
|
||||||
|
|
|
@ -1 +1,23 @@
|
||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
from flask import request
|
||||||
|
from flask import abort
|
||||||
|
|
||||||
|
from api.lib.perm.acl.cache import AppCache
|
||||||
|
|
||||||
|
|
||||||
|
def validate_app(func):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
app_id = request.values.get('app_id')
|
||||||
|
app = AppCache.get(app_id)
|
||||||
|
if app is None:
|
||||||
|
return abort(400, "App <{0}> does not exist".format(app_id))
|
||||||
|
request.values['app_id'] = app.id
|
||||||
|
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
|
@ -1,11 +1,37 @@
|
||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
from api.extensions import cache
|
from api.extensions import cache
|
||||||
|
from api.models.acl import App
|
||||||
from api.models.acl import Permission
|
from api.models.acl import Permission
|
||||||
from api.models.acl import Role
|
from api.models.acl import Role
|
||||||
from api.models.acl import User
|
from api.models.acl import User
|
||||||
|
|
||||||
|
|
||||||
|
class AppCache(object):
|
||||||
|
PREFIX_ID = "App::id::{0}"
|
||||||
|
PREFIX_NAME = "App::name::{0}"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, key):
|
||||||
|
app = cache.get(cls.PREFIX_ID.format(key)) or cache.get(cls.PREFIX_NAME.format(key))
|
||||||
|
if app is None:
|
||||||
|
app = App.get_by_id(key) or App.get_by(name=key, to_dict=False, first=True)
|
||||||
|
if app is not None:
|
||||||
|
cls.set(app)
|
||||||
|
|
||||||
|
return app
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set(cls, app):
|
||||||
|
cache.set(cls.PREFIX_ID.format(app.id), app)
|
||||||
|
cache.set(cls.PREFIX_NAME.format(app.name), app)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clean(cls, app):
|
||||||
|
cache.delete(cls.PREFIX_ID.format(app.id))
|
||||||
|
cache.delete(cls.PREFIX_NAME.format(app.name))
|
||||||
|
|
||||||
|
|
||||||
class UserCache(object):
|
class UserCache(object):
|
||||||
PREFIX_ID = "User::uid::{0}"
|
PREFIX_ID = "User::uid::{0}"
|
||||||
PREFIX_NAME = "User::username::{0}"
|
PREFIX_NAME = "User::username::{0}"
|
||||||
|
|
|
@ -156,6 +156,3 @@ class ResourceCRUD(object):
|
||||||
resource = Resource.get_by_id(_id) or abort(404, "Resource <{0}> is not found".format(_id))
|
resource = Resource.get_by_id(_id) or abort(404, "Resource <{0}> is not found".format(_id))
|
||||||
|
|
||||||
resource.soft_delete()
|
resource.soft_delete()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
import string
|
||||||
|
import random
|
||||||
|
|
||||||
from flask import abort
|
from flask import abort
|
||||||
|
from flask import g
|
||||||
|
|
||||||
from api.extensions import db
|
from api.extensions import db
|
||||||
from api.lib.perm.acl.cache import UserCache
|
from api.lib.perm.acl.cache import UserCache
|
||||||
|
@ -20,12 +25,21 @@ class UserCRUD(object):
|
||||||
return numfound, query.offset((page - 1) * page_size).limit(page_size)
|
return numfound, query.offset((page - 1) * page_size).limit(page_size)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add(**kwargs):
|
def _gen_key_secret():
|
||||||
|
key = uuid.uuid4().hex
|
||||||
|
secret = ''.join(random.sample(string.ascii_letters + string.digits + '~!@#$%^&*?', 32))
|
||||||
|
|
||||||
|
return key, secret
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add(cls, **kwargs):
|
||||||
existed = User.get_by(username=kwargs['username'], email=kwargs['email'])
|
existed = User.get_by(username=kwargs['username'], email=kwargs['email'])
|
||||||
existed and abort(400, "User <{0}> is already existed".format(kwargs['username']))
|
existed and abort(400, "User <{0}> is already existed".format(kwargs['username']))
|
||||||
|
|
||||||
kwargs['nickname'] = kwargs['username'] if not kwargs.get('nickname') else kwargs['nickname']
|
kwargs['nickname'] = kwargs.get('nickname') or kwargs['username']
|
||||||
kwargs['block'] = 0
|
kwargs['block'] = 0
|
||||||
|
kwargs['key'], kwargs['secret'] = cls._gen_key_secret()
|
||||||
|
|
||||||
return User.create(**kwargs)
|
return User.create(**kwargs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -36,6 +50,13 @@ class UserCRUD(object):
|
||||||
|
|
||||||
return user.update(**kwargs)
|
return user.update(**kwargs)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def reset_key_secret(cls):
|
||||||
|
key, secret = cls._gen_key_secret()
|
||||||
|
g.user.update(key=key, secret=secret)
|
||||||
|
|
||||||
|
return key, secret
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def delete(cls, uid):
|
def delete(cls, uid):
|
||||||
user = User.get_by_id(uid) or abort(404, "User <{0}> does not exist".format(uid))
|
user = User.get_by_id(uid) or abort(404, "User <{0}> does not exist".format(uid))
|
||||||
|
|
|
@ -11,6 +11,7 @@ from flask_sqlalchemy import BaseQuery
|
||||||
from api.extensions import db
|
from api.extensions import db
|
||||||
from api.lib.database import CRUDModel
|
from api.lib.database import CRUDModel
|
||||||
from api.lib.database import Model
|
from api.lib.database import Model
|
||||||
|
from api.lib.database import SoftDeleteMixin
|
||||||
|
|
||||||
|
|
||||||
class App(Model):
|
class App(Model):
|
||||||
|
@ -34,6 +35,7 @@ class UserQuery(BaseQuery):
|
||||||
authenticated = user.check_password(password)
|
authenticated = user.check_password(password)
|
||||||
else:
|
else:
|
||||||
authenticated = False
|
authenticated = False
|
||||||
|
|
||||||
return user, authenticated
|
return user, authenticated
|
||||||
|
|
||||||
def authenticate_with_key(self, key, secret, args, path):
|
def authenticate_with_key(self, key, secret, args, path):
|
||||||
|
@ -45,6 +47,7 @@ class UserQuery(BaseQuery):
|
||||||
authenticated = True
|
authenticated = True
|
||||||
else:
|
else:
|
||||||
authenticated = False
|
authenticated = False
|
||||||
|
|
||||||
return user, authenticated
|
return user, authenticated
|
||||||
|
|
||||||
def search(self, key):
|
def search(self, key):
|
||||||
|
@ -55,18 +58,21 @@ class UserQuery(BaseQuery):
|
||||||
|
|
||||||
def get_by_username(self, username):
|
def get_by_username(self, username):
|
||||||
user = self.filter(User.username == username).first()
|
user = self.filter(User.username == username).first()
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
def get_by_nickname(self, nickname):
|
def get_by_nickname(self, nickname):
|
||||||
user = self.filter(User.nickname == nickname).first()
|
user = self.filter(User.nickname == nickname).first()
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
def get(self, uid):
|
def get(self, uid):
|
||||||
user = self.filter(User.uid == uid).first()
|
user = self.filter(User.uid == uid).first()
|
||||||
|
|
||||||
return copy.deepcopy(user)
|
return copy.deepcopy(user)
|
||||||
|
|
||||||
|
|
||||||
class User(CRUDModel):
|
class User(CRUDModel, SoftDeleteMixin):
|
||||||
__tablename__ = 'users'
|
__tablename__ = 'users'
|
||||||
# __bind_key__ = "user"
|
# __bind_key__ = "user"
|
||||||
query_class = UserQuery
|
query_class = UserQuery
|
||||||
|
@ -107,9 +113,7 @@ class User(CRUDModel):
|
||||||
def _set_password(self, password):
|
def _set_password(self, password):
|
||||||
self._password = hashlib.md5(password.encode('utf-8')).hexdigest()
|
self._password = hashlib.md5(password.encode('utf-8')).hexdigest()
|
||||||
|
|
||||||
password = db.synonym("_password",
|
password = db.synonym("_password", descriptor=property(_get_password, _set_password))
|
||||||
descriptor=property(_get_password,
|
|
||||||
_set_password))
|
|
||||||
|
|
||||||
def check_password(self, password):
|
def check_password(self, password):
|
||||||
if self.password is None:
|
if self.password is None:
|
||||||
|
|
|
@ -1,3 +1 @@
|
||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
__author__ = 'pycook'
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
from flask import request
|
from flask import request
|
||||||
|
|
||||||
from api.lib.decorator import args_required
|
from api.lib.decorator import args_required
|
||||||
|
from api.lib.perm.acl import validate_app
|
||||||
from api.lib.perm.acl.resource import ResourceCRUD
|
from api.lib.perm.acl.resource import ResourceCRUD
|
||||||
from api.lib.perm.acl.resource import ResourceGroupCRUD
|
from api.lib.perm.acl.resource import ResourceGroupCRUD
|
||||||
from api.lib.utils import get_page
|
from api.lib.utils import get_page
|
||||||
|
@ -15,6 +16,7 @@ class ResourceView(APIView):
|
||||||
url_prefix = ("/resources", "/resources/<int:resource_id>")
|
url_prefix = ("/resources", "/resources/<int:resource_id>")
|
||||||
|
|
||||||
@args_required('app_id')
|
@args_required('app_id')
|
||||||
|
@validate_app
|
||||||
def get(self):
|
def get(self):
|
||||||
page = get_page(request.values.get("page", 1))
|
page = get_page(request.values.get("page", 1))
|
||||||
page_size = get_page_size(request.values.get("page_size"))
|
page_size = get_page_size(request.values.get("page_size"))
|
||||||
|
@ -31,6 +33,7 @@ class ResourceView(APIView):
|
||||||
@args_required('name')
|
@args_required('name')
|
||||||
@args_required('type_id')
|
@args_required('type_id')
|
||||||
@args_required('app_id')
|
@args_required('app_id')
|
||||||
|
@validate_app
|
||||||
def post(self):
|
def post(self):
|
||||||
name = request.values.get('name')
|
name = request.values.get('name')
|
||||||
type_id = request.values.get('type_id')
|
type_id = request.values.get('type_id')
|
||||||
|
@ -57,6 +60,7 @@ class ResourceView(APIView):
|
||||||
class ResourceGroupView(APIView):
|
class ResourceGroupView(APIView):
|
||||||
url_prefix = ("/resource_groups", "/resource_groups/<int:group_id>")
|
url_prefix = ("/resource_groups", "/resource_groups/<int:group_id>")
|
||||||
|
|
||||||
|
@validate_app
|
||||||
def get(self):
|
def get(self):
|
||||||
page = get_page(request.values.get("page", 1))
|
page = get_page(request.values.get("page", 1))
|
||||||
page_size = get_page_size(request.values.get("page_size"))
|
page_size = get_page_size(request.values.get("page_size"))
|
||||||
|
@ -73,6 +77,7 @@ class ResourceGroupView(APIView):
|
||||||
@args_required('name')
|
@args_required('name')
|
||||||
@args_required('type_id')
|
@args_required('type_id')
|
||||||
@args_required('app_id')
|
@args_required('app_id')
|
||||||
|
@validate_app
|
||||||
def post(self):
|
def post(self):
|
||||||
name = request.values.get('name')
|
name = request.values.get('name')
|
||||||
type_id = request.values.get('type_id')
|
type_id = request.values.get('type_id')
|
|
@ -3,6 +3,7 @@
|
||||||
from flask import request
|
from flask import request
|
||||||
|
|
||||||
from api.lib.decorator import args_required
|
from api.lib.decorator import args_required
|
||||||
|
from api.lib.perm.acl import validate_app
|
||||||
from api.lib.perm.acl.role import RoleCRUD
|
from api.lib.perm.acl.role import RoleCRUD
|
||||||
from api.lib.perm.acl.role import RoleRelationCRUD
|
from api.lib.perm.acl.role import RoleRelationCRUD
|
||||||
from api.lib.utils import get_page
|
from api.lib.utils import get_page
|
||||||
|
@ -14,6 +15,7 @@ class RoleView(APIView):
|
||||||
url_prefix = ("/roles", "/roles/<int:rid>")
|
url_prefix = ("/roles", "/roles/<int:rid>")
|
||||||
|
|
||||||
@args_required('app_id')
|
@args_required('app_id')
|
||||||
|
@validate_app
|
||||||
def get(self):
|
def get(self):
|
||||||
page = get_page(request.values.get("page", 1))
|
page = get_page(request.values.get("page", 1))
|
||||||
page_size = get_page_size(request.values.get("page_size"))
|
page_size = get_page_size(request.values.get("page_size"))
|
||||||
|
@ -32,6 +34,7 @@ class RoleView(APIView):
|
||||||
|
|
||||||
@args_required('name')
|
@args_required('name')
|
||||||
@args_required('app_id')
|
@args_required('app_id')
|
||||||
|
@validate_app
|
||||||
def post(self):
|
def post(self):
|
||||||
name = request.values.get('name')
|
name = request.values.get('name')
|
||||||
app_id = request.values.get('app_id')
|
app_id = request.values.get('app_id')
|
||||||
|
|
|
@ -6,8 +6,8 @@ from flask import session
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
|
||||||
from api.lib.decorator import args_required
|
from api.lib.decorator import args_required
|
||||||
from api.lib.perm.acl.user import UserCRUD
|
|
||||||
from api.lib.perm.acl.role import RoleRelationCRUD
|
from api.lib.perm.acl.role import RoleRelationCRUD
|
||||||
|
from api.lib.perm.acl.user import UserCRUD
|
||||||
from api.lib.utils import get_page
|
from api.lib.utils import get_page
|
||||||
from api.lib.utils import get_page_size
|
from api.lib.utils import get_page_size
|
||||||
from api.resource import APIView
|
from api.resource import APIView
|
||||||
|
@ -36,11 +36,17 @@ class UserView(APIView):
|
||||||
|
|
||||||
id2parents = RoleRelationCRUD.get_parents(uids=[i.uid for i in users])
|
id2parents = RoleRelationCRUD.get_parents(uids=[i.uid for i in users])
|
||||||
|
|
||||||
|
users = [i.to_dict() for i in users]
|
||||||
|
for u in users:
|
||||||
|
u.pop('password', None)
|
||||||
|
u.pop('key', None)
|
||||||
|
u.pop('secret', None)
|
||||||
|
|
||||||
return self.jsonify(numfound=numfound,
|
return self.jsonify(numfound=numfound,
|
||||||
page=page,
|
page=page,
|
||||||
page_size=page_size,
|
page_size=page_size,
|
||||||
id2parents=id2parents,
|
id2parents=id2parents,
|
||||||
users=[i.to_dict() for i in users])
|
users=users)
|
||||||
|
|
||||||
@args_required('username')
|
@args_required('username')
|
||||||
@args_required('email')
|
@args_required('email')
|
||||||
|
@ -58,3 +64,15 @@ class UserView(APIView):
|
||||||
UserCRUD.delete(uid)
|
UserCRUD.delete(uid)
|
||||||
|
|
||||||
return self.jsonify(uid=uid)
|
return self.jsonify(uid=uid)
|
||||||
|
|
||||||
|
|
||||||
|
class UserResetKeySecretView(APIView):
|
||||||
|
url_prefix = "/users/reset_key_secret"
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
key, secret = UserCRUD.reset_key_secret()
|
||||||
|
|
||||||
|
return self.jsonify(key=key, secret=secret)
|
||||||
|
|
||||||
|
def put(self):
|
||||||
|
return self.post()
|
||||||
|
|
Loading…
Reference in New Issue