Skip to content

Commit

Permalink
feat: redirect to custom URL when third-party auth account is unlinked
Browse files Browse the repository at this point in the history
  • Loading branch information
ArturGaspar committed Oct 16, 2023
1 parent 22e2a23 commit f56931f
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 18 deletions.
23 changes: 22 additions & 1 deletion openedx/core/djangoapps/user_authn/views/login_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
update_logistration_context_for_enterprise
)
from common.djangoapps.student.helpers import get_next_url_for_login_page
from common.djangoapps.third_party_auth import pipeline
from common.djangoapps.third_party_auth import pipeline, provider
from common.djangoapps.third_party_auth.decorators import xframe_allow_whitelisted
from common.djangoapps.util.password_policy_validators import DEFAULT_MAX_PASSWORD_LENGTH

Expand Down Expand Up @@ -126,6 +126,19 @@ def get_login_session_form(request):
return form_desc


def _get_unlinked_provision_url(current_provider):
try:
unlinked_provision_url = current_provider.get_setting(
'unlinked_account_provision_url'
)
except (AttributeError, KeyError):
# Not all provider subclasses implement get_setting().
pass
else:
if isinstance(unlinked_provision_url, str):
return unlinked_provision_url


@require_http_methods(['GET'])
@ratelimit(
key='openedx.core.djangoapps.util.ratelimit.real_ip',
Expand Down Expand Up @@ -196,6 +209,14 @@ def login_and_registration_form(request, initial_mode="login"):
saml_provider = False
running_pipeline = pipeline.get(request)
if running_pipeline:
# Redirect to provisioning URL if there is a current third-party
# authentication provider but the user is not authenticated.
current_provider = provider.Registry.get_from_pipeline(running_pipeline)
if current_provider:
unlinked_provision_url = _get_unlinked_provision_url(current_provider)
if unlinked_provision_url:
return redirect(unlinked_provision_url)

saml_provider, __ = third_party_auth.utils.is_saml_provider(
running_pipeline.get('backend'), running_pipeline.get('kwargs')
)
Expand Down
67 changes: 50 additions & 17 deletions openedx/core/djangoapps/user_authn/views/tests/test_logistration.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,16 +193,17 @@ def test_third_party_auth_disabled(self, url_name):
response = self.client.get(reverse(url_name))
self._assert_third_party_auth_data(response, None, None, [], None)

@mock.patch('common.djangoapps.third_party_auth.models.OAuth2ProviderConfig.get_setting')
@mock.patch('openedx.core.djangoapps.user_authn.views.login_form.enterprise_customer_for_request')
@ddt.data(
("signin_user", None, None, None, False),
("register_user", None, None, None, False),
("signin_user", "google-oauth2", "Google", None, False),
("register_user", "google-oauth2", "Google", None, False),
("signin_user", "facebook", "Facebook", None, False),
("register_user", "facebook", "Facebook", None, False),
("signin_user", "dummy", "Dummy", None, False),
("register_user", "dummy", "Dummy", None, False),
("signin_user", None, None, None, False, None),
("register_user", None, None, None, False, None),
("signin_user", "google-oauth2", "Google", None, False, None),
("register_user", "google-oauth2", "Google", None, False, None),
("signin_user", "facebook", "Facebook", None, False, None),
("register_user", "facebook", "Facebook", None, False, None),
("signin_user", "dummy", "Dummy", None, False, None),
("register_user", "dummy", "Dummy", None, False, None),
(
"signin_user",
"google-oauth2",
Expand All @@ -212,7 +213,24 @@ def test_third_party_auth_disabled(self, url_name):
'logo': 'https://host.com/logo.jpg',
'welcome_msg': 'No message'
},
True
True,
None
),
(
"signin_user",
"google-oauth2",
"Google",
None,
False,
'http://example.com'
),
(
"signin_user",
"google-oauth2",
"Google",
None,
False,
123 # Test invalid configuration.
)
)
@ddt.unpack
Expand All @@ -223,7 +241,9 @@ def test_third_party_auth(
current_provider,
expected_enterprise_customer_mock_attrs,
add_user_details,
unlinked_account_provision_url,
enterprise_customer_mock,
get_setting_mock
):
params = [
('course_id', 'course-v1:Org+Course+Run'),
Expand All @@ -249,6 +269,12 @@ def test_third_party_auth(
email = '[email protected]'
enterprise_customer_mock.return_value = expected_ec

if unlinked_account_provision_url is not None:
other_settings = {
'unlinked_account_provision_url': unlinked_account_provision_url
}
get_setting_mock.side_effect = other_settings.__getitem__

# Simulate a running pipeline
if current_backend is not None:
pipeline_target = "openedx.core.djangoapps.user_authn.views.login_form.third_party_auth.pipeline"
Expand Down Expand Up @@ -292,14 +318,21 @@ def test_third_party_auth(
"registerUrl": self._third_party_login_url("google-oauth2", "register", params)
},
]
self._assert_third_party_auth_data(
response,
current_backend,
current_provider,
expected_providers,
expected_ec,
add_user_details
)
if unlinked_account_provision_url and isinstance(unlinked_account_provision_url, str):
self.assertRedirects(
response,
unlinked_account_provision_url,
fetch_redirect_response=False
)
else:
self._assert_third_party_auth_data(
response,
current_backend,
current_provider,
expected_providers,
expected_ec,
add_user_details
)

def _configure_testshib_provider(self, provider_name, idp_slug):
"""
Expand Down

0 comments on commit f56931f

Please sign in to comment.