Merge pull request #12152 from edx/cdyer/oauth-toolkit-allow-inactive
Fix django-oauth-toolkit email and inactive user issues
This commit is contained in:
43
lms/djangoapps/oauth_dispatch/dot_overrides.py
Normal file
43
lms/djangoapps/oauth_dispatch/dot_overrides.py
Normal file
@@ -0,0 +1,43 @@
|
||||
"""
|
||||
Classes that override default django-oauth-toolkit behavior
|
||||
"""
|
||||
|
||||
from django.contrib.auth import authenticate, get_user_model
|
||||
from oauth2_provider.oauth2_validators import OAuth2Validator
|
||||
|
||||
|
||||
class EdxOAuth2Validator(OAuth2Validator):
|
||||
"""
|
||||
Validator class that implements edX-specific custom behavior:
|
||||
|
||||
* It allows users to log in with their email or username.
|
||||
* It does not require users to be active before logging in.
|
||||
"""
|
||||
|
||||
def validate_user(self, username, password, client, request, *args, **kwargs):
|
||||
"""
|
||||
Authenticate users, but allow inactive users (with u.is_active == False)
|
||||
to authenticate.
|
||||
"""
|
||||
user = self._authenticate(username=username, password=password)
|
||||
if user is not None:
|
||||
request.user = user
|
||||
return True
|
||||
return False
|
||||
|
||||
def _authenticate(self, username, password):
|
||||
"""
|
||||
Authenticate the user, allowing the user to identify themself either by
|
||||
username or email
|
||||
"""
|
||||
|
||||
authenticated_user = authenticate(username=username, password=password)
|
||||
if authenticated_user is None:
|
||||
UserModel = get_user_model() # pylint: disable=invalid-name
|
||||
try:
|
||||
email_user = UserModel.objects.get(email=username)
|
||||
except UserModel.DoesNotExist:
|
||||
authenticated_user = None
|
||||
else:
|
||||
authenticated_user = authenticate(username=email_user.username, password=password)
|
||||
return authenticated_user
|
||||
66
lms/djangoapps/oauth_dispatch/tests/test_dot_overrides.py
Normal file
66
lms/djangoapps/oauth_dispatch/tests/test_dot_overrides.py
Normal file
@@ -0,0 +1,66 @@
|
||||
"""
|
||||
Test of custom django-oauth-toolkit behavior
|
||||
"""
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import TestCase, RequestFactory
|
||||
from ..dot_overrides import EdxOAuth2Validator
|
||||
|
||||
|
||||
class AuthenticateTestCase(TestCase):
|
||||
"""
|
||||
Test that users can authenticate with either username or email
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(AuthenticateTestCase, self).setUp()
|
||||
self.user = User.objects.create_user(
|
||||
username='darkhelmet',
|
||||
password='12345',
|
||||
email='darkhelmet@spaceball_one.org',
|
||||
)
|
||||
self.validator = EdxOAuth2Validator()
|
||||
|
||||
def test_authenticate_with_username(self):
|
||||
user = self.validator._authenticate(username='darkhelmet', password='12345')
|
||||
self.assertEqual(
|
||||
self.user,
|
||||
user
|
||||
)
|
||||
|
||||
def test_authenticate_with_email(self):
|
||||
user = self.validator._authenticate(username='darkhelmet@spaceball_one.org', password='12345')
|
||||
self.assertEqual(
|
||||
self.user,
|
||||
user
|
||||
)
|
||||
|
||||
|
||||
class CustomValidationTestCase(TestCase):
|
||||
"""
|
||||
Test custom user validation works.
|
||||
|
||||
In particular, inactive users should be able to validate.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(CustomValidationTestCase, self).setUp()
|
||||
self.user = User.objects.create_user(
|
||||
username='darkhelmet',
|
||||
password='12345',
|
||||
email='darkhelmet@spaceball_one.org',
|
||||
)
|
||||
self.validator = EdxOAuth2Validator()
|
||||
self.request_factory = RequestFactory()
|
||||
|
||||
def test_active_user_validates(self):
|
||||
self.assertTrue(self.user.is_active)
|
||||
request = self.request_factory.get('/')
|
||||
self.assertTrue(self.validator.validate_user('darkhelmet', '12345', client=None, request=request))
|
||||
|
||||
def test_inactive_user_validates(self):
|
||||
self.user.is_active = False
|
||||
self.user.save()
|
||||
request = self.request_factory.get('/')
|
||||
self.assertTrue(self.validator.validate_user('darkhelmet', '12345', client=None, request=request))
|
||||
@@ -455,6 +455,12 @@ OAUTH_OIDC_USERINFO_HANDLERS = (
|
||||
OAUTH_EXPIRE_CONFIDENTIAL_CLIENT_DAYS = 365
|
||||
OAUTH_EXPIRE_PUBLIC_CLIENT_DAYS = 30
|
||||
|
||||
################################## DJANGO OAUTH TOOLKIT #######################################
|
||||
|
||||
OAUTH2_PROVIDER = {
|
||||
'OAUTH2_VALIDATOR_CLASS': 'lms.djangoapps.oauth_dispatch.dot_overrides.EdxOAuth2Validator'
|
||||
}
|
||||
|
||||
################################## TEMPLATE CONFIGURATION #####################################
|
||||
# Mako templating
|
||||
# TODO: Move the Mako templating into a different engine in TEMPLATES below.
|
||||
|
||||
Reference in New Issue
Block a user