Browse Source

Merge branch 'feature/578_add_api_for_workspace/apps' of github.com:tracim/tracim_backend into fix/few_more_tests

Guénaël Muller 6 years ago
parent
commit
fee8ec3a45

+ 1 - 0
setup.py View File

@@ -39,6 +39,7 @@ requires = [
39 39
     'lxml',
40 40
     'redis',
41 41
     'rq',
42
+    'python-slugify',
42 43
 ]
43 44
 
44 45
 tests_require = [

+ 11 - 2
tracim/__init__.py View File

@@ -18,6 +18,9 @@ from tracim.lib.utils.authorization import TRACIM_DEFAULT_PERM
18 18
 from tracim.lib.webdav import WebdavAppFactory
19 19
 from tracim.views import BASE_API_V2
20 20
 from tracim.views.core_api.session_controller import SessionController
21
+from tracim.views.core_api.system_controller import SystemController
22
+from tracim.views.core_api.user_controller import UserController
23
+from tracim.views.core_api.workspace_controller import WorkspaceController
21 24
 from tracim.views.errors import ErrorSchema
22 25
 from tracim.lib.utils.cors import add_cors_support
23 26
 
@@ -62,8 +65,14 @@ def web(global_config, **local_settings):
62 65
     context.handle_exception(NotFound, 404)
63 66
     context.handle_exception(OperationalError, 500)
64 67
     # Add controllers
65
-    session_api = SessionController()
66
-    configurator.include(session_api.bind, route_prefix=BASE_API_V2)
68
+    session_controller = SessionController()
69
+    system_controller = SystemController()
70
+    user_controller = UserController()
71
+    workspace_controller = WorkspaceController()
72
+    configurator.include(session_controller.bind, route_prefix=BASE_API_V2)
73
+    configurator.include(system_controller.bind, route_prefix=BASE_API_V2)
74
+    configurator.include(user_controller.bind, route_prefix=BASE_API_V2)
75
+    configurator.include(workspace_controller.bind, route_prefix=BASE_API_V2)
67 76
     hapic.add_documentation_view(
68 77
         '/api/v2/doc',
69 78
         'Tracim v2 API',

+ 1 - 0
tracim/command/user.py View File

@@ -175,6 +175,7 @@ class UserCommand(AppContextCommand):
175 175
         self._group_api = GroupApi(
176 176
             current_user=None,
177 177
             session=self._session,
178
+            config=self._app_config,
178 179
         )
179 180
         user = self._proceed_user(parsed_args)
180 181
         self._proceed_groups(user, parsed_args)

+ 18 - 3
tracim/fixtures/content.py View File

@@ -24,10 +24,12 @@ class Content(Fixture):
24 24
         admin_workspace_api = WorkspaceApi(
25 25
             current_user=admin,
26 26
             session=self._session,
27
+            config=self._config,
27 28
         )
28 29
         bob_workspace_api = WorkspaceApi(
29 30
             current_user=bob,
30 31
             session=self._session,
32
+            config=self._config
31 33
         )
32 34
         content_api = ContentApi(
33 35
             current_user=admin,
@@ -37,12 +39,25 @@ class Content(Fixture):
37 39
         role_api = RoleApi(
38 40
             current_user=admin,
39 41
             session=self._session,
42
+            config=self._config,
40 43
         )
41 44
 
42 45
         # Workspaces
43
-        w1 = admin_workspace_api.create_workspace('w1', save_now=True)
44
-        w2 = bob_workspace_api.create_workspace('w2', save_now=True)
45
-        w3 = admin_workspace_api.create_workspace('w3', save_now=True)
46
+        w1 = admin_workspace_api.create_workspace(
47
+            'w1',
48
+            description='This is a workspace',
49
+            save_now=True
50
+        )
51
+        w2 = bob_workspace_api.create_workspace(
52
+            'w2',
53
+            description='A great workspace',
54
+            save_now=True
55
+        )
56
+        w3 = admin_workspace_api.create_workspace(
57
+            'w3',
58
+            description='Just another workspace',
59
+            save_now=True
60
+        )
46 61
 
47 62
         # Workspaces roles
48 63
         role_api.create_one(

+ 5 - 0
tracim/lib/core/group.py View File

@@ -4,6 +4,8 @@ import typing
4 4
 from sqlalchemy.orm.exc import NoResultFound
5 5
 
6 6
 from tracim.exceptions import GroupNotExist
7
+from tracim import CFG
8
+
7 9
 
8 10
 __author__ = 'damien'
9 11
 
@@ -18,9 +20,12 @@ class GroupApi(object):
18 20
             self,
19 21
             session: Session,
20 22
             current_user: typing.Optional[User],
23
+            config: CFG
24
+
21 25
     ):
22 26
         self._user = current_user
23 27
         self._session = session
28
+        self._config = config
24 29
 
25 30
     def _base_query(self) -> Query:
26 31
         return self._session.query(Group)

+ 12 - 4
tracim/lib/core/user.py View File

@@ -10,7 +10,7 @@ from tracim.lib.mail_notifier.notifier import get_email_manager
10 10
 from sqlalchemy.orm import Session
11 11
 
12 12
 from tracim import CFG
13
-from tracim.models.auth import User
13
+from tracim.models.auth import User, Group
14 14
 from sqlalchemy.orm.exc import NoResultFound
15 15
 from tracim.exceptions import WrongUserPassword, UserNotExist
16 16
 from tracim.exceptions import AuthenticationFailed
@@ -49,7 +49,11 @@ class UserApi(object):
49 49
         """
50 50
         Get one user by user id
51 51
         """
52
-        return self._base_query().filter(User.user_id == user_id).one()
52
+        try:
53
+            user = self._base_query().filter(User.user_id == user_id).one()
54
+        except NoResultFound:
55
+            raise UserNotExist()
56
+        return user
53 57
 
54 58
     def get_one_by_email(self, email: str) -> User:
55 59
         """
@@ -57,7 +61,11 @@ class UserApi(object):
57 61
         :param email: Email of the user
58 62
         :return: one user
59 63
         """
60
-        return self._base_query().filter(User.email == email).one()
64
+        try:
65
+            user = self._base_query().filter(User.email == email).one()
66
+        except NoResultFound:
67
+            raise UserNotExist()
68
+        return user
61 69
 
62 70
     # FIXME - G.M - 24-04-2018 - Duplicate method with get_one.
63 71
     def get_one_by_id(self, id: int) -> User:
@@ -98,7 +106,7 @@ class UserApi(object):
98 106
                 return user
99 107
             else:
100 108
                 raise WrongUserPassword()
101
-        except (WrongUserPassword, NoResultFound):
109
+        except (WrongUserPassword, NoResultFound, UserNotExist):
102 110
             raise AuthenticationFailed()
103 111
 
104 112
     # Actions

+ 27 - 6
tracim/lib/core/userworkspace.py View File

@@ -1,10 +1,13 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 import typing
3 3
 
4
+from tracim import CFG
5
+from tracim.models.context_models import UserRoleWorkspaceInContext
6
+
4 7
 __author__ = 'damien'
5 8
 
6 9
 from sqlalchemy.orm import Session
7
-from tracim.models.auth import User
10
+from tracim.models.auth import User, Group
8 11
 from tracim.models.data import Workspace
9 12
 from tracim.models.data import UserRoleInWorkspace
10 13
 from tracim.models.data import RoleType
@@ -38,6 +41,20 @@ class RoleApi(object):
38 41
         ],
39 42
     }
40 43
 
44
+    def get_user_role_workspace_with_context(
45
+            self,
46
+            user_role: UserRoleInWorkspace
47
+    ) -> UserRoleWorkspaceInContext:
48
+        """
49
+        Return WorkspaceInContext object from Workspace
50
+        """
51
+        workspace = UserRoleWorkspaceInContext(
52
+            user_role=user_role,
53
+            dbsession=self._session,
54
+            config=self._config,
55
+        )
56
+        return workspace
57
+
41 58
     @classmethod
42 59
     def role_can_read_member_role(cls, reader_role: int, tested_role: int) \
43 60
             -> bool:
@@ -56,9 +73,13 @@ class RoleApi(object):
56 73
 
57 74
         return role
58 75
 
59
-    def __init__(self, session: Session, current_user: typing.Optional[User]):
76
+    def __init__(self,
77
+                 session: Session,
78
+                 current_user: typing.Optional[User],
79
+                 config: CFG):
60 80
         self._session = session
61 81
         self._user = current_user
82
+        self._config = config
62 83
 
63 84
     def _get_one_rsc(self, user_id, workspace_id):
64 85
         """
@@ -99,8 +120,8 @@ class RoleApi(object):
99 120
         return self._session.query(UserRoleInWorkspace)\
100 121
             .filter(UserRoleInWorkspace.user_id == user_id)
101 122
 
102
-    def get_all_for_user(self, user_id):
103
-        return self._get_all_for_user(user_id).all()
123
+    def get_all_for_user(self, user: User):
124
+        return self._get_all_for_user(user.user_id).all()
104 125
 
105 126
     def get_all_for_user_order_by_workspace(
106 127
             self,
@@ -109,9 +130,9 @@ class RoleApi(object):
109 130
         return self._get_all_for_user(user_id)\
110 131
             .join(UserRoleInWorkspace.workspace).order_by(Workspace.label).all()
111 132
 
112
-    def get_all_for_workspace(self, workspace_id):
133
+    def get_all_for_workspace(self, workspace:Workspace):
113 134
         return self._session.query(UserRoleInWorkspace)\
114
-            .filter(UserRoleInWorkspace.workspace_id == workspace_id).all()
135
+            .filter(UserRoleInWorkspace.workspace_id == workspace.workspace_id).all()  # nopep8
115 136
 
116 137
     def save(self, role: UserRoleInWorkspace):
117 138
         self._session.flush()

+ 20 - 0
tracim/lib/core/workspace.py View File

@@ -3,11 +3,14 @@ import typing
3 3
 
4 4
 from sqlalchemy.orm import Query
5 5
 from sqlalchemy.orm import Session
6
+
7
+from tracim import CFG
6 8
 from tracim.lib.utils.translation import fake_translator as _
7 9
 
8 10
 from tracim.lib.core.userworkspace import RoleApi
9 11
 from tracim.models.auth import Group
10 12
 from tracim.models.auth import User
13
+from tracim.models.context_models import WorkspaceInContext
11 14
 from tracim.models.data import UserRoleInWorkspace
12 15
 from tracim.models.data import Workspace
13 16
 
@@ -20,6 +23,7 @@ class WorkspaceApi(object):
20 23
             self,
21 24
             session: Session,
22 25
             current_user: User,
26
+            config: CFG,
23 27
             force_role: bool=False
24 28
     ):
25 29
         """
@@ -28,6 +32,7 @@ class WorkspaceApi(object):
28 32
         """
29 33
         self._session = session
30 34
         self._user = current_user
35
+        self._config = config
31 36
         self._force_role = force_role
32 37
 
33 38
     def _base_query_without_roles(self):
@@ -42,6 +47,20 @@ class WorkspaceApi(object):
42 47
             filter(UserRoleInWorkspace.user_id == self._user.user_id).\
43 48
             filter(Workspace.is_deleted == False)
44 49
 
50
+    def get_workspace_with_context(
51
+            self,
52
+            workspace: Workspace
53
+    ) -> WorkspaceInContext:
54
+        """
55
+        Return WorkspaceInContext object from Workspace
56
+        """
57
+        workspace = WorkspaceInContext(
58
+            workspace=workspace,
59
+            dbsession=self._session,
60
+            config=self._config,
61
+        )
62
+        return workspace
63
+
45 64
     def create_workspace(
46 65
             self,
47 66
             label: str='',
@@ -62,6 +81,7 @@ class WorkspaceApi(object):
62 81
         role_api = RoleApi(
63 82
             session=self._session,
64 83
             current_user=self._user,
84
+            config=self._config
65 85
         )
66 86
 
67 87
         role = role_api.create_one(

+ 1 - 0
tracim/lib/mail_notifier/notifier.py View File

@@ -238,6 +238,7 @@ class EmailManager(object):
238 238
         notifiable_roles = WorkspaceApi(
239 239
             current_user=user,
240 240
             session=self.session,
241
+            config=self.config
241 242
         ).get_notifiable_roles(content.workspace)
242 243
 
243 244
         if len(notifiable_roles) <= 0:

+ 2 - 1
tracim/lib/utils/authentification.py View File

@@ -4,6 +4,7 @@ from pyramid.request import Request
4 4
 from sqlalchemy.orm.exc import NoResultFound
5 5
 
6 6
 from tracim import TracimRequest
7
+from tracim.exceptions import UserNotExist
7 8
 from tracim.lib.core.user import UserApi
8 9
 from tracim.models import User
9 10
 
@@ -50,6 +51,6 @@ def _get_basic_auth_unsafe_user(
50 51
         if not login:
51 52
             return None
52 53
         user = uapi.get_one_by_email(login)
53
-    except NoResultFound:
54
+    except (NoResultFound, UserNotExist):
54 55
         return None
55 56
     return user

+ 21 - 1
tracim/lib/utils/authorization.py View File

@@ -44,6 +44,26 @@ class AcceptAllAuthorizationPolicy(object):
44 44
 # We prefer to use decorators
45 45
 
46 46
 
47
+def require_same_user_or_profile(group):
48
+    """
49
+    Decorator for view to restrict access of tracim request if candidate user
50
+    is distinct from authenticated user and not with high enough profile.
51
+    :param group: value from Group Object
52
+    like Group.TIM_USER or Group.TIM_MANAGER
53
+    :return:
54
+    """
55
+    def decorator(func):
56
+        def wrapper(self, context, request: 'TracimRequest'):
57
+            auth_user = request.current_user
58
+            candidate_user = request.candidate_user
59
+            if auth_user.user_id == candidate_user.user_id or \
60
+                    auth_user.profile.id >= group:
61
+                return func(self, context, request)
62
+            raise InsufficientUserProfile()
63
+        return wrapper
64
+    return decorator
65
+
66
+
47 67
 def require_profile(group):
48 68
     """
49 69
     Decorator for view to restrict access of tracim request if profile is
@@ -80,4 +100,4 @@ def require_workspace_role(minimal_required_role):
80 100
             raise InsufficientUserWorkspaceRole()
81 101
 
82 102
         return wrapper
83
-    return decorator
103
+    return decorator

+ 69 - 8
tracim/lib/utils/request.py View File

@@ -1,8 +1,11 @@
1
-import typing
1
+# -*- coding: utf-8 -*-
2
+"""
3
+TracimRequest and related functions
4
+"""
2 5
 from pyramid.request import Request
3 6
 from sqlalchemy.orm.exc import NoResultFound
4 7
 
5
-from tracim.exceptions import NotAuthentificated
8
+from tracim.exceptions import NotAuthentificated, UserNotExist
6 9
 from tracim.exceptions import WorkspaceNotFound
7 10
 from tracim.exceptions import ImmutableAttribute
8 11
 from tracim.lib.core.user import UserApi
@@ -32,8 +35,13 @@ class TracimRequest(Request):
32 35
             decode_param_names,
33 36
             **kw
34 37
         )
