|
@@ -1,5 +1,9 @@
|
1
|
1
|
# -*- coding: utf-8 -*-
|
2
|
2
|
import typing
|
|
3
|
+
|
|
4
|
+from pyramid.interfaces import IAuthorizationPolicy
|
|
5
|
+from zope.interface import implementer
|
|
6
|
+
|
3
|
7
|
try:
|
4
|
8
|
from json.decoder import JSONDecodeError
|
5
|
9
|
except ImportError: # python3.4
|
|
@@ -7,112 +11,136 @@ except ImportError: # python3.4
|
7
|
11
|
from sqlalchemy.orm.exc import NoResultFound
|
8
|
12
|
|
9
|
13
|
from pyramid.request import Request
|
10
|
|
-from pyramid.security import ALL_PERMISSIONS
|
11
|
|
-from pyramid.security import Allow
|
12
|
|
-from pyramid.security import unauthenticated_userid
|
13
|
14
|
|
14
|
|
-from tracim.models.auth import Group
|
15
|
15
|
from tracim.models.auth import User
|
16
|
16
|
from tracim.models.data import Workspace
|
17
|
|
-from tracim.models.data import UserRoleInWorkspace
|
18
|
17
|
from tracim.lib.core.user import UserApi
|
19
|
18
|
from tracim.lib.core.workspace import WorkspaceApi
|
20
|
|
-from tracim.lib.core.userworkspace import RoleApi
|
21
|
19
|
|
22
|
|
-# INFO - G.M - 06-04-2018 - Auth for pyramid
|
23
|
|
-# based on this tutorial : https://docs.pylonsproject.org/projects/pyramid-cookbook/en/latest/auth/basic.html # nopep8
|
|
20
|
+from tracim.exceptions import NotAuthentificated
|
|
21
|
+from tracim.exceptions import WorkspaceNotFound
|
|
22
|
+from tracim.exceptions import InsufficientUserWorkspaceRole
|
24
|
23
|
BASIC_AUTH_WEBUI_REALM = "tracim"
|
|
24
|
+TRACIM_DEFAULT_PERM = 'tracim'
|
25
|
25
|
|
26
|
26
|
|
27
|
|
-def get_user(request: Request) -> typing.Optional[User]:
|
|
27
|
+def get_safe_user(
|
|
28
|
+ request: Request,
|
|
29
|
+) -> User:
|
28
|
30
|
"""
|
29
|
|
- Get current pyramid user from request
|
|
31
|
+ Get current pyramid authenticated user from request
|
30
|
32
|
:param request: pyramid request
|
31
|
|
- :return:
|
|
33
|
+ :return: current authenticated user
|
32
|
34
|
"""
|
33
|
35
|
app_config = request.registry.settings['CFG']
|
34
|
36
|
uapi = UserApi(None, session=request.dbsession, config=app_config)
|
35
|
37
|
user = None
|
36
|
38
|
try:
|
37
|
|
- login = unauthenticated_userid(request)
|
|
39
|
+ login = request.authenticated_userid
|
|
40
|
+ if not login:
|
|
41
|
+ raise NotAuthentificated('not authenticated user_id,'
|
|
42
|
+ 'Failed Authentification ?')
|
38
|
43
|
user = uapi.get_one_by_email(login)
|
39
|
44
|
except NoResultFound:
|
40
|
|
- pass
|
|
45
|
+ raise NotAuthentificated('User not found')
|
41
|
46
|
return user
|
42
|
47
|
|
43
|
48
|
|
44
|
|
-def get_workspace(request: Request) -> typing.Optional[Workspace]:
|
|
49
|
+def get_workspace(user: User, request: Request) -> typing.Optional[Workspace]:
|
45
|
50
|
"""
|
46
|
51
|
Get current workspace from request
|
|
52
|
+ :param user: User who want to check the workspace
|
47
|
53
|
:param request: pyramid request
|
48
|
54
|
:return:
|
49
|
55
|
"""
|
50
|
|
- workspace = None
|
|
56
|
+ workspace_id = ''
|
51
|
57
|
try:
|
52
|
58
|
if 'workspace_id' not in request.json_body:
|
53
|
59
|
return None
|
54
|
60
|
workspace_id = request.json_body['workspace_id']
|
55
|
|
- wapi = WorkspaceApi(current_user=None, session=request.dbsession)
|
|
61
|
+ wapi = WorkspaceApi(current_user=user, session=request.dbsession)
|
56
|
62
|
workspace = wapi.get_one(workspace_id)
|
57
|
63
|
except JSONDecodeError:
|
58
|
|
- pass
|
|
64
|
+ raise WorkspaceNotFound('Bad json body')
|
59
|
65
|
except NoResultFound:
|
60
|
|
- pass
|
|
66
|
+ raise WorkspaceNotFound(
|
|
67
|
+ 'Workspace {} does not exist '
|
|
68
|
+ 'or is not visible for this user'.format(workspace_id)
|
|
69
|
+ )
|
61
|
70
|
return workspace
|
62
|
71
|
|
|
72
|
+###
|
|
73
|
+# BASIC AUTH
|
|
74
|
+###
|
|
75
|
+
|
63
|
76
|
|
64
|
|
-def check_credentials(
|
|
77
|
+def basic_auth_check_credentials(
|
65
|
78
|
login: str,
|
66
|
79
|
cleartext_password: str,
|
67
|
|
- request: Request
|
|
80
|
+ request: 'TracimRequest'
|
68
|
81
|
) -> typing.Optional[list]:
|
69
|
82
|
"""
|
70
|
|
- Check credential for pyramid basic_auth, checks also for
|
71
|
|
- global and Workspace related permissions.
|
|
83
|
+ Check credential for pyramid basic_auth
|
72
|
84
|
:param login: login of user
|
73
|
85
|
:param cleartext_password: user password in cleartext
|
74
|
86
|
:param request: Pyramid request
|
75
|
87
|
:return: None if auth failed, list of permissions if auth succeed
|
76
|
88
|
"""
|
77
|
|
- user = get_user(request)
|
78
|
89
|
|
79
|
90
|
# Do not accept invalid user
|
|
91
|
+ user = _get_basic_auth_unsafe_user(request)
|
80
|
92
|
if not user \
|
81
|
93
|
or user.email != login \
|
82
|
94
|
or not user.validate_password(cleartext_password):
|
83
|
95
|
return None
|
84
|
|
- permissions = []
|
85
|
|
-
|
86
|
|
- # Global groups
|
87
|
|
- for group in user.groups:
|
88
|
|
- permissions.append(group.group_id)
|
89
|
|
-
|
90
|
|
- # Current workspace related group
|
91
|
|
- workspace = get_workspace(request)
|
92
|
|
- if workspace:
|
93
|
|
- roleapi = RoleApi(current_user=user, session=request.dbsession)
|
94
|
|
- role = roleapi.get_one(
|
95
|
|
- user_id=user.user_id,
|
96
|
|
- workspace_id=workspace.workspace_id,
|
97
|
|
- )
|
98
|
|
- permissions.append(role)
|
|
96
|
+ return []
|
99
|
97
|
|
100
|
|
- return permissions
|
101
|
98
|
|
|
99
|
+def _get_basic_auth_unsafe_user(
|
|
100
|
+ request: Request,
|
|
101
|
+) -> typing.Optional[User]:
|
|
102
|
+ """
|
|
103
|
+ :param request: pyramid request
|
|
104
|
+ :return: User or None
|
|
105
|
+ """
|
|
106
|
+ app_config = request.registry.settings['CFG']
|
|
107
|
+ uapi = UserApi(None, session=request.dbsession, config=app_config)
|
|
108
|
+ try:
|
|
109
|
+ login = request.unauthenticated_userid
|
|
110
|
+ if not login:
|
|
111
|
+ return None
|
|
112
|
+ user = uapi.get_one_by_email(login)
|
|
113
|
+ except NoResultFound:
|
|
114
|
+ return None
|
|
115
|
+ return user
|
|
116
|
+
|
|
117
|
+####
|
102
|
118
|
|
103
|
|
-# Global Permissions
|
104
|
|
-ADMIN_PERM = 'admin'
|
105
|
|
-MANAGE_GLOBAL_PERM = 'manage_global'
|
106
|
|
-USER_PERM = 'user'
|
107
|
|
-# Workspace-specific permission
|
108
|
|
-READ_PERM = 'read'
|
109
|
|
-CONTRIBUTE_PERM = 'contribute'
|
110
|
|
-MANAGE_CONTENT_PERM = 'manage_content'
|
111
|
|
-MANAGE_WORKSPACE_PERM = 'manage_workspace'
|
112
|
119
|
|
|
120
|
+def require_workspace_role(minimal_required_role):
|
|
121
|
+ def decorator(func):
|
113
|
122
|
|
114
|
|
-class Root(object):
|
|
123
|
+ def wrapper(self, request: 'TracimRequest'):
|
|
124
|
+ user = request.current_user
|
|
125
|
+ workspace = request.current_workspace
|
|
126
|
+ if workspace.get_user_role(user) >= minimal_required_role:
|
|
127
|
+ return func(self, request)
|
|
128
|
+ raise InsufficientUserWorkspaceRole()
|
|
129
|
+
|
|
130
|
+ return wrapper
|
|
131
|
+ return decorator
|
|
132
|
+
|
|
133
|
+###
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+@implementer(IAuthorizationPolicy)
|
|
137
|
+class AcceptAllAuthorizationPolicy(object):
|
115
|
138
|
"""
|
116
|
|
- Root of all Pyramid requests, used to store global acl
|
|
139
|
+ Simple AuthorizationPolicy to avoid trouble with pyramid.
|
|
140
|
+ Acceot any request.
|
117
|
141
|
"""
|
118
|
|
- __acl__ = ()
|
|
142
|
+ def permits(self, context, principals, permision):
|
|
143
|
+ return True
|
|
144
|
+
|
|
145
|
+ def principals_allowed_by_permission(self, context, permission):
|
|
146
|
+ raise NotImplementedError()
|