From 4fe1d00753160bade9f6b844ace6c02b8d156088 Mon Sep 17 00:00:00 2001 From: Maria Fernanda Magallanes Zubillaga Date: Tue, 21 Nov 2023 17:18:20 -0500 Subject: [PATCH] feat: add support for JWT authentication --- .../eox-core-test.postman_collection.json | 400 ++++++++++-------- eox_core/api/support/v1/views.py | 7 +- eox_core/api/task_dispatcher/v1/views.py | 3 +- eox_core/api/v1/views.py | 13 +- requirements/base.in | 3 +- requirements/base.txt | 4 +- requirements/test.txt | 3 +- 7 files changed, 237 insertions(+), 196 deletions(-) diff --git a/docs/resources/eox-core-test.postman_collection.json b/docs/resources/eox-core-test.postman_collection.json index b71ac90ef..a40d7b298 100644 --- a/docs/resources/eox-core-test.postman_collection.json +++ b/docs/resources/eox-core-test.postman_collection.json @@ -26,17 +26,16 @@ ], "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] + "type": "noauth" }, "method": "POST", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n \"username\": \"{{username}}\",\n \"email\": \"{{email}}\",\n \"password\": \"p@ssword\",\n \"fullname\": \"Edunext test\"\n}", @@ -65,7 +64,13 @@ "name": "Create user", "originalRequest": { "method": "POST", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n\t\"username\": \"lhurtado\",\n\t\"email\": \"hurtado@example.com\",\n\t\"password\": \"1234\",\n\t\"fullname\": \"Maga Hurtado\"\n}", @@ -143,17 +148,16 @@ "name": "Get user", "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] + "type": "noauth" }, "method": "GET", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "url": { "raw": "{{domain}}/eox-core/api/v1/user/?email={{email}}", "host": [ @@ -179,7 +183,13 @@ "name": "Get user", "originalRequest": { "method": "GET", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "url": { "raw": "http://lms.nutmeg.edunext.link/eox-core/api/v1/user/?email=hurtado@example.com", "protocol": "http", @@ -254,17 +264,16 @@ "name": "Update user", "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] + "type": "noauth" }, "method": "PATCH", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n \"email\": \"{{email}}\",\n \"password\": \"new-pass\"\n}\n", @@ -293,7 +302,13 @@ "name": "Update user", "originalRequest": { "method": "PATCH", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n\t\"email\": \"hurtado@example.com\",\n\t\"password\": \"new-pass\"\n}\n", @@ -372,17 +387,16 @@ "name": "Create enrollment", "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] + "type": "noauth" }, "method": "POST", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n \"username\": \"{{username}}\",\n \"is_active\": true,\n \"mode\": \"audit\",\n \"enrollment_attributes\": [],\n \"course_id\": \"{{course_id}}\"\n}", @@ -411,7 +425,13 @@ "name": "Create enrollment", "originalRequest": { "method": "POST", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n \"username\": \"lhurtado\",\n \"is_active\": true,\n \"mode\": \"audit\",\n \"enrollment_attributes\": [],\n \"course_id\": \"course-v1:edX+DemoX+Demo_Course\"\n}\n", @@ -488,17 +508,16 @@ }, "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] + "type": "noauth" }, "method": "GET", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n\"email\":\"{{email}}\",\n\"course_id\":\"{{course_id}}\"\n}", @@ -527,7 +546,13 @@ "name": "Get enrollment", "originalRequest": { "method": "GET", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "url": { "raw": "{{domain}}/eox-core/api/v1/enrollment/", "host": [ @@ -604,17 +629,16 @@ "name": "Update enrollment", "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] + "type": "noauth" }, "method": "PUT", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n \"username\": \"{{username}}\",\n \"is_active\": false,\n \"mode\": \"audit\",\n \"course_id\": \"{{course_id}}\"\n}", @@ -643,7 +667,13 @@ "name": "Update enrollment", "originalRequest": { "method": "PUT", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n \"username\": \"lhurtado\",\n \"is_active\": false,\n \"mode\": \"audit\",\n \"course_id\": \"course-v1:edX+DemoX+Demo_Course\"\n}", @@ -717,17 +747,16 @@ "name": "Delete enrollment", "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] + "type": "noauth" }, "method": "DELETE", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n\"email\": \"{{email}}\",\n\"course_id\": \"{{course_id}}\"\n}", @@ -756,7 +785,13 @@ "name": "Delete enrollment", "originalRequest": { "method": "DELETE", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n \"email\": \"hurtado@example.com\",\n \"course_id\": \"course-v1:edX+DemoX+Demo_Course\"\n}", @@ -822,17 +857,16 @@ "name": "Create enrollment to test grade", "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] + "type": "noauth" }, "method": "POST", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n \"username\": \"{{username}}\",\n \"is_active\": true,\n \"mode\": \"audit\",\n \"enrollment_attributes\": [],\n \"course_id\": \"{{course_id}}\"\n}\n", @@ -861,7 +895,13 @@ "name": "Create enrollment", "originalRequest": { "method": "POST", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n \"username\": \"lhurtado\",\n \"is_active\": true,\n \"mode\": \"audit\",\n \"enrollment_attributes\": [],\n \"course_id\": \"course-v1:edX+DemoX+Demo_Course\"\n}\n", @@ -940,17 +980,16 @@ "name": "Create whitelistings", "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] + "type": "noauth" }, "method": "POST", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n \"email\": \"newtest@example.com\",\n \"auto_enroll\": false,\n \"course_id\": \"{{course_id}}\"\n}\n", @@ -1064,17 +1103,16 @@ }, "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] + "type": "noauth" }, "method": "GET", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n \"email\": \"newtest@example.com\",\n \"course_id\": \"{{course_id}}\"\n}", @@ -1176,17 +1214,16 @@ "name": "Update whitelistings", "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] + "type": "noauth" }, "method": "PUT", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": " {\n \"email\": \"newtest@example.com\",\n \"auto_enroll\": true,\n \"course_id\": \"{{course_id}}\"\n}", @@ -1298,17 +1335,16 @@ "name": "Delete pre-enrollment", "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] + "type": "noauth" }, "method": "DELETE", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n \"email\": \"newtest@example.com\",\n \"course_id\": \"{{course_id}}\"\n}", @@ -1346,17 +1382,16 @@ }, "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] + "type": "noauth" }, "method": "GET", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n\"email\": \"{{email}}\",\n\"course_id\": \"{{course_id}}\",\n\"detailed\": \"true\",\n\"grading_policy\": \"true\"\n}", @@ -1463,17 +1498,16 @@ "name": "Get user related to the authorization token", "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] + "type": "noauth" }, "method": "GET", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "url": { "raw": "{{domain}}/eox-core/api/v1/userinfo", "host": [ @@ -1583,17 +1617,16 @@ ], "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] + "type": "noauth" }, "method": "POST", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n \"task_path\": \"{{task_path}}\",\n \"task_parameters\": {\n \"message\": \"Don't lose your grip on the dreams of the past, you must fight just to keep them alive\"\n }\n}", @@ -1637,17 +1670,16 @@ }, "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, + "type": "noauth" + }, "method": "GET", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "formdata", "formdata": [] @@ -1679,17 +1711,16 @@ "name": "Check the status", "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, + "type": "noauth" + }, "method": "GET", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "url": { "raw": "{{domain}}/eox-core/tasks-api/v1/tasks/?id=4ed69ed1-97f2-47eb-bf45-6f5bf01963b4", "host": [ @@ -1721,17 +1752,16 @@ "name": "Update username", "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, + "type": "noauth" + }, "method": "PATCH", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "body": { "mode": "raw", "raw": "{\n \"new_username\": \"supercool-{{username}}\"\n}", @@ -1768,17 +1798,16 @@ "name": "Delete User", "request": { "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{token}}", - "type": "string" - } - ] - }, + "type": "noauth" + }, "method": "DELETE", - "header": [], + "header": [ + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], "url": { "raw": "{{domain}}/eox-core/support-api/v1/user/?username=supercool-{{username}}", "host": [ @@ -1837,6 +1866,11 @@ "key": "grant_type", "value": "{{grant_type}}", "type": "text" + }, + { + "key": "token_type", + "value": "jwt", + "type": "text" } ] }, diff --git a/eox_core/api/support/v1/views.py b/eox_core/api/support/v1/views.py index 561d5697c..00a742a90 100644 --- a/eox_core/api/support/v1/views.py +++ b/eox_core/api/support/v1/views.py @@ -18,6 +18,7 @@ from rest_framework.renderers import BrowsableAPIRenderer, JSONRenderer from rest_framework.response import Response from rest_framework.views import APIView +from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication from eox_core.api.support.v1.permissions import EoxCoreSupportAPIPermission from eox_core.api.support.v1.serializers import ( @@ -55,7 +56,7 @@ class EdxappUser(UserQueryMixin, APIView): Handles API requests to remove users """ - authentication_classes = (BearerAuthentication, SessionAuthentication) + authentication_classes = (BearerAuthentication, SessionAuthentication, JwtAuthentication) permission_classes = (EoxCoreSupportAPIPermission,) renderer_classes = (JSONRenderer, BrowsableAPIRenderer) @@ -98,7 +99,7 @@ class EdxappReplaceUsername(UserQueryMixin, APIView): Handles the replacement of the username. """ - authentication_classes = (BearerAuthentication, SessionAuthentication) + authentication_classes = (BearerAuthentication, SessionAuthentication, JwtAuthentication) permission_classes = (EoxCoreSupportAPIPermission,) renderer_classes = (JSONRenderer, BrowsableAPIRenderer) @@ -153,7 +154,7 @@ class OauthApplicationAPIView(UserQueryMixin, APIView): django_oauth_toolkit Application object. """ - authentication_classes = (BearerAuthentication, SessionAuthentication) + authentication_classes = (BearerAuthentication, SessionAuthentication, JwtAuthentication) permission_classes = (EoxCoreSupportAPIPermission,) renderer_classes = (JSONRenderer, BrowsableAPIRenderer) diff --git a/eox_core/api/task_dispatcher/v1/views.py b/eox_core/api/task_dispatcher/v1/views.py index f610efac2..98f586358 100644 --- a/eox_core/api/task_dispatcher/v1/views.py +++ b/eox_core/api/task_dispatcher/v1/views.py @@ -11,6 +11,7 @@ from rest_framework.response import Response from rest_framework.status import HTTP_400_BAD_REQUEST, HTTP_404_NOT_FOUND from rest_framework.views import APIView +from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication from eox_core.edxapp_wrapper.bearer_authentication import BearerAuthentication @@ -29,7 +30,7 @@ class TaskAPI(APIView): Celery task dispatcher API """ - authentication_classes = [BearerAuthentication] + authentication_classes = (BearerAuthentication, JwtAuthentication) permission_classes = [IsAdminUser] def get(self, request): diff --git a/eox_core/api/v1/views.py b/eox_core/api/v1/views.py index 921163f42..207af0d3d 100644 --- a/eox_core/api/v1/views.py +++ b/eox_core/api/v1/views.py @@ -17,6 +17,7 @@ from rest_framework.renderers import BrowsableAPIRenderer, JSONRenderer from rest_framework.response import Response from rest_framework.views import APIView +from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication from eox_core.api.v1.permissions import EoxCoreAPIPermission from eox_core.api.v1.serializers import ( @@ -162,7 +163,7 @@ class EdxappUser(UserQueryMixin, APIView): """ - authentication_classes = (BearerAuthentication, SessionAuthentication) + authentication_classes = (BearerAuthentication, SessionAuthentication, JwtAuthentication) permission_classes = (EoxCoreAPIPermission,) renderer_classes = (JSONRenderer, BrowsableAPIRenderer) @@ -395,7 +396,7 @@ class EdxappUserUpdater(UserQueryMixin, APIView): } """ - authentication_classes = (BearerAuthentication, SessionAuthentication) + authentication_classes = (BearerAuthentication, SessionAuthentication, JwtAuthentication) permission_classes = (EoxCoreAPIPermission,) renderer_classes = (JSONRenderer, BrowsableAPIRenderer) @@ -531,7 +532,7 @@ class EdxappEnrollment(UserQueryMixin, APIView): Handles API requests to create users """ - authentication_classes = (BearerAuthentication, SessionAuthentication) + authentication_classes = (BearerAuthentication, SessionAuthentication, JwtAuthentication) permission_classes = (EoxCoreAPIPermission,) renderer_classes = (JSONRenderer, BrowsableAPIRenderer) @@ -931,7 +932,7 @@ class EdxappPreEnrollment(APIView): Handles API requests to manage whitelistings (pre-enrollments) """ - authentication_classes = (BearerAuthentication, SessionAuthentication) + authentication_classes = (BearerAuthentication, SessionAuthentication, JwtAuthentication) permission_classes = (EoxCoreAPIPermission,) renderer_classes = (JSONRenderer, BrowsableAPIRenderer) @@ -1053,7 +1054,7 @@ class EdxappGrade(UserQueryMixin, APIView): Handles API requests to manage course grades """ - authentication_classes = (BearerAuthentication, SessionAuthentication) + authentication_classes = (BearerAuthentication, SessionAuthentication, JwtAuthentication) permission_classes = (EoxCoreAPIPermission,) renderer_classes = (JSONRenderer, BrowsableAPIRenderer) @@ -1203,7 +1204,7 @@ class UserInfo(APIView): Can use Oauth2/Session """ - authentication_classes = (BearerAuthentication, SessionAuthentication) + authentication_classes = (BearerAuthentication, SessionAuthentication, JwtAuthentication) permission_classes = (EoxCoreAPIPermission,) renderer_classes = (JSONRenderer, BrowsableAPIRenderer) diff --git a/requirements/base.in b/requirements/base.in index 7a4ff6059..a43071cf8 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -10,7 +10,8 @@ django-oauth2-provider django-waffle edx-api-doc-tools edx-proctoring>=2.0.1 -edx-opaque-keys[django]==2.3.0 +edx-opaque-keys[django]<3.0 openedx-events django click +edx-drf-extensions diff --git a/requirements/base.txt b/requirements/base.txt index 45d3ed699..852be873c 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -126,9 +126,10 @@ edx-django-utils==5.8.0 edx-drf-extensions==8.13.1 # via # -c requirements/constraints.txt + # -r requirements/base.in # edx-proctoring # edx-when -edx-opaque-keys[django]==2.3.0 +edx-opaque-keys[django]==2.5.1 # via # -c requirements/constraints.txt # -r requirements/base.in @@ -249,6 +250,7 @@ stevedore==5.1.0 typing-extensions==4.8.0 # via # asgiref + # edx-opaque-keys # kombu tzdata==2023.3 # via diff --git a/requirements/test.txt b/requirements/test.txt index 56d7dc4cf..718310818 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -177,7 +177,7 @@ edx-drf-extensions==8.13.1 # -r requirements/base.txt # edx-proctoring # edx-when -edx-opaque-keys[django]==2.3.0 +edx-opaque-keys[django]==2.5.1 # via # -c requirements/constraints.txt # -r requirements/base.txt @@ -405,6 +405,7 @@ typing-extensions==4.8.0 # asgiref # astroid # django-countries + # edx-opaque-keys # faker # kombu # pylint