diff --git a/openedx/core/djangoapps/user_authn/views/login.py b/openedx/core/djangoapps/user_authn/views/login.py index 5c6e304f5dc2c982dd9deee0b92e0494fc1f2ca0..efabd7d5710c21068b11f4f7392d8e14e91720f2 100644 --- a/openedx/core/djangoapps/user_authn/views/login.py +++ b/openedx/core/djangoapps/user_authn/views/login.py @@ -181,7 +181,7 @@ def _log_and_raise_inactive_user_auth_error(unauthenticated_user): raise AuthFailedError(_generate_not_activated_message(unauthenticated_user)) -def _authenticate_first_party(request, unauthenticated_user): +def _authenticate_first_party(request, unauthenticated_user, third_party_auth_requested): """ Use Django authentication on the given request, using rate limiting if configured """ @@ -190,7 +190,11 @@ def _authenticate_first_party(request, unauthenticated_user): # to fail and we can take advantage of the ratelimited backend username = unauthenticated_user.username if unauthenticated_user else "" - _check_user_auth_flow(request.site, unauthenticated_user) + # First time when a user login through third_party_auth account then user needs to link + # third_party account with the platform account by login through email and password that's + # why we need to by-pass this check when user is already authenticated by third_party_auth. + if not third_party_auth_requested: + _check_user_auth_flow(request.site, unauthenticated_user) try: password = normalize_password(request.POST['password']) @@ -403,7 +407,7 @@ def login_user(request): possibly_authenticated_user = user if not is_user_third_party_authenticated: - possibly_authenticated_user = _authenticate_first_party(request, user) + possibly_authenticated_user = _authenticate_first_party(request, user, third_party_auth_requested) if possibly_authenticated_user and password_policy_compliance.should_enforce_compliance_on_login(): # Important: This call must be made AFTER the user was successfully authenticated. _enforce_password_policy_compliance(request, possibly_authenticated_user) diff --git a/openedx/core/djangoapps/user_authn/views/tests/test_login.py b/openedx/core/djangoapps/user_authn/views/tests/test_login.py index c63b44edd371b5798388bff77599fc7af5ebe7ac..fc0fe5c6fb67a92ad8d7c7d06ca1333eb0f9f432 100644 --- a/openedx/core/djangoapps/user_authn/views/tests/test_login.py +++ b/openedx/core/djangoapps/user_authn/views/tests/test_login.py @@ -555,76 +555,123 @@ class LoginTest(SiteMixin, CacheIsolationTestCase): 'whitelisted': False, 'allowed_domain': 'edx.org', 'user_domain': 'edx.org', - 'success': True + 'success': True, + 'is_third_party_authenticated': False }, { 'switch_enabled': False, 'whitelisted': True, 'allowed_domain': 'edx.org', 'user_domain': 'edx.org', - 'success': True + 'success': True, + 'is_third_party_authenticated': False }, { 'switch_enabled': True, 'whitelisted': False, 'allowed_domain': 'edx.org', 'user_domain': 'edx.org', - 'success': False + 'success': False, + 'is_third_party_authenticated': False }, { 'switch_enabled': True, 'whitelisted': False, 'allowed_domain': 'fake.org', 'user_domain': 'edx.org', - 'success': True + 'success': True, + 'is_third_party_authenticated': False }, { 'switch_enabled': True, 'whitelisted': True, 'allowed_domain': 'edx.org', 'user_domain': 'edx.org', - 'success': True + 'success': True, + 'is_third_party_authenticated': False }, { 'switch_enabled': True, 'whitelisted': False, 'allowed_domain': 'batman.gotham', 'user_domain': 'batman.gotham', - 'success': False + 'success': False, + 'is_third_party_authenticated': False + }, + { + 'switch_enabled': True, + 'whitelisted': True, + 'allowed_domain': 'edx.org', + 'user_domain': 'edx.org', + 'success': True, + 'is_third_party_authenticated': True + }, + { + 'switch_enabled': False, + 'whitelisted': False, + 'allowed_domain': 'edx.org', + 'user_domain': 'fake.org', + 'success': True, + 'is_third_party_authenticated': True }, ) @ddt.unpack - def test_login_for_user_auth_flow(self, switch_enabled, whitelisted, allowed_domain, user_domain, success): + def test_login_for_user_auth_flow( + self, + switch_enabled, + whitelisted, + allowed_domain, + user_domain, + success, + is_third_party_authenticated + ): """ Verify that `login._check_user_auth_flow` works as expected. """ + provider = 'Google' username = 'batman' user_email = '{username}@{domain}'.format(username=username, domain=user_domain) user = self._create_user(username, user_email) - - provider = 'Google' - site = self.set_up_site(allowed_domain, { + default_site_configuration_values = { 'SITE_NAME': allowed_domain, 'THIRD_PARTY_AUTH_ONLY_DOMAIN': allowed_domain, - 'THIRD_PARTY_AUTH_ONLY_PROVIDER': provider - }) - - if whitelisted: - AllowedAuthUser.objects.create(site=site, email=user.email) - else: - AllowedAuthUser.objects.filter(site=site, email=user.email).delete() + 'THIRD_PARTY_AUTH_ONLY_PROVIDER': provider, + } with ENABLE_LOGIN_USING_THIRDPARTY_AUTH_ONLY.override(switch_enabled): - value = None if success else u'As an {0} user, You must login with your {0} {1} account.'.format( - allowed_domain, - provider - ) - response, __ = self._login_response(user.email, self.password) - self._assert_response( - response, - success=success, - value=value, - ) + if not is_third_party_authenticated: + site = self.set_up_site(allowed_domain, default_site_configuration_values) + + if whitelisted: + AllowedAuthUser.objects.create(site=site, email=user.email) + else: + AllowedAuthUser.objects.filter(site=site, email=user.email).delete() + + value = None if success else u'As an {0} user, You must login with your {0} {1} account.'.format( + allowed_domain, + provider + ) + response, __ = self._login_response(user.email, self.password) + self._assert_response( + response, + success=success, + value=value, + ) + else: + default_site_configuration_values.update({'ENABLE_THIRD_PARTY_AUTH': True}) + self.set_up_site(allowed_domain, default_site_configuration_values) + with patch('openedx.core.djangoapps.user_authn.views.login.pipeline'): + with patch( + 'openedx.core.djangoapps.user_authn.views.login._check_user_auth_flow' + ) as mock_check_user_auth_flow: + # user is already authenticated by third_party_auth then + # we should by-pass _check_user_auth_flow function + response, __ = self._login_response(user.email, self.password) + self._assert_response( + response, + success=success + ) + self.assertFalse(mock_check_user_auth_flow.called) @ddt.ddt