def test_already_associated_exception_populates_dashboard_with_error(self): # Instrument the pipeline with an exception. We test that the # exception is raised correctly separately, so it's ok that we're # raising it artificially here. This makes the linked=True artificial # in the final assert because in practice the account would be # unlinked, but getting that behavior is cumbersome here and already # covered in other tests. Using linked=True does, however, let us test # that the duplicate error has no effect on the state of the controls. request, strategy = self.get_request_and_strategy( auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete') strategy.request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy)) user = self.create_user_models_for_existing_account( strategy, '*****@*****.**', 'password', self.get_username()) self.assert_social_auth_exists_for_user(user, strategy) self.client.get('/login') self.client.get(pipeline.get_login_url(self.provider.provider_id, pipeline.AUTH_ENTRY_LOGIN)) actions.do_complete(request.backend, social_views._do_login, # pylint: disable=protected-access request=request) with self._patch_edxmako_current_request(strategy.request): signin_user(strategy.request) login_user(strategy.request) actions.do_complete(request.backend, social_views._do_login, # pylint: disable=protected-access user=user, request=request) # Monkey-patch storage for messaging; pylint: disable=protected-access request._messages = fallback.FallbackStorage(request) middleware.ExceptionMiddleware().process_exception( request, exceptions.AuthAlreadyAssociated(self.provider.backend_name, 'account is already in use.')) self.assert_account_settings_context_looks_correct( account_settings_context(request), duplicate=True, linked=True)
def test_full_pipeline_succeeds_for_linking_account(self): # First, create, the request and strategy that store pipeline state, # configure the backend, and mock out wire traffic. request, strategy = self.get_request_and_strategy( auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete') request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy)) pipeline.analytics.track = mock.MagicMock() request.user = self.create_user_models_for_existing_account( strategy, '*****@*****.**', 'password', self.get_username(), skip_social_auth=True) # Instrument the pipeline to get to the dashboard with the full # expected state. self.client.get( pipeline.get_login_url(self.provider.provider_id, pipeline.AUTH_ENTRY_LOGIN)) actions.do_complete(request.backend, social_views._do_login, # pylint: disable=protected-access request=request) signin_user(strategy.request) login_user(strategy.request) actions.do_complete(request.backend, social_views._do_login, # pylint: disable=protected-access request=request) # First we expect that we're in the unlinked state, and that there # really is no association in the backend. self.assert_account_settings_context_looks_correct(account_settings_context(request), linked=False) self.assert_social_auth_does_not_exist_for_user(request.user, strategy) # We should be redirected back to the complete page, setting # the "logged in" cookie for the marketing site. self.assert_logged_in_cookie_redirect(actions.do_complete( request.backend, social_views._do_login, request.user, None, # pylint: disable=protected-access redirect_field_name=auth.REDIRECT_FIELD_NAME, request=request )) # Set the cookie and try again self.set_logged_in_cookies(request) # Fire off the auth pipeline to link. self.assert_redirect_to_dashboard_looks_correct( actions.do_complete( request.backend, social_views._do_login, # pylint: disable=protected-access request.user, None, redirect_field_name=auth.REDIRECT_FIELD_NAME, request=request ) ) # Now we expect to be in the linked state, with a backend entry. self.assert_social_auth_exists_for_user(request.user, strategy) self.assert_account_settings_context_looks_correct(account_settings_context(request), linked=True)
def test_full_pipeline_succeeds_for_signing_in_to_existing_active_account(self, _mock_segment_track): # First, create, the request and strategy that store pipeline state, # configure the backend, and mock out wire traffic. request, strategy = self.get_request_and_strategy( auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete') strategy.request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy)) user = self.create_user_models_for_existing_account( strategy, '*****@*****.**', 'password', self.get_username()) self.assert_social_auth_exists_for_user(user, strategy) self.assertTrue(user.is_active) # Begin! Ensure that the login form contains expected controls before # the user starts the pipeline. self.assert_login_response_before_pipeline_looks_correct(self.client.get('/login')) # The pipeline starts by a user GETting /auth/login/<provider>. # Synthesize that request and check that it redirects to the correct # provider page. self.assert_redirect_to_provider_looks_correct(self.client.get( pipeline.get_login_url(self.provider.provider_id, pipeline.AUTH_ENTRY_LOGIN))) # Next, the provider makes a request against /auth/complete/<provider> # to resume the pipeline. # pylint: disable=protected-access self.assert_redirect_to_login_looks_correct(actions.do_complete(request.backend, social_views._do_login, request=request)) # At this point we know the pipeline has resumed correctly. Next we # fire off the view that displays the login form and posts it via JS. with self._patch_edxmako_current_request(strategy.request): self.assert_login_response_in_pipeline_looks_correct(login_user(strategy.request)) # Next, we invoke the view that handles the POST, and expect it # redirects to /auth/complete. In the browser ajax handlers will # redirect the user to the dashboard; we invoke it manually here. self.assert_json_success_response_looks_correct(login_user(strategy.request), verify_redirect_url=True) # We should be redirected back to the complete page, setting # the "logged in" cookie for the marketing site. self.assert_logged_in_cookie_redirect(actions.do_complete( request.backend, social_views._do_login, request.user, None, # pylint: disable=protected-access redirect_field_name=auth.REDIRECT_FIELD_NAME, request=request )) # Set the cookie and try again self.set_logged_in_cookies(request) self.assert_redirect_after_pipeline_completes( actions.do_complete(request.backend, social_views._do_login, user=user, request=request)) self.assert_account_settings_context_looks_correct(account_settings_context(request))
def test_signin_fails_if_no_account_associated(self): _, strategy = self.get_request_and_strategy( auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete') strategy.request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy)) self.create_user_models_for_existing_account( strategy, '*****@*****.**', 'password', self.get_username(), skip_social_auth=True) self.assert_json_failure_response_is_missing_social_auth(login_user(strategy.request))
def test_full_pipeline_succeeds_for_unlinking_account(self): # First, create, the request and strategy that store pipeline state, # configure the backend, and mock out wire traffic. request, strategy = self.get_request_and_strategy( auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete') request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy)) user = self.create_user_models_for_existing_account( strategy, '*****@*****.**', 'password', self.get_username()) self.assert_social_auth_exists_for_user(user, strategy) # We're already logged in, so simulate that the cookie is set correctly self.set_logged_in_cookies(request) # Instrument the pipeline to get to the dashboard with the full # expected state. self.client.get( pipeline.get_login_url(self.provider.provider_id, pipeline.AUTH_ENTRY_LOGIN)) actions.do_complete(request.backend, social_views._do_login, # pylint: disable=protected-access request=request) with self._patch_edxmako_current_request(strategy.request): signin_user(strategy.request) login_user(strategy.request) actions.do_complete(request.backend, social_views._do_login, user=user, # pylint: disable=protected-access request=request) # First we expect that we're in the linked state, with a backend entry. self.assert_account_settings_context_looks_correct(account_settings_context(request), linked=True) self.assert_social_auth_exists_for_user(request.user, strategy) # Fire off the disconnect pipeline to unlink. self.assert_redirect_to_dashboard_looks_correct( actions.do_disconnect( request.backend, request.user, None, redirect_field_name=auth.REDIRECT_FIELD_NAME ) ) # Now we expect to be in the unlinked state, with no backend entry. self.assert_account_settings_context_looks_correct(account_settings_context(request), linked=False) self.assert_social_auth_does_not_exist_for_user(user, strategy)
def test_full_pipeline_succeeds_for_unlinking_account(self): # First, create, the request and strategy that store pipeline state, # configure the backend, and mock out wire traffic. request, strategy = self.get_request_and_strategy( auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete') request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy)) user = self.create_user_models_for_existing_account( strategy, '*****@*****.**', 'password', self.get_username()) self.assert_social_auth_exists_for_user(user, strategy) # We're already logged in, so simulate that the cookie is set correctly self.set_logged_in_cookies(request) # Instrument the pipeline to get to the dashboard with the full # expected state. self.client.get( pipeline.get_login_url(self.provider.provider_id, pipeline.AUTH_ENTRY_LOGIN)) actions.do_complete(request.backend, social_views._do_login, # pylint: disable=protected-access request=request) with self._patch_edxmako_current_request(strategy.request): signin_user(strategy.request) login_user(strategy.request) actions.do_complete(request.backend, social_views._do_login, user=user, # pylint: disable=protected-access request=request) # First we expect that we're in the linked state, with a backend entry. self.assert_account_settings_context_looks_correct(account_settings_context(request), linked=True) self.assert_social_auth_exists_for_user(request.user, strategy) # Fire off the disconnect pipeline to unlink. self.assert_redirect_after_pipeline_completes( actions.do_disconnect( request.backend, request.user, None, redirect_field_name=auth.REDIRECT_FIELD_NAME ) ) # Now we expect to be in the unlinked state, with no backend entry. self.assert_account_settings_context_looks_correct(account_settings_context(request), linked=False) self.assert_social_auth_does_not_exist_for_user(user, strategy)
def test_signin_fails_if_account_not_active(self): _, strategy = self.get_request_and_strategy( auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete') strategy.request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy)) user = self.create_user_models_for_existing_account(strategy, '*****@*****.**', 'password', self.get_username()) user.is_active = False user.save() with self._patch_edxmako_current_request(strategy.request): self.assert_json_failure_response_is_inactive_account(login_user(strategy.request))
def test_full_pipeline_succeeds_for_signing_in_to_existing_active_account(self): # First, create, the request and strategy that store pipeline state, # configure the backend, and mock out wire traffic. request, strategy = self.get_request_and_strategy( auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete') strategy.request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy)) pipeline.analytics.track = mock.MagicMock() user = self.create_user_models_for_existing_account( strategy, '*****@*****.**', 'password', self.get_username()) self.assert_social_auth_exists_for_user(user, strategy) self.assertTrue(user.is_active) # Begin! Ensure that the login form contains expected controls before # the user starts the pipeline. self.assert_login_response_before_pipeline_looks_correct(self.client.get('/login')) # The pipeline starts by a user GETting /auth/login/<provider>. # Synthesize that request and check that it redirects to the correct # provider page. self.assert_redirect_to_provider_looks_correct(self.client.get( pipeline.get_login_url(self.provider.provider_id, pipeline.AUTH_ENTRY_LOGIN))) # Next, the provider makes a request against /auth/complete/<provider> # to resume the pipeline. # pylint: disable=protected-access self.assert_redirect_to_login_looks_correct(actions.do_complete(request.backend, social_views._do_login, request=request)) # At this point we know the pipeline has resumed correctly. Next we # fire off the view that displays the login form and posts it via JS. with self._patch_edxmako_current_request(strategy.request): self.assert_login_response_in_pipeline_looks_correct(signin_user(strategy.request)) # Next, we invoke the view that handles the POST, and expect it # redirects to /auth/complete. In the browser ajax handlers will # redirect the user to the dashboard; we invoke it manually here. self.assert_json_success_response_looks_correct(login_user(strategy.request)) # We should be redirected back to the complete page, setting # the "logged in" cookie for the marketing site. self.assert_logged_in_cookie_redirect(actions.do_complete( request.backend, social_views._do_login, request.user, None, # pylint: disable=protected-access redirect_field_name=auth.REDIRECT_FIELD_NAME, request=request )) # Set the cookie and try again self.set_logged_in_cookies(request) self.assert_redirect_to_dashboard_looks_correct( actions.do_complete(request.backend, social_views._do_login, user=user, request=request)) self.assert_account_settings_context_looks_correct(account_settings_context(request))
def assert_first_party_auth_trumps_third_party_auth(self, email=None, password=None, success=None): """Asserts first party auth was used in place of third party auth. Args: email: string. The user's email. If not None, will be set on POST. password: string. The user's password. If not None, will be set on POST. success: None or bool. Whether we expect auth to be successful. Set to None to indicate we expect the request to be invalid (meaning one of username or password will be missing). """ _, strategy = self.get_request_and_strategy( auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete') strategy.request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy)) self.create_user_models_for_existing_account( strategy, email, password, self.get_username(), skip_social_auth=True) strategy.request.POST = dict(strategy.request.POST) if email: strategy.request.POST['email'] = email if password: strategy.request.POST['password'] = '******' + password if success is False else password self.assert_pipeline_running(strategy.request) payload = json.loads(login_user(strategy.request).content) if success is None: # Request malformed -- just one of email/password given. self.assertFalse(payload.get('success')) self.assertIn('There was an error receiving your login information', payload.get('value')) elif success: # Request well-formed and credentials good. self.assertTrue(payload.get('success')) else: # Request well-formed but credentials bad. self.assertFalse(payload.get('success')) self.assertIn('incorrect', payload.get('value'))
def test_full_pipeline_succeeds_for_unlinking_testshib_account( self, mock_get_enterprise_customer_for_learner_settings_view, mock_get_enterprise_customer_for_learner, mock_enterprise_customer_for_request, ): # First, create, the request and strategy that store pipeline state, # configure the backend, and mock out wire traffic. self.provider = self._configure_testshib_provider() request, strategy = self.get_request_and_strategy( auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete') request.backend.auth_complete = MagicMock( return_value=self.fake_auth_complete(strategy)) user = self.create_user_models_for_existing_account( strategy, '*****@*****.**', 'password', self.get_username()) self.assert_social_auth_exists_for_user(user, strategy) request.user = user # We're already logged in, so simulate that the cookie is set correctly self.set_logged_in_cookies(request) # linking a learner with enterprise customer. enterprise_customer = EnterpriseCustomerFactory() assert EnterpriseCustomerUser.objects.count( ) == 0, "Precondition check: no link records should exist" EnterpriseCustomerUser.objects.link_user(enterprise_customer, user.email) self.assertTrue( EnterpriseCustomerUser.objects.filter( enterprise_customer=enterprise_customer, user_id=user.id).count() == 1) EnterpriseCustomerIdentityProvider.objects.get_or_create( enterprise_customer=enterprise_customer, provider_id=self.provider.provider_id) enterprise_customer_data = { 'uuid': enterprise_customer.uuid, 'name': enterprise_customer.name, 'identity_provider': 'saml-default', } mock_enterprise_customer_for_request.return_value = enterprise_customer_data mock_get_enterprise_customer_for_learner.return_value = enterprise_customer_data mock_get_enterprise_customer_for_learner_settings_view.return_value = enterprise_customer_data # Instrument the pipeline to get to the dashboard with the full expected state. self.client.get( pipeline.get_login_url(self.provider.provider_id, pipeline.AUTH_ENTRY_LOGIN)) actions.do_complete( request.backend, social_views._do_login, # pylint: disable=protected-access request=request) with self._patch_edxmako_current_request(strategy.request): login_user(strategy.request) actions.do_complete( request.backend, social_views._do_login, user=user, # pylint: disable=protected-access request=request) # First we expect that we're in the linked state, with a backend entry. self.assert_account_settings_context_looks_correct( account_settings_context(request), linked=True) self.assert_social_auth_exists_for_user(request.user, strategy) FEATURES_WITH_ENTERPRISE_ENABLED = settings.FEATURES.copy() FEATURES_WITH_ENTERPRISE_ENABLED[ 'ENABLE_ENTERPRISE_INTEGRATION'] = True with patch.dict("django.conf.settings.FEATURES", FEATURES_WITH_ENTERPRISE_ENABLED): # Fire off the disconnect pipeline without the user information. actions.do_disconnect(request.backend, None, None, redirect_field_name=auth.REDIRECT_FIELD_NAME, request=request) self.assertNotEqual( EnterpriseCustomerUser.objects.filter( enterprise_customer=enterprise_customer, user_id=user.id).count(), 0) # Fire off the disconnect pipeline to unlink. self.assert_redirect_after_pipeline_completes( actions.do_disconnect( request.backend, user, None, redirect_field_name=auth.REDIRECT_FIELD_NAME, request=request)) # Now we expect to be in the unlinked state, with no backend entry. self.assert_account_settings_context_looks_correct( account_settings_context(request), linked=False) self.assert_social_auth_does_not_exist_for_user(user, strategy) self.assertEqual( EnterpriseCustomerUser.objects.filter( enterprise_customer=enterprise_customer, user_id=user.id).count(), 0)
def test_full_pipeline_succeeds_for_unlinking_testshib_account(self): # First, create, the request and strategy that store pipeline state, # configure the backend, and mock out wire traffic. self.provider = self._configure_testshib_provider() request, strategy = self.get_request_and_strategy( auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete') request.backend.auth_complete = MagicMock(return_value=self.fake_auth_complete(strategy)) user = self.create_user_models_for_existing_account( strategy, '*****@*****.**', 'password', self.get_username()) self.assert_social_auth_exists_for_user(user, strategy) request.user = user # We're already logged in, so simulate that the cookie is set correctly self.set_logged_in_cookies(request) # linking a learner with enterprise customer. enterprise_customer = EnterpriseCustomerFactory() assert EnterpriseCustomerUser.objects.count() == 0, "Precondition check: no link records should exist" EnterpriseCustomerUser.objects.link_user(enterprise_customer, user.email) self.assertTrue( EnterpriseCustomerUser.objects.filter(enterprise_customer=enterprise_customer, user_id=user.id).count() == 1 ) EnterpriseCustomerIdentityProvider.objects.get_or_create(enterprise_customer=enterprise_customer, provider_id=self.provider.provider_id) # Instrument the pipeline to get to the dashboard with the full expected state. self.client.get( pipeline.get_login_url(self.provider.provider_id, pipeline.AUTH_ENTRY_LOGIN)) actions.do_complete(request.backend, social_views._do_login, # pylint: disable=protected-access request=request) with self._patch_edxmako_current_request(strategy.request): signin_user(strategy.request) login_user(strategy.request) actions.do_complete(request.backend, social_views._do_login, user=user, # pylint: disable=protected-access request=request) # First we expect that we're in the linked state, with a backend entry. self.assert_account_settings_context_looks_correct(account_settings_context(request), linked=True) self.assert_social_auth_exists_for_user(request.user, strategy) # Fire off the disconnect pipeline without the user information. actions.do_disconnect( request.backend, None, None, redirect_field_name=auth.REDIRECT_FIELD_NAME, request=request ) self.assertFalse( EnterpriseCustomerUser.objects.filter(enterprise_customer=enterprise_customer, user_id=user.id).count() == 0 ) # Fire off the disconnect pipeline to unlink. self.assert_redirect_to_dashboard_looks_correct( actions.do_disconnect( request.backend, user, None, redirect_field_name=auth.REDIRECT_FIELD_NAME, request=request ) ) # Now we expect to be in the unlinked state, with no backend entry. self.assert_account_settings_context_looks_correct(account_settings_context(request), linked=False) self.assert_social_auth_does_not_exist_for_user(user, strategy) self.assertTrue( EnterpriseCustomerUser.objects.filter(enterprise_customer=enterprise_customer, user_id=user.id).count() == 0 )