mirror of
https://github.com/mediacms-io/mediacms.git
synced 2026-01-20 07:12:58 -05:00
this
This commit is contained in:
@@ -31,12 +31,6 @@ class SelectMediaView(View):
|
|||||||
def get(self, request):
|
def get(self, request):
|
||||||
"""Display media selection interface"""
|
"""Display media selection interface"""
|
||||||
|
|
||||||
# Get deep link session data
|
|
||||||
deep_link_data = request.session.get('lti_deep_link')
|
|
||||||
|
|
||||||
if not deep_link_data:
|
|
||||||
return JsonResponse({'error': 'Invalid session', 'message': 'No deep linking session data found'}, status=400)
|
|
||||||
|
|
||||||
# Get accessible media for user
|
# Get accessible media for user
|
||||||
user = request.user
|
user = request.user
|
||||||
|
|
||||||
@@ -58,7 +52,6 @@ class SelectMediaView(View):
|
|||||||
|
|
||||||
context = {
|
context = {
|
||||||
'media_list': media_list,
|
'media_list': media_list,
|
||||||
'deep_link_data': deep_link_data,
|
|
||||||
'show_my_media_only': show_my_media_only,
|
'show_my_media_only': show_my_media_only,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
74
lti/views.py
74
lti/views.py
@@ -10,8 +10,6 @@ Implements the LTI 1.3 / LTI Advantage flow:
|
|||||||
- Manual NRPS Sync
|
- Manual NRPS Sync
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
|
||||||
import traceback
|
|
||||||
import uuid
|
import uuid
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
@@ -45,8 +43,6 @@ from .handlers import (
|
|||||||
from .models import LTILaunchLog, LTIPlatform, LTIResourceLink
|
from .models import LTILaunchLog, LTIPlatform, LTIResourceLink
|
||||||
from .services import LTINRPSClient
|
from .services import LTINRPSClient
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def get_client_ip(request):
|
def get_client_ip(request):
|
||||||
"""Get client IP address from request"""
|
"""Get client IP address from request"""
|
||||||
@@ -74,13 +70,7 @@ class OIDCLoginView(View):
|
|||||||
|
|
||||||
def handle_oidc_login(self, request):
|
def handle_oidc_login(self, request):
|
||||||
"""Handle OIDC login initiation"""
|
"""Handle OIDC login initiation"""
|
||||||
print("=== OIDC Login Started ===", flush=True)
|
|
||||||
logger.info("=== OIDC Login Started ===")
|
|
||||||
try:
|
try:
|
||||||
# Get all request parameters for debugging
|
|
||||||
all_params = dict(request.GET.items()) if request.method == 'GET' else dict(request.POST.items())
|
|
||||||
print(f"All OIDC request params: {all_params}", flush=True)
|
|
||||||
|
|
||||||
# Get target_link_uri and other OIDC params
|
# Get target_link_uri and other OIDC params
|
||||||
target_link_uri = request.GET.get('target_link_uri') or request.POST.get('target_link_uri')
|
target_link_uri = request.GET.get('target_link_uri') or request.POST.get('target_link_uri')
|
||||||
iss = request.GET.get('iss') or request.POST.get('iss')
|
iss = request.GET.get('iss') or request.POST.get('iss')
|
||||||
@@ -88,23 +78,14 @@ class OIDCLoginView(View):
|
|||||||
login_hint = request.GET.get('login_hint') or request.POST.get('login_hint')
|
login_hint = request.GET.get('login_hint') or request.POST.get('login_hint')
|
||||||
lti_message_hint = request.GET.get('lti_message_hint') or request.POST.get('lti_message_hint')
|
lti_message_hint = request.GET.get('lti_message_hint') or request.POST.get('lti_message_hint')
|
||||||
|
|
||||||
print(f"OIDC params - iss: {iss}, client_id: {client_id}, target: {target_link_uri}", flush=True)
|
|
||||||
print(f"login_hint: {login_hint}, lti_message_hint: {lti_message_hint}", flush=True)
|
|
||||||
logger.info(f"OIDC params - iss: {iss}, client_id: {client_id}, target: {target_link_uri}")
|
|
||||||
|
|
||||||
if not all([target_link_uri, iss, client_id]):
|
if not all([target_link_uri, iss, client_id]):
|
||||||
print("ERROR: Missing OIDC parameters", flush=True)
|
|
||||||
logger.error("Missing OIDC parameters")
|
|
||||||
return JsonResponse({'error': 'Missing required OIDC parameters'}, status=400)
|
return JsonResponse({'error': 'Missing required OIDC parameters'}, status=400)
|
||||||
|
|
||||||
# Get platform configuration
|
# Get platform configuration
|
||||||
platform = get_object_or_404(LTIPlatform, platform_id=iss, client_id=client_id, active=True)
|
platform = get_object_or_404(LTIPlatform, platform_id=iss, client_id=client_id, active=True)
|
||||||
print(f"Found platform: {platform.name}", flush=True)
|
|
||||||
logger.info(f"Found platform: {platform.name}")
|
|
||||||
|
|
||||||
# Create tool config for this platform
|
# Create tool config for this platform
|
||||||
tool_config = DjangoToolConfig.from_platform(platform)
|
tool_config = DjangoToolConfig.from_platform(platform)
|
||||||
print(f"Tool config: {tool_config._config}", flush=True)
|
|
||||||
|
|
||||||
# Wrap Django request for PyLTI1p3
|
# Wrap Django request for PyLTI1p3
|
||||||
lti_request = DjangoRequest(request)
|
lti_request = DjangoRequest(request)
|
||||||
@@ -113,26 +94,14 @@ class OIDCLoginView(View):
|
|||||||
session_service = DjangoSessionService(request)
|
session_service = DjangoSessionService(request)
|
||||||
cookie_service = DjangoSessionService(request) # Using same service for cookies
|
cookie_service = DjangoSessionService(request) # Using same service for cookies
|
||||||
|
|
||||||
print("Creating OIDCLogin...", flush=True)
|
|
||||||
oidc_login = OIDCLogin(lti_request, tool_config, session_service=session_service, cookie_service=cookie_service)
|
oidc_login = OIDCLogin(lti_request, tool_config, session_service=session_service, cookie_service=cookie_service)
|
||||||
print("OIDCLogin created successfully", flush=True)
|
|
||||||
|
|
||||||
# Redirect to platform's authorization endpoint
|
# Redirect to platform's authorization endpoint
|
||||||
print(f"Target link URI: {target_link_uri}", flush=True)
|
|
||||||
print(f"Auth login URL: {platform.auth_login_url}", flush=True)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
print("Calling enable_check_cookies()...", flush=True)
|
|
||||||
oidc_with_cookies = oidc_login.enable_check_cookies()
|
oidc_with_cookies = oidc_login.enable_check_cookies()
|
||||||
print(f"Calling redirect({target_link_uri})...", flush=True)
|
|
||||||
redirect_url = oidc_with_cookies.redirect(target_link_uri)
|
redirect_url = oidc_with_cookies.redirect(target_link_uri)
|
||||||
print(f"Redirect returned: '{redirect_url}'", flush=True)
|
|
||||||
print(f"OIDC redirect URL type: {type(redirect_url)}", flush=True)
|
|
||||||
print(f"OIDC redirecting to: {redirect_url}", flush=True)
|
|
||||||
logger.info(f"OIDC redirecting to: {redirect_url}")
|
|
||||||
|
|
||||||
if not redirect_url:
|
if not redirect_url:
|
||||||
print("PyLTI1p3 redirect failed, building URL manually...", flush=True)
|
|
||||||
# Manual OIDC redirect construction with all required OAuth 2.0 parameters
|
# Manual OIDC redirect construction with all required OAuth 2.0 parameters
|
||||||
|
|
||||||
state = str(uuid.uuid4())
|
state = str(uuid.uuid4())
|
||||||
@@ -159,20 +128,14 @@ class OIDCLoginView(View):
|
|||||||
params['lti_message_hint'] = lti_message_hint
|
params['lti_message_hint'] = lti_message_hint
|
||||||
|
|
||||||
redirect_url = f"{platform.auth_login_url}?{urlencode(params)}"
|
redirect_url = f"{platform.auth_login_url}?{urlencode(params)}"
|
||||||
print(f"Manually built redirect URL: {redirect_url}", flush=True)
|
|
||||||
|
|
||||||
return HttpResponseRedirect(redirect_url)
|
return HttpResponseRedirect(redirect_url)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
print(f"ERROR in OIDC redirect: {str(e)}", flush=True)
|
|
||||||
|
|
||||||
traceback.print_exc()
|
|
||||||
raise
|
raise
|
||||||
|
|
||||||
except LtiException as e:
|
except LtiException as e:
|
||||||
logger.error(f"LTI OIDC Login Error: {str(e)}")
|
|
||||||
return render(request, 'lti/launch_error.html', {'error': 'OIDC Login Failed', 'message': str(e)}, status=400)
|
return render(request, 'lti/launch_error.html', {'error': 'OIDC Login Failed', 'message': str(e)}, status=400)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.error(f"OIDC Login Error: {str(e)}", exc_info=True)
|
|
||||||
return JsonResponse({'error': 'Internal server error during OIDC login'}, status=500)
|
return JsonResponse({'error': 'Internal server error during OIDC login'}, status=500)
|
||||||
|
|
||||||
|
|
||||||
@@ -187,8 +150,6 @@ class LaunchView(View):
|
|||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""Handle LTI launch with JWT validation"""
|
"""Handle LTI launch with JWT validation"""
|
||||||
print("=== LTI Launch Started ===", flush=True)
|
|
||||||
logger.info("=== LTI Launch Started ===")
|
|
||||||
platform = None
|
platform = None
|
||||||
user = None
|
user = None
|
||||||
error_message = ''
|
error_message = ''
|
||||||
@@ -208,8 +169,6 @@ class LaunchView(View):
|
|||||||
|
|
||||||
# Get platform
|
# Get platform
|
||||||
platform = get_object_or_404(LTIPlatform, platform_id=iss, client_id=aud, active=True)
|
platform = get_object_or_404(LTIPlatform, platform_id=iss, client_id=aud, active=True)
|
||||||
print(f"Launch from platform: {platform.name}", flush=True)
|
|
||||||
logger.info(f"Launch from platform: {platform.name}")
|
|
||||||
|
|
||||||
# Create tool config
|
# Create tool config
|
||||||
tool_config = DjangoToolConfig.from_platform(platform)
|
tool_config = DjangoToolConfig.from_platform(platform)
|
||||||
@@ -247,12 +206,8 @@ class LaunchView(View):
|
|||||||
return self.handle_deep_linking_launch(request, message_launch, platform, launch_data)
|
return self.handle_deep_linking_launch(request, message_launch, platform, launch_data)
|
||||||
|
|
||||||
# Provision user
|
# Provision user
|
||||||
print(f"Provisioning user, sub: {sub}", flush=True)
|
|
||||||
logger.info(f"Provisioning user, sub: {sub}")
|
|
||||||
if platform.auto_create_users:
|
if platform.auto_create_users:
|
||||||
user = provision_lti_user(platform, launch_data)
|
user = provision_lti_user(platform, launch_data)
|
||||||
print(f"User provisioned: {user.username}", flush=True)
|
|
||||||
logger.info(f"User provisioned: {user.username}")
|
|
||||||
else:
|
else:
|
||||||
# Must find existing user
|
# Must find existing user
|
||||||
from .models import LTIUserMapping
|
from .models import LTIUserMapping
|
||||||
@@ -264,38 +219,29 @@ class LaunchView(View):
|
|||||||
|
|
||||||
# Provision context (category + RBAC group)
|
# Provision context (category + RBAC group)
|
||||||
if 'https://purl.imsglobal.org/spec/lti/claim/context' in launch_data:
|
if 'https://purl.imsglobal.org/spec/lti/claim/context' in launch_data:
|
||||||
logger.info("Provisioning context...")
|
|
||||||
category, rbac_group, resource_link_obj = provision_lti_context(platform, launch_data, resource_link_id)
|
category, rbac_group, resource_link_obj = provision_lti_context(platform, launch_data, resource_link_id)
|
||||||
logger.info(f"Context provisioned: category={category.title if category else None}")
|
|
||||||
|
|
||||||
# Apply roles
|
# Apply roles
|
||||||
apply_lti_roles(user, platform, roles, rbac_group)
|
apply_lti_roles(user, platform, roles, rbac_group)
|
||||||
logger.info(f"Roles applied: {roles}")
|
|
||||||
else:
|
else:
|
||||||
# No context - might be a direct media embed
|
# No context - might be a direct media embed
|
||||||
resource_link_obj = None
|
resource_link_obj = None
|
||||||
|
|
||||||
# Create session
|
# Create session
|
||||||
create_lti_session(request, user, message_launch, platform)
|
create_lti_session(request, user, message_launch, platform)
|
||||||
logger.info("LTI session created")
|
|
||||||
|
|
||||||
# Log successful launch
|
# Log successful launch
|
||||||
LTILaunchLog.objects.create(platform=platform, user=user, resource_link=resource_link_obj, launch_type='resource_link', success=True, claims=claims, ip_address=get_client_ip(request))
|
LTILaunchLog.objects.create(platform=platform, user=user, resource_link=resource_link_obj, launch_type='resource_link', success=True, claims=claims, ip_address=get_client_ip(request))
|
||||||
logger.info("Launch logged")
|
|
||||||
|
|
||||||
# Determine where to redirect
|
# Determine where to redirect
|
||||||
redirect_url = self.determine_redirect(launch_data, resource_link_obj)
|
redirect_url = self.determine_redirect(launch_data, resource_link_obj)
|
||||||
print(f"=== Launch Success - Redirecting to: {redirect_url} ===", flush=True)
|
|
||||||
logger.info(f"=== Launch Success - Redirecting to: {redirect_url} ===")
|
|
||||||
|
|
||||||
return HttpResponseRedirect(redirect_url)
|
return HttpResponseRedirect(redirect_url)
|
||||||
|
|
||||||
except LtiException as e:
|
except LtiException as e:
|
||||||
error_message = f"LTI Launch Error: {str(e)}"
|
error_message = f"LTI Launch Error: {str(e)}"
|
||||||
logger.error(error_message)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_message = f"Launch Error: {str(e)}"
|
error_message = f"Launch Error: {str(e)}"
|
||||||
logger.error(error_message, exc_info=True)
|
|
||||||
|
|
||||||
# Log failed launch
|
# Log failed launch
|
||||||
if platform:
|
if platform:
|
||||||
@@ -315,18 +261,13 @@ class LaunchView(View):
|
|||||||
# Check for custom parameters indicating what to show
|
# Check for custom parameters indicating what to show
|
||||||
custom = launch_data.get('https://purl.imsglobal.org/spec/lti/claim/custom', {})
|
custom = launch_data.get('https://purl.imsglobal.org/spec/lti/claim/custom', {})
|
||||||
|
|
||||||
# Debug: Print custom parameters
|
|
||||||
print(f"DEBUG: Custom parameters received: {custom}", flush=True)
|
|
||||||
|
|
||||||
# Check for custom redirect URL (any MediaCMS path)
|
# Check for custom redirect URL (any MediaCMS path)
|
||||||
custom_path = custom.get('redirect_path')
|
custom_path = custom.get('redirect_path')
|
||||||
print(f"DEBUG: redirect_path value: {custom_path}", flush=True)
|
|
||||||
|
|
||||||
if custom_path:
|
if custom_path:
|
||||||
# Ensure it starts with / and doesn't include domain
|
# Ensure it starts with / and doesn't include domain
|
||||||
if not custom_path.startswith('/'):
|
if not custom_path.startswith('/'):
|
||||||
custom_path = '/' + custom_path
|
custom_path = '/' + custom_path
|
||||||
print(f"DEBUG: Redirecting to custom path: {custom_path}", flush=True)
|
|
||||||
return custom_path
|
return custom_path
|
||||||
|
|
||||||
# Check if specific media is requested
|
# Check if specific media is requested
|
||||||
@@ -339,7 +280,6 @@ class LaunchView(View):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
# Default: redirect to my media
|
# Default: redirect to my media
|
||||||
print("DEBUG: No custom parameters, using default (my media)", flush=True)
|
|
||||||
return reverse('lti:my_media')
|
return reverse('lti:my_media')
|
||||||
|
|
||||||
def handle_deep_linking_launch(self, request, message_launch, platform, launch_data):
|
def handle_deep_linking_launch(self, request, message_launch, platform, launch_data):
|
||||||
@@ -383,24 +323,15 @@ class MyMediaLTIView(View):
|
|||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
"""Display my media page"""
|
"""Display my media page"""
|
||||||
print(f"=== My Media LTI View - User: {request.user} ===", flush=True)
|
|
||||||
logger.info(f"=== My Media LTI View - User: {request.user} ===")
|
|
||||||
|
|
||||||
# Validate LTI session
|
# Validate LTI session
|
||||||
lti_session = validate_lti_session(request)
|
lti_session = validate_lti_session(request)
|
||||||
print(f"LTI session valid: {bool(lti_session)}", flush=True)
|
|
||||||
logger.info(f"LTI session valid: {bool(lti_session)}")
|
|
||||||
|
|
||||||
if not lti_session:
|
if not lti_session:
|
||||||
print("ERROR: LTI session validation failed", flush=True)
|
|
||||||
logger.error("LTI session validation failed")
|
|
||||||
return JsonResponse({'error': 'Not authenticated via LTI'}, status=403)
|
return JsonResponse({'error': 'Not authenticated via LTI'}, status=403)
|
||||||
|
|
||||||
# Redirect to user's profile page
|
# Redirect to user's profile page
|
||||||
# The existing user profile page is already iframe-compatible
|
# The existing user profile page is already iframe-compatible
|
||||||
profile_url = f"/user/{request.user.username}"
|
profile_url = f"/user/{request.user.username}"
|
||||||
print(f"Redirecting to profile: {profile_url}", flush=True)
|
|
||||||
logger.info(f"Redirecting to profile: {profile_url}")
|
|
||||||
return HttpResponseRedirect(profile_url)
|
return HttpResponseRedirect(profile_url)
|
||||||
|
|
||||||
|
|
||||||
@@ -493,5 +424,4 @@ class ManualSyncView(APIView):
|
|||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"NRPS sync error: {str(e)}", exc_info=True)
|
|
||||||
return Response({'error': 'Sync failed', 'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
return Response({'error': 'Sync failed', 'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|||||||
Reference in New Issue
Block a user