Browse Source

WIP Session Related method of Tracim v2 API

Guénaël Muller 7 years ago
parent
commit
dab07ac8f9

+ 9 - 6
tracim/__init__.py View File

@@ -13,7 +13,8 @@ from tracim.lib.utils.authentification import basic_auth_check_credentials
13 13
 from tracim.lib.utils.authentification import BASIC_AUTH_WEBUI_REALM
14 14
 from tracim.lib.utils.authorization import AcceptAllAuthorizationPolicy
15 15
 from tracim.lib.utils.authorization import TRACIM_DEFAULT_PERM
16
-from tracim.views.example_api.example_api_controller import ExampleApiController
16
+from tracim.views.core_api.session_controller import SessionController
17
+#from tracim.views.example_api.example_api_controller import ExampleApiController
17 18
 from tracim.views.default.default_controller import DefaultController
18 19
 
19 20
 
@@ -48,15 +49,17 @@ def main(global_config, **settings):
48 49
     # Add controllers
49 50
     default_controllers = DefaultController()
50 51
     default_controllers.bind(configurator)
51
-    example_api_controllers = ExampleApiController()
52
-    example_api_controllers.bind(configurator)
53
-
52
+    #example_api_controllers = ExampleApiController()
53
+    #example_api_controllers.bind(configurator)
54
+    session_api = SessionController()
55
+    session_api.bind(configurator)
54 56
     # TODO - G.M - 09-04-2018 - Enable swagger ui doc
55 57
     # time.sleep(1)
56 58
     # s = json.dumps(
57 59
     #     hapic.generate_doc(
58
-    #         title='Fake API',
59
-    #         description='just an example of hapic API'
60
+    #         title='Tracim v2 API',
61
+    #         description='API of Tracim v2',
62
+    #
60 63
     #     )
61 64
     # )
62 65
     # time.sleep(1)

+ 4 - 0
tracim/exceptions.py View File

@@ -83,3 +83,7 @@ class ImmutableAttribute(TracimException):
83 83
 
84 84
 class DigestAuthNotImplemented(Exception):
85 85
     pass
86
+
87
+
88
+class LoginFailed(TracimException):
89
+    pass

+ 36 - 0
tracim/views/core_api/schemas.py View File

@@ -0,0 +1,36 @@
1
+# coding=utf-8
2
+import marshmallow
3
+
4
+
5
+class ProfileSchema(marshmallow.Schema):
6
+    id = marshmallow.fields.Int(dump_only=True, required=True)
7
+    name = marshmallow.fields.String()
8
+
9
+
10
+class UserSchema(marshmallow.Schema):
11
+    user_id = marshmallow.fields.Int(dump_only=True, required=True)
12
+    email = marshmallow.fields.Email(required=True)
13
+    display_name = marshmallow.fields.String()
14
+    created = marshmallow.fields.DateTime(format='iso8601')
15
+    is_active = marshmallow.fields.Bool()
16
+    # TODO - G.M - 17-04-2018 - Restrict timezone values
17
+    timezone = marshmallow.fields.String()
18
+    # TODO - G.M - 17-04-2018 - check this, relative url allowed ?
19
+    caldav_url = marshmallow.fields.Url(relative=True)
20
+    profile = marshmallow.fields.Nested(
21
+        ProfileSchema,
22
+        many=False,
23
+    )
24
+
25
+
26
+class BasicAuthSchema(marshmallow.Schema):
27
+    email = marshmallow.fields.Email(required=True)
28
+    password = marshmallow.fields.String(required=True, load_only=True)
29
+
30
+
31
+class LoginOutputHeaders(marshmallow.Schema):
32
+    expire_after = marshmallow.fields.String()
33
+
34
+
35
+class OkResponse(marshmallow.Schema):
36
+    message = marshmallow.fields.String(required=True)

+ 114 - 0
tracim/views/core_api/session_controller.py View File

