mirror of
https://github.com/mediacms-io/mediacms.git
synced 2026-01-20 15:22:58 -05:00
wtv
This commit is contained in:
@@ -4,8 +4,10 @@ PyLTI1p3 Django adapters for MediaCMS
|
||||
Provides Django-specific implementations for PyLTI1p3 interfaces
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import time
|
||||
import uuid
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import jwt
|
||||
@@ -192,8 +194,10 @@ class DjangoServiceConnector(ServiceConnector):
|
||||
self._access_token_expires = 0
|
||||
|
||||
def get_access_token(self, scopes):
|
||||
if self._access_token and time.time() < self._access_token_expires:
|
||||
return self._access_token
|
||||
cache_key = 'lti_access_token_' + self._registration.get_issuer() + '_' + hashlib.sha1(' '.join(scopes).encode('utf-8')).hexdigest()
|
||||
token_data = cache.get(cache_key)
|
||||
if token_data:
|
||||
return token_data['access_token']
|
||||
|
||||
key_obj = LTIToolKeys.get_or_create_keys()
|
||||
jwk_obj = jwk.JWK(**key_obj.private_key_jwk)
|
||||
@@ -201,13 +205,17 @@ class DjangoServiceConnector(ServiceConnector):
|
||||
private_key = serialization.load_pem_private_key(pem_bytes, password=None, backend=default_backend())
|
||||
|
||||
now = int(time.time())
|
||||
|
||||
# Moodle can be picky about audience. Including both token URL and issuer is safer.
|
||||
audience = [self._registration.get_auth_token_url(), self._registration.get_issuer()]
|
||||
|
||||
payload = {
|
||||
'iss': self._registration.get_client_id(),
|
||||
'sub': self._registration.get_client_id(),
|
||||
'aud': self._registration.get_auth_token_url(),
|
||||
'aud': audience,
|
||||
'iat': now,
|
||||
'exp': now + 300,
|
||||
'jti': str(time.time()),
|
||||
'jti': str(uuid.uuid4()),
|
||||
}
|
||||
|
||||
client_assertion = jwt.encode(payload, private_key, algorithm='RS256', headers={'kid': key_obj.private_key_jwk['kid']})
|
||||
@@ -220,15 +228,24 @@ class DjangoServiceConnector(ServiceConnector):
|
||||
'scope': ' '.join(scopes),
|
||||
}
|
||||
|
||||
print(f"LTI Service: Requesting access token from {token_url} with scopes: {scopes}")
|
||||
response = requests.post(token_url, data=data, timeout=10)
|
||||
|
||||
try:
|
||||
response.raise_for_status()
|
||||
|
||||
token_data = response.json()
|
||||
self._access_token = token_data['access_token']
|
||||
expires_in = token_data.get('expires_in', 3600)
|
||||
self._access_token_expires = time.time() + expires_in - 10
|
||||
print(f"LTI Service: Successfully received access token. Expires in: {token_data.get('expires_in', 'N/A')}")
|
||||
|
||||
return self._access_token
|
||||
expires_in = token_data.get('expires_in', 3600)
|
||||
cache.set(cache_key, token_data, timeout=expires_in - 10)
|
||||
|
||||
return token_data['access_token']
|
||||
except requests.exceptions.HTTPError as e:
|
||||
print(f"LTI Service Error: Failed to get access token. Status: {e.response.status_code}, Response: {e.response.text}")
|
||||
raise
|
||||
except json.JSONDecodeError:
|
||||
print(f"LTI Service Error: Failed to decode JSON from token endpoint. Response: {response.text}")
|
||||
raise
|
||||
|
||||
def make_service_request(self, scopes, url, is_post=False, data=None, **kwargs):
|
||||
access_token = self.get_access_token(scopes)
|
||||
|
||||
18
lti/views.py
18
lti/views.py
@@ -158,11 +158,25 @@ class LaunchView(View):
|
||||
|
||||
unverified = jwt.decode(id_token, options={"verify_signature": False})
|
||||
iss = unverified.get('iss')
|
||||
aud = unverified.get('aud')
|
||||
aud = unverified.get('aud') # Can be a string or a list
|
||||
|
||||
platform = None
|
||||
if isinstance(aud, list):
|
||||
# If aud is a list, find a platform where the client_id is in the list
|
||||
platforms = LTIPlatform.objects.filter(platform_id=iss, client_id__in=aud)
|
||||
if platforms.count() == 1:
|
||||
platform = platforms.first()
|
||||
elif platforms.count() > 1:
|
||||
raise LtiException(f"Multiple platforms found for issuer '{iss}' and client_ids '{aud}'")
|
||||
else:
|
||||
# If aud is a string, find it directly
|
||||
try:
|
||||
platform = LTIPlatform.objects.get(platform_id=iss, client_id=aud)
|
||||
except LTIPlatform.DoesNotExist:
|
||||
raise
|
||||
pass # Platform will be None
|
||||
|
||||
if not platform:
|
||||
raise LtiException(f"Platform not found for issuer '{iss}' and client_id(s) '{aud}'")
|
||||
|
||||
tool_config = DjangoToolConfig.from_platform(platform)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user