Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

change to new edx user service #224

Merged
merged 5 commits into from
Mar 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions lti_consumer/lti_xblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,8 @@ def role(self):
"""
Get system user role and convert it to LTI role.
"""
return ROLE_MAP.get(self.runtime.get_user_role(), 'Student')
role = self.runtime.service(self, 'user').get_current_user().opt_attrs.get('edx-platform.user_role', 'student')
return ROLE_MAP.get(role, 'Student')

@property
def course(self):
Expand Down Expand Up @@ -723,11 +724,15 @@ def lti_provider_key_secret(self):
def user_id(self):
"""
Returns the opaque anonymous_student_id for the current user.
This defaults to 'student' when testing in studio.
It will return the proper anonymous ID in the LMS.
"""
user_id = self.runtime.anonymous_student_id
user_id = self.runtime.service(self, 'user').get_current_user().opt_attrs.get(
'edx-platform.anonymous_user_id', None)

if user_id is None:
raise LtiError(self.ugettext("Could not get user id for current request"))
return str(urllib.parse.quote(user_id))
return str(user_id)

def get_icon_class(self):
""" Returns the icon class """
Expand Down Expand Up @@ -912,22 +917,20 @@ def extract_real_user_data(self):
"""
Extract and return real user data from the runtime
"""
user = self.runtime.service(self, 'user').get_current_user()
user_data = {
'user_email': None,
'user_username': None,
'user_language': None,
}

if callable(self.runtime.get_real_user):
real_user_object = self.runtime.get_real_user(self.runtime.anonymous_student_id)
user_data['user_email'] = getattr(real_user_object, "email", "")
user_data['user_username'] = getattr(real_user_object, "username", "")
user_preferences = getattr(real_user_object, "preferences", None)
try:
user_data['user_email'] = user.emails[0]
except IndexError:
user_data['user_email'] = None

if user_preferences is not None:
language_preference = user_preferences.filter(key='pref-lang')
if len(language_preference) == 1:
user_data['user_language'] = language_preference[0].value
user_data['user_username'] = user.opt_attrs.get("edx-platform.username", None)
user_data['user_language'] = user.opt_attrs.get("edx-platform.user_preferences", {}).get("pref-lang", None)

return user_data

Expand Down
103 changes: 64 additions & 39 deletions lti_consumer/tests/unit/test_lti_xblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import json
import logging
from datetime import timedelta
from unittest.mock import Mock, NonCallableMock, PropertyMock, patch
from unittest.mock import Mock, PropertyMock, patch

import ddt
from Cryptodome.PublicKey import RSA
Expand Down Expand Up @@ -132,16 +132,29 @@ def test_role(self):
"""
Test `role` returns the correct LTI role string
"""
self.xblock.runtime.get_user_role.return_value = 'student'
fake_user = Mock()
fake_user.opt_attrs = {
'edx-platform.user_role': 'student'
}
self.xblock.runtime.service(self, 'user').get_current_user = Mock(return_value=fake_user)
self.assertEqual(self.xblock.role, 'Student')

self.xblock.runtime.get_user_role.return_value = 'guest'
fake_user.opt_attrs = {
'edx-platform.user_role': 'guest'
}
self.xblock.runtime.service(self, 'user').get_current_user = Mock(return_value=fake_user)
self.assertEqual(self.xblock.role, 'Student')

self.xblock.runtime.get_user_role.return_value = 'staff'
fake_user.opt_attrs = {
'edx-platform.user_role': 'staff'
}
self.xblock.runtime.service(self, 'user').get_current_user = Mock(return_value=fake_user)
self.assertEqual(self.xblock.role, 'Administrator')

self.xblock.runtime.get_user_role.return_value = 'instructor'
fake_user.opt_attrs = {
'edx-platform.user_role': 'instructor'
}
self.xblock.runtime.service(self, 'user').get_current_user = Mock(return_value=fake_user)
self.assertEqual(self.xblock.role, 'Instructor')

def test_course(self):
Expand Down Expand Up @@ -218,21 +231,25 @@ def test_user_id(self):
"""
Test `user_id` returns the user_id string
"""
self.xblock.runtime.anonymous_student_id = FAKE_USER_ID
self.assertEqual(self.xblock.user_id, FAKE_USER_ID)
fake_user = Mock()
fake_user.opt_attrs = {
'edx-platform.anonymous_user_id': FAKE_USER_ID
}