@@ -0,0 +1,114 @@
1
+# coding=utf-8
2
+from sqlalchemy.orm.exc import NoResultFound
3
+
4
+from tracim import TracimRequest
5
+from tracim.extensions import hapic
6
+from tracim.lib.core.user import UserApi
7
+from tracim.views.controllers import Controller
8
+from pyramid.config import Configurator
9
+
10
+from tracim.views.core_api.schemas import UserSchema, OkResponse
11
+from tracim.views.core_api.schemas import LoginOutputHeaders
12
+from tracim.views.core_api.schemas import BasicAuthSchema
13
+from tracim.exceptions import NotAuthentificated, LoginFailed
14
+
15
+try:  # Python 3.5+
16
+    from http import HTTPStatus
17
+except ImportError:
18
+    from http import client as HTTPStatus
19
+
20
+
21
+class SessionController(Controller):
22
+
23
+    @hapic.with_api_doc()
24
+    @hapic.input_headers(LoginOutputHeaders())
25
+    @hapic.input_query(BasicAuthSchema())
26
+    @hapic.handle_exception(LoginFailed, http_code=HTTPStatus.BAD_REQUEST)
27
+    # TODO - G.M - 17-04-2018 - fix output header ?
28
+    # @hapic.output_headers()
29
+    @hapic.output_body(OkResponse())
30
+    def login(self, context, request: TracimRequest, hapic_data=None):
31
+        """
32
+        Logs user into the system
33
+        """
34
+        email = request.params['email']
35
+        password = request.params['password']
36
+        if not (email and password):
37
+            raise Exception
38
+        app_config = request.registry.settings['CFG']
39
+        try:
40
+            uapi = UserApi(
41
+                None,
42
+                session=request.dbsession,
43
+                config=app_config,
44
+            )
45
+            user = uapi.get_one_by_email(email)
46
+            valid_password = user.validate_password(password)
47
+            if not valid_password:
48
+                # Bad password
49
+                raise LoginFailed('Bad Credentials')
50
+        except NoResultFound:
51
+            # User does not exist
52
+            raise LoginFailed('Bad Credentials')
53
+        return {'message': 'ok'}
54
+
55
+    @hapic.with_api_doc()
56
+    @hapic.output_body(OkResponse())
57
+    def logout(self, context, request: TracimRequest, hapic_data=None):
58
+        """
59
+        Logs out current logged in user session
60
+        """
61
+        return {'message': 'ok'}
62
+
63
+    @hapic.with_api_doc()
64
+    @hapic.handle_exception(
65
+        NotAuthentificated,
66
+        http_code=HTTPStatus.UNAUTHORIZED
67
+    )
68
+    @hapic.output_body(
69
+        UserSchema(),
70
+        default_http_code=HTTPStatus.OK,
71
+    )
72
+    def whoami(self, context, request: TracimRequest, hapic_data=None):
73
+        """
74
+        Return current logged in user or 401
75
+        """
76
+        return request.current_user
77
+
78
+    def bind(self, configurator: Configurator):
79
+
80
+        # Login
81
+        configurator.add_route(
82
+            'login',
83
+            '/sessions/login',
84
+            request_method='GET'
85
+        )
86
+        configurator.add_view(
87
+            self.login,
88
+            route_name='login',
89
+            renderer='json'
90
+        )
91
+        # Logout
92
+        configurator.add_route(
93
+            'logout',
94
+            '/sessions/logout',
95
+            request_method='GET'
96
+        )
97
+
98
+        configurator.add_view(
99
+            self.logout,
100
+            route_name='logout',
101
+            renderer='json'
102
+
103
+        )
104
+        # Whoami
105
+        configurator.add_route(
106
+            'whoami',
107
+            '/sessions/whoami',
108
+            request_method='GET'
109
+        )
110
+        configurator.add_view(
111
+            self.whoami,
112
+            route_name='whoami',
113
+            renderer='json'
114
+        )