Commit 269abc54 by Carlos Andrés Rocha

Read list of courses during authentication, if available.

parent 1c63cb59
......@@ -2,8 +2,10 @@
This file contains Django authentication backends. For more information visit
https://docs.djangoproject.com/en/dev/topics/auth/customizing/.
"""
import json
from django.conf import settings
import django.dispatch
from social.backends.open_id import OpenIdConnectAuth
......@@ -31,11 +33,36 @@ class EdXOpenIdConnect(OpenIdConnectAuth):
'locale': u'language',
}
auth_complete_signal = django.dispatch.Signal(providing_args=["user", "id_token"])
def user_data(self, _access_token, *_args, **_kwargs):
# Include decoded id_token fields in user data.
return self.id_token
def auth_complete_params(self, state=None):
params = super(EdXOpenIdConnect, self).auth_complete_params(state)
# TODO: Due a limitation in the oidc provider in the LMS, the list of all course permissions
# is computed during the authentication process. As an optimization, we explicitly request
# the list here, avoiding further roundtrips. This is no longer necessary once the limitation
# is resolved and instead the course permissions can be requested on a need to have basis,
# reducing overhead significantly.
claim_names = settings.COURSE_PERMISSIONS_CLAIMS
courses_claims_request = {name: {'essential': True} for name in claim_names}
params['claims'] = json.dumps({'id_token': courses_claims_request})
return params
def auth_complete(self, *args, **kwargs):
# WARNING: during testing, the user model class is `social.tests.models` and not the one
# specified for the application.
user = super(EdXOpenIdConnect, self).auth_complete(*args, **kwargs)
self.auth_complete_signal.send(sender=self.__class__, user=user, id_token=self.id_token)
return user
def get_user_claims(self, access_token, claims=None):
""" Returns a dictionary with the values for each claim requested. """
data = self.get_json(
self.USER_INFO_URL,
headers={'Authorization': 'Bearer {0}'.format(access_token)}
......
......@@ -3,7 +3,36 @@ from django.conf import settings
from social.tests.backends.oauth import OAuth2Test
from social.tests.backends.open_id import OpenIdConnectTestMixin
import courses
DUMMY_AUTHORIZED_COURSE = 'dummy/course/id'
class EdXOpenIdConnectTests(OpenIdConnectTestMixin, OAuth2Test):
backend_path = 'analytics_dashboard.backends.EdXOpenIdConnect'
issuer = settings.SOCIAL_AUTH_EDX_OIDC_URL_ROOT
expected_username = 'test_user'
def get_id_token(self, *args, **kwargs):
data = super(EdXOpenIdConnectTests, self).get_id_token(*args, **kwargs)
# Set the field used to derive the username of the logged user.
data['preferred_username'] = self.expected_username
# Include a dummy list of authorized courses.
claim_name = settings.COURSE_PERMISSIONS_CLAIMS[0]
data[claim_name] = [DUMMY_AUTHORIZED_COURSE]
return data
def test_login(self):
user = self.do_login()
self.assertIsNotNone(user)
def test_course_permissions(self):
user = self.do_login()
authorized_courses = courses.permissions.get_user_course_permissions(user)
self.assertEqual(len(authorized_courses), 1)
self.assertIn(DUMMY_AUTHORIZED_COURSE, authorized_courses)
import datetime
import logging
from django.conf import settings
from django.core.cache import cache
from django.dispatch import receiver
from social.apps.django_app import load_strategy
from analytics_dashboard.backends import EdXOpenIdConnect
from courses.exceptions import UserNotAssociatedWithBackendError, InvalidAccessTokenError, \
PermissionsRetrievalFailedError
......@@ -15,8 +20,8 @@ def _get_course_permission_cache_keys(user):
"""
Return the cache keys used for user-course permissions
"""
key_last_updated = 'course_permissions_updated_at_{}'.format(user.pk)
key_courses = 'course_permissions_{}'.format(user.pk)
key_last_updated = 'course_permissions_updated_at_{}'.format(user.id)
key_courses = 'course_permissions_{}'.format(user.id)
return key_courses, key_last_updated
......@@ -135,3 +140,15 @@ def user_can_view_course(user, course_id):
courses = get_user_course_permissions(user)
return course_id in courses
# pylint: disable=unused-argument
@receiver(EdXOpenIdConnect.auth_complete_signal)
def on_auth_complete(sender, user, id_token, **kwargs):
""" Callback to cache course permissions if available in the IDToken. """
allowed_courses = set()
for name in settings.COURSE_PERMISSIONS_CLAIMS:
if name in id_token:
allowed_courses.update(id_token[name])
if allowed_courses:
set_user_course_permissions(user, allowed_courses)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment