def base_extauth_bypass_sending_activation_email(self, bypass_activation_email): """ Tests user creation without sending activation email when doing external auth """ request = self.request_factory.post(self.url, self.params) request.site = self.site # now indicate we are doing ext_auth by setting 'ExternalAuthMap' in the session. request.session = import_module( settings.SESSION_ENGINE).SessionStore() # empty session extauth = ExternalAuthMap( external_id='*****@*****.**', external_email='*****@*****.**', internal_password=self.params['password'], external_domain='shib:https://idp.stanford.edu/') request.session['ExternalAuthMap'] = extauth request.user = AnonymousUser() with mock.patch('edxmako.request_context.get_current_request', return_value=request): with mock.patch('django.core.mail.send_mail') as mock_send_mail: create_account(request) # check that send_mail is called if bypass_activation_email: self.assertFalse(mock_send_mail.called) else: self.assertTrue(mock_send_mail.called)
def test_new_account_registration_fails_if_email_exists(self): request, strategy = self.get_request_and_strategy( auth_entry=pipeline.AUTH_ENTRY_REGISTER, redirect_uri='social:complete') backend = strategy.request.backend backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy)) # pylint: disable=protected-access self.assert_redirect_to_register_looks_correct(actions.do_complete(backend, social_views._do_login, request=request)) with self._patch_edxmako_current_request(request): self.assert_register_response_in_pipeline_looks_correct( register_user(strategy.request), pipeline.get(request)['kwargs'], ['name', 'username', 'email'] ) with self._patch_edxmako_current_request(strategy.request): strategy.request.POST = self.get_registration_post_vars() # Create twice: once successfully, and once causing a collision. create_account(strategy.request) self.assert_json_failure_response_is_username_collision(create_account(strategy.request))
def test_ext_auth_password_length_too_short(self): """ Tests that even if password policy is enforced, ext_auth registrations aren't subject to it """ self.url_params['password'] = u'aaa' # shouldn't pass validation request = self.request_factory.post(self.url, self.url_params) request.site = SiteFactory.create() # now indicate we are doing ext_auth by setting 'ExternalAuthMap' in the session. request.session = import_module(settings.SESSION_ENGINE).SessionStore() # empty session extauth = ExternalAuthMap(external_id='*****@*****.**', external_email='*****@*****.**', internal_password=self.url_params['password'], external_domain='shib:https://idp.stanford.edu/') request.session['ExternalAuthMap'] = extauth request.user = AnonymousUser() with patch('edxmako.request_context.get_current_request', return_value=request): response = create_account(request) self.assertEqual(response.status_code, 200) obj = json.loads(response.content) self.assertTrue(obj['success'])
def test_ext_auth_password_length_too_short(self): """ Tests that even if password policy is enforced, ext_auth registrations aren't subject to it """ self.url_params['password'] = '******' # shouldn't pass validation request = self.request_factory.post(self.url, self.url_params) request.site = SiteFactory.create() # now indicate we are doing ext_auth by setting 'ExternalAuthMap' in the session. request.session = import_module(settings.SESSION_ENGINE).SessionStore() # empty session extauth = ExternalAuthMap(external_id='*****@*****.**', external_email='*****@*****.**', internal_password=self.url_params['password'], external_domain='shib:https://idp.stanford.edu/') request.session['ExternalAuthMap'] = extauth request.user = AnonymousUser() with patch('edxmako.request_context.get_current_request', return_value=request): response = create_account(request) self.assertEqual(response.status_code, 200) obj = json.loads(response.content) self.assertTrue(obj['success'])
def test_full_pipeline_succeeds_registering_new_account(self): # First, create, the request and strategy that store pipeline state. # Mock out wire traffic. request, strategy = self.get_request_and_strategy( auth_entry=pipeline.AUTH_ENTRY_REGISTER, redirect_uri='social:complete') strategy.request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy)) # Begin! Grab the registration page and check the login control on it. self.assert_register_response_before_pipeline_looks_correct(self.client.get('/register')) # 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>. # pylint: disable=protected-access self.assert_redirect_to_register_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 registration form. with self._patch_edxmako_current_request(request): self.assert_register_response_in_pipeline_looks_correct( register_user(strategy.request), pipeline.get(request)['kwargs'], ['name', 'username', 'email'] ) # Next, we invoke the view that handles the POST. Not all providers # supply email. Manually add it as the user would have to; this # also serves as a test of overriding provider values. Always provide a # password for us to check that we override it properly. overridden_password = strategy.request.POST.get('password') email = '*****@*****.**' if not strategy.request.POST.get('email'): strategy.request.POST = self.get_registration_post_vars({'email': email}) # The user must not exist yet... with self.assertRaises(auth_models.User.DoesNotExist): self.get_user_by_email(strategy, email) # ...but when we invoke create_account the existing edX view will make # it, but not social auths. The pipeline creates those later. with self._patch_edxmako_current_request(strategy.request): self.assert_json_success_response_looks_correct(create_account(strategy.request)) # We've overridden the user's password, so authenticate() with the old # value won't work: created_user = self.get_user_by_email(strategy, email) self.assert_password_overridden_by_pipeline(overridden_password, created_user.username) # At this point the user object exists, but there is no associated # social auth. self.assert_social_auth_does_not_exist_for_user(created_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) self.assert_redirect_to_dashboard_looks_correct( actions.do_complete(strategy.request.backend, social_views._do_login, user=created_user, request=request)) # Now the user has been redirected to the dashboard. Their third party account should now be linked. self.assert_social_auth_exists_for_user(created_user, strategy) self.assert_account_settings_context_looks_correct(account_settings_context(request), linked=True)
def test_full_pipeline_succeeds_registering_new_account(self): # First, create, the request and strategy that store pipeline state. # Mock out wire traffic. request, strategy = self.get_request_and_strategy( auth_entry=pipeline.AUTH_ENTRY_REGISTER, redirect_uri='social:complete') strategy.request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy)) # Begin! Grab the registration page and check the login control on it. self.assert_register_response_before_pipeline_looks_correct(self.client.get('/register')) # 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>. # pylint: disable=protected-access self.assert_redirect_to_register_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 registration form. with self._patch_edxmako_current_request(request): self.assert_register_response_in_pipeline_looks_correct( register_user(strategy.request), pipeline.get(request)['kwargs'], ['name', 'username', 'email'] ) # Next, we invoke the view that handles the POST. Not all providers # supply email. Manually add it as the user would have to; this # also serves as a test of overriding provider values. Always provide a # password for us to check that we override it properly. overridden_password = strategy.request.POST.get('password') email = '*****@*****.**' if not strategy.request.POST.get('email'): strategy.request.POST = self.get_registration_post_vars({'email': email}) # The user must not exist yet... with self.assertRaises(auth_models.User.DoesNotExist): self.get_user_by_email(strategy, email) # ...but when we invoke create_account the existing edX view will make # it, but not social auths. The pipeline creates those later. with self._patch_edxmako_current_request(strategy.request): self.assert_json_success_response_looks_correct(create_account(strategy.request)) # We've overridden the user's password, so authenticate() with the old # value won't work: created_user = self.get_user_by_email(strategy, email) self.assert_password_overridden_by_pipeline(overridden_password, created_user.username) # At this point the user object exists, but there is no associated # social auth. self.assert_social_auth_does_not_exist_for_user(created_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) self.assert_redirect_after_pipeline_completes( actions.do_complete(strategy.request.backend, social_views._do_login, user=created_user, request=request)) # Now the user has been redirected to the dashboard. Their third party account should now be linked. self.assert_social_auth_exists_for_user(created_user, strategy) self.assert_account_settings_context_looks_correct(account_settings_context(request), linked=True)
def _signup(request, eamap, retfun=None): """ Present form to complete for signup via external authentication. Even though the user has external credentials, he/she still needs to create an account on the edX system, and fill in the user registration form. eamap is an ExternalAuthMap object, specifying the external user for which to complete the signup. retfun is a function to execute for the return value, if immediate signup is used. That allows @ssl_login_shortcut() to work. """ from openedx.core.djangoapps.user_authn.views.deprecated import create_account, register_user # save this for use by create_account request.session['ExternalAuthMap'] = eamap if settings.FEATURES.get('AUTH_USE_CERTIFICATES_IMMEDIATE_SIGNUP', ''): # do signin immediately, by calling create_account, instead of asking # student to fill in form. MIT students already have information filed. username = eamap.external_email.split('@', 1)[0] username = username.replace('.', '_') post_vars = dict(username=username, honor_code=u'true', terms_of_service=u'true') log.info(u'doing immediate signup for %s, params=%s', username, post_vars) create_account(request, post_vars) # should check return content for successful completion before if retfun is not None: return retfun() else: return redirect('/') # default conjoin name, no spaces, flattened to ascii b/c django can't handle unicode usernames, sadly # but this only affects username, not fullname username = re.sub(r'\s', '', _flatten_to_ascii(eamap.external_name), flags=re.UNICODE) context = { 'has_extauth_info': True, 'show_signup_immediately': True, 'extauth_domain': eamap.external_domain, 'extauth_id': eamap.external_id, 'extauth_email': eamap.external_email, 'extauth_username': username, 'extauth_name': eamap.external_name, 'ask_for_tos': True, } # Some openEdX instances can't have terms of service for shib users, like # according to Stanford's Office of General Counsel uses_shibboleth = ( settings.FEATURES.get('AUTH_USE_SHIB') and eamap.external_domain.startswith(SHIBBOLETH_DOMAIN_PREFIX)) if uses_shibboleth and settings.FEATURES.get('SHIB_DISABLE_TOS'): context['ask_for_tos'] = False # detect if full name is blank and ask for it from user context['ask_for_fullname'] = eamap.external_name.strip() == '' # validate provided mail and if it's not valid ask the user try: validate_email(eamap.external_email) context['ask_for_email'] = False except ValidationError: context['ask_for_email'] = True log.info(u'EXTAUTH: Doing signup for %s', eamap.external_id) return register_user(request, extra_context=context)
def _signup(request, eamap, retfun=None): """ Present form to complete for signup via external authentication. Even though the user has external credentials, he/she still needs to create an account on the edX system, and fill in the user registration form. eamap is an ExternalAuthMap object, specifying the external user for which to complete the signup. retfun is a function to execute for the return value, if immediate signup is used. That allows @ssl_login_shortcut() to work. """ from openedx.core.djangoapps.user_authn.views.deprecated import create_account, register_user # save this for use by create_account request.session['ExternalAuthMap'] = eamap if settings.FEATURES.get('AUTH_USE_CERTIFICATES_IMMEDIATE_SIGNUP', ''): # do signin immediately, by calling create_account, instead of asking # student to fill in form. MIT students already have information filed. username = eamap.external_email.split('@', 1)[0] username = username.replace('.', '_') post_vars = dict(username=username, honor_code=u'true', terms_of_service=u'true') log.info(u'doing immediate signup for %s, params=%s', username, post_vars) create_account(request, post_vars) # should check return content for successful completion before if retfun is not None: return retfun() else: return redirect('/') # default conjoin name, no spaces, flattened to ascii b/c django can't handle unicode usernames, sadly # but this only affects username, not fullname username = re.sub(r'\s', '', _flatten_to_ascii(eamap.external_name), flags=re.UNICODE) context = { 'has_extauth_info': True, 'show_signup_immediately': True, 'extauth_domain': eamap.external_domain, 'extauth_id': eamap.external_id, 'extauth_email': eamap.external_email, 'extauth_username': username, 'extauth_name': eamap.external_name, 'ask_for_tos': True, } # Some openEdX instances can't have terms of service for shib users, like # according to Stanford's Office of General Counsel uses_shibboleth = (settings.FEATURES.get('AUTH_USE_SHIB') and eamap.external_domain.startswith(SHIBBOLETH_DOMAIN_PREFIX)) if uses_shibboleth and settings.FEATURES.get('SHIB_DISABLE_TOS'): context['ask_for_tos'] = False # detect if full name is blank and ask for it from user context['ask_for_fullname'] = eamap.external_name.strip() == '' # validate provided mail and if it's not valid ask the user try: validate_email(eamap.external_email) context['ask_for_email'] = False except ValidationError: context['ask_for_email'] = True log.info(u'EXTAUTH: Doing signup for %s', eamap.external_id) return register_user(request, extra_context=context)