cms-jwt 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cms_jwt-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.4
2
+ Name: cms-jwt
3
+ Version: 0.2.0
4
+ Requires-Python: >=3.10
5
+ Dynamic: requires-python
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.4
2
+ Name: cms-jwt
3
+ Version: 0.2.0
4
+ Requires-Python: >=3.10
5
+ Dynamic: requires-python
@@ -0,0 +1,10 @@
1
+ setup.py
2
+ cms_jwt.egg-info/PKG-INFO
3
+ cms_jwt.egg-info/SOURCES.txt
4
+ cms_jwt.egg-info/dependency_links.txt
5
+ cms_jwt.egg-info/entry_points.txt
6
+ cms_jwt.egg-info/top_level.txt
7
+ cms_jwt_import_auth/__init__.py
8
+ cms_jwt_import_auth/apps.py
9
+ cms_jwt_import_auth/bkp_tutor.py
10
+ cms_jwt_import_auth/middleware.py
@@ -0,0 +1,2 @@
1
+ [cms.djangoapp]
2
+ cms_jwt = cms_jwt_import_auth.apps:CMSJWTImportAuthConfig
@@ -0,0 +1 @@
1
+ cms_jwt_import_auth
File without changes
@@ -0,0 +1,15 @@
1
+ from django.apps import AppConfig
2
+
3
+ class CMSJWTImportAuthConfig(AppConfig):
4
+ name = "cms_jwt_import_auth"
5
+ verbose_name = "CMS JWT-or-Session auth for course import"
6
+
7
+ plugin_app = {
8
+ 'settings_config': {
9
+ 'cms.djangoapp': {
10
+ 'common': {'relative_path': 'settings.common'},
11
+ 'production': {'relative_path': 'settings.production'},
12
+ },
13
+ },
14
+ }
15
+
@@ -0,0 +1,28 @@
1
+ from tutor import hooks
2
+
3
+ def register():
4
+ # Ensure the Django app is present in the image
5
+ hooks.Filters.CMS_EXTRA_REQUIREMENTS.add_item("edx-drf-extensions")
6
+ # If your Django app is published to pip, add it here too:
7
+ # hooks.Filters.CMS_EXTRA_REQUIREMENTS.add_item("cms-jwt-import-auth>=0.1.0")
8
+
9
+ # Load the Django app + middleware
10
+ hooks.Filters.CMS_EXTRA_APPS.add_item("cms_jwt_import_auth")
11
+ hooks.Filters.CMS_EXTRA_MIDDLEWARE.add_item(
12
+ "cms_jwt_import_auth.middleware.ImportJWTOrSessionMiddleware"
13
+ )
14
+
15
+ # Optional settings patch
16
+ def _patch(settings):
17
+ settings.setdefault("REST_FRAMEWORK", {})
18
+ settings["REST_FRAMEWORK"]["DEFAULT_AUTHENTICATION_CLASSES"] = (
19
+ "edx_rest_framework_extensions.auth.jwt.authentication.JwtAuthentication",
20
+ "rest_framework.authentication.SessionAuthentication",
21
+ )
22
+ settings.setdefault("JWT_AUTH", {})
23
+ settings["JWT_AUTH"].update({
24
+ "JWT_ISSUER": "https://campus-dev.nextere.com/oauth2",
25
+ "JWT_AUDIENCE": "openedx",
26
+ })
27
+ hooks.Filters.CMS_EXTRA_SETTINGS.add_item(_patch)
28
+
@@ -0,0 +1,56 @@
1
+ from __future__ import annotations
2
+ import re
3
+ from typing import Optional
4
+
5
+ from django.http import JsonResponse
6
+ from django.utils.deprecation import MiddlewareMixin
7
+ from django.contrib.auth.models import AnonymousUser
8
+
9
+ from rest_framework.request import Request as DRFRequest
10
+ from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
11
+
12
+ class ImportJWTOrSessionMiddleware(MiddlewareMixin):
13
+ """
14
+ Enforce authentication for the course import endpoint in CMS:
15
+ /api/courses/v1/<course_id>/import/
16
+ Accept either:
17
+ - existing logged-in Studio session, OR
18
+ - JWT in Authorization header (JWT or Bearer)
19
+ If neither present/valid -> 401 JSON.
20
+
21
+ NOTE:
22
+ - Keep this middleware scoped to just the import route to avoid overhead.
23
+ """
24
+
25
+ # Path matcher (strict, trailing slash optional)
26
+ # Example: /api/courses/v1/course-v1:ACME+ONB101+2025_T1/import/
27
+ PATTERN = re.compile(
28
+ r"^/api/courses/v1/[^/]+/import/?$"
29
+ )
30
+
31
+ def process_request(self, request):
32
+ path = request.path or ""
33
+ if not self.PATTERN.match(path):
34
+ return None # ignore all other routes
35
+
36
+ # If session-authenticated, allow through
37
+ user = getattr(request, "user", None)
38
+ if user is not None and user.is_authenticated:
39
+ return None
40
+
41
+ # Try JWT auth (accept JWT or Bearer header schemes)
42
+ auth = JwtAuthentication()
43
+ drf_request = DRFRequest(request)
44
+ try:
45
+ user_auth_tuple: Optional[tuple] = auth.authenticate(drf_request)
46
+ except Exception as e:
47
+ # Invalid token format / signature, return 401
48
+ return JsonResponse({"detail": f"Invalid JWT: {str(e)}"}, status=401)
49
+
50
+ if user_auth_tuple:
51
+ request.user, _ = user_auth_tuple
52
+ return None # authenticated via JWT
53
+
54
+ # Neither session nor JWT
55
+ return JsonResponse({"detail": "Authentication required"}, status=401)
56
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
cms_jwt-0.2.0/setup.py ADDED
@@ -0,0 +1,27 @@
1
+ """
2
+ Setup file for eox_core Django plugin.
3
+ """
4
+ #!/usr/bin/env python
5
+ # -*- coding: utf-8 -*-
6
+ import os
7
+ import re
8
+
9
+ from setuptools import setup
10
+
11
+ VERSION = "0.2.0"
12
+
13
+
14
+
15
+ setup(
16
+ name="cms-jwt",
17
+ python_requires='>=3.10',
18
+ version=VERSION,
19
+
20
+ packages=['cms_jwt_import_auth'],
21
+ include_package_data=True,
22
+ entry_points={
23
+ "cms.djangoapp": [
24
+ "cms_jwt = cms_jwt_import_auth.apps:CMSJWTImportAuthConfig",
25
+ ],
26
+ }
27
+ )