From f1c2bcbd797e8d48e3ff065d7f5d02c1dd158441 Mon Sep 17 00:00:00 2001 From: pycook Date: Fri, 15 Nov 2019 16:54:56 +0800 Subject: [PATCH] update acl --- README.md | 3 +++ api/lib/database.py | 6 +++--- api/lib/perm/acl/user.py | 25 +++++++++++++++++++++++-- api/models/acl.py | 12 ++++++++---- api/views/acl/user.py | 22 ++++++++++++++++++++-- 5 files changed, 57 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index e0e475f..5a168e5 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,9 @@ - username: admin - password: admin +> **重要提示**: `master` 分支在开发过程中可能处于 *不稳定的状态* 。 +请通过[releases](https://github.com/pycook/cmdb/releases)获取 + Overview ---- ![基础资源视图](https://raw.githubusercontent.com/pycook/cmdb/master/ui/public/cmdb01.jpeg) diff --git a/api/lib/database.py b/api/lib/database.py index 82c6c62..7b50d38 100644 --- a/api/lib/database.py +++ b/api/lib/database.py @@ -10,9 +10,9 @@ from api.lib.exception import CommitException class FormatMixin(object): def to_dict(self): - return dict([(k.name, - getattr(self, k.name) if not isinstance(getattr(self, k.name), datetime.datetime) else getattr( - self, k.name).strftime('%Y-%m-%d %H:%M:%S')) for k in getattr(self, "__table__").columns]) + return dict([(k.name, isinstance(getattr(self, k.name), datetime.datetime) and + getattr(self, k.name).strftime('%Y-%m-%d %H:%M:%S')) or + getattr(self, k.name) for k in getattr(self, "__table__").columns]) @classmethod def get_columns(cls): diff --git a/api/lib/perm/acl/user.py b/api/lib/perm/acl/user.py index 5832a47..178502c 100644 --- a/api/lib/perm/acl/user.py +++ b/api/lib/perm/acl/user.py @@ -1,7 +1,12 @@ # -*- coding:utf-8 -*- +import uuid +import string +import random + from flask import abort +from flask import g from api.extensions import db 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) @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 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['key'], kwargs['secret'] = cls._gen_key_secret() + return User.create(**kwargs) @staticmethod @@ -36,6 +50,13 @@ class UserCRUD(object): 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 def delete(cls, uid): user = User.get_by_id(uid) or abort(404, "User <{0}> does not exist".format(uid)) diff --git a/api/models/acl.py b/api/models/acl.py index d1b3056..b6e43e0 100644 --- a/api/models/acl.py +++ b/api/models/acl.py @@ -11,6 +11,7 @@ from flask_sqlalchemy import BaseQuery from api.extensions import db from api.lib.database import CRUDModel from api.lib.database import Model +from api.lib.database import SoftDeleteMixin class App(Model): @@ -34,6 +35,7 @@ class UserQuery(BaseQuery): authenticated = user.check_password(password) else: authenticated = False + return user, authenticated def authenticate_with_key(self, key, secret, args, path): @@ -45,6 +47,7 @@ class UserQuery(BaseQuery): authenticated = True else: authenticated = False + return user, authenticated def search(self, key): @@ -55,18 +58,21 @@ class UserQuery(BaseQuery): def get_by_username(self, username): user = self.filter(User.username == username).first() + return user def get_by_nickname(self, nickname): user = self.filter(User.nickname == nickname).first() + return user def get(self, uid): user = self.filter(User.uid == uid).first() + return copy.deepcopy(user) -class User(CRUDModel): +class User(CRUDModel, SoftDeleteMixin): __tablename__ = 'users' __bind_key__ = "user" query_class = UserQuery @@ -107,9 +113,7 @@ class User(CRUDModel): def _set_password(self, password): self._password = hashlib.md5(password.encode('utf-8')).hexdigest() - password = db.synonym("_password", - descriptor=property(_get_password, - _set_password)) + password = db.synonym("_password", descriptor=property(_get_password, _set_password)) def check_password(self, password): if self.password is None: diff --git a/api/views/acl/user.py b/api/views/acl/user.py index 9e0d576..e564535 100644 --- a/api/views/acl/user.py +++ b/api/views/acl/user.py @@ -6,8 +6,8 @@ from flask import session from flask_login import current_user 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.user import UserCRUD from api.lib.utils import get_page from api.lib.utils import get_page_size from api.resource import APIView @@ -36,11 +36,17 @@ class UserView(APIView): 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, page=page, page_size=page_size, id2parents=id2parents, - users=[i.to_dict() for i in users]) + users=users) @args_required('username') @args_required('email') @@ -58,3 +64,15 @@ class UserView(APIView): UserCRUD.delete(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()