255 lines
9.5 KiB
Python
255 lines
9.5 KiB
Python
"""
|
|
Tests for the LTI user management functionality
|
|
"""
|
|
|
|
|
|
import string
|
|
from unittest.mock import MagicMock, PropertyMock, patch
|
|
|
|
import pytest
|
|
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
|
|
from django.core.exceptions import PermissionDenied
|
|
from django.test import TestCase
|
|
from django.test.client import RequestFactory
|
|
|
|
from common.djangoapps.student.tests.factories import UserFactory
|
|
|
|
from .. import users
|
|
from ..models import LtiConsumer, LtiUser
|
|
|
|
|
|
class UserManagementHelperTest(TestCase):
|
|
"""
|
|
Tests for the helper functions in users.py
|
|
"""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.request = RequestFactory().post('/')
|
|
self.old_user = UserFactory.create()
|
|
self.new_user = UserFactory.create()
|
|
self.new_user.save()
|
|
self.request.user = self.old_user
|
|
self.lti_consumer = LtiConsumer(
|
|
consumer_name='TestConsumer',
|
|
consumer_key='TestKey',
|
|
consumer_secret='TestSecret'
|
|
)
|
|
self.lti_consumer.save()
|
|
self.lti_user = LtiUser(
|
|
lti_user_id='lti_user_id',
|
|
edx_user=self.new_user
|
|
)
|
|
|
|
@patch('django.contrib.auth.authenticate', return_value=None)
|
|
def test_permission_denied_for_unknown_user(self, _authenticate_mock):
|
|
with pytest.raises(PermissionDenied):
|
|
users.switch_user(self.request, self.lti_user, self.lti_consumer)
|
|
|
|
@patch('lms.djangoapps.lti_provider.users.login')
|
|
def test_authenticate_called(self, _login_mock):
|
|
with patch('lms.djangoapps.lti_provider.users.authenticate', return_value=self.new_user) as authenticate:
|
|
users.switch_user(self.request, self.lti_user, self.lti_consumer)
|
|
authenticate.assert_called_with(
|
|
username=self.new_user.username,
|
|
lti_user_id=self.lti_user.lti_user_id,
|
|
lti_consumer=self.lti_consumer
|
|
)
|
|
|
|
@patch('lms.djangoapps.lti_provider.users.login')
|
|
def test_login_called(self, login_mock):
|
|
with patch('lms.djangoapps.lti_provider.users.authenticate', return_value=self.new_user):
|
|
users.switch_user(self.request, self.lti_user, self.lti_consumer)
|
|
login_mock.assert_called_with(self.request, self.new_user)
|
|
|
|
def test_random_username_generator(self):
|
|
for _idx in range(1000):
|
|
username = users.generate_random_edx_username()
|
|
assert len(username) <= 30, 'Username too long'
|
|
# Check that the username contains only allowable characters
|
|
for char in range(len(username)): # lint-amnesty, pylint: disable=consider-using-enumerate
|
|
assert username[char] in (string.ascii_letters + string.digits), \
|
|
"Username has forbidden character '{}'".format(username[char])
|
|
|
|
|
|
@patch('lms.djangoapps.lti_provider.users.switch_user', autospec=True)
|
|
@patch('lms.djangoapps.lti_provider.users.create_lti_user', autospec=True)
|
|
class AuthenticateLtiUserTest(TestCase):
|
|
"""
|
|
Tests for the authenticate_lti_user function in users.py
|
|
"""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.lti_consumer = LtiConsumer(
|
|
consumer_name='TestConsumer',
|
|
consumer_key='TestKey',
|
|
consumer_secret='TestSecret'
|
|
)
|
|
self.lti_consumer.save()
|
|
self.lti_user_id = 'lti_user_id'
|
|
self.edx_user_id = 'edx_user_id'
|
|
self.old_user = UserFactory.create()
|
|
self.request = RequestFactory().post('/')
|
|
self.request.user = self.old_user
|
|
|
|
def create_lti_user_model(self):
|
|
"""
|
|
Generate and save a User and an LTI user model
|
|
"""
|
|
edx_user = User(username=self.edx_user_id)
|
|
edx_user.save()
|
|
lti_user = LtiUser(
|
|
lti_consumer=self.lti_consumer,
|
|
lti_user_id=self.lti_user_id,
|
|
edx_user=edx_user
|
|
)
|
|
lti_user.save()
|
|
return lti_user
|
|
|
|
def test_authentication_with_new_user(self, _create_user, switch_user):
|
|
lti_user = MagicMock()
|
|
lti_user.edx_user_id = self.edx_user_id
|
|
with patch('lms.djangoapps.lti_provider.users.create_lti_user', return_value=lti_user) as create_user:
|
|
users.authenticate_lti_user(self.request, self.lti_user_id, self.lti_consumer)
|
|
create_user.assert_called_with(self.lti_user_id, self.lti_consumer)
|
|
switch_user.assert_called_with(self.request, lti_user, self.lti_consumer)
|
|
|
|
def test_authentication_with_authenticated_user(self, create_user, switch_user):
|
|
lti_user = self.create_lti_user_model()
|
|
self.request.user = lti_user.edx_user
|
|
assert self.request.user.is_authenticated
|
|
users.authenticate_lti_user(self.request, self.lti_user_id, self.lti_consumer)
|
|
assert not create_user.called
|
|
assert not switch_user.called
|
|
|
|
def test_authentication_with_unauthenticated_user(self, create_user, switch_user):
|
|
lti_user = self.create_lti_user_model()
|
|
self.request.user = lti_user.edx_user
|
|
with patch('django.contrib.auth.models.User.is_authenticated', new_callable=PropertyMock) as mock_is_auth:
|
|
mock_is_auth.return_value = False
|
|
users.authenticate_lti_user(self.request, self.lti_user_id, self.lti_consumer)
|
|
assert not create_user.called
|
|
switch_user.assert_called_with(self.request, lti_user, self.lti_consumer)
|
|
|
|
def test_authentication_with_wrong_user(self, create_user, switch_user):
|
|
lti_user = self.create_lti_user_model()
|
|
self.request.user = self.old_user
|
|
assert self.request.user.is_authenticated
|
|
users.authenticate_lti_user(self.request, self.lti_user_id, self.lti_consumer)
|
|
assert not create_user.called
|
|
switch_user.assert_called_with(self.request, lti_user, self.lti_consumer)
|
|
|
|
|
|
class CreateLtiUserTest(TestCase):
|
|
"""
|
|
Tests for the create_lti_user function in users.py
|
|
"""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.lti_consumer = LtiConsumer(
|
|
consumer_name='TestConsumer',
|
|
consumer_key='TestKey',
|
|
consumer_secret='TestSecret'
|
|
)
|
|
self.lti_consumer.save()
|
|
|
|
def test_create_lti_user_creates_auth_user_model(self):
|
|
users.create_lti_user('lti_user_id', self.lti_consumer)
|
|
assert User.objects.count() == 1
|
|
|
|
@patch('uuid.uuid4', return_value='random_uuid')
|
|
@patch('lms.djangoapps.lti_provider.users.generate_random_edx_username', return_value='edx_id')
|
|
def test_create_lti_user_creates_correct_user(self, uuid_mock, _username_mock):
|
|
users.create_lti_user('lti_user_id', self.lti_consumer)
|
|
assert User.objects.count() == 1
|
|
user = User.objects.get(username='edx_id')
|
|
assert user.email == 'edx_id@lti.example.com'
|
|
uuid_mock.assert_called_with()
|
|
|
|
@patch('lms.djangoapps.lti_provider.users.generate_random_edx_username', side_effect=['edx_id', 'new_edx_id'])
|
|
def test_unique_username_created(self, username_mock):
|
|
User(username='edx_id').save()
|
|
users.create_lti_user('lti_user_id', self.lti_consumer)
|
|
assert username_mock.call_count == 2
|
|
assert User.objects.count() == 2
|
|
user = User.objects.get(username='new_edx_id')
|
|
assert user.email == 'new_edx_id@lti.example.com'
|
|
|
|
|
|
class LtiBackendTest(TestCase):
|
|
"""
|
|
Tests for the authentication backend that authenticates LTI users.
|
|
"""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.edx_user = UserFactory.create()
|
|
self.edx_user.save()
|
|
self.lti_consumer = LtiConsumer(
|
|
consumer_key="Consumer Key",
|
|
consumer_secret="Consumer Secret"
|
|
)
|
|
self.lti_consumer.save()
|
|
self.lti_user_id = 'LTI User ID'
|
|
LtiUser(
|
|
lti_consumer=self.lti_consumer,
|
|
lti_user_id=self.lti_user_id,
|
|
edx_user=self.edx_user
|
|
).save()
|
|
self.old_user = UserFactory.create()
|
|
self.request = RequestFactory().post('/')
|
|
self.request.user = self.old_user
|
|
|
|
def test_valid_user_authenticates(self):
|
|
user = users.LtiBackend().authenticate(
|
|
self.request,
|
|
username=self.edx_user.username,
|
|
lti_user_id=self.lti_user_id,
|
|
lti_consumer=self.lti_consumer
|
|
)
|
|
assert user == self.edx_user
|
|
|
|
def test_missing_user_returns_none(self):
|
|
user = users.LtiBackend().authenticate(
|
|
self.request,
|
|
username=self.edx_user.username,
|
|
lti_user_id='Invalid Username',
|
|
lti_consumer=self.lti_consumer
|
|
)
|
|
assert user is None
|
|
|
|
def test_non_lti_user_returns_none(self):
|
|
non_edx_user = UserFactory.create()
|
|
non_edx_user.save()
|
|
user = users.LtiBackend().authenticate(
|
|
self.request,
|
|
username=non_edx_user.username,
|
|
)
|
|
assert user is None
|
|
|
|
def test_missing_lti_id_returns_null(self):
|
|
user = users.LtiBackend().authenticate(
|
|
self.request,
|
|
username=self.edx_user.username,
|
|
lti_consumer=self.lti_consumer
|
|
)
|
|
assert user is None
|
|
|
|
def test_missing_lti_consumer_returns_null(self):
|
|
user = users.LtiBackend().authenticate(
|
|
self.request,
|
|
username=self.edx_user.username,
|
|
lti_user_id=self.lti_user_id,
|
|
)
|
|
assert user is None
|
|
|
|
def test_existing_user_returned_by_get_user(self):
|
|
user = users.LtiBackend().get_user(self.edx_user.id)
|
|
assert user == self.edx_user
|
|
|
|
def test_get_user_returns_none_for_invalid_user(self):
|
|
user = users.LtiBackend().get_user(-1)
|
|
assert user is None
|