Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: redirect to custom URL when third-party auth account is unlinked #33513

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
Loading