Commit 86da9c1c by Saleem Latif

Disable linking of personal accounts to enterprise customers via SSO

parent 257de20d
......@@ -84,6 +84,7 @@ import student
from edxmako.shortcuts import render_to_string
from eventtracking import tracker
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from third_party_auth.utils import user_exists
from . import provider
......@@ -501,7 +502,7 @@ def set_pipeline_timeout(strategy, user, *args, **kwargs):
# choice of the user.
def redirect_to_custom_form(request, auth_entry, kwargs):
def redirect_to_custom_form(request, auth_entry, details, kwargs):
"""
If auth_entry is found in AUTH_ENTRY_CUSTOM, this is used to send provider
data to an external server's registration/login page.
......@@ -520,7 +521,7 @@ def redirect_to_custom_form(request, auth_entry, kwargs):
"auth_entry": auth_entry,
"backend_name": backend_name,
"provider_id": provider_id,
"user_details": kwargs['details'],
"user_details": details,
})
digest = hmac.new(secret_key, msg=data_str, digestmod=hashlib.sha256).digest()
# Store the data in the session temporarily, then redirect to a page that will POST it to
......@@ -535,7 +536,7 @@ def redirect_to_custom_form(request, auth_entry, kwargs):
@partial.partial
def ensure_user_information(strategy, auth_entry, backend=None, user=None, social=None, current_partial=None,
allow_inactive_user=False, *args, **kwargs):
allow_inactive_user=False, details=None, *args, **kwargs):
"""
Ensure that we have the necessary information about a user (either an
existing account or registration data) to proceed with the pipeline.
......@@ -567,6 +568,11 @@ def ensure_user_information(strategy, auth_entry, backend=None, user=None, socia
(current_provider.skip_email_verification or current_provider.send_to_registration_first))
if not user:
if user_exists(details or {}):
# User has not already authenticated and the details sent over from
# identity provider belong to an existing user.
return dispatch_to_login()
if is_api(auth_entry):
return HttpResponseBadRequest()
elif auth_entry == AUTH_ENTRY_LOGIN:
......@@ -583,7 +589,7 @@ def ensure_user_information(strategy, auth_entry, backend=None, user=None, socia
raise AuthEntryError(backend, 'auth_entry is wrong. Settings requires a user.')
elif auth_entry in AUTH_ENTRY_CUSTOM:
# Pass the username, email, etc. via query params to the custom entry page:
return redirect_to_custom_form(strategy.request, auth_entry, kwargs)
return redirect_to_custom_form(strategy.request, auth_entry, details or {}, kwargs)
else:
raise AuthEntryError(backend, 'auth_entry invalid')
......
......@@ -894,8 +894,9 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
strategy.storage.user.create_user(username=self.get_username(), email='user@email.com', password='password')
backend = strategy.request.backend
backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy))
# If learner already has an account then make sure login page is served instead of registration.
# pylint: disable=protected-access
self.assert_redirect_to_register_looks_correct(actions.do_complete(backend, social_views._do_login))
self.assert_redirect_to_login_looks_correct(actions.do_complete(backend, social_views._do_login))
distinct_username = pipeline.get(request)['kwargs']['username']
self.assertNotEqual(original_username, distinct_username)
......
......@@ -35,8 +35,8 @@ class AzureADOauth2IntegrationTest(base.Oauth2IntegrationTest):
'aud': 'abcdefgh-1234-5678-900a-0aa0a00aa0aa',
'tid': 'abcdefgh-1234-5678-900a-0aa0a00aa0aa',
'amr': ['pwd'],
'unique_name': 'email_value@example.com',
'upn': 'email_value@example.com',
'unique_name': 'user@email.com',
'upn': 'user@email.com',
'family_name': 'family_name_value',
'name': 'name_value',
'given_name': 'given_name_value',
......
......@@ -31,7 +31,7 @@ class GoogleOauth2IntegrationTest(base.Oauth2IntegrationTest):
'token_type': 'token_type_value',
}
USER_RESPONSE_DATA = {
'email': 'email_value@example.com',
'email': 'user@email.com',
'family_name': 'family_name_value',
'given_name': 'given_name_value',
'id': 'id_value',
......@@ -85,8 +85,8 @@ class GoogleOauth2IntegrationTest(base.Oauth2IntegrationTest):
'backend_name': 'google-oauth2',
'provider_id': 'oa2-google-oauth2',
'user_details': {
'username': 'email_value',
'email': 'email_value@example.com',
'username': 'user',
'email': 'user@email.com',
'fullname': 'name_value',
'first_name': 'given_name_value',
'last_name': 'family_name_value',
......
"""
Tests for third_party_auth utility functions.
"""
import unittest
from django.conf import settings
from third_party_auth.tests.testutil import TestCase
from third_party_auth.utils import user_exists
from student.tests.factories import UserFactory
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class TestUtils(TestCase):
"""
Test the utility functions.
"""
def test_user_exists(self):
"""
Verify that user_exists function returns correct response.
"""
# Create users from factory
UserFactory(username='test_user', email='test_user@example.com')
self.assertTrue(
user_exists({'username': 'test_user', 'email': 'test_user@example.com'}),
)
self.assertTrue(
user_exists({'username': 'test_user'}),
)
self.assertTrue(
user_exists({'email': 'test_user@example.com'}),
)
self.assertFalse(
user_exists({'username': 'invalid_user'}),
)
"""
Utility functions for third_party_auth
"""
from django.contrib.auth.models import User
def user_exists(details):
"""
Return True if user with given details exist in the system.
Arguments:
details (dict): dictionary containing user infor like email, username etc.
Returns:
(bool): True if user with given details exists, `False` otherwise.
"""
user_queryset_filter = {}
email = details.get('email')
username = details.get('username')
if email:
user_queryset_filter['email'] = email
elif username:
user_queryset_filter['username'] = username
if user_queryset_filter:
return User.objects.filter(**user_queryset_filter).exists()
return False
......@@ -640,6 +640,7 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
"finishAuthUrl": finish_auth_url,
"errorMessage": None,
"registerFormSubmitButtonText": "Create Account",
"syncLearnerProfileData": False,
}
if expected_ec is not None:
# If we set an EnterpriseCustomer, third-party auth providers ought to be hidden.
......
......@@ -326,6 +326,7 @@ def _third_party_auth_context(request, redirect_to, tpa_hint=None):
"finishAuthUrl": None,
"errorMessage": None,
"registerFormSubmitButtonText": _("Create Account"),
"syncLearnerProfileData": False,
}
if third_party_auth.is_enabled():
......@@ -357,6 +358,7 @@ def _third_party_auth_context(request, redirect_to, tpa_hint=None):
if current_provider is not None:
context["currentProvider"] = current_provider.name
context["finishAuthUrl"] = pipeline.get_complete_url(current_provider.backend_name)
context["syncLearnerProfileData"] = current_provider.sync_learner_profile_data
if current_provider.skip_registration_form:
# For enterprise (and later for everyone), we need to get explicit consent to the
......
......@@ -41,6 +41,7 @@
data.thirdPartyAuth.secondaryProviders && data.thirdPartyAuth.secondaryProviders.length
);
this.currentProvider = data.thirdPartyAuth.currentProvider || '';
this.syncLearnerProfileData = data.thirdPartyAuth.syncLearnerProfileData || false;
this.errorMessage = data.thirdPartyAuth.errorMessage || '';
this.platformName = data.platformName;
this.resetModel = data.resetModel;
......@@ -63,6 +64,7 @@
context: {
fields: fields,
currentProvider: this.currentProvider,
syncLearnerProfileData: this.syncLearnerProfileData,
providers: this.providers,
hasSecondaryProviders: this.hasSecondaryProviders,
platformName: this.platformName,
......
......@@ -55,6 +55,7 @@
data.thirdPartyAuth.secondaryProviders && data.thirdPartyAuth.secondaryProviders.length
);
this.currentProvider = data.thirdPartyAuth.currentProvider || '';
this.syncLearnerProfileData = data.thirdPartyAuth.syncLearnerProfileData || false;
this.errorMessage = data.thirdPartyAuth.errorMessage || '';
this.platformName = data.platformName;
this.autoSubmit = data.thirdPartyAuth.autoSubmitRegForm;
......@@ -141,6 +142,7 @@
context: {
fields: fields,
currentProvider: this.currentProvider,
syncLearnerProfileData: this.syncLearnerProfileData,
providers: this.providers,
hasSecondaryProviders: this.hasSecondaryProviders,
platformName: this.platformName,
......
<div class="js-form-feedback" aria-live="assertive" tabindex="-1">
</div>
<% if ( context.createAccountOption !== false ) { %>
<% if ( context.createAccountOption !== false && !context.syncLearnerProfileData) { %>
<div class="toggle-form">
<span class="text"><%- gettext("First time here?") %></span>
<a href="#login" class="form-toggle" data-type="register"><%- gettext("Create an Account.") %></a>
......
<div class="js-form-feedback" aria-live="assertive" tabindex="-1">
</div>
<div class="toggle-form">
<span class="text"><%- edx.StringUtils.interpolate(gettext('Already have an {platformName} account?'), {platformName: context.platformName }) %></span>
<a href="#login" class="form-toggle" data-type="login"><%- gettext("Sign in.") %></a>
</div>
<% if (!context.syncLearnerProfileData) { %>
<div class="toggle-form">
<span class="text"><%- edx.StringUtils.interpolate(gettext('Already have an {platformName} account?'), {platformName: context.platformName }) %></span>
<a href="#login" class="form-toggle" data-type="login"><%- gettext("Sign in.") %></a>
</div>
<% } %>
<h2><%- gettext('Create an Account')%></h2>
<form id="register" class="register-form" autocomplete="off" tabindex="-1" method="POST">
......
......@@ -843,8 +843,11 @@ class RegistrationFormFactory(object):
# When the TPA Provider is configured to skip the registration form and we are in an
# enterprise context, we need to hide all fields except for terms of service and
# ensure that the user explicitly checks that field.
hide_registration_fields_except_tos = (current_provider.skip_registration_form and
enterprise_customer_for_request(request))
hide_registration_fields_except_tos = (
(
current_provider.skip_registration_form and enterprise_customer_for_request(request)
) or current_provider.sync_learner_profile_data
)
for field_name in self.DEFAULT_FIELDS + self.EXTRA_FIELDS:
if field_name in field_overrides:
......
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