diff --git a/common/djangoapps/third_party_auth/migrations/0012_auto_20170626_1135.py b/common/djangoapps/third_party_auth/migrations/0012_auto_20170626_1135.py new file mode 100644 index 0000000000..66ddd11e71 --- /dev/null +++ b/common/djangoapps/third_party_auth/migrations/0012_auto_20170626_1135.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('third_party_auth', '0011_auto_20170616_0112'), + ] + + operations = [ + migrations.AddField( + model_name='ltiproviderconfig', + name='send_to_registration_first', + field=models.BooleanField(default=False, help_text='If this option is selected, users will be directed to the registration page immediately after authenticating with the third party instead of the login page.'), + ), + migrations.AddField( + model_name='oauth2providerconfig', + name='send_to_registration_first', + field=models.BooleanField(default=False, help_text='If this option is selected, users will be directed to the registration page immediately after authenticating with the third party instead of the login page.'), + ), + migrations.AddField( + model_name='samlproviderconfig', + name='send_to_registration_first', + field=models.BooleanField(default=False, help_text='If this option is selected, users will be directed to the registration page immediately after authenticating with the third party instead of the login page.'), + ), + ] diff --git a/common/djangoapps/third_party_auth/models.py b/common/djangoapps/third_party_auth/models.py index 865edb53d8..90541e780e 100644 --- a/common/djangoapps/third_party_auth/models.py +++ b/common/djangoapps/third_party_auth/models.py @@ -176,6 +176,13 @@ class ProviderConfig(ConfigurationModel): "Django platform session default length will be used." ) ) + send_to_registration_first = models.BooleanField( + default=False, + help_text=_( + "If this option is selected, users will be directed to the registration page " + "immediately after authenticating with the third party instead of the login page." + ), + ) prefix = None # used for provider_id. Set to a string value in subclass backend_name = None # Set to a field or fixed value in subclass accepts_logins = True # Whether to display a sign-in button when the provider is enabled diff --git a/common/djangoapps/third_party_auth/pipeline.py b/common/djangoapps/third_party_auth/pipeline.py index 47a16bdce2..d2dba1cb01 100644 --- a/common/djangoapps/third_party_auth/pipeline.py +++ b/common/djangoapps/third_party_auth/pipeline.py @@ -558,7 +558,8 @@ def ensure_user_information(strategy, auth_entry, backend=None, user=None, socia def should_force_account_creation(): """ For some third party providers, we auto-create user accounts """ current_provider = provider.Registry.get_from_pipeline({'backend': current_partial.backend, 'kwargs': kwargs}) - return current_provider and current_provider.skip_email_verification + return (current_provider and + (current_provider.skip_email_verification or current_provider.send_to_registration_first)) if not user: if auth_entry in [AUTH_ENTRY_LOGIN_API, AUTH_ENTRY_REGISTER_API]: diff --git a/common/djangoapps/third_party_auth/tests/test_pipeline_integration.py b/common/djangoapps/third_party_auth/tests/test_pipeline_integration.py index fe9dba2665..8cd64e4156 100644 --- a/common/djangoapps/third_party_auth/tests/test_pipeline_integration.py +++ b/common/djangoapps/third_party_auth/tests/test_pipeline_integration.py @@ -3,6 +3,7 @@ import unittest import mock +import ddt from django import test from django.conf import settings from django.contrib.auth import models @@ -296,3 +297,42 @@ class TestPipelineUtilityFunctions(TestCase, test.TestCase): ) pipeline.lift_quarantine(request) self.assertNotIn('third_party_auth_quarantined_modules', request.session) + + +@unittest.skipUnless(testutil.AUTH_FEATURE_ENABLED, testutil.AUTH_FEATURES_KEY + ' not enabled') +@ddt.ddt +class EnsureUserInformationTestCase(testutil.TestCase, test.TestCase): + """Tests ensuring that we have the necessary user information to proceed with the pipeline.""" + + def setUp(self): + super(EnsureUserInformationTestCase, self).setUp() + + @ddt.data( + (True, '/register'), + (False, '/login') + ) + @ddt.unpack + def test_provider_settings_redirect_to_registration(self, send_to_registration_first, expected_redirect_url): + """ + Test if user is not authenticated, that they get redirected to the appropriate page + based on the provider's setting for send_to_registration_first. + """ + + provider = mock.MagicMock( + send_to_registration_first=send_to_registration_first, + skip_email_verification=False + ) + + with mock.patch('third_party_auth.pipeline.provider.Registry.get_from_pipeline') as get_from_pipeline: + get_from_pipeline.return_value = provider + with mock.patch('social_core.pipeline.partial.partial_prepare') as partial_prepare: + partial_prepare.return_value = mock.MagicMock(token='') + strategy = mock.MagicMock() + response = pipeline.ensure_user_information( + strategy=strategy, + backend=None, + auth_entry=pipeline.AUTH_ENTRY_LOGIN, + pipeline_index=0 + ) + assert response.status_code == 302 + assert response.url == expected_redirect_url