Skip to content

Commit 3d8890d

Browse files
authored
Merge pull request #325 from WeBankPartners/dev_v1.3.0
Dev v1.3.0
2 parents 54e05a9 + f48c5f1 commit 3d8890d

39 files changed

+2784
-357
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ yarn-debug.log*
3838
yarn-error.log*
3939
/*/target/
4040
/*/dist/
41+
/*/.env
4142

4243

4344
*.pyc

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ package_py: image_py
2727
mkdir -p package
2828
cd package && docker save $(project_name):$(version) -o image.tar
2929
cd package && cp ../register.xml .
30+
cd package && cp ../init.sql ./init.sql
3031
cd package && sed -i "s~{{REPOSITORY}}~$(project_name)~g" register.xml
3132
cd package && sed -i "s~{{VERSION}}~$(version)~g" register.xml
3233
cd artifacts-ui/dist && zip -r ui.zip .
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import absolute_import
3+
4+
from artifacts_corepy.apps.auth import route
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import absolute_import
3+
4+
import logging
5+
import time
6+
7+
import jwt
8+
from talos.core import config, utils
9+
from talos.core.i18n import _
10+
from talos.core import exceptions as core_ex
11+
12+
from artifacts_corepy.common import exceptions
13+
from artifacts_corepy.common import utils as terminal_utils
14+
from artifacts_corepy.db import resource as db_resource
15+
16+
CONF = config.CONF
17+
LOG = logging.getLogger(__name__)
18+
19+
20+
class SysUser(db_resource.SysUser):
21+
def generate_tokens(self, rid):
22+
roles = self.get_roles(rid)
23+
tokens = []
24+
current_time = int(time.time() * 1000)
25+
access_token_iat = int(current_time / 1000)
26+
access_token_exp = access_token_iat + CONF.access_token_exipres
27+
refresh_token_exp = access_token_iat + CONF.refresh_token_exipres
28+
decoded_secret = terminal_utils.b64decode_key(CONF.jwt_signing_key)
29+
tokens.append({
30+
"expiration": str(current_time + CONF.access_token_exipres * 1000),
31+
"token": jwt.encode({
32+
"sub": rid,
33+
"iat": access_token_iat,
34+
"type": "accessToken",
35+
"clientType": "USER",
36+
"exp": access_token_exp,
37+
"authority": "[" + ','.join([r['id'] for r in roles]) + "]"
38+
}, decoded_secret, "HS512").decode(),
39+
"tokenType": "accessToken"
40+
})
41+
tokens.append({
42+
"expiration": str(current_time + CONF.refresh_token_exipres * 1000),
43+
"token": jwt.encode({
44+
"sub": rid,
45+
"iat": access_token_iat,
46+
"type": "refreshToken",
47+
"clientType": "USER",
48+
"exp": refresh_token_exp
49+
}, decoded_secret, "HS512",
50+
).decode(),
51+
"tokenType": "refreshToken"
52+
})
53+
return tokens
54+
55+
def login(self, username, password):
56+
with self.get_session():
57+
if self.check_password(username, password):
58+
return self.generate_tokens(username)
59+
else:
60+
raise core_ex.LoginError()
61+
62+
def refresh(self, token):
63+
with self.get_session():
64+
try:
65+
decoded_secret = terminal_utils.b64decode_key(CONF.jwt_signing_key)
66+
info = jwt.decode(token, key=decoded_secret, verify=True)
67+
if info['type'] != 'refreshToken':
68+
raise core_ex.AuthError()
69+
return self.generate_tokens(info['sub'])
70+
except jwt.exceptions.ExpiredSignatureError:
71+
raise core_ex.AuthError()
72+
except jwt.exceptions.DecodeError:
73+
raise core_ex.AuthError()
74+
75+
def get_menus(self, rid):
76+
menus = []
77+
exists = {}
78+
roles = self.get_roles(rid)
79+
for role in roles:
80+
for menu in role['menus']:
81+
if menu['is_active'] == 'yes' and menu['id'] not in exists:
82+
menus.append(menu)
83+
exists[menu['id']] = True
84+
return menus
85+
86+
def get_roles(self, rid):
87+
ref = self.get(rid)
88+
if ref:
89+
return ref['roles']
90+
return []
91+
92+
def create(self, resource, validate=True, detail=True):
93+
resource['salt'] = utils.generate_salt(16)
94+
password = utils.generate_salt(16)
95+
resource['password'] = utils.encrypt_password(password, resource['salt'])
96+
ref = super().create(resource, validate=validate, detail=detail)
97+
ref['password'] = password
98+
return ref
99+
100+
def reset_password(self, rid, password=None):
101+
resource = {}
102+
resource['salt'] = utils.generate_salt(16)
103+
password = password or utils.generate_salt(16)
104+
resource['password'] = utils.encrypt_password(password, resource['salt'])
105+
before_update, after_update = self.update(rid, resource, validate=False)
106+
if after_update:
107+
after_update['password'] = password
108+
return after_update
109+
110+
def check_password(self, rid, password):
111+
refs = self.list_internal({'id': rid})
112+
if refs:
113+
return utils.check_password(refs[0]['password'], password, refs[0]['salt'])
114+
return False
115+
116+
def update_password(self, rid, password, origin_password):
117+
if not password:
118+
raise exceptions.PluginError(message=_('unabled to set empty password'))
119+
if self.check_password(rid, origin_password):
120+
resource = {}
121+
resource['salt'] = utils.generate_salt(16)
122+
password = password or utils.generate_salt(16)
123+
resource['password'] = utils.encrypt_password(password, resource['salt'])
124+
before_update, after_update = self.update(rid, resource, validate=False)
125+
return after_update
126+
else:
127+
raise exceptions.PluginError(message=_('faild to set new password: incorrect origin password'))
128+
129+
def delete(self, rid, filters=None, detail=True):
130+
refs = self.list({'id': rid})
131+
if refs and refs[0]['is_system'] == 'yes':
132+
raise exceptions.PluginError(message=_('unable to delete system user'))
133+
with self.transaction() as session:
134+
db_resource.SysRoleUser(transaction=session).delete_all({'user_id': rid})
135+
return super().delete(rid, filters=filters, detail=detail)
136+
137+
138+
class SysRole(db_resource.SysRole):
139+
def get_users(self, rid):
140+
ref = self.get(rid)
141+
if ref:
142+
return ref['users']
143+
return []
144+
145+
def _update_intersect_refs(self, rid, self_field, ref_field, resource_type, refs, session):
146+
old_refs = [result[ref_field] for result in resource_type(session=session).list(filters={self_field: rid})]
147+
create_refs = list(set(refs) - set(old_refs))
148+
create_refs.sort(key=refs.index)
149+
delete_refs = set(old_refs) - set(refs)
150+
if delete_refs:
151+
resource_type(transaction=session).delete_all(filters={
152+
self_field: rid,
153+
ref_field: {
154+
'in': list(delete_refs)
155+
}
156+
})
157+
for ref in create_refs:
158+
new_ref = {}
159+
new_ref[self_field] = rid
160+
new_ref[ref_field] = ref
161+
resource_type(transaction=session).create(new_ref)
162+
163+
def set_users(self, rid, users):
164+
with self.transaction() as session:
165+
self._update_intersect_refs(rid, 'role_id', 'user_id', db_resource.SysRoleUser, users, session)
166+
return self.get_users(rid)
167+
168+
def get_menus(self, rid, is_active=True):
169+
ref = self.get(rid)
170+
if ref:
171+
if is_active:
172+
return [menu for menu in ref['menus'] if menu['is_active'] == 'yes']
173+
else:
174+
return ref['menus']
175+
return []
176+
177+
def set_menus(self, rid, menus):
178+
with self.transaction() as session:
179+
self._update_intersect_refs(rid, 'role_id', 'menu_id', db_resource.SysRoleMenu, menus, session)
180+
return self.get_menus(rid, is_active=False)
181+
182+
def delete(self, rid, filters=None, detail=True):
183+
refs = self.list({'id': rid})
184+
if refs and refs[0]['is_system'] == 'yes':
185+
raise exceptions.PluginError(message=_('unable to delete system role'))
186+
with self.transaction() as session:
187+
bindings = db_resource.SysRoleUser(transaction=session).list({'role_id': rid})
188+
if len(bindings) > 0:
189+
users = ','.join([bind['user_id'] for bind in bindings])
190+
raise exceptions.PluginError(message=_('role binds with %(users)s') % {'users': users})
191+
return super().delete(rid, filters=filters, detail=detail)
192+
193+
194+
class SysMenu(db_resource.SysMenu):
195+
pass
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import absolute_import
3+
4+
from talos.core.i18n import _
5+
from talos.utils.scoped_globals import GLOBALS
6+
7+
from artifacts_corepy.common import exceptions
8+
from artifacts_corepy.common.mixin import Controller, CollectionController as Collection, ItemController as Item
9+
from artifacts_corepy.apps.auth import api as auth_api
10+
11+
12+
class Token(Controller):
13+
name = 'auth.token'
14+
resource = auth_api.SysUser
15+
allow_methods = ('POST',)
16+
17+
def create(self, req, data, **kwargs):
18+
return self.make_resource(req).login(data.get('username', ''), data.get('password', ''))
19+
20+
21+
class TokenRefresh(Controller):
22+
name = 'auth.token'
23+
resource = auth_api.SysUser
24+
allow_methods = ('GET',)
25+
26+
def get(self, req, **kwargs):
27+
return self.make_resource(req).refresh(GLOBALS.request.auth_token)
28+
29+
30+
class UserMenus(Controller):
31+
name = 'auth.user-menus'
32+
resource = auth_api.SysUser
33+
allow_methods = ('GET',)
34+
35+
def get(self, req, **kwargs):
36+
return self.make_resource(req).get_menus(GLOBALS.request.auth_user)
37+
38+
39+
class UserPassword(Controller):
40+
name = 'auth.user-password'
41+
resource = auth_api.SysUser
42+
allow_methods = ('POST',)
43+
44+
def create(self, req, data, **kwargs):
45+
return self.make_resource(req).update_password(GLOBALS.request.auth_user, data.get('newPassword', ''),
46+
data.get('oldPassword', ''))
47+
48+
49+
class User(Collection):
50+
name = 'auth.users'
51+
resource = auth_api.SysUser
52+
53+
54+
class UserItem(Item):
55+
name = 'auth.users'
56+
resource = auth_api.SysUser
57+
58+
59+
class UserItemMenu(Item):
60+
name = 'auth.users'
61+
resource = auth_api.SysUser
62+
allow_methods = ('GET',)
63+
64+
def get(self, req, rid):
65+
return self.make_resource(req).get_menus(rid)
66+
67+
68+
class UserItemResetPassword(Controller):
69+
name = 'auth.users.password'
70+
resource = auth_api.SysUser
71+
allow_methods = ('POST',)
72+
73+
def create(self, req, data, rid):
74+
data = data or {}
75+
return self.make_resource(req).reset_password(rid, password=data.get('password', None))
76+
77+
78+
class UserItemRole(Item):
79+
name = 'auth.users'
80+
resource = auth_api.SysUser
81+
allow_methods = ('GET',)
82+
83+
def get(self, req, rid):
84+
return self.make_resource(req).get_roles(rid)
85+
86+
87+
class Role(Collection):
88+
name = 'auth.roles'
89+
resource = auth_api.SysRole
90+
91+
92+
class RoleItem(Item):
93+
name = 'auth.roles'
94+
resource = auth_api.SysRole
95+
96+
97+
class RoleItemMenu(Controller):
98+
name = 'auth.roles'
99+
resource = auth_api.SysRole
100+
allow_methods = ('GET', 'POST')
101+
102+
def get(self, req, rid):
103+
return self.make_resource(req).get_menus(rid)
104+
105+
def create(self, req, data, rid):
106+
return self.make_resource(req).set_menus(rid, data)
107+
108+
109+
class RoleItemUser(Controller):
110+
name = 'auth.roles'
111+
resource = auth_api.SysRole
112+
allow_methods = ('GET', 'POST')
113+
114+
def get(self, req, rid):
115+
return self.make_resource(req).get_users(rid)
116+
117+
def create(self, req, data, rid):
118+
return self.make_resource(req).set_users(rid, data)
119+
120+
121+
class Menu(Collection):
122+
name = 'auth.menus'
123+
resource = auth_api.SysMenu
124+
allow_methods = ('GET',)
125+
126+
127+
class MenuItem(Item):
128+
name = 'auth.menus'
129+
resource = auth_api.SysMenu
130+
allow_methods = ('GET',)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import absolute_import
3+
4+
from artifacts_corepy.apps.auth import controller
5+
6+
7+
def add_routes(api):
8+
# 不需要登陆
9+
api.add_route('/artifacts/v1/login', controller.Token())
10+
api.add_route('/artifacts/v1/refresh-token', controller.TokenRefresh())
11+
# 登陆但不需要权限校验
12+
api.add_route('/artifacts/v1/user-menus', controller.UserMenus())
13+
api.add_route('/artifacts/v1/user-password', controller.UserPassword())
14+
# 登陆且经过权限校验
15+
api.add_route('/artifacts/v1/users', controller.User())
16+
api.add_route('/artifacts/v1/users/{rid}', controller.UserItem())
17+
api.add_route('/artifacts/v1/users/{rid}/reset-password', controller.UserItemResetPassword())
18+
api.add_route('/artifacts/v1/users/{rid}/menus', controller.UserItemMenu())
19+
api.add_route('/artifacts/v1/users/{rid}/roles', controller.UserItemRole())
20+
api.add_route('/artifacts/v1/roles', controller.Role())
21+
api.add_route('/artifacts/v1/roles/{rid}', controller.RoleItem())
22+
api.add_route('/artifacts/v1/roles/{rid}/menus', controller.RoleItemMenu())
23+
api.add_route('/artifacts/v1/roles/{rid}/users', controller.RoleItemUser())
24+
api.add_route('/artifacts/v1/menus', controller.Menu())
25+
# api.add_route('/artifacts/v1/menus/{rid}', controller.MenuItem())

0 commit comments

Comments
 (0)