def test_user_id_url_encoded(self):
"""
Test `user_id` url encodes the user id
"""
self.xblock.runtime.anonymous_student_id = 'user_id?&. '
self.assertEqual(self.xblock.user_id, 'user_id%3F%26.%20')
self.xblock.runtime.service(self, 'user').get_current_user = Mock(return_value=fake_user)
self.assertEqual(self.xblock.user_id, FAKE_USER_ID)

def test_user_id_none(self):
"""
Test `user_id` raises LtiError when the user id cannot be returned
"""
self.xblock.runtime.anonymous_student_id = None
fake_user = Mock()
fake_user.opt_attrs = {
'edx-platform.anonymous_user_id': None
}

self.xblock.runtime.service(self, 'user').get_current_user = Mock(return_value=fake_user)

with self.assertRaises(LtiError):
__ = self.xblock.user_id

Expand Down Expand Up @@ -490,48 +507,47 @@ class TestExtractRealUserData(TestLtiConsumerXBlock):
Unit tests for LtiConsumerXBlock.extract_real_user_data()
"""

def test_get_real_user_not_callable(self):
"""
Test user_email, user_username, and user_language not available
"""
self.xblock.runtime.get_real_user = NonCallableMock()

real_user_data = self.xblock.extract_real_user_data()
self.assertIsNone(real_user_data['user_email'])
self.assertIsNone(real_user_data['user_username'])
self.assertIsNone(real_user_data['user_language'])

def test_get_real_user_callable(self):
"""
Test user_email, and user_username available, but not user_language
See also documentation of new user service:
https://github.com/openedx/XBlock/blob/master/xblock/reference/user_service.py
"""
fake_user = Mock()
fake_user.email = '[email protected]'
fake_user.username = 'fake'
fake_user.preferences = None
fake_user_email = '[email protected]'
fake_user.emails = [fake_user_email]
fake_username = 'fake'
fake_user.opt_attrs = {
"edx-platform.username": fake_username
}

self.xblock.runtime.get_real_user = Mock(return_value=fake_user)
self.xblock.runtime.service(self, 'user').get_current_user = Mock(return_value=fake_user)

real_user_data = self.xblock.extract_real_user_data()
self.assertEqual(real_user_data['user_email'], fake_user.email)
self.assertEqual(real_user_data['user_username'], fake_user.username)
self.assertEqual(real_user_data['user_email'], fake_user_email)
self.assertEqual(real_user_data['user_username'], fake_username)
self.assertIsNone(real_user_data['user_language'])

def test_get_real_user_callable_with_language_preference(self):
"""
Test user_language available
See also documentation of new user service:
https://github.com/openedx/XBlock/blob/master/xblock/reference/user_service.py
"""
fake_user = Mock()
fake_user.email = '[email protected]'
fake_user.username = 'fake'
mock_language_pref = Mock()
mock_language_pref.value = PropertyMock(return_value='en')
fake_user.preferences.filter = Mock(return_value=[mock_language_pref])
fake_user.emails = ['[email protected]']
fake_user.full_name = 'fake'
pref_language = "en"
fake_user.opt_attrs = {
"edx-platform.user_preferences": {
"pref-lang": "en"
}
}

self.xblock.runtime.get_real_user = Mock(return_value=fake_user)
self.xblock.runtime.service(self, 'user').get_current_user = Mock(return_value=fake_user)

real_user_data = self.xblock.extract_real_user_data()
self.assertEqual(real_user_data['user_language'], mock_language_pref.value)
self.assertEqual(real_user_data['user_language'], pref_language)


class TestStudentView(TestLtiConsumerXBlock):
Expand Down Expand Up @@ -630,7 +646,16 @@ def setUp(self):
self.xblock._get_lti_consumer = Mock(return_value=self.mock_lti_consumer) # pylint: disable=protected-access
self.xblock.due = timezone.now()
self.xblock.graceperiod = timedelta(days=1)
self.xblock.runtime.get_real_user = Mock(return_value=None)

fake_user = Mock()
fake_user_email = '[email protected]'
fake_user.emails = [fake_user_email]
fake_username = 'fake'
fake_user.opt_attrs = {
"edx-platform.username": fake_username
}

self.xblock.runtime.service(self, 'user').get_current_user = Mock(return_value=fake_user)

@patch('lti_consumer.lti_xblock.LtiConsumerXBlock.course')
@patch('lti_consumer.lti_xblock.LtiConsumerXBlock.user_id', PropertyMock(return_value=FAKE_USER_ID))
Expand Down