38
+        # Current workspace, found by request headers or content
35 39
         self._current_workspace = None  # type: Workspace
40
+        # Authenticated user
36 41
         self._current_user = None  # type: User
42
+        # User found from request headers, content, distinct from authenticated
43
+        # user
44
+        self._user_candidate = None  # type: User
37 45
         # INFO - G.M - 18-05-2018 - Close db at the end of the request
38 46
         self.add_finished_callback(self._cleanup)
39 47
 
@@ -64,8 +72,11 @@ class TracimRequest(Request):
64 72
 
65 73
     @property
66 74
     def current_user(self) -> User:
75
+        """
76
+        Get user from authentication mecanism.
77
+        """
67 78
         if self._current_user is None:
68
-            self.current_user = get_safe_user(self)
79
+            self.current_user = get_auth_safe_user(self)
69 80
         return self._current_user
70 81
 
71 82
     @current_user.setter
@@ -76,6 +87,19 @@ class TracimRequest(Request):
76 87
             )
77 88
         self._current_user = user
78 89
 
90
+    # TODO - G.M - 24-05-2018 - Find a better naming for this ?
91
+    @property
92
+    def candidate_user(self) -> User:
93
+        """
94
+        Get user from headers/body request. This user is not
95
+        the one found by authentication mecanism. This user
96
+        can help user to know about who one page is about in
97
+        a similar way as current_workspace.
98
+        """
99
+        if self._user_candidate is None:
100
+            self.candidate_user = get_candidate_user(self)
101
+        return self._user_candidate
102
+
79 103
     def _cleanup(self, request: 'TracimRequest') -> None:
80 104
         """
81 105
         Close dbsession at the end of the request in order to avoid exception
@@ -89,11 +113,43 @@ class TracimRequest(Request):
89 113
         self._current_workspace = None
90 114
         self.dbsession.close()
91 115
 
116
+
117
+    @candidate_user.setter
118
+    def candidate_user(self, user: User) -> None:
119
+        if self._user_candidate is not None:
120
+            raise ImmutableAttribute(
121
+                "Can't modify already setted candidate_user"
122
+            )
123
+        self._user_candidate = user
92 124
 ###
93 125
 # Utils for TracimRequest
94 126
 ###
95 127
 
96
-def get_safe_user(
128
+
129
+def get_candidate_user(
130
+        request: TracimRequest
131
+) -> User:
132
+    """
133
+    Get candidate user
134
+    :param request: pyramid request
135
+    :return: user found from header/body
136
+    """
137
+    app_config = request.registry.settings['CFG']
138
+    uapi = UserApi(None, session=request.dbsession, config=app_config)
139
+
140
+    try:
141
+        login = None
142
+        if 'user_id' in request.matchdict:
143
+            login = request.matchdict['user_id']
144
+        if not login:
145
+            raise UserNotExist('no user_id found, incorrect request ?')
146
+        user = uapi.get_one(login)
147
+    except NoResultFound:
148
+        raise NotAuthentificated('User not found')
149
+    return user
150
+
151
+
152
+def get_auth_safe_user(
97 153
         request: TracimRequest,
98 154
 ) -> User:
99 155
     """
@@ -126,10 +182,15 @@ def get_workspace(
126 182
     """
127 183
     workspace_id = ''
128 184
     try:
129
-        if 'workspace_id' not in request.json_body:
130
-            raise WorkspaceNotFound('No workspace_id param in json body')
131
-        workspace_id = request.json_body['workspace_id']
132
-        wapi = WorkspaceApi(current_user=user, session=request.dbsession)
185
+        if 'workspace_id' in request.matchdict:
186
+            workspace_id = request.matchdict['workspace_id']
187
+        if not workspace_id:
188
+            raise WorkspaceNotFound('No workspace_id param')
189
+        wapi = WorkspaceApi(
190
+            current_user=user,
191
+            session=request.dbsession,
192
+            config=request.registry.settings['CFG']
193
+        )
133 194
         workspace = wapi.get_one(workspace_id)
134 195
     except JSONDecodeError:
135 196
         raise WorkspaceNotFound('Bad json body')

+ 10 - 2
tracim/lib/webdav/dav_provider.py View File

@@ -72,7 +72,11 @@ class Provider(DAVProvider):
72 72
         if path == root_path:
73 73
             return resources.RootResource(path, environ, user=user, session=session)
74 74
 
75
-        workspace_api = WorkspaceApi(current_user=user, session=session)
75
+        workspace_api = WorkspaceApi(
76
+            current_user=user,
77
+            session=session,
78
+            config=self.app_config,
79
+        )
76 80
         workspace = self.get_workspace_from_path(path, workspace_api)
77 81
 
78 82
         # If the request path is in the form root/name, then we return a WorkspaceResource resource
@@ -194,7 +198,11 @@ class Provider(DAVProvider):
194 198
 
195 199
         workspace = self.get_workspace_from_path(
196 200
             path,
197
-            WorkspaceApi(current_user=user, session=session)
201
+            WorkspaceApi(
202
+                current_user=user,
203
+                session=session,
204
+                config=self.app_config,
205
+            )
198 206
         )
199 207
 
200 208
         if parent_path == root_path or workspace is None:

+ 3 - 1
tracim/lib/webdav/resources.py View File

@@ -90,7 +90,8 @@ class RootResource(DAVCollection):
90 90
         self.workspace_api = WorkspaceApi(
91 91
             current_user=self.user,
92 92
             session=session,
93
-            force_role=True
93
+            force_role=True,
94
+            config=self.provider.app_config
94 95
         )
95 96
 
96 97
     def __repr__(self) -> str:
@@ -1254,6 +1255,7 @@ class FileResource(DAVNonCollection):
1254 1255
             workspace_api = WorkspaceApi(
1255 1256
                 current_user=self.user,
1256 1257
                 session=self.session,
1258
+                config=self.provider.app_config,
1257 1259
                 )
1258 1260
             content_api = ContentApi(
1259 1261
                 current_user=self.user,

+ 87 - 0
tracim/models/applications.py View File

@@ -0,0 +1,87 @@
1
+# coding=utf-8
2
+import typing
3
+
4
+
5
+class Application(object):
6
+    """
7
+    Application class with data needed for frontend
8
+    """
9
+    def __init__(
10
+            self,
11
+            label: str,
12
+            slug: str,
13
+            icon: str,
14
+            hexcolor: str,
15
+            is_active: bool,
16
+            config: typing.Dict[str, str],
17
+            main_route: str,
18
+    ) -> None:
19
+        self.label = label
20
+        self.slug = slug
21
+        self.icon = icon
22
+        self.hexcolor = hexcolor
23
+        self.is_active = is_active
24
+        self.config = config
25
+        self.main_route = main_route
26
+
27
+
28
+# TODO - G.M - 21-05-2018 Do not rely on hardcoded app list
29
+# default apps
30
+calendar = Application(
31
+    label='Calendar',
32
+    slug='calendar',
33
+    icon='calendar-alt',
34
+    hexcolor='#757575',
35
+    is_active=True,
36
+    config={},
37
+    main_route='/#/workspaces/{workspace_id}/calendar',
38
+)
39
+
40
+thread = Application(
41
+    label='Threads',
42
+    slug='contents/threads',
43
+    icon='comments-o',
44
+    hexcolor='#ad4cf9',
45
+    is_active=True,
46
+    config={},
47
+    main_route='/#/workspaces/{workspace_id}/contents?type=thread',
48
+
49
+)
50
+
51
+file = Application(
52
+    label='Files',
53
+    slug='contents/files',
54
+    icon='paperclip',
55
+    hexcolor='#FF9900',
56
+    is_active=True,
57
+    config={},
58
+    main_route='/#/workspaces/{workspace_id}/contents?type=file',
59
+)
60
+
61
+pagemarkdownplus = Application(
62
+    label='Rich Markdown Files',  # TODO - G.M - 24-05-2018 - Check label
63
+    slug='contents/pagemarkdownplus',
64
+    icon='file-code',
65
+    hexcolor='#f12d2d',
66
+    is_active=True,
67
+    config={},
68
+    main_route='/#/workspaces/{workspace_id}/contents?type=pagemarkdownplus',
69
+)
70
+
71
+pagehtml = Application(
72
+    label='Text Documents',  # TODO - G.M - 24-05-2018 - Check label
73
+    slug='contents/pagehtml',
74
+    icon='file-text-o',
75
+    hexcolor='#3f52e3',
76
+    is_active=True,
77
+    config={},
78
+    main_route='/#/workspaces/{workspace_id}/contents?type=pagehtml',
79
+)
80
+# List of applications
81
+applications = [
82
+    pagehtml,
83
+    pagemarkdownplus,
84
+    file,
85
+    thread,
86
+    calendar,
87
+]

+ 5 - 0
tracim/models/auth.py View File

@@ -96,6 +96,11 @@ class Profile(object):
96 96
              Group.TIM_MANAGER_GROUPNAME,
97 97
              Group.TIM_ADMIN_GROUPNAME]
98 98
 
99
+    _IDS = [Group.TIM_NOBODY,
100
+            Group.TIM_USER,
101
+            Group.TIM_MANAGER,
102
+            Group.TIM_ADMIN]
103
+
99 104
     # TODO - G.M - 18-04-2018 [Cleanup] Drop this
100 105
     # _LABEL = [l_('Nobody'),
101 106
     #           l_('Users'),

+ 137 - 0
tracim/models/context_models.py View File

@@ -2,10 +2,14 @@
2 2
 import typing
3 3
 from datetime import datetime
4 4
 
5
+from slugify import slugify
5 6
 from sqlalchemy.orm import Session
6 7
 from tracim import CFG
7 8
 from tracim.models import User
8 9
 from tracim.models.auth import Profile
10
+from tracim.models.data import Workspace, UserRoleInWorkspace
11
+from tracim.models.workspace_menu_entries import default_workspace_menu_entry, \
12
+    WorkspaceMenuEntry
9 13
 
10 14
 
11 15
 class LoginCredentials(object):
@@ -74,3 +78,136 @@ class UserInContext(object):
74 78
     def avatar_url(self) -> typing.Optional[str]:
75 79
         # TODO - G-M - 20-04-2018 - [Avatar] Add user avatar feature
76 80
         return None
81
+
82
+
83
+class WorkspaceInContext(object):
84
+    """
85
+    Interface to get Workspace data and Workspace data related to context.
86
+    """
87
+
88
+    def __init__(self, workspace: Workspace, dbsession: Session, config: CFG):
89
+        self.workspace = workspace
90
+        self.dbsession = dbsession
91
+        self.config = config
92
+
93
+    @property
94
+    def workspace_id(self) -> int:
95
+        """
96
+        numeric id of the workspace.
97
+        """
98
+        return self.workspace.workspace_id
99
+
100
+    @property
101
+    def id(self) -> int:
102
+        """
103
+        alias of workspace_id
104
+        """
105
+        return self.workspace_id
106
+
107
+    @property
108
+    def label(self) -> str:
109
+        """
110
+        get workspace label
111
+        """
112
+        return self.workspace.label
113
+
114
+    @property
115
+    def description(self) -> str:
116
+        """
117
+        get workspace description
118
+        """
119
+        return self.workspace.description
120
+
121
+    @property
122
+    def slug(self) -> str:
123
+        """
124
+        get workspace slug
125
+        """
126
+        return slugify(self.workspace.label)
127
+
128
+    @property
129
+    def sidebar_entries(self) -> typing.List[WorkspaceMenuEntry]:
130
+        """
131
+        get sidebar entries, those depends on activated apps.
132
+        """
133
+        # TODO - G.M - 22-05-2018 - Rework on this in
134
+        # order to not use hardcoded list
135
+        # list should be able to change (depending on activated/disabled
136
+        # apps)
137
+        return default_workspace_menu_entry(self.workspace)
138
+
139
+
140
+class UserRoleWorkspaceInContext(object):
141
+    """
142
+    Interface to get UserRoleInWorkspace data and related content
143
+
144
+    """
145
+    def __init__(
146
+            self,
147
+            user_role: UserRoleInWorkspace,
148
+            dbsession: Session,
149
+            config: CFG,
150
+    )-> None:
151
+        self.user_role = user_role
152
+        self.dbsession = dbsession
153
+        self.config = config
154
+
155
+    @property
156
+    def user_id(self) -> int:
157
+        """
158
+        User who has the role has this id
159
+        :return: user id as integer
160
+        """
161
+        return self.user_role.user_id
162
+
163
+    @property
164
+    def workspace_id(self) -> int:
165
+        """
166
+        This role apply only on the workspace with this workspace_id
167
+        :return: workspace id as integer
168
+        """
169
+        return self.user_role.workspace_id
170
+
171
+    # TODO - G.M - 23-05-2018 - Check the API spec for this this !
172
+
173
+    @property
174
+    def role_id(self) -> int:
175
+        """
176
+        role as int id, each value refer to a different role.
177
+        """
178
+        return self.user_role.role
179
+
180
+    @property
181
+    def role_slug(self) -> str:
182
+        """
183
+        simple name of the role of the user.
184
+        can be anything from UserRoleInWorkspace SLUG, like
185
+        'not_applicable', 'reader',
186
+        'contributor', 'content_manager', 'workspace_manager'
187
+        :return: user workspace role as slug.
188
+        """
189
+        return UserRoleInWorkspace.SLUG[self.user_role.role]
190
+
191
+    @property
192
+    def user(self) -> UserInContext:
193
+        """
194
+        User who has this role, with context data
195
+        :return: UserInContext object
196
+        """
197
+        return UserInContext(
198
+            self.user_role.user,
199
+            self.dbsession,
200
+            self.config
201
+        )
202
+
203
+    @property
204
+    def workspace(self) -> WorkspaceInContext:
205
+        """
206
+        Workspace related to this role, with his context data
207
+        :return: WorkspaceInContext object
208
+        """
209
+        return WorkspaceInContext(
210
+            self.user_role.workspace,
211
+            self.dbsession,
212
+            self.config
213
+        )

+ 23 - 1
tracim/models/data.py View File

@@ -131,6 +131,14 @@ class UserRoleInWorkspace(DeclarativeBase):
131 131
     WORKSPACE_MANAGER = 8
132 132
 
133 133
     # TODO - G.M - 10-04-2018 - [Cleanup] Drop this
134
+
135
+    SLUG = dict()
136
+    SLUG[NOT_APPLICABLE] = 'not_applicable'
137
+    SLUG[READER] = 'reader'
138
+    SLUG[CONTRIBUTOR] = 'contributor'
139
+    SLUG[CONTENT_MANAGER] = 'content_manager'
140
+    SLUG[WORKSPACE_MANAGER] = 'workspace_manager'
141
+
134 142
     LABEL = dict()
135 143
     LABEL[0] = l_('N/A')
136 144
     LABEL[1] = l_('Reader')
@@ -166,7 +174,10 @@ class UserRoleInWorkspace(DeclarativeBase):
166 174
         return UserRoleInWorkspace.LABEL[self.role]
167 175
 
168 176
     @classmethod
169
-    def get_all_role_values(self):
177
+    def get_all_role_values(cls) -> typing.List[int]:
178
+        """
179
+        Return all valid role value
180
+        """
170 181
         return [
171 182
             UserRoleInWorkspace.READER,
172 183
             UserRoleInWorkspace.CONTRIBUTOR,
@@ -174,6 +185,17 @@ class UserRoleInWorkspace(DeclarativeBase):
174 185
             UserRoleInWorkspace.WORKSPACE_MANAGER
175 186
         ]
176 187
 
188
+    @classmethod
189
+    def get_all_role_slug(cls) -> typing.List[str]:
190
+        """
191
+        Return all valid role slug
192
+        """
193
+        # INFO - G.M - 25-05-2018 - Be carefull, as long as this method
194
+        # and get_all_role_values are both used for API, this method should
195
+        # return item in the same order as get_all_role_values
196
+        return [cls.SLUG[value] for value in cls.get_all_role_values()]
197
+
198
+
177 199
 class RoleType(object):
178 200
     def __init__(self, role_id):
179 201
         self.role_type_id = role_id

+ 71 - 0
tracim/models/workspace_menu_entries.py View File

@@ -0,0 +1,71 @@
1
+# coding=utf-8
2
+import typing
3
+from copy import copy
4
+
5
+from tracim.models.applications import applications
6
+from tracim.models.data import Workspace
7
+
8
+
9
+class WorkspaceMenuEntry(object):
10
+    """
11
+    Application class with data needed for frontend
12
+    """
13
+    def __init__(
14
+            self,
15
+            label: str,
16
+            slug: str,
17
+            icon: str,
18
+            hexcolor: str,
19
+            route: str,
20
+    ) -> None:
21
+        self.slug = slug
22
+        self.label = label
23
+        self.route = route
24
+        self.hexcolor = hexcolor
25
+        self.icon = icon
26
+
27
+
28
+dashboard_menu_entry = WorkspaceMenuEntry(
29
+  slug='dashboard',
30
+  label='Dashboard',
31
+  route='/#/workspaces/{workspace_id}/dashboard',
32
+  hexcolor='#252525',
33
+  icon="",
34
+)
35
+all_content_menu_entry = WorkspaceMenuEntry(
36
+  slug="contents/all",
37
+  label="All Contents",
38
+  route="/#/workspaces/{workspace_id}/contents",
39
+  hexcolor="#fdfdfd",
40
+  icon="",
41
+)
42
+
43
+
44
+def default_workspace_menu_entry(
45
+    workspace: Workspace,
46
+)-> typing.List[WorkspaceMenuEntry]:
47
+    """
48
+    Get default menu entry for a workspace
49
+    """
50
+    menu_entries = [
51
+        copy(dashboard_menu_entry),
52
+        copy(all_content_menu_entry),
53
+    ]
54
+    for app in applications:
55
+        if app.main_route:
56
+            new_entry = WorkspaceMenuEntry(
57
+                slug=app.slug,
58
+                label=app.label,
59
+                hexcolor=app.hexcolor,
60
+                icon=app.icon,
61
+                route=app.main_route
62
+            )
63
+            menu_entries.append(new_entry)
64
+
65
+    for entry in menu_entries:
66
+        entry.route = entry.route.replace(
67
+            '{workspace_id}',
68
+            str(workspace.workspace_id)
69
+        )
70
+
71
+    return menu_entries

+ 7 - 5
tracim/tests/__init__.py View File

@@ -45,19 +45,20 @@ class FunctionalTest(unittest.TestCase):
45 45
 
46 46
         }
47 47
         hapic.reset_context()
48
+        self.engine = get_engine(self.settings)
49
+        DeclarativeBase.metadata.create_all(self.engine)
50
+        self.session_factory = get_session_factory(self.engine)
51
+        self.app_config = CFG(self.settings)
52
+        self.app_config.configure_filedepot()
48 53
         self.init_database(self.settings)
54
+        DepotManager._clear()
49 55
         self.run_app()
50 56
 
51 57
     def run_app(self):
52 58
         app = web({}, **self.settings)
53
-        self.init_database(self.settings)
54 59
         self.testapp = TestApp(app)
55 60
 
56 61
     def init_database(self, settings):
57
-        self.engine = get_engine(settings)
58
-        DeclarativeBase.metadata.create_all(self.engine)
59
-        self.session_factory = get_session_factory(self.engine)
60
-        self.app_config = CFG(settings)
61 62
         with transaction.manager:
62 63
             dbsession = get_tm_session(self.session_factory, transaction.manager)
63 64
             try:
@@ -171,6 +172,7 @@ class DefaultTest(StandardTest):
171 172
         WorkspaceApi(
172 173
             current_user=user,
173 174
             session=self.session,
175
+            config=self.app_config,
174 176
         ).create_workspace(name, save_now=True)
175 177
 
176 178
         eq_(

+ 3 - 3
tracim/tests/commands/test_commands.py View File

@@ -11,7 +11,7 @@ import tracim
11 11
 from tracim.command import TracimCLI
12 12
 from tracim.command.user import UserCommand
13 13
 from tracim.exceptions import UserAlreadyExistError, BadCommandError, \
14
-    GroupNotExist
14
+    GroupNotExist, UserNotExist
15 15
 from tracim.lib.core.user import UserApi
16 16
 from tracim.tests import CommandFunctionalTest
17 17
 
@@ -48,7 +48,7 @@ class TestCommands(CommandFunctionalTest):
48 48
             session=self.session,
49 49
             config=self.app_config,
50 50
         )
51
-        with pytest.raises(NoResultFound):
51
+        with pytest.raises(UserNotExist):
52 52
             api.get_one_by_email('command_test@user')
53 53
         app = TracimCLI()
54 54
         result = app.run([
@@ -72,7 +72,7 @@ class TestCommands(CommandFunctionalTest):
72 72
             session=self.session,
73 73
             config=self.app_config,
74 74
         )
75
-        with pytest.raises(NoResultFound):
75
+        with pytest.raises(UserNotExist):
76 76
             api.get_one_by_email('command_test@user')
77 77
         app = TracimCLI()
78 78
         result = app.run([

+ 2 - 0
tracim/tests/functional/test_mail_notification.py View File

@@ -127,6 +127,7 @@ class TestNotificationsSync(MailHogTest):
127 127
         wapi = WorkspaceApi(
128 128
             current_user=current_user,
129 129
             session=self.session,
130
+            config=self.app_config,
130 131
         )
131 132
         workspace = wapi.get_one_by_label('w1')
132 133
         user = uapi.get_one_by_email('bob@fsf.local')
@@ -218,6 +219,7 @@ class TestNotificationsAsync(MailHogTest):
218 219
         wapi = WorkspaceApi(
219 220
             current_user=current_user,
220 221
             session=self.session,
222
+            config=self.app_config,
221 223
         )
222 224
         workspace = wapi.get_one_by_label('w1')
223 225
         user = uapi.get_one_by_email('bob@fsf.local')

+ 77 - 0
tracim/tests/functional/test_system.py View File

@@ -0,0 +1,77 @@
1
+# coding=utf-8
2
+"""
3
+Tests for /api/v2/system subpath endpoints.
4
+"""
5
+from tracim.tests import FunctionalTest
6
+
7
+
8
+class TestApplicationsEndpoint(FunctionalTest):
9
+    """
10
+    Tests for /api/v2/system/applications
11
+    """
12
+
13
+    def test_api__get_applications__ok_200__nominal_case(self):
14
+        """
15
+        Get applications list with a registered user.
16
+        """
17
+        self.testapp.authorization = (
18
+            'Basic',
19
+            (
20
+                'admin@admin.admin',
21
+                'admin@admin.admin'
22
+            )
23
+        )
24
+        res = self.testapp.get('/api/v2/system/applications', status=200)
25
+        res = res.json_body
26
+        application = res[0]
27
+        assert application['label'] == "Text Documents"
28
+        assert application['slug'] == 'contents/pagehtml'
29
+        assert application['icon'] == 'file-text-o'
30
+        assert application['hexcolor'] == '#3f52e3'
31
+        assert application['is_active'] is True
32
+        assert 'config' in application
33
+        application = res[1]
34
+        assert application['label'] == "Rich Markdown Files"
35
+        assert application['slug'] == 'contents/pagemarkdownplus'
36
+        assert application['icon'] == 'file-code'
37
+        assert application['hexcolor'] == '#f12d2d'
38
+        assert application['is_active'] is True
39
+        assert 'config' in application
40
+        application = res[2]
41
+        assert application['label'] == "Files"
42
+        assert application['slug'] == 'contents/files'
43
+        assert application['icon'] == 'paperclip'
44
+        assert application['hexcolor'] == '#FF9900'
45
+        assert application['is_active'] is True
46
+        assert 'config' in application
47
+        application = res[3]
48
+        assert application['label'] == "Threads"
49
+        assert application['slug'] == 'contents/threads'
50
+        assert application['icon'] == 'comments-o'
51
+        assert application['hexcolor'] == '#ad4cf9'
52
+        assert application['is_active'] is True
53
+        assert 'config' in application
54
+        application = res[4]
55
+        assert application['label'] == "Calendar"
56
+        assert application['slug'] == 'calendar'
57
+        assert application['icon'] == 'calendar-alt'
58
+        assert application['hexcolor'] == '#757575'
59
+        assert application['is_active'] is True
60
+        assert 'config' in application
61
+
62
+    def test_api__get_workspace__err_401__unregistered_user(self):
63
+        """
64
+        Get applications list with an unregistered user (bad auth)
65
+        """
66
+        self.testapp.authorization = (
67
+            'Basic',
68
+            (
69
+                'john@doe.doe',
70
+                'lapin'
71
+            )
72
+        )
73
+        res = self.testapp.get('/api/v2/system/applications', status=401)
74
+        assert isinstance(res.json, dict)
75
+        assert 'code' in res.json.keys()
76
+        assert 'message' in res.json.keys()
77
+        assert 'details' in res.json.keys()

+ 137 - 0
tracim/tests/functional/test_user.py View File

@@ -0,0 +1,137 @@
1
+# -*- coding: utf-8 -*-
2
+"""
3
+Tests for /api/v2/users subpath endpoints.
4
+"""
5
+from tracim.tests import FunctionalTest
6
+from tracim.fixtures.content import Content as ContentFixtures
7
+from tracim.fixtures.users_and_groups import Base as BaseFixture
8
+
9
+
10
+class TestUserWorkspaceEndpoint(FunctionalTest):
11
+    # -*- coding: utf-8 -*-
12
+    """
13
+    Tests for /api/v2/users/{user_id}/workspaces
14
+    """
15
+    fixtures = [BaseFixture, ContentFixtures]
16
+
17
+    def test_api__get_user_workspaces__ok_200__nominal_case(self):
18
+        """
19
+        Check obtain all workspaces reachables for user with user auth.
20
+        """
21
+        self.testapp.authorization = (
22
+            'Basic',
23
+            (
24
+                'admin@admin.admin',
25
+                'admin@admin.admin'
26
+            )
27
+        )
28
+        res = self.testapp.get('/api/v2/users/1/workspaces', status=200)
29
+        res = res.json_body
30
+        workspace = res[0]
31
+        assert workspace['id'] == 1
32
+        assert workspace['label'] == 'w1'
33
+        assert len(workspace['sidebar_entries']) == 7
34
+
35
+        sidebar_entry = workspace['sidebar_entries'][0]
36
+        assert sidebar_entry['slug'] == 'dashboard'
37
+        assert sidebar_entry['label'] == 'Dashboard'
38
+        assert sidebar_entry['route'] == '/#/workspaces/1/dashboard'  # nopep8
39
+        assert sidebar_entry['hexcolor'] == "#252525"
40
+        assert sidebar_entry['icon'] == ""
41
+
42
+        sidebar_entry = workspace['sidebar_entries'][1]
43
+        assert sidebar_entry['slug'] == 'contents/all'
44
+        assert sidebar_entry['label'] == 'All Contents'
45
+        assert sidebar_entry['route'] == "/#/workspaces/1/contents"  # nopep8
46
+        assert sidebar_entry['hexcolor'] == "#fdfdfd"
47
+        assert sidebar_entry['icon'] == ""
48
+
49
+        sidebar_entry = workspace['sidebar_entries'][2]
50
+        assert sidebar_entry['slug'] == 'contents/pagehtml'
51
+        assert sidebar_entry['label'] == 'Text Documents'
52
+        assert sidebar_entry['route'] == '/#/workspaces/1/contents?type=pagehtml'  # nopep8
53
+        assert sidebar_entry['hexcolor'] == "#3f52e3"
54
+        assert sidebar_entry['icon'] == "file-text-o"
55
+
56
+        sidebar_entry = workspace['sidebar_entries'][3]
57
+        assert sidebar_entry['slug'] == 'contents/pagemarkdownplus'
58
+        assert sidebar_entry['label'] == 'Rich Markdown Files'
59
+        assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=pagemarkdownplus"    # nopep8
60
+        assert sidebar_entry['hexcolor'] == "#f12d2d"
61
+        assert sidebar_entry['icon'] == "file-code"
62
+
63
+        sidebar_entry = workspace['sidebar_entries'][4]
64
+        assert sidebar_entry['slug'] == 'contents/files'
65
+        assert sidebar_entry['label'] == 'Files'
66
+        assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=file"  # nopep8
67
+        assert sidebar_entry['hexcolor'] == "#FF9900"
68
+        assert sidebar_entry['icon'] == "paperclip"
69
+
70
+        sidebar_entry = workspace['sidebar_entries'][5]
71
+        assert sidebar_entry['slug'] == 'contents/threads'
72
+        assert sidebar_entry['label'] == 'Threads'
73
+        assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=thread"  # nopep8
74
+        assert sidebar_entry['hexcolor'] == "#ad4cf9"
75
+        assert sidebar_entry['icon'] == "comments-o"
76
+
77
+        sidebar_entry = workspace['sidebar_entries'][6]
78
+        assert sidebar_entry['slug'] == 'calendar'
79
+        assert sidebar_entry['label'] == 'Calendar'
80
+        assert sidebar_entry['route'] == "/#/workspaces/1/calendar"  # nopep8
81
+        assert sidebar_entry['hexcolor'] == "#757575"
82
+        assert sidebar_entry['icon'] == "calendar-alt"
83
+
84
+    def test_api__get_user_workspaces__err_403__unallowed_user(self):
85
+        """
86
+        Check obtain all workspaces reachables for one user
87
+        with another non-admin user auth.
88
+        """
89
+        self.testapp.authorization = (
90
+            'Basic',
91
+            (
92
+                'lawrence-not-real-email@fsf.local',
93
+                'foobarbaz'
94
+            )
95
+        )
96
+        res = self.testapp.get('/api/v2/users/1/workspaces', status=403)
97
+        assert isinstance(res.json, dict)
98
+        assert 'code' in res.json.keys()
99
+        assert 'message' in res.json.keys()
100
+        assert 'details' in res.json.keys()
101
+
102
+    def test_api__get_user_workspaces__err_401__unregistered_user(self):
103
+        """
104
+        Check obtain all workspaces reachables for one user
105
+        without correct user auth (user unregistered).
106
+        """
107
+        self.testapp.authorization = (
108
+            'Basic',
109
+            (
110
+                'john@doe.doe',
111
+                'lapin'
112
+            )
113
+        )
114
+        res = self.testapp.get('/api/v2/users/1/workspaces', status=401)
115
+        assert isinstance(res.json, dict)
116
+        assert 'code' in res.json.keys()
117
+        assert 'message' in res.json.keys()
118
+        assert 'details' in res.json.keys()
119
+
120
+    def test_api__get_user_workspaces__err_404__user_does_not_exist(self):
121
+        """
122
+        Check obtain all workspaces reachables for one user who does
123
+        not exist
124
+        with a correct user auth.
125
+        """
126
+        self.testapp.authorization = (
127
+            'Basic',
128
+            (
129
+                'admin@admin.admin',
130
+                'admin@admin.admin'
131
+            )
132
+        )
133
+        res = self.testapp.get('/api/v2/users/5/workspaces', status=404)
134
+        assert isinstance(res.json, dict)
135
+        assert 'code' in res.json.keys()
136
+        assert 'message' in res.json.keys()
137
+        assert 'details' in res.json.keys()

+ 218 - 0
tracim/tests/functional/test_workspaces.py View File

@@ -0,0 +1,218 @@
1
+# -*- coding: utf-8 -*-
2
+"""
3
+Tests for /api/v2/workspaces subpath endpoints.
4
+"""
5
+from tracim.tests import FunctionalTest
6
+from tracim.fixtures.content import Content as ContentFixtures
7
+from tracim.fixtures.users_and_groups import Base as BaseFixture
8
+
9
+
10
+class TestWorkspaceEndpoint(FunctionalTest):
11
+    """
12
+    Tests for /api/v2/workspaces/{workspace_id} endpoint
13
+    """
14
+
15
+    fixtures = [BaseFixture, ContentFixtures]
16
+
17
+    def test_api__get_workspace__ok_200__nominal_case(self) -> None:
18
+        """
19
+        Check obtain workspace reachable for user.
20
+        """
21
+        self.testapp.authorization = (
22
+            'Basic',
23
+            (
24
+                'admin@admin.admin',
25
+                'admin@admin.admin'
26
+            )
27
+        )
28
+        res = self.testapp.get('/api/v2/workspaces/1', status=200)
29
+        workspace = res.json_body
30
+        assert workspace['id'] == 1
31
+        assert workspace['slug'] == 'w1'
32
+        assert workspace['label'] == 'w1'
33
+        assert workspace['description'] == 'This is a workspace'
34
+        assert len(workspace['sidebar_entries']) == 7
35
+
36
+        sidebar_entry = workspace['sidebar_entries'][0]
37
+        assert sidebar_entry['slug'] == 'dashboard'
38
+        assert sidebar_entry['label'] == 'Dashboard'
39
+        assert sidebar_entry['route'] == '/#/workspaces/1/dashboard'  # nopep8
40
+        assert sidebar_entry['hexcolor'] == "#252525"
41
+        assert sidebar_entry['icon'] == ""
42
+
43
+        sidebar_entry = workspace['sidebar_entries'][1]
44
+        assert sidebar_entry['slug'] == 'contents/all'
45
+        assert sidebar_entry['label'] == 'All Contents'
46
+        assert sidebar_entry['route'] == "/#/workspaces/1/contents"  # nopep8
47
+        assert sidebar_entry['hexcolor'] == "#fdfdfd"
48
+        assert sidebar_entry['icon'] == ""
49
+
50
+        sidebar_entry = workspace['sidebar_entries'][2]
51
+        assert sidebar_entry['slug'] == 'contents/pagehtml'
52
+        assert sidebar_entry['label'] == 'Text Documents'
53
+        assert sidebar_entry['route'] == '/#/workspaces/1/contents?type=pagehtml'  # nopep8
54
+        assert sidebar_entry['hexcolor'] == "#3f52e3"
55
+        assert sidebar_entry['icon'] == "file-text-o"
56
+
57
+        sidebar_entry = workspace['sidebar_entries'][3]
58
+        assert sidebar_entry['slug'] == 'contents/pagemarkdownplus'
59
+        assert sidebar_entry['label'] == 'Rich Markdown Files'
60
+        assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=pagemarkdownplus"    # nopep8
61
+        assert sidebar_entry['hexcolor'] == "#f12d2d"
62
+        assert sidebar_entry['icon'] == "file-code"
63
+
64
+        sidebar_entry = workspace['sidebar_entries'][4]
65
+        assert sidebar_entry['slug'] == 'contents/files'
66
+        assert sidebar_entry['label'] == 'Files'
67
+        assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=file"  # nopep8
68
+        assert sidebar_entry['hexcolor'] == "#FF9900"
69
+        assert sidebar_entry['icon'] == "paperclip"
70
+
71
+        sidebar_entry = workspace['sidebar_entries'][5]
72
+        assert sidebar_entry['slug'] == 'contents/threads'
73
+        assert sidebar_entry['label'] == 'Threads'
74
+        assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=thread"  # nopep8
75
+        assert sidebar_entry['hexcolor'] == "#ad4cf9"
76
+        assert sidebar_entry['icon'] == "comments-o"
77
+
78
+        sidebar_entry = workspace['sidebar_entries'][6]
79
+        assert sidebar_entry['slug'] == 'calendar'
80
+        assert sidebar_entry['label'] == 'Calendar'
81
+        assert sidebar_entry['route'] == "/#/workspaces/1/calendar"  # nopep8
82
+        assert sidebar_entry['hexcolor'] == "#757575"
83
+        assert sidebar_entry['icon'] == "calendar-alt"
84
+
85
+    def test_api__get_workspace__err_403__unallowed_user(self) -> None:
86
+        """
87
+        Check obtain workspace unreachable for user
88
+        """
89
+        self.testapp.authorization = (
90
+            'Basic',
91
+            (
92
+                'lawrence-not-real-email@fsf.local',
93
+                'foobarbaz'
94
+            )
95
+        )
96
+        res = self.testapp.get('/api/v2/workspaces/1', status=403)
97
+        assert isinstance(res.json, dict)
98
+        assert 'code' in res.json.keys()
99
+        assert 'message' in res.json.keys()
100
+        assert 'details' in res.json.keys()
101
+
102
+    def test_api__get_workspace__err_401__unregistered_user(self) -> None:
103
+        """
104
+        Check obtain workspace without registered user.
105
+        """
106
+        self.testapp.authorization = (
107
+            'Basic',
108
+            (
109
+                'john@doe.doe',
110
+                'lapin'
111
+            )
112
+        )
113
+        res = self.testapp.get('/api/v2/workspaces/1', status=401)
114
+        assert isinstance(res.json, dict)
115
+        assert 'code' in res.json.keys()
116
+        assert 'message' in res.json.keys()
117
+        assert 'details' in res.json.keys()
118
+
119
+    def test_api__get_workspace__err_403__workspace_does_not_exist(self) -> None:  # nopep8
120
+        """
121
+        Check obtain workspace who does not exist with an existing user.
122
+        """
123
+        self.testapp.authorization = (
124
+            'Basic',
125
+            (
126
+                'admin@admin.admin',
127
+                'admin@admin.admin'
128
+            )
129
+        )
130
+        res = self.testapp.get('/api/v2/workspaces/5', status=403)
131
+        assert isinstance(res.json, dict)
132
+        assert 'code' in res.json.keys()
133
+        assert 'message' in res.json.keys()
134
+        assert 'details' in res.json.keys()
135
+
136
+
137
+class TestWorkspaceMembersEndpoint(FunctionalTest):
138
+    """
139
+    Tests for /api/v2/workspaces/{workspace_id}/members endpoint
140
+    """
141
+
142
+    fixtures = [BaseFixture, ContentFixtures]
143
+
144
+    def test_api__get_workspace_members__ok_200__nominal_case(self):
145
+        """
146
+        Check obtain workspace members list with a reachable workspace for user
147
+        """
148
+        self.testapp.authorization = (
149
+            'Basic',
150
+            (
151
+                'admin@admin.admin',
152
+                'admin@admin.admin'
153
+            )
154
+        )
155
+        res = self.testapp.get('/api/v2/workspaces/1/members', status=200).json_body   # nopep8
156
+        assert len(res) == 2
157
+        user_role = res[0]
158
+        assert user_role['role_slug'] == 'workspace_manager'
159
+        assert user_role['role_id'] == 8
160
+        assert user_role['user_id'] == 1
161
+        assert user_role['workspace_id'] == 1
162
+        assert user_role['user']['display_name'] == 'Global manager'
163
+        # TODO - G.M - 24-05-2018 - [Avatar] Replace
164
+        # by correct value when avatar feature will be enabled
165
+        assert user_role['user']['avatar_url'] is None
166
+
167
+    def test_api__get_workspace_members__err_403__unallowed_user(self):
168
+        """
169
+        Check obtain workspace members list with an unreachable workspace for
170
+        user
171
+        """
172
+        self.testapp.authorization = (
173
+            'Basic',
174
+            (
175
+                'lawrence-not-real-email@fsf.local',
176
+                'foobarbaz'
177
+            )
178
+        )
179
+        res = self.testapp.get('/api/v2/workspaces/3/members', status=403)
180
+        assert isinstance(res.json, dict)
181
+        assert 'code' in res.json.keys()
182
+        assert 'message' in res.json.keys()
183
+        assert 'details' in res.json.keys()
184
+
185
+    def test_api__get_workspace_members__err_401__unregistered_user(self):
186
+        """
187
+        Check obtain workspace members list with an unregistered user
188
+        """
189
+        self.testapp.authorization = (
190
+            'Basic',
191
+            (
192
+                'john@doe.doe',
193
+                'lapin'
194
+            )
195
+        )
196
+        res = self.testapp.get('/api/v2/workspaces/1/members', status=401)
197
+        assert isinstance(res.json, dict)
198
+        assert 'code' in res.json.keys()
199
+        assert 'message' in res.json.keys()
200
+        assert 'details' in res.json.keys()
201
+
202
+    def test_api__get_workspace_members__err_403__workspace_does_not_exist(self):  # nopep8
203
+        """
204
+        Check obtain workspace members list with an existing user but
205
+        an unexisting workspace
206
+        """
207
+        self.testapp.authorization = (
208
+            'Basic',
209
+            (
210
+                'admin@admin.admin',
211
+                'admin@admin.admin'
212
+            )
213
+        )
214
+        res = self.testapp.get('/api/v2/workspaces/5/members', status=403)
215
+        assert isinstance(res.json, dict)
216
+        assert 'code' in res.json.keys()
217
+        assert 'message' in res.json.keys()
218
+        assert 'details' in res.json.keys()

+ 222 - 61
tracim/tests/library/test_content_api.py View File

@@ -107,7 +107,11 @@ class TestContentApi(DefaultTest):
107 107
             config=self.app_config,
108 108
             current_user=None,
109 109
         )
110
-        group_api = GroupApi(current_user=None,session=self.session)
110
+        group_api = GroupApi(
111
+            current_user=None,
112
+            session=self.session,
113
+            config=self.app_config,
114
+        )
111 115
         groups = [group_api.get_one(Group.TIM_USER),
112 116
                   group_api.get_one(Group.TIM_MANAGER),
113 117
                   group_api.get_one(Group.TIM_ADMIN)]
@@ -116,7 +120,8 @@ class TestContentApi(DefaultTest):
116 120
                                         groups=groups, save_now=True)
117 121
         workspace = WorkspaceApi(
118 122
             current_user=user,
119
-            session=self.session
123
+            session=self.session,
124
+            config=self.app_config,
120 125
         ).create_workspace('test workspace', save_now=True)
121 126
         api = ContentApi(
122 127
             current_user=user,
@@ -133,7 +138,11 @@ class TestContentApi(DefaultTest):
133 138
 
134 139
         # Refresh instances after commit
135 140
         user = uapi.get_one(uid)
136
-        workspace_api = WorkspaceApi(current_user=user, session=self.session)
141
+        workspace_api = WorkspaceApi(
142
+            current_user=user,
143
+            session=self.session,
144
+            config=self.app_config
145
+        )
137 146
         workspace = workspace_api.get_one(wid)
138 147
         api = ContentApi(
139 148
             current_user=user,
@@ -154,7 +163,11 @@ class TestContentApi(DefaultTest):
154 163
 
155 164
         # Refresh instances after commit
156 165
         user = uapi.get_one(uid)
157
-        workspace_api = WorkspaceApi(current_user=user, session=self.session)
166
+        workspace_api = WorkspaceApi(
167
+            current_user=user,
168
+            session=self.session,
169
+            config=self.app_config
170
+        )
158 171
         workspace = workspace_api.get_one(wid)
159 172
         api = ContentApi(
160 173
             current_user=user, 
@@ -168,7 +181,11 @@ class TestContentApi(DefaultTest):
168 181
         # Test that the item is still available if "show deleted" is activated
169 182
         # Refresh instances after commit
170 183
         user = uapi.get_one(uid)
171
-        workspace_api = WorkspaceApi(current_user=user, session=self.session)
184
+        workspace_api = WorkspaceApi(
185
+            current_user=user,
186
+            session=self.session,
187
+            config=self.app_config,
188
+        )
172 189
         api = ContentApi(
173 190
             current_user=user,
174 191
             session=self.session,
@@ -184,14 +201,25 @@ class TestContentApi(DefaultTest):
184 201
             config=self.app_config,
185 202
             current_user=None,
186 203
         )
187
-        group_api = GroupApi(current_user=None, session=self.session)
204
+        group_api = GroupApi(
205
+            current_user=None,
206
+            session=self.session,
207
+            config=self.app_config,
208
+        )
188 209
         groups = [group_api.get_one(Group.TIM_USER),
189 210
                   group_api.get_one(Group.TIM_MANAGER),
190 211
                   group_api.get_one(Group.TIM_ADMIN)]
191 212
 
192
-        user = uapi.create_minimal_user(email='this.is@user',
193
-                                        groups=groups, save_now=True)
194
-        workspace_api = WorkspaceApi(current_user=user, session=self.session)
213
+        user = uapi.create_minimal_user(
214
+            email='this.is@user',
215
+            groups=groups,
216
+            save_now=True,
217
+        )
218
+        workspace_api = WorkspaceApi(
219
+            current_user=user,
220
+            session=self.session,
221
+            config=self.app_config,
222
+        )
195 223
         workspace = workspace_api.create_workspace(
196 224
             'test workspace',
197 225
             save_now=True
@@ -210,7 +238,11 @@ class TestContentApi(DefaultTest):
210 238
         transaction.commit()
211 239
         # Refresh instances after commit
212 240
         user = uapi.get_one(uid)
213
-        workspace_api = WorkspaceApi(current_user=user, session=self.session)
241
+        workspace_api = WorkspaceApi(
242
+            current_user=user,
243
+            session=self.session,
244
+            config=self.app_config,
245
+        )
214 246
         api = ContentApi(
215 247
             session=self.session,
216 248
             current_user=user,
@@ -231,7 +263,11 @@ class TestContentApi(DefaultTest):
231 263
 
232 264
         # Refresh instances after commit
233 265
         user = uapi.get_one(uid)
234
-        workspace_api = WorkspaceApi(current_user=user, session=self.session)
266
+        workspace_api = WorkspaceApi(
267
+            current_user=user,
268
+            session=self.session,
269
+            config=self.app_config,
270
+        )
235 271
         workspace = workspace_api.get_one(wid)
236 272
         api = ContentApi(
237 273
             current_user=user, 
@@ -245,7 +281,11 @@ class TestContentApi(DefaultTest):
245 281
 
246 282
         # Refresh instances after commit
247 283
         user = uapi.get_one(uid)
248
-        workspace_api = WorkspaceApi(current_user=user, session=self.session)
284
+        workspace_api = WorkspaceApi(
285
+            current_user=user,
286
+            session=self.session,
287
+            config=self.app_config,
288
+        )
249 289
         workspace = workspace_api.get_one(wid)
250 290
         api = ContentApi(
251 291
             current_user=user,
@@ -269,7 +309,11 @@ class TestContentApi(DefaultTest):
269 309
             config=self.app_config,
270 310
             current_user=None,
271 311
         )
272
-        group_api = GroupApi(current_user=None, session=self.session)
312
+        group_api = GroupApi(
313
+            current_user=None,
314
+            session=self.session,
315
+            config=self.app_config,
316
+        )
273 317
         groups = [group_api.get_one(Group.TIM_USER),
274 318
                   group_api.get_one(Group.TIM_MANAGER),
275 319
                   group_api.get_one(Group.TIM_ADMIN)]
@@ -281,7 +325,8 @@ class TestContentApi(DefaultTest):
281 325
         )
282 326
         workspace = WorkspaceApi(
283 327
             current_user=user,
284
-            session=self.session
328
+            session=self.session,
329
+            config=self.app_config,
285 330
         ).create_workspace(
286 331
             'test workspace',
287 332
             save_now=True
@@ -301,7 +346,11 @@ class TestContentApi(DefaultTest):
301 346
 
302 347
         # Refresh instances after commit
303 348
         user = uapi.get_one(uid)
304
-        workspace_api = WorkspaceApi(current_user=user, session=self.session)
349
+        workspace_api = WorkspaceApi(
350
+            current_user=user,
351
+            session=self.session,
352
+            config=self.app_config,
353
+        )
305 354
         workspace = workspace_api.get_one(wid)
306 355
         api = ContentApi(
307 356
             current_user=user, 
@@ -326,7 +375,11 @@ class TestContentApi(DefaultTest):
326 375
             config=self.app_config,
327 376
             current_user=None,
328 377
         )
329
-        group_api = GroupApi(current_user=None, session=self.session)
378
+        group_api = GroupApi(
379
+            current_user=None,
380
+            session=self.session,
381
+            config=self.app_config
382
+        )
330 383
         groups = [group_api.get_one(Group.TIM_USER),
331 384
                   group_api.get_one(Group.TIM_MANAGER),
332 385
                   group_api.get_one(Group.TIM_ADMIN)]
@@ -335,7 +388,8 @@ class TestContentApi(DefaultTest):
335 388
                                         groups=groups, save_now=True)
336 389
         workspace = WorkspaceApi(
337 390
             current_user=user,
338
-            session=self.session
391
+            session=self.session,
392
+            config=self.app_config
339 393
         ).create_workspace('test workspace', save_now=True)
340 394
         api = ContentApi(
341 395
             current_user=user, 
@@ -371,7 +425,11 @@ class TestContentApi(DefaultTest):
371 425
 
372 426
         # Refresh instances after commit
373 427
         user = uapi.get_one(uid)
374
-        workspace_api = WorkspaceApi(current_user=user, session=self.session)
428
+        workspace_api = WorkspaceApi(
429
+            current_user=user,
430
+            session=self.session,
431
+            config=self.app_config,
432
+        )
375 433
         workspace = workspace_api.get_one(wid)
376 434
         api = ContentApi(
377 435
             current_user=user,
@@ -394,7 +452,8 @@ class TestContentApi(DefaultTest):
394 452
         )
395 453
         group_api = GroupApi(
396 454
             current_user=None,
397
-            session=self.session
455
+            session=self.session,
456
+            config=self.app_config,
398 457
         )
399 458
         groups = [group_api.get_one(Group.TIM_USER),
400 459
                   group_api.get_one(Group.TIM_MANAGER),
@@ -405,7 +464,8 @@ class TestContentApi(DefaultTest):
405 464
 
406 465
         workspace = WorkspaceApi(
407 466
             current_user=user,
408
-            session=self.session
467
+            session=self.session,
468
+            config=self.app_config,
409 469
         ).create_workspace(
410 470
             'test workspace',
411 471
             save_now=True
@@ -432,7 +492,8 @@ class TestContentApi(DefaultTest):
432 492
         )
433 493
         group_api = GroupApi(
434 494
             current_user=None,
435
-            session=self.session
495
+            session=self.session,
496
+            config=self.app_config,
436 497
         )
437 498
         groups = [group_api.get_one(Group.TIM_USER),
438 499
                   group_api.get_one(Group.TIM_MANAGER),
@@ -443,7 +504,8 @@ class TestContentApi(DefaultTest):
443 504
 
444 505
         workspace = WorkspaceApi(
445 506
             current_user=user,
446
-            session=self.session
507
+            session=self.session,
508
+            config=self.app_config,
447 509
         ).create_workspace(
448 510
             'test workspace',
449 511
             save_now=True
@@ -474,7 +536,8 @@ class TestContentApi(DefaultTest):
474 536
         )
475 537
         group_api = GroupApi(
476 538
             current_user=None,
477
-            session=self.session
539
+            session=self.session,
540
+            config=self.config,
478 541
         )
479 542
         groups = [group_api.get_one(Group.TIM_USER),
480 543
                   group_api.get_one(Group.TIM_MANAGER),
@@ -485,7 +548,8 @@ class TestContentApi(DefaultTest):
485 548
 
486 549
         workspace = WorkspaceApi(
487 550
             current_user=user,
488
-            session=self.session
551
+            session=self.session,
552
+            config=self.app_config,
489 553
         ).create_workspace(
490 554
             'test workspace',
491 555
             save_now=True
@@ -516,7 +580,8 @@ class TestContentApi(DefaultTest):
516 580
         )
517 581
         group_api = GroupApi(
518 582
             current_user=None,
519
-            session=self.session
583
+            session=self.session,
584
+            config=self.app_config
520 585
         )
521 586
         groups = [group_api.get_one(Group.TIM_USER),
522 587
                   group_api.get_one(Group.TIM_MANAGER),
@@ -534,12 +599,17 @@ class TestContentApi(DefaultTest):
534 599
         )
535 600
         workspace = WorkspaceApi(
536 601
             current_user=user,
537
-            session=self.session
602
+            session=self.session,
603
+            config=self.app_config,
538 604
         ).create_workspace(
539 605
             'test workspace',
540 606
             save_now=True
541 607
         )
542
-        RoleApi(current_user=user, session=self.session).create_one(
608
+        RoleApi(
609
+            current_user=user,
610
+            session=self.session,
611
+            config=self.app_config,
612
+        ).create_one(
543 613
             user2,
544 614
             workspace,
545 615
             UserRoleInWorkspace.WORKSPACE_MANAGER,
@@ -581,6 +651,7 @@ class TestContentApi(DefaultTest):
581 651
         workspace2 = WorkspaceApi(
582 652
             current_user=user2,
583 653
             session=self.session,
654
+            config=self.app_config,
584 655
         ).create_workspace(
585 656
             'test workspace2',
586 657
             save_now=True
@@ -628,7 +699,8 @@ class TestContentApi(DefaultTest):
628 699
         )
629 700
         group_api = GroupApi(
630 701
             current_user=None,
631
-            session=self.session
702
+            session=self.session,
703
+            config=self.app_config,
632 704
         )
633 705
         groups = [group_api.get_one(Group.TIM_USER),
634 706
                   group_api.get_one(Group.TIM_MANAGER),
@@ -646,12 +718,17 @@ class TestContentApi(DefaultTest):
646 718
         )
647 719
         workspace = WorkspaceApi(
648 720
             current_user=user,
649
-            session=self.session
721
+            session=self.session,
722
+            config=self.app_config,
650 723
         ).create_workspace(
651 724
             'test workspace',
652 725
             save_now=True
653 726
         )
654
-        RoleApi(current_user=user, session=self.session).create_one(
727
+        RoleApi(
728
+            current_user=user,
729
+            session=self.session,
730
+            config=self.app_config,
731
+        ).create_one(
655 732
             user2,
656 733
             workspace,
657 734
             UserRoleInWorkspace.WORKSPACE_MANAGER,
@@ -692,7 +769,8 @@ class TestContentApi(DefaultTest):
692 769
         )
693 770
         workspace2 = WorkspaceApi(
694 771
             current_user=user2,
695
-            session=self.session
772
+            session=self.session,
773
+            config=self.app_config,
696 774
         ).create_workspace(
697 775
             'test workspace2',
698 776
             save_now=True
@@ -738,7 +816,8 @@ class TestContentApi(DefaultTest):
738 816
         )
739 817
         group_api = GroupApi(
740 818
             current_user=None,
741
-            session=self.session
819
+            session=self.session,
820
+            config=self.app_config,
742 821
         )
743 822
         groups = [group_api.get_one(Group.TIM_USER),
744 823
                   group_api.get_one(Group.TIM_MANAGER),
@@ -756,12 +835,17 @@ class TestContentApi(DefaultTest):
756 835
         )
757 836
         workspace = WorkspaceApi(
758 837
             current_user=user,
759
-            session=self.session
838
+            session=self.session,
839
+            config=self.app_config,
760 840
         ).create_workspace(
761 841
             'test workspace',
762 842
             save_now=True
763 843
         )
764
-        RoleApi(current_user=user, session=self.session).create_one(
844
+        RoleApi(
845
+            current_user=user,
846
+            session=self.session,
847
+            config=self.app_config,
848
+        ).create_one(
765 849
             user2, workspace,
766 850
             UserRoleInWorkspace.WORKSPACE_MANAGER,
767 851
             with_notif=False
@@ -837,7 +921,8 @@ class TestContentApi(DefaultTest):
837 921
         )
838 922
         group_api = GroupApi(
839 923
             current_user=None,
840
-            session=self.session
924
+            session=self.session,
925
+            config=self.app_config,
841 926
         )
842 927
         groups = [group_api.get_one(Group.TIM_USER),
843 928
                   group_api.get_one(Group.TIM_MANAGER),
@@ -851,6 +936,7 @@ class TestContentApi(DefaultTest):
851 936
         wapi = WorkspaceApi(
852 937
             current_user=user_a,
853 938
             session=self.session,
939
+            config=self.app_config,
854 940
         )
855 941
         workspace1 = wapi.create_workspace(
856 942
             'test workspace n°1',
@@ -862,6 +948,7 @@ class TestContentApi(DefaultTest):
862 948
         role_api1 = RoleApi(
863 949
             current_user=user_a,
864 950
             session=self.session,
951
+            config=self.app_config,
865 952
         )
866 953
         role_api1.create_one(
867 954
             user_b,
@@ -873,6 +960,7 @@ class TestContentApi(DefaultTest):
873 960
         role_api2 = RoleApi(
874 961
             current_user=user_b,
875 962
             session=self.session,
963
+            config=self.app_config,
876 964
         )
877 965
         role_api2.create_one(user_b, workspace2, UserRoleInWorkspace.READER,
878 966
                              False)
@@ -940,7 +1028,8 @@ class TestContentApi(DefaultTest):
940 1028
         )
941 1029
         group_api = GroupApi(
942 1030
             current_user=None,
943
-            session=self.session
1031
+            session=self.session,
1032
+            config = self.app_config,
944 1033
         )
945 1034
         groups = [group_api.get_one(Group.TIM_USER),
946 1035
                   group_api.get_one(Group.TIM_MANAGER),
@@ -957,10 +1046,15 @@ class TestContentApi(DefaultTest):
957 1046
             save_now=True
958 1047
         )
959 1048
 
960
-        wapi = WorkspaceApi(current_user=user_a, session=self.session)
1049
+        wapi = WorkspaceApi(
1050
+            current_user=user_a,
1051
+            session=self.session,
1052
+            config=self.app_config,
1053
+        )
961 1054
         workspace_api = WorkspaceApi(
962 1055
             current_user=user_a,
963
-            session=self.session
1056
+            session=self.session,
1057
+            config=self.app_config,
964 1058
         )
965 1059
         workspace = wapi.create_workspace(
966 1060
             'test workspace',
@@ -969,6 +1063,7 @@ class TestContentApi(DefaultTest):
969 1063
         role_api = RoleApi(
970 1064
             current_user=user_a,
971 1065
             session=self.session,
1066
+            config=self.app_config,
972 1067
         )
973 1068
         role_api.create_one(
974 1069
             user_b,
@@ -1004,7 +1099,11 @@ class TestContentApi(DefaultTest):
1004 1099
             config=self.app_config,
1005 1100
             current_user=None,
1006 1101
         )
1007
-        group_api = GroupApi(current_user=None, session=self.session)
1102
+        group_api = GroupApi(
1103
+            current_user=None,
1104
+            session=self.session,
1105
+            config=self.app_config,
1106
+        )
1008 1107
         groups = [group_api.get_one(Group.TIM_USER),
1009 1108
                   group_api.get_one(Group.TIM_MANAGER),
1010 1109
                   group_api.get_one(Group.TIM_ADMIN)]
@@ -1023,6 +1122,7 @@ class TestContentApi(DefaultTest):
1023 1122
         wapi = WorkspaceApi(
1024 1123
             current_user=user_a,
1025 1124
             session=self.session,
1125
+            config=self.app_config,
1026 1126
         )
1027 1127
         workspace = wapi.create_workspace(
1028 1128
             'test workspace',
@@ -1031,6 +1131,7 @@ class TestContentApi(DefaultTest):
1031 1131
         role_api = RoleApi(
1032 1132
             current_user=user_a,
1033 1133
             session=self.session,
1134
+            config=self.app_config,
1034 1135
         )
1035 1136
         role_api.create_one(
1036 1137
             user_b,
@@ -1097,7 +1198,11 @@ class TestContentApi(DefaultTest):
1097 1198
             config=self.app_config,
1098 1199
             current_user=None,
1099 1200
         )
1100
-        group_api = GroupApi(current_user=None, session=self.session)
1201
+        group_api = GroupApi(
1202
+            current_user=None,
1203
+            session=self.session,
1204
+            config=self.app_config,
1205
+        )
1101 1206
         groups = [group_api.get_one(Group.TIM_USER),
1102 1207
                   group_api.get_one(Group.TIM_MANAGER),
1103 1208
                   group_api.get_one(Group.TIM_ADMIN)]
@@ -1108,7 +1213,11 @@ class TestContentApi(DefaultTest):
1108 1213
             save_now=True
1109 1214
         )
1110 1215
 
1111
-        workspace_api = WorkspaceApi(current_user=user1, session=self.session)
1216
+        workspace_api = WorkspaceApi(
1217
+            current_user=user1,
1218
+            session=self.session,
1219
+            config=self.app_config,
1220
+        )
1112 1221
         workspace = workspace_api.create_workspace(
1113 1222
             'test workspace',
1114 1223
             save_now=True
@@ -1121,7 +1230,8 @@ class TestContentApi(DefaultTest):
1121 1230
 
1122 1231
         RoleApi(
1123 1232
             current_user=user1,
1124
-            session=self.session
1233
+            session=self.session,
1234
+            config=self.app_config,
1125 1235
         ).create_one(
1126 1236
             user2,
1127 1237
             workspace,
@@ -1152,7 +1262,8 @@ class TestContentApi(DefaultTest):
1152 1262
         user1 = uapi.get_one(u1id)
1153 1263
         workspace = WorkspaceApi(
1154 1264
             current_user=user1,
1155
-            session=self.session
1265
+            session=self.session,
1266
+            config=self.app_config,
1156 1267
         ).get_one(wid)
1157 1268
         api = ContentApi(
1158 1269
             current_user=user1,
@@ -1193,6 +1304,7 @@ class TestContentApi(DefaultTest):
1193 1304
         workspace = WorkspaceApi(
1194 1305
             current_user=user1,
1195 1306
             session=self.session,
1307
+            config=self.app_config,
1196 1308
         ).get_one(wid)
1197 1309
         api = ContentApi(
1198 1310
             current_user=user1,
@@ -1216,7 +1328,8 @@ class TestContentApi(DefaultTest):
1216 1328
         )
1217 1329
         group_api = GroupApi(
1218 1330
             current_user=None,
1219
-            session=self.session
1331
+            session=self.session,
1332
+            config = self.app_config,
1220 1333
         )
1221 1334
         groups = [group_api.get_one(Group.TIM_USER),
1222 1335
                   group_api.get_one(Group.TIM_MANAGER),
@@ -1231,6 +1344,7 @@ class TestContentApi(DefaultTest):
1231 1344
         workspace = WorkspaceApi(
1232 1345
             current_user=user1,
1233 1346
             session=self.session,
1347
+            config=self.app_config,
1234 1348
         ).create_workspace(
1235 1349
             'test workspace',
1236 1350
             save_now=True
@@ -1241,7 +1355,8 @@ class TestContentApi(DefaultTest):
1241 1355
 
1242 1356
         RoleApi(
1243 1357
             current_user=user1,
1244
-            session=self.session
1358
+            session=self.session,
1359
+            config=self.app_config,
1245 1360
         ).create_one(
1246 1361
             user2,
1247 1362
             workspace,
@@ -1293,7 +1408,8 @@ class TestContentApi(DefaultTest):
1293 1408
         )
1294 1409
         group_api = GroupApi(
1295 1410
             current_user=None,
1296
-            session=self.session
1411
+            session=self.session,
1412
+            config=self.app_config,
1297 1413
         )
1298 1414
         groups = [group_api.get_one(Group.TIM_USER),
1299 1415
                   group_api.get_one(Group.TIM_MANAGER),
@@ -1305,7 +1421,11 @@ class TestContentApi(DefaultTest):
1305 1421
             save_now=True
1306 1422
         )
1307 1423
 
1308
-        workspace_api = WorkspaceApi(current_user=user1, session=self.session)
1424
+        workspace_api = WorkspaceApi(
1425
+            current_user=user1,
1426
+            session=self.session,
1427
+            config=self.app_config,
1428
+        )
1309 1429
         workspace = workspace_api.create_workspace(
1310 1430
             'test workspace',
1311 1431
             save_now=True
@@ -1318,6 +1438,7 @@ class TestContentApi(DefaultTest):
1318 1438
         RoleApi(
1319 1439
             current_user=user1,
1320 1440
             session=self.session,
1441
+            config=self.app_config,
1321 1442
         ).create_one(
1322 1443
             user2,
1323 1444
             workspace,
@@ -1345,7 +1466,11 @@ class TestContentApi(DefaultTest):
1345 1466
 
1346 1467
         # Refresh instances after commit
1347 1468
         user1 = uapi.get_one(u1id)
1348
-        workspace_api2 = WorkspaceApi(current_user=user1, session=self.session)
1469
+        workspace_api2 = WorkspaceApi(
1470
+            current_user=user1,
1471
+            session=self.session,
1472
+            config=self.app_config,
1473
+        )
1349 1474
         workspace = workspace_api2.get_one(wid)
1350 1475
         api = ContentApi(
1351 1476
             current_user=user1,
@@ -1387,6 +1512,7 @@ class TestContentApi(DefaultTest):
1387 1512
         workspace = WorkspaceApi(
1388 1513
             current_user=user1,
1389 1514
             session=self.session,
1515
+            config=self.app_config,
1390 1516
         ).get_one(wid)
1391 1517
 
1392 1518
         updated = api.get_one(pcid, ContentType.Any, workspace)
@@ -1407,6 +1533,7 @@ class TestContentApi(DefaultTest):
1407 1533
         group_api = GroupApi(
1408 1534
             current_user=None,
1409 1535
             session=self.session,
1536
+            config=self.app_config,
1410 1537
         )
1411 1538
         groups = [group_api.get_one(Group.TIM_USER),
1412 1539
                   group_api.get_one(Group.TIM_MANAGER),
@@ -1418,7 +1545,11 @@ class TestContentApi(DefaultTest):
1418 1545
             save_now=True,
1419 1546
         )
1420 1547
 
1421
-        workspace_api = WorkspaceApi(current_user=user1, session=self.session)
1548
+        workspace_api = WorkspaceApi(
1549
+            current_user=user1,
1550
+            session=self.session,
1551
+            config=self.app_config,
1552
+        )
1422 1553
         workspace = workspace_api.create_workspace(
1423 1554
             'test workspace',
1424 1555
             save_now=True
@@ -1430,6 +1561,7 @@ class TestContentApi(DefaultTest):
1430 1561
         RoleApi(
1431 1562
             current_user=user1,
1432 1563
             session=self.session,
1564
+            config=self.app_config,
1433 1565
         ).create_one(
1434 1566
             user2,
1435 1567
             workspace,
@@ -1485,7 +1617,11 @@ class TestContentApi(DefaultTest):
1485 1617
             config=self.app_config,
1486 1618
             current_user=None,
1487 1619
         )
1488
-        group_api = GroupApi(current_user=None, session=self.session)
1620
+        group_api = GroupApi(
1621
+            current_user=None,
1622
+            session=self.session,
1623
+            config=self.app_config,
1624
+        )
1489 1625
         groups = [group_api.get_one(Group.TIM_USER),
1490 1626
                   group_api.get_one(Group.TIM_MANAGER),
1491 1627
                   group_api.get_one(Group.TIM_ADMIN)]
@@ -1497,7 +1633,11 @@ class TestContentApi(DefaultTest):
1497 1633
         )
1498 1634
         u1id = user1.user_id
1499 1635
 
1500
-        workspace_api = WorkspaceApi(current_user=user1, session=self.session)
1636
+        workspace_api = WorkspaceApi(
1637
+            current_user=user1,
1638
+            session=self.session,
1639
+            config=self.app_config,
1640
+        )
1501 1641
         workspace = workspace_api.create_workspace(
1502 1642
             'test workspace',
1503 1643
             save_now=True
@@ -1509,7 +1649,8 @@ class TestContentApi(DefaultTest):
1509 1649
 
1510 1650
         RoleApi(
1511 1651
             current_user=user1,
1512
-            session=self.session
1652
+            session=self.session,
1653
+            config=self.app_config,
1513 1654
         ).create_one(
1514 1655
             user2,
1515 1656
             workspace,
@@ -1545,7 +1686,8 @@ class TestContentApi(DefaultTest):
1545 1686
         ).get_one(u1id)
1546 1687
         workspace = WorkspaceApi(
1547 1688
             current_user=user1,
1548
-            session=self.session
1689
+            session=self.session,
1690
+            config=self.app_config,
1549 1691
         ).get_one(wid)
1550 1692
 
1551 1693
         content = api.get_one(pcid, ContentType.Any, workspace)
@@ -1583,6 +1725,7 @@ class TestContentApi(DefaultTest):
1583 1725
         workspace = WorkspaceApi(
1584 1726
             current_user=user1,
1585 1727
             session=self.session,
1728
+            config=self.app_config,
1586 1729
         ).get_one(wid)
1587 1730
         u2 = UserApi(
1588 1731
             current_user=None,
@@ -1632,7 +1775,8 @@ class TestContentApi(DefaultTest):
1632 1775
         )
1633 1776
         group_api = GroupApi(
1634 1777
             current_user=None,
1635
-            session=self.session
1778
+            session=self.session,
1779
+            config=self.app_config,
1636 1780
         )
1637 1781
         groups = [group_api.get_one(Group.TIM_USER),
1638 1782
                   group_api.get_one(Group.TIM_MANAGER),
@@ -1645,7 +1789,11 @@ class TestContentApi(DefaultTest):
1645 1789
         )
1646 1790
         u1id = user1.user_id
1647 1791
 
1648
-        workspace_api = WorkspaceApi(current_user=user1, session=self.session)
1792
+        workspace_api = WorkspaceApi(
1793
+            current_user=user1,
1794
+            session=self.session,
1795
+            config=self.app_config,
1796
+        )
1649 1797
         workspace = workspace_api.create_workspace(
1650 1798
             'test workspace',
1651 1799
             save_now=True
@@ -1657,7 +1805,8 @@ class TestContentApi(DefaultTest):
1657 1805
 
1658 1806
         RoleApi(
1659 1807
             current_user=user1,
1660
-            session=self.session
1808
+            session=self.session,
1809
+            config=self.app_config,
1661 1810
         ).create_one(
1662 1811
             user2,
1663 1812
             workspace,
@@ -1692,6 +1841,7 @@ class TestContentApi(DefaultTest):
1692 1841
         workspace = WorkspaceApi(
1693 1842
             current_user=user1,
1694 1843
             session=self.session,
1844
+            config=self.app_config,
1695 1845
         ).get_one(wid)
1696 1846
 
1697 1847
         content = api.get_one(pcid, ContentType.Any, workspace)
@@ -1729,6 +1879,7 @@ class TestContentApi(DefaultTest):
1729 1879
         workspace = WorkspaceApi(
1730 1880
             current_user=user1,
1731 1881
             session=self.session,
1882
+            config=self.app_config,
1732 1883
         ).get_one(wid)
1733 1884
         # show archived is used at the top end of the test
1734 1885
         api = ContentApi(
@@ -1782,6 +1933,7 @@ class TestContentApi(DefaultTest):
1782 1933
         group_api = GroupApi(
1783 1934
             current_user=None,
1784 1935
             session=self.session,
1936
+            config=self.app_config,
1785 1937
         )
1786 1938
         groups = [group_api.get_one(Group.TIM_USER),
1787 1939
                   group_api.get_one(Group.TIM_MANAGER),
@@ -1792,7 +1944,8 @@ class TestContentApi(DefaultTest):
1792 1944
 
1793 1945
         workspace = WorkspaceApi(
1794 1946
             current_user=user,
1795
-            session=self.session
1947
+            session=self.session,
1948
+            config=self.app_config,
1796 1949
         ).create_workspace(
1797 1950
             'test workspace',
1798 1951
             save_now=True
@@ -1802,7 +1955,6 @@ class TestContentApi(DefaultTest):
1802 1955
             current_user=user, 
1803 1956
             session=self.session,
1804 1957
             config=self.app_config,
1805
-
1806 1958
         )
1807 1959
         a = api.create(ContentType.Folder, workspace, None,
1808 1960
                        'this is randomized folder', True)
@@ -1837,6 +1989,7 @@ class TestContentApi(DefaultTest):
1837 1989
         group_api = GroupApi(
1838 1990
             current_user=None,
1839 1991
             session=self.session,
1992
+            config=self.app_config,
1840 1993
         )
1841 1994
         groups = [group_api.get_one(Group.TIM_USER),
1842 1995
                   group_api.get_one(Group.TIM_MANAGER),
@@ -1847,7 +2000,8 @@ class TestContentApi(DefaultTest):
1847 2000
 
1848 2001
         workspace = WorkspaceApi(
1849 2002
             current_user=user,
1850
-            session=self.session
2003
+            session=self.session,
2004
+            config=self.app_config,
1851 2005
         ).create_workspace(
1852 2006
             'test workspace',
1853 2007
             save_now=True,
@@ -1888,7 +2042,11 @@ class TestContentApi(DefaultTest):
1888 2042
             config=self.app_config,
1889 2043
             current_user=None,
1890 2044
         )
1891
-        group_api = GroupApi(current_user=None, session=self.session)
2045
+        group_api = GroupApi(
2046
+            current_user=None,
2047
+            session=self.session,
2048
+            config=self.app_config,
2049
+        )
1892 2050
         groups = [group_api.get_one(Group.TIM_USER),
1893 2051
                   group_api.get_one(Group.TIM_MANAGER),
1894 2052
                   group_api.get_one(Group.TIM_ADMIN)]
@@ -1898,7 +2056,8 @@ class TestContentApi(DefaultTest):
1898 2056
 
1899 2057
         workspace = WorkspaceApi(
1900 2058
             current_user=user,
1901
-            session=self.session
2059
+            session=self.session,
2060
+            config=self.app_config,
1902 2061
         ).create_workspace('test workspace', save_now=True)
1903 2062
 
1904 2063
         api = ContentApi(
@@ -2032,6 +2191,7 @@ class TestContentApiSecurity(DefaultTest):
2032 2191
         bob_workspace = WorkspaceApi(
2033 2192
             current_user=bob,
2034 2193
             session=self.session,
2194
+            config=self.app_config,
2035 2195
         ).create_workspace(
2036 2196
             'bob_workspace',
2037 2197
             save_now=True,
@@ -2039,6 +2199,7 @@ class TestContentApiSecurity(DefaultTest):
2039 2199
         admin_workspace = WorkspaceApi(
2040 2200
             current_user=admin,
2041 2201
             session=self.session,
2202
+            config=self.app_config,
2042 2203
         ).create_workspace(
2043 2204
             'admin_workspace',
2044 2205
             save_now=True,

+ 5 - 0
tracim/tests/library/test_group_api.py View File

@@ -18,6 +18,7 @@ class TestGroupApi(DefaultTest):
18 18
         api = GroupApi(
19 19
             current_user=None,
20 20
             session=self.session,
21
+            config=self.app_config,
21 22
         )
22 23
         group = api.get_one(1)
23 24
         assert group.group_id == 1
@@ -30,6 +31,7 @@ class TestGroupApi(DefaultTest):
30 31
         api = GroupApi(
31 32
             current_user=None,
32 33
             session=self.session,
34
+            config=self.app_config,
33 35
         )
34 36
         with pytest.raises(GroupNotExist):
35 37
             group = api.get_one(10)
@@ -41,6 +43,7 @@ class TestGroupApi(DefaultTest):
41 43
         api = GroupApi(
42 44
             current_user=None,
43 45
             session=self.session,
46
+            config=self.app_config,
44 47
         )
45 48
         group = api.get_one_with_name('administrators')
46 49
         assert group.group_id == 3
@@ -53,6 +56,7 @@ class TestGroupApi(DefaultTest):
53 56
         api = GroupApi(
54 57
             current_user=None,
55 58
             session=self.session,
59
+            config=self.app_config,
56 60
         )
57 61
         with pytest.raises(GroupNotExist):
58 62
             group = api.get_one_with_name('unknown_group')
@@ -64,6 +68,7 @@ class TestGroupApi(DefaultTest):
64 68
         api = GroupApi(
65 69
             current_user=None,
66 70
             session=self.session,
71
+            config=self.app_config,
67 72
         )
68 73
         groups = api.get_all()
69 74
         assert ['users', 'managers', 'administrators'] == [group.group_name for group in groups]  # nopep8

+ 2 - 2
tracim/tests/library/test_user_api.py View File

@@ -91,7 +91,7 @@ class TestUserApi(DefaultTest):
91 91
             session=self.session,
92 92
             config=self.config,
93 93
         )
94
-        with pytest.raises(NoResultFound):
94
+        with pytest.raises(UserNotExist):
95 95
             api.get_one_by_email('unknown')
96 96
 
97 97
     def test_unit__get_all__ok__nominal_case(self):
@@ -187,4 +187,4 @@ class TestUserApi(DefaultTest):
187 187
             config=self.config,
188 188
         )
189 189
         with pytest.raises(AuthenticationFailed):
190
-            api.authenticate_user('unknown_user', 'wrong_password')
190
+            api.authenticate_user('admin@admin.admin', 'wrong_password')

+ 8 - 2
tracim/tests/library/test_workspace.py View File

@@ -44,6 +44,7 @@ class TestThread(DefaultTest):
44 44
             .filter(User.email == 'admin@admin.admin').one()
45 45
         wapi = WorkspaceApi(
46 46
             session=self.session,
47
+            config=self.app_config,
47 48
             current_user=admin,
48 49
         )
49 50
         w = wapi.create_workspace(label='workspace w', save_now=True)
@@ -57,6 +58,7 @@ class TestThread(DefaultTest):
57 58
         rapi = RoleApi(
58 59
             session=self.session,
59 60
             current_user=admin,
61
+            config=self.app_config,
60 62
         )
61 63
         r = rapi.create_one(u, w, UserRoleInWorkspace.READER, with_notif=True)
62 64
         eq_([r, ], wapi.get_notifiable_roles(workspace=w))
@@ -75,6 +77,7 @@ class TestThread(DefaultTest):
75 77
         wapi = WorkspaceApi(
76 78
             session=self.session,
77 79
             current_user=admin,
80
+            config=self.app_config,
78 81
         )
79 82
         eq_([], wapi.get_all_manageable())
80 83
         # Checks an admin gets all workspaces.
@@ -87,15 +90,18 @@ class TestThread(DefaultTest):
87 90
         gapi = GroupApi(
88 91
             session=self.session,
89 92
             current_user=None,
93
+            config=self.app_config,
90 94
         )
91 95
         u = uapi.create_minimal_user('u.s@e.r', [gapi.get_one(Group.TIM_USER)], True)
92 96
         wapi = WorkspaceApi(
93 97
             session=self.session,
94
-            current_user=u
98
+            current_user=u,
99
+            config=self.app_config,
95 100
         )
96 101
         rapi = RoleApi(
97 102
             session=self.session,
98
-            current_user=u
103
+            current_user=u,
104
+            config=self.app_config,
99 105
         )
100 106
         rapi.create_one(u, w4, UserRoleInWorkspace.READER, False)
101 107
         rapi.create_one(u, w3, UserRoleInWorkspace.CONTRIBUTOR, False)

+ 0 - 0
tracim/views/core_api/__init__.py View File


+ 68 - 3
tracim/views/core_api/schemas.py View File

@@ -1,13 +1,16 @@
1 1
 # coding=utf-8
2 2
 import marshmallow
3 3
 from marshmallow import post_load
4
+from marshmallow.validate import OneOf
4 5
 
5
-from tracim.models.context_models import LoginCredentials, UserInContext
6
+from tracim.models.auth import Profile
7
+from tracim.models.context_models import LoginCredentials
8
+from tracim.models.data import UserRoleInWorkspace
6 9
 
7 10
 
8 11
 class ProfileSchema(marshmallow.Schema):
9
-    id = marshmallow.fields.Int(dump_only=True)
10
-    slug = marshmallow.fields.String(attribute='name')
12
+    id = marshmallow.fields.Int(dump_only=True, validate=OneOf(Profile._IDS))
13
+    slug = marshmallow.fields.String(attribute='name', validate=OneOf(Profile._NAME))
11 14
 
12 15
 
13 16
 class UserSchema(marshmallow.Schema):
@@ -32,6 +35,14 @@ class UserSchema(marshmallow.Schema):
32 35
     )
33 36
 
34 37
 
38
+class UserIdPathSchema(marshmallow.Schema):
39
+    user_id = marshmallow.fields.Int()
40
+
41
+
42
+class WorkspaceIdPathSchema(marshmallow.Schema):
43
+    workspace_id = marshmallow.fields.Int()
44
+
45
+
35 46
 class BasicAuthSchema(marshmallow.Schema):
36 47
 
37 48
     email = marshmallow.fields.Email(required=True)
@@ -48,3 +59,57 @@ class LoginOutputHeaders(marshmallow.Schema):
48 59
 
49 60
 class NoContentSchema(marshmallow.Schema):
50 61
     pass
62
+
63
+
64
+class WorkspaceMenuEntrySchema(marshmallow.Schema):
65
+    slug = marshmallow.fields.String()
66
+    label = marshmallow.fields.String()
67
+    route = marshmallow.fields.String()
68
+    hexcolor = marshmallow.fields.String()
69
+    icon = marshmallow.fields.String()
70
+
71
+
72
+class WorkspaceSchema(marshmallow.Schema):
73
+    id = marshmallow.fields.Int()
74
+    slug = marshmallow.fields.String()
75
+    label = marshmallow.fields.String()
76
+    description = marshmallow.fields.String()
77
+    sidebar_entries = marshmallow.fields.Nested(
78
+        WorkspaceMenuEntrySchema,
79
+        many=True,
80
+    )
81
+
82
+
83
+class WorkspaceDigestSchema(marshmallow.Schema):
84
+    id = marshmallow.fields.Int()
85
+    label = marshmallow.fields.String()
86
+    sidebar_entries = marshmallow.fields.Nested(
87
+        WorkspaceMenuEntrySchema,
88
+        many=True,
89
+    )
90
+
91
+
92
+class WorkspaceMemberSchema(marshmallow.Schema):
93
+    role_id = marshmallow.fields.Int(validate=OneOf(UserRoleInWorkspace.get_all_role_values()))  # nopep8
94
+    role_slug = marshmallow.fields.String(validate=OneOf(UserRoleInWorkspace.get_all_role_slug()))  # nopep8
95
+    user_id = marshmallow.fields.Int()
96
+    workspace_id = marshmallow.fields.Int()
97
+    user = marshmallow.fields.Nested(
98
+        UserSchema(only=('display_name', 'avatar_url'))
99
+    )
100
+
101
+
102
+class ApplicationConfigSchema(marshmallow.Schema):
103
+    pass
104
+    #  TODO - G.M - 24-05-2018 - Set this
105
+
106
+
107
+class ApplicationSchema(marshmallow.Schema):
108
+    label = marshmallow.fields.String()
109
+    slug = marshmallow.fields.String()
110
+    icon = marshmallow.fields.String()
111
+    hexcolor = marshmallow.fields.String()
112
+    is_active = marshmallow.fields.Boolean()
113
+    config = marshmallow.fields.Nested(
114
+        ApplicationConfigSchema,
115
+    )

+ 42 - 0
tracim/views/core_api/system_controller.py View File

@@ -0,0 +1,42 @@
1
+# coding=utf-8
2
+from pyramid.config import Configurator
3
+
4
+from tracim.exceptions import NotAuthentificated, InsufficientUserProfile
5
+from tracim.lib.utils.authorization import require_profile
6
+from tracim.models import Group
7
+from tracim.models.applications import applications
8
+
9
+try:  # Python 3.5+
10
+    from http import HTTPStatus
11
+except ImportError:
12
+    from http import client as HTTPStatus
13
+
14
+from tracim import TracimRequest
15
+from tracim.extensions import hapic
16
+from tracim.views.controllers import Controller
17
+from tracim.views.core_api.schemas import ApplicationSchema
18
+
19
+
20
+class SystemController(Controller):
21
+
22
+    @hapic.with_api_doc()
23
+    @hapic.handle_exception(NotAuthentificated, HTTPStatus.UNAUTHORIZED)
24
+    @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
25
+    @require_profile(Group.TIM_USER)
26
+    @hapic.output_body(ApplicationSchema(many=True),)
27
+    def applications(self, context, request: TracimRequest, hapic_data=None):
28
+        """
29
+        Get list of alls applications installed in this tracim instance.
30
+        """
31
+        return applications
32
+
33
+    def bind(self, configurator: Configurator) -> None:
34
+        """
35
+        Create all routes and views using pyramid configurator
36
+        for this controller
37
+        """
38
+
39
+        # Applications
40
+        configurator.add_route('applications', '/system/applications', request_method='GET')  # nopep8
41
+        configurator.add_view(self.applications, route_name='applications')
42
+

+ 55 - 0
tracim/views/core_api/user_controller.py View File

@@ -0,0 +1,55 @@
1
+from pyramid.config import Configurator
2
+from sqlalchemy.orm.exc import NoResultFound
3
+
4
+from tracim.lib.utils.authorization import require_same_user_or_profile
5
+from tracim.models import Group
6
+from tracim.models.context_models import WorkspaceInContext
7
+
8
+try:  # Python 3.5+
9
+    from http import HTTPStatus
10
+except ImportError:
11
+    from http import client as HTTPStatus
12
+
13
+from tracim import hapic, TracimRequest
14
+from tracim.exceptions import NotAuthentificated, InsufficientUserProfile, \
15
+    UserNotExist
16
+from tracim.lib.core.user import UserApi
17
+from tracim.lib.core.workspace import WorkspaceApi
18
+from tracim.views.controllers import Controller
19
+from tracim.views.core_api.schemas import UserIdPathSchema, \
20
+    WorkspaceDigestSchema
21
+
22
+
23
+class UserController(Controller):
24
+
25
+    @hapic.with_api_doc()
26
+    @hapic.handle_exception(NotAuthentificated, HTTPStatus.UNAUTHORIZED)
27
+    @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
28
+    @hapic.handle_exception(UserNotExist, HTTPStatus.NOT_FOUND)
29
+    @require_same_user_or_profile(Group.TIM_ADMIN)
30
+    @hapic.input_path(UserIdPathSchema())
31
+    @hapic.output_body(WorkspaceDigestSchema(many=True),)
32
+    def user_workspace(self, context, request: TracimRequest, hapic_data=None):
33
+        """
34
+        Get list of user workspaces
35
+        """
36
+        app_config = request.registry.settings['CFG']
37
+        wapi = WorkspaceApi(
38
+            current_user=request.current_user,  # User
39
+            session=request.dbsession,
40
+            config=app_config,
41
+        )
42
+        return [
43
+            WorkspaceInContext(workspace, request.dbsession, app_config)
44
+            for workspace in wapi.get_all_for_user(request.candidate_user)
45
+        ]
46
+
47
+    def bind(self, configurator: Configurator) -> None:
48
+        """
49
+        Create all routes and views using pyramid configurator
50
+        for this controller
51
+        """
52
+
53
+        # Applications
54
+        configurator.add_route('user_workspace', '/users/{user_id}/workspaces', request_method='GET')  # nopep8
55
+        configurator.add_view(self.user_workspace, route_name='user_workspace')

+ 86 - 0
tracim/views/core_api/workspace_controller.py View File

@@ -0,0 +1,86 @@
1
+import typing
2
+
3
+from pyramid.config import Configurator
4
+from sqlalchemy.orm.exc import NoResultFound
5
+
6
+from tracim.lib.core.userworkspace import RoleApi
7
+from tracim.lib.utils.authorization import require_workspace_role
8
+from tracim.models.context_models import WorkspaceInContext, \
9
+    UserRoleWorkspaceInContext
10
+from tracim.models.data import UserRoleInWorkspace
11
+
12
+try:  # Python 3.5+
13
+    from http import HTTPStatus
14
+except ImportError:
15
+    from http import client as HTTPStatus
16
+
17
+from tracim import hapic, TracimRequest
18
+from tracim.exceptions import NotAuthentificated, InsufficientUserProfile, \
19
+    WorkspaceNotFound
20
+from tracim.lib.core.user import UserApi
21
+from tracim.lib.core.workspace import WorkspaceApi
22
+from tracim.views.controllers import Controller
23
+from tracim.views.core_api.schemas import WorkspaceSchema, UserSchema, \
24
+    WorkspaceIdPathSchema, WorkspaceMemberSchema
25
+
26
+
27
+class WorkspaceController(Controller):
28
+
29
+    @hapic.with_api_doc()
30
+    @hapic.handle_exception(NotAuthentificated, HTTPStatus.UNAUTHORIZED)
31
+    @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
32
+    @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
33
+    @require_workspace_role(UserRoleInWorkspace.READER)
34
+    @hapic.input_path(WorkspaceIdPathSchema())
35
+    @hapic.output_body(WorkspaceSchema())
36
+    def workspace(self, context, request: TracimRequest, hapic_data=None):
37
+        """
38
+        Get workspace informations
39
+        """
40
+        wid = hapic_data.path['workspace_id']
41
+        app_config = request.registry.settings['CFG']
42
+        wapi = WorkspaceApi(
43
+            current_user=request.current_user,  # User
44
+            session=request.dbsession,
45
+            config=app_config,
46
+        )
47
+        return wapi.get_workspace_with_context(request.current_workspace)
48
+
49
+    @hapic.with_api_doc()
50
+    @hapic.handle_exception(NotAuthentificated, HTTPStatus.UNAUTHORIZED)
51
+    @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
52
+    @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
53
+    @require_workspace_role(UserRoleInWorkspace.READER)
54
+    @hapic.input_path(WorkspaceIdPathSchema())
55
+    @hapic.output_body(WorkspaceMemberSchema(many=True))
56
+    def workspaces_members(
57
+            self,
58
+            context,
59
+            request: TracimRequest,
60
+            hapic_data=None
61
+    ) -> typing.List[UserRoleWorkspaceInContext]:
62
+        """
63
+        Get Members of this workspace
64
+        """
65
+        app_config = request.registry.settings['CFG']
66
+        rapi = RoleApi(
67
+            current_user=request.current_user,
68
+            session=request.dbsession,
69
+            config=app_config,
70
+        )
71
+        return [
72
+            rapi.get_user_role_workspace_with_context(user_role)
73
+            for user_role in rapi.get_all_for_workspace(request.current_workspace)
74
+        ]
75
+
76
+    def bind(self, configurator: Configurator) -> None:
77
+        """
78
+        Create all routes and views using pyramid configurator
79
+        for this controller
80
+        """
81
+
82
+        # Applications
83
+        configurator.add_route('workspace', '/workspaces/{workspace_id}', request_method='GET')  # nopep8
84
+        configurator.add_view(self.workspace, route_name='workspace')
85
+        configurator.add_route('workspace_members', '/workspaces/{workspace_id}/members', request_method='GET')  # nopep8
86
+        configurator.add_view(self.workspaces_members, route_name='workspace_members')  # nopep8