def test_unsafe_next(self): """ Test unsafe next parameter """ unsafe_url = "https://www.amazon.com" with LogCapture(LOGGER_NAME, level=logging.ERROR) as logger: req = self.request.get(reverse("login") + "?next={url}".format(url=unsafe_url)) get_next_url_for_login_page(req) logger.check( (LOGGER_NAME, "ERROR", u"Unsafe redirect parameter detected: u'{url}'".format(url=unsafe_url)) )
def test_unsafe_next(self, unsafe_url, http_accept, user_agent, expected_log): """ Test unsafe next parameter """ with LogCapture(LOGGER_NAME, level=logging.WARNING) as logger: req = self.request.get(reverse("login") + "?next={url}".format(url=unsafe_url)) req.META["HTTP_ACCEPT"] = http_accept # pylint: disable=no-member req.META["HTTP_USER_AGENT"] = user_agent # pylint: disable=no-member get_next_url_for_login_page(req) logger.check( (LOGGER_NAME, "WARNING", expected_log) )
def test_next_failures(self, log_level, log_name, unsafe_url, http_accept, user_agent, expected_log): """ Test unsafe next parameter """ with LogCapture(LOGGER_NAME, level=log_level) as logger: req = self.request.get(settings.LOGIN_URL + "?next={url}".format(url=unsafe_url)) req.META["HTTP_ACCEPT"] = http_accept req.META["HTTP_USER_AGENT"] = user_agent get_next_url_for_login_page(req) logger.check((LOGGER_NAME, log_name, expected_log))
def test_next_failures(self, log_level, log_name, unsafe_url, http_accept, user_agent, expected_log): """ Test unsafe next parameter """ with LogCapture(LOGGER_NAME, level=log_level) as logger: req = self.request.get( reverse("login") + "?next={url}".format(url=unsafe_url)) req.META["HTTP_ACCEPT"] = http_accept # pylint: disable=no-member req.META["HTTP_USER_AGENT"] = user_agent # pylint: disable=no-member get_next_url_for_login_page(req) logger.check((LOGGER_NAME, log_name, expected_log))
def test_unsafe_next(self): """ Test unsafe next parameter """ unsafe_url = "https://www.amazon.com" with LogCapture(LOGGER_NAME, level=logging.ERROR) as logger: req = self.request.get( reverse("login") + "?next={url}".format(url=unsafe_url)) get_next_url_for_login_page(req) logger.check( (LOGGER_NAME, "ERROR", u"Unsafe redirect parameter detected: u'{url}'".format( url=unsafe_url)))
def test_unsafe_next(self, unsafe_url, http_accept): """ Test unsafe next parameter """ with LogCapture(LOGGER_NAME, level=logging.WARNING) as logger: req = self.request.get( reverse("login") + "?next={url}".format(url=unsafe_url)) req.META["HTTP_ACCEPT"] = http_accept # pylint: disable=no-member get_next_url_for_login_page(req) logger.check(( LOGGER_NAME, "WARNING", u"Unsafe redirect parameter detected after login page: u'{url}'" .format(url=unsafe_url)))
def test_safe_next(self): """ Test safe next parameter """ req = self.request.get( reverse("login") + "?next={url}".format(url="/dashboard")) req.META["HTTP_ACCEPT"] = "text/html" # pylint: disable=no-member next_page = get_next_url_for_login_page(req) self.assertEqual(next_page, u'/dashboard')
def get(self, request, *args, **kwargs): """ Return either a redirect to the login page of an identity provider that corresponds to the provider_slug keyword argument or a 404 if the provider_slug does not correspond to an identity provider. Args: request (HttpRequest) Keyword Args: provider_slug (str): a slug corresponding to a configured identity provider Returns: HttpResponse: 302 to a provider's login url if the provider_slug kwarg matches an identity provider HttpResponse: 404 if the provider_slug kwarg does not match an identity provider """ # this gets the url to redirect to after login/registration/third_party_auth # it also handles checking the safety of the redirect url (next query parameter) # it checks against settings.LOGIN_REDIRECT_WHITELIST, so be sure to add the url # to this setting next_destination_url = get_next_url_for_login_page(request) try: url = pipeline.get_login_url(kwargs['provider_slug'], pipeline.AUTH_ENTRY_LOGIN, next_destination_url) return redirect(url) except ValueError: return HttpResponseNotFound()
def signin_user(request): """Deprecated. To be replaced by :class:`student_account.views.login_and_registration_form`.""" external_auth_response = external_auth_login(request) if external_auth_response is not None: return external_auth_response # Determine the URL to redirect to following login: redirect_to = get_next_url_for_login_page(request) if request.user.is_authenticated: return redirect(redirect_to) third_party_auth_error = None for msg in messages.get_messages(request): if msg.extra_tags.split()[0] == "social-auth": # msg may or may not be translated. Try translating [again] in case we are able to: third_party_auth_error = _(text_type(msg)) # pylint: disable=translation-of-non-string break context = { 'login_redirect_url': redirect_to, # This gets added to the query string of the "Sign In" button in the header # Bool injected into JS to submit form if we're inside a running third- # party auth pipeline; distinct from the actual instance of the running # pipeline, if any. 'pipeline_running': 'true' if pipeline.running(request) else 'false', 'pipeline_url': auth_pipeline_urls(pipeline.AUTH_ENTRY_LOGIN, redirect_url=redirect_to), 'platform_name': configuration_helpers.get_value( 'platform_name', settings.PLATFORM_NAME ), 'third_party_auth_error': third_party_auth_error } return render_to_response('login.html', context)
def openid_login_complete(request, redirect_field_name=REDIRECT_FIELD_NAME, # pylint: disable=unused-argument render_failure=None): """Complete the openid login process""" render_failure = (render_failure or default_render_failure) openid_response = openid_views.parse_openid_response(request) if not openid_response: return render_failure(request, 'This is an OpenID relying party endpoint.') if openid_response.status == SUCCESS: external_id = openid_response.identity_url oid_backend = openid_auth.OpenIDBackend() details = oid_backend._extract_user_details(openid_response) # pylint: disable=protected-access log.debug('openid success, details=%s', details) url = getattr(settings, 'OPENID_SSO_SERVER_URL', None) external_domain = "{0}{1}".format(OPENID_DOMAIN_PREFIX, url) fullname = '%s %s' % (details.get('first_name', ''), details.get('last_name', '')) return _external_login_or_signup( request, external_id, external_domain, details, details.get('email', ''), fullname, retfun=functools.partial(redirect, get_next_url_for_login_page(request)), ) return render_failure(request, 'Openid failure')
def validate_login(): req = self.request.get( reverse("login") + "?next={url}".format(url=next_url)) req.META["HTTP_ACCEPT"] = "text/html" # pylint: disable=no-member self._add_session(req) next_page = get_next_url_for_login_page(req) self.assertEqual(next_page, expected_url)
def openid_login_complete(request, redirect_field_name=REDIRECT_FIELD_NAME, render_failure=None): """Complete the openid login process""" render_failure = (render_failure or default_render_failure) openid_response = openid_views.parse_openid_response(request) if not openid_response: return render_failure(request, 'This is an OpenID relying party endpoint.') if openid_response.status == SUCCESS: external_id = openid_response.identity_url oid_backend = openid_auth.OpenIDBackend() details = oid_backend._extract_user_details(openid_response) log.debug('openid success, details=%s', details) url = getattr(settings, 'OPENID_SSO_SERVER_URL', None) external_domain = "{0}{1}".format(OPENID_DOMAIN_PREFIX, url) fullname = '%s %s' % (details.get('first_name', ''), details.get('last_name', '')) return _external_login_or_signup( request, external_id, external_domain, details, details.get('email', ''), fullname, retfun=functools.partial(redirect, get_next_url_for_login_page(request)), ) return render_failure(request, 'Openid failure')
def test_safe_next(self, next_url, host): """ Test safe next parameter """ req = self.request.get(settings.LOGIN_URL + "?next={url}".format(url=next_url), HTTP_HOST=host) req.META["HTTP_ACCEPT"] = "text/html" # pylint: disable=no-member next_page = get_next_url_for_login_page(req) self.assertEqual(next_page, next_url)
def test_safe_next(self, next_url, http_accept, host): """ Test safe next parameter """ req = self.request.get(settings.LOGIN_URL + "?next={url}".format(url=next_url), HTTP_HOST=host) req.META["HTTP_ACCEPT"] = http_accept next_page = get_next_url_for_login_page(req) self.assertEqual(next_page, next_url)
def test_safe_next(self, url, host, expected_url): """ Test safe next parameter """ req = self.request.get(reverse("login") + "?next={url}".format(url=url), HTTP_HOST=host) req.META["HTTP_ACCEPT"] = "text/html" # pylint: disable=no-member next_page = get_next_url_for_login_page(req) self.assertEqual(next_page, expected_url)
def dispatch(self, request, *args, **kwargs): """ Override dispatch to perform permission checks. """ if not self.check_permission(): return redirect('/login?next=' + get_next_url_for_login_page(request)) return super(BaseViewMixin, self).dispatch(request, *args, **kwargs)
def register_user(request, extra_context=None): """ Deprecated. To be replaced by :class:`user_authn.views.login_form.login_and_registration_form`. """ # Determine the URL to redirect to following login: redirect_to = get_next_url_for_login_page(request) if request.user.is_authenticated: return redirect(redirect_to) external_auth_response = external_auth_register(request) if external_auth_response is not None: return external_auth_response context = { 'login_redirect_url': redirect_to, # This gets added to the query string of the "Sign In" button in the header 'email': '', 'name': '', 'running_pipeline': None, 'pipeline_urls': auth_pipeline_urls(pipeline.AUTH_ENTRY_REGISTER, redirect_url=redirect_to), 'platform_name': configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME), 'selected_provider': '', 'username': '', } if extra_context is not None: context.update(extra_context) if context.get("extauth_domain", '').startswith(settings.SHIBBOLETH_DOMAIN_PREFIX): return render_to_response('register-shib.html', context) # If third-party auth is enabled, prepopulate the form with data from the # selected provider. if third_party_auth.is_enabled() and pipeline.running(request): running_pipeline = pipeline.get(request) current_provider = provider.Registry.get_from_pipeline( running_pipeline) if current_provider is not None: overrides = current_provider.get_register_form_data( running_pipeline.get('kwargs')) overrides['running_pipeline'] = running_pipeline overrides['selected_provider'] = current_provider.name context.update(overrides) return render_to_response('register.html', context)
def shib_login(request): """ Uses Apache's REMOTE_USER environment variable as the external id. This in turn typically uses EduPersonPrincipalName http://www.incommonfederation.org/attributesummary.html#eduPersonPrincipal but the configuration is in the shibboleth software. """ shib_error_msg = _( dedent(""" Your university identity server did not return your ID information to us. Please try logging in again. (You may need to restart your browser.) """)) if not request.META.get('REMOTE_USER'): log.error(u"SHIB: no REMOTE_USER found in request.META") return default_render_failure(request, shib_error_msg) elif not request.META.get('Shib-Identity-Provider'): log.error(u"SHIB: no Shib-Identity-Provider in request.META") return default_render_failure(request, shib_error_msg) else: # If we get here, the user has authenticated properly shib = { attr: request.META.get(attr, '').decode('utf-8') for attr in [ 'REMOTE_USER', 'givenName', 'sn', 'mail', 'Shib-Identity-Provider', 'displayName' ] } # Clean up first name, last name, and email address # TODO: Make this less hardcoded re: format, but split will work # even if ";" is not present, since we are accessing 1st element shib['sn'] = shib['sn'].split(";")[0].strip().capitalize() shib['givenName'] = shib['givenName'].split( ";")[0].strip().capitalize() # TODO: should we be logging creds here, at info level? log.info(u"SHIB creds returned: %r", shib) fullname = shib['displayName'] if shib['displayName'] else u'%s %s' % ( shib['givenName'], shib['sn']) redirect_to = get_next_url_for_login_page(request) retfun = functools.partial(_safe_postlogin_redirect, redirect_to, request.get_host()) return _external_login_or_signup(request, external_id=shib['REMOTE_USER'], external_domain=SHIBBOLETH_DOMAIN_PREFIX + shib['Shib-Identity-Provider'], credentials=shib, email=shib['mail'], fullname=fullname, retfun=retfun)
def shib_login(request): """ Uses Apache's REMOTE_USER environment variable as the external id. This in turn typically uses EduPersonPrincipalName http://www.incommonfederation.org/attributesummary.html#eduPersonPrincipal but the configuration is in the shibboleth software. """ shib_error_msg = _( dedent( """ Your university identity server did not return your ID information to us. Please try logging in again. (You may need to restart your browser.) """ ) ) if not request.META.get("REMOTE_USER"): log.error(u"SHIB: no REMOTE_USER found in request.META") return default_render_failure(request, shib_error_msg) elif not request.META.get("Shib-Identity-Provider"): log.error(u"SHIB: no Shib-Identity-Provider in request.META") return default_render_failure(request, shib_error_msg) else: # If we get here, the user has authenticated properly shib = { attr: request.META.get(attr, "").decode("utf-8") for attr in ["REMOTE_USER", "givenName", "sn", "mail", "Shib-Identity-Provider", "displayName"] } # Clean up first name, last name, and email address # TODO: Make this less hardcoded re: format, but split will work # even if ";" is not present, since we are accessing 1st element shib["sn"] = shib["sn"].split(";")[0].strip().capitalize() shib["givenName"] = shib["givenName"].split(";")[0].strip().capitalize() # TODO: should we be logging creds here, at info level? log.info(u"SHIB creds returned: %r", shib) fullname = shib["displayName"] if shib["displayName"] else u"%s %s" % (shib["givenName"], shib["sn"]) redirect_to = get_next_url_for_login_page(request) retfun = functools.partial(_safe_postlogin_redirect, redirect_to, request.get_host()) return _external_login_or_signup( request, external_id=shib["REMOTE_USER"], external_domain=SHIBBOLETH_DOMAIN_PREFIX + shib["Shib-Identity-Provider"], credentials=shib, email=shib["mail"], fullname=fullname, retfun=retfun, )
def login_and_registration_form(request, initial_mode="login"): """Render the combined login/registration form, defaulting to login This relies on the JS to asynchronously load the actual form from the user_api. Keyword Args: initial_mode (string): Either "login" or "register". """ # Determine the URL to redirect to following login/registration/third_party_auth redirect_to = get_next_url_for_login_page(request) # If we're already logged in, redirect to the dashboard if request.user.is_authenticated(): return redirect(redirect_to) # Retrieve the form descriptions from the user API form_descriptions = _get_form_descriptions(request) # If this is a microsite, revert to the old login/registration pages. # We need to do this for now to support existing themes. if microsite.is_request_in_microsite(): if initial_mode == "login": return old_login_view(request) elif initial_mode == "register": return old_register_view(request) # Allow external auth to intercept and handle the request ext_auth_response = _external_auth_intercept(request, initial_mode) if ext_auth_response is not None: return ext_auth_response # Otherwise, render the combined login/registration page context = { 'login_redirect_url': redirect_to, # This gets added to the query string of the "Sign In" button in the header 'disable_courseware_js': True, 'initial_mode': initial_mode, 'third_party_auth': json.dumps(_third_party_auth_context(request, redirect_to)), 'platform_name': settings.PLATFORM_NAME, 'responsive': True, # Include form descriptions retrieved from the user API. # We could have the JS client make these requests directly, # but we include them in the initial page load to avoid # the additional round-trip to the server. 'login_form_desc': form_descriptions['login'], 'registration_form_desc': form_descriptions['registration'], 'password_reset_form_desc': form_descriptions['password_reset'], } return render_to_response('student_account/login_and_register.html', context)
def get_provider_login_url(request, provider_id, redirect_url=None): """ Return the given provider's login URL. This method is here to avoid the importing of pipeline and student app in enterprise. """ provider_login_url = third_party_auth.pipeline.get_login_url( provider_id, third_party_auth.pipeline.AUTH_ENTRY_LOGIN, redirect_url=redirect_url if redirect_url else get_next_url_for_login_page(request) ) return provider_login_url
def validate_login(): """ Assert that get_next_url_for_login_page returns as expected. """ if method == 'GET': req = self.request.get(settings.LOGIN_URL + "?next={url}".format(url=next_url)) elif method == 'POST': req = self.request.post(settings.LOGIN_URL, {'next': next_url}) req.META["HTTP_ACCEPT"] = "text/html" self._add_session(req) next_page = get_next_url_for_login_page(req) self.assertEqual(next_page, expected_url)
def test_custom_redirect_url(self, redirect, expected_url): """ Test custom redirect after login """ configuration_values = {"DEFAULT_REDIRECT_AFTER_LOGIN": redirect} req = self.request.get(settings.LOGIN_URL) req.META["HTTP_ACCEPT"] = "text/html" with with_site_configuration_context( configuration=configuration_values): next_page = get_next_url_for_login_page(req) self.assertEqual(next_page, expected_url)
def process_exception(self, request, exception): """Handles specific exception raised by Python Social Auth eg HTTPError.""" referer_url = request.META.get('HTTP_REFERER', '') if (referer_url and isinstance(exception, HTTPError) and exception.response.status_code == 502): referer_url = urlparse.urlparse(referer_url).path if referer_url == reverse('signin_user'): messages.error(request, _('Unable to connect with the external provider, please try again'), extra_tags='social-auth') redirect_url = get_next_url_for_login_page(request) return redirect('/login?next=' + redirect_url) return super(ExceptionMiddleware, self).process_exception(request, exception)
def process_exception(self, request, exception): """Handles specific exception raised by Python Social Auth eg HTTPError.""" referer_url = request.META.get('HTTP_REFERER', '') if (referer_url and isinstance(exception, HTTPError) and exception.response.status_code == 502): referer_url = six.moves.urllib.parse.urlparse(referer_url).path if referer_url == reverse('signin_user'): messages.error(request, _('Unable to connect with the external provider, please try again'), extra_tags='social-auth') redirect_url = get_next_url_for_login_page(request) return redirect('/login?next=' + redirect_url) return super(ExceptionMiddleware, self).process_exception(request, exception)
def render_auth_form(self, request, forus_params): """Render the combined login/registration form, defaulting to login This relies on the JS to asynchronously load the actual form from the user_api. """ initial_mode = 'register' # Determine the URL to redirect to following login/registration/third_party_auth redirect_to = get_next_url_for_login_page(request) # If we're already logged in, redirect to the dashboard if request.user.is_authenticated(): logout(request) delete_logged_in_cookies(request) return redirect(redirect_to) # Otherwise, render the combined login/registration page context = { 'data': { 'login_redirect_url': redirect_to, 'initial_mode': initial_mode, 'third_party_auth': {}, 'third_party_auth_hint': '', 'platform_name': settings.PLATFORM_NAME, # Include form descriptions retrieved from the user API. # We could have the JS client make these requests directly, # but we include them in the initial page load to avoid # the additional round-trip to the server. 'registration_form_desc': self.get_registration_form_description(request, forus_params), }, 'login_redirect_url': redirect_to, # This gets added to the query string of the "Sign In" button in header 'responsive': True, 'allow_iframing': True, 'disable_courseware_js': True, 'disable_footer': True, } return render_to_response('edraak_forus/auth.html', context)
def register_user(request, extra_context=None): """ Deprecated. To be replaced by :class:`user_authn.views.login_form.login_and_registration_form`. """ # Determine the URL to redirect to following login: redirect_to = get_next_url_for_login_page(request) if request.user.is_authenticated: return redirect(redirect_to) external_auth_response = external_auth_register(request) if external_auth_response is not None: return external_auth_response context = { 'login_redirect_url': redirect_to, # This gets added to the query string of the "Sign In" button in the header 'email': '', 'name': '', 'running_pipeline': None, 'pipeline_urls': auth_pipeline_urls(pipeline.AUTH_ENTRY_REGISTER, redirect_url=redirect_to), 'platform_name': configuration_helpers.get_value( 'platform_name', settings.PLATFORM_NAME ), 'selected_provider': '', 'username': '', } if extra_context is not None: context.update(extra_context) if context.get("extauth_domain", '').startswith(settings.SHIBBOLETH_DOMAIN_PREFIX): return render_to_response('register-shib.html', context) # If third-party auth is enabled, prepopulate the form with data from the # selected provider. if third_party_auth.is_enabled() and pipeline.running(request): running_pipeline = pipeline.get(request) current_provider = provider.Registry.get_from_pipeline(running_pipeline) if current_provider is not None: overrides = current_provider.get_register_form_data(running_pipeline.get('kwargs')) overrides['running_pipeline'] = running_pipeline overrides['selected_provider'] = current_provider.name context.update(overrides) return render_to_response('register.html', context)
def get(self, request, **kwargs): context = {} if request.user.is_authenticated: return redirect(reverse('dashboard')) hc_specialization_list = hcspecializations.objects.all() specializations_list = specializations.objects.all() context["hc_specialization_list"] = hc_specialization_list context["specializations_list"] = specializations_list context["states"] = states.objects.all() redirect_to = get_next_url_for_login_page(request) if third_party_auth.is_enabled(): providers = third_party_auth_context(request, redirect_to, '') #providers = {} else: providers = {} context["providers"] = providers context["redirect_to"] = redirect_to context["root_url"] = settings.LMS_ROOT_URL return render_to_response('login_email_phone.html',context)
def test_http_exception_redirection(self): """ Test ExceptionMiddleware is correctly redirected to login page when PSA raises HttpError exception. """ request = RequestFactory().get("dummy_url") next_url = get_next_url_for_login_page(request) login_url = '/login?next=' + next_url request.META['HTTP_REFERER'] = 'http://example.com:8000/login' exception = HTTPError() exception.response = HttpResponse(status=502) # Add error message for error in auth pipeline MessageMiddleware().process_request(request) response = ExceptionMiddleware().process_exception(request, exception) target_url = response.url self.assertEqual(response.status_code, 302) self.assertTrue(target_url.endswith(login_url))
def ssl_login(request): """ This is called by branding.views.index when FEATURES['AUTH_USE_CERTIFICATES'] = True Used for MIT user authentication. This presumes the web server (nginx) has been configured to require specific client certificates. If the incoming protocol is HTTPS (SSL) then authenticate via client certificate. The certificate provides user email and fullname; this populates the ExternalAuthMap. The user is nevertheless still asked to complete the edX signup. Else continues on with student.views.index, and no authentication. """ # Just to make sure we're calling this only at MIT: if not settings.FEATURES['AUTH_USE_CERTIFICATES']: return HttpResponseForbidden() cert = ssl_get_cert_from_request(request) if not cert: # no certificate information - go onward to main index return student.views.index(request) (_user, email, fullname) = _ssl_dn_extract_info(cert) redirect_to = get_next_url_for_login_page(request) retfun = functools.partial(redirect, redirect_to) return _external_login_or_signup( request, external_id=email, external_domain="ssl:MIT", credentials=cert, email=email, fullname=fullname, retfun=retfun )
def test_http_exception_redirection(self): """ Test ExceptionMiddleware is correctly redirected to login page when PSA raises HttpError exception. """ request = RequestFactory().get("dummy_url") next_url = get_next_url_for_login_page(request) login_url = '/login?next=' + next_url request.META['HTTP_REFERER'] = 'http://example.com:8000/login' exception = HTTPError() exception.response = HttpResponse(status=502) # Add error message for error in auth pipeline MessageMiddleware().process_request(request) response = ExceptionMiddleware().process_exception( request, exception ) target_url = response.url self.assertEqual(response.status_code, 302) self.assertTrue(target_url.endswith(login_url))
def login_and_registration_form(request, initial_mode="login"): """Render the combined login/registration form, defaulting to login This relies on the JS to asynchronously load the actual form from the user_api. Keyword Args: initial_mode (string): Either "login" or "register". """ # Determine the URL to redirect to following login/registration/third_party_auth redirect_to = get_next_url_for_login_page(request) # If we're already logged in, redirect to the dashboard if request.user.is_authenticated(): return redirect(redirect_to) # Retrieve the form descriptions from the user API form_descriptions = _get_form_descriptions(request) # If this is a themed site, revert to the old login/registration pages. # We need to do this for now to support existing themes. # Themed sites can use the new logistration page by setting # 'ENABLE_COMBINED_LOGIN_REGISTRATION' in their # configuration settings. if is_request_in_themed_site() and not configuration_helpers.get_value('ENABLE_COMBINED_LOGIN_REGISTRATION', False): if initial_mode == "login": return old_login_view(request) elif initial_mode == "register": return old_register_view(request) # Allow external auth to intercept and handle the request ext_auth_response = _external_auth_intercept(request, initial_mode) if ext_auth_response is not None: return ext_auth_response # Our ?next= URL may itself contain a parameter 'tpa_hint=x' that we need to check. # If present, we display a login page focused on third-party auth with that provider. third_party_auth_hint = None if '?' in redirect_to: try: next_args = urlparse.parse_qs(urlparse.urlparse(redirect_to).query) provider_id = next_args['tpa_hint'][0] if third_party_auth.provider.Registry.get(provider_id=provider_id): third_party_auth_hint = provider_id initial_mode = "hinted_login" except (KeyError, ValueError, IndexError): pass # Otherwise, render the combined login/registration page context = { 'data': { 'login_redirect_url': redirect_to, 'initial_mode': initial_mode, 'third_party_auth': _third_party_auth_context(request, redirect_to), 'third_party_auth_hint': third_party_auth_hint or '', 'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME), # Include form descriptions retrieved from the user API. # We could have the JS client make these requests directly, # but we include them in the initial page load to avoid # the additional round-trip to the server. 'login_form_desc': json.loads(form_descriptions['login']), 'registration_form_desc': json.loads(form_descriptions['registration']), 'password_reset_form_desc': json.loads(form_descriptions['password_reset']), }, 'login_redirect_url': redirect_to, # This gets added to the query string of the "Sign In" button in header 'responsive': True, 'allow_iframing': True, 'disable_courseware_js': True, 'disable_footer': not configuration_helpers.get_value( 'ENABLE_COMBINED_LOGIN_REGISTRATION_FOOTER', settings.FEATURES['ENABLE_COMBINED_LOGIN_REGISTRATION_FOOTER'] ), } return render_to_response('student_account/login_and_register.html', context)
def cme_create_account(request, post_override=None): ''' JSON call to create new edX account; modified for the CME registration form. Used by form in signup_modal.html, which is included into navigation.html ''' json_string = {'success': False} post_vars = post_override if post_override else request.POST.copy() # Confirm we have a properly formed request for var in ['username', 'email', 'password', 'name']: if var not in post_vars: json_string['value'] = "Error (401 {field}). E-mail us.".format(field=var) json_string['field'] = var return HttpResponse(json.dumps(json_string)) # Validate required fields set1 error = validate_required_fields_set1(post_vars) if error is not None: return HttpResponse(json.dumps(error)) # Validate length of middle initial error = validate_middle_initial_length(post_vars) if error is not None: return HttpResponse(json.dumps(error)) # Validate birth date error = validate_birth_date_format(post_vars) if error is not None: return HttpResponse(json.dumps(error)) # Validate fields dependent on Professional Designation error = validate_professional_fields(post_vars) if error is not None: return HttpResponse(json.dumps(error)) # Setup post_vars for correct sub_affiliation field post_vars = setup_sub_affiliation_field(post_vars) # Validate affiliation fields error = validate_affiliation_fields(post_vars) if error is not None: return HttpResponse(json.dumps(error)) # Validate required fields set2 error = validate_required_fields_set2(post_vars) if error is not None: return HttpResponse(json.dumps(error)) # Validate required check boxes error = validate_required_boxes(post_vars) if error is not None: return HttpResponse(json.dumps(error)) # Validate required radio buttons # Commented out while no radios exist # error = validate_required_radios(post_vars) # if error is not None: # return HttpResponse(json.dumps(error)) # Validate required secondary fields error = validate_required_secondaries(post_vars) if error is not None: return HttpResponse(json.dumps(error)) # Validate email address try: validate_email(post_vars['email']) except ValidationError: json_string['value'] = "Valid e-mail is required." json_string['field'] = 'email' return HttpResponse(json.dumps(json_string)) # Validate username conforms try: validate_slug(post_vars['username']) except ValidationError: json_string['value'] = "Username should only consist of A-Z and 0-9, with no spaces." json_string['field'] = 'username' return HttpResponse(json.dumps(json_string)) # Validate Export controls error = validate_export_controls(post_vars) if error is not None: return HttpResponse(json.dumps(error)) # Ok, looks like everything is legit. Create the account. ret = _do_cme_create_account(post_vars) if isinstance(ret, HttpResponse): # if there was an error then return that return ret (user, cme_user_profile, registration) = ret email_dict = { 'name': post_vars['name'], 'key': registration.activation_key, } # composes activation email subject = render_to_string('emails/activation_email_subject.txt', email_dict) # Email subject *must not* contain newlines subject = ''.join(subject.splitlines()) message = render_to_string('emails/activation_email.txt', email_dict) try: if settings.FEATURES.get('REROUTE_ACTIVATION_EMAIL'): dest_addr = settings.FEATURES['REROUTE_ACTIVATION_EMAIL'] message = ("Activation for %s (%s): %s\n" % (user, user.email, cme_user_profile.name) + '-' * 80 + '\n\n' + message) send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [dest_addr], fail_silently=False) else: user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL) except: log.warning('Unable to send activation email to user', exc_info=True) json_string['value'] = 'Could not send activation e-mail.' return HttpResponse(json.dumps(json_string)) # Immediately after a user creates an account, we log them in. They are only # logged in until they close the browser. They can't log in again until they click # the activation link from the email. login_user = authenticate(username=post_vars['username'], password=post_vars['password']) login(request, login_user) request.session.set_expiry(0) redirect_url = get_next_url_for_login_page(request) dog_stats_api.increment("common.student.successful_login") json_string = {'success': True, 'redirect_url': redirect_url} response = HttpResponse(json.dumps(json_string)) return response
def test_safe_next(self): """ Test safe next parameter """ req = self.request.get(reverse("login") + "?next={url}".format(url="/dashboard")) next_page = get_next_url_for_login_page(req) self.assertEqual(next_page, u'/dashboard')
def validate_login(): req = self.request.get(reverse("login") + "?next={url}".format(url=next_url)) req.META["HTTP_ACCEPT"] = "text/html" # pylint: disable=no-member self._add_session(req) next_page = get_next_url_for_login_page(req) self.assertEqual(next_page, expected_url)
def test_safe_next(self): """ Test safe next parameter """ req = self.request.get( reverse("login") + "?next={url}".format(url="/dashboard")) next_page = get_next_url_for_login_page(req) self.assertEqual(next_page, u'/dashboard')
def test_safe_next(self): """ Test safe next parameter """ req = self.request.get(reverse("login") + "?next={url}".format(url="/dashboard")) req.META["HTTP_ACCEPT"] = "text/html" # pylint: disable=no-member next_page = get_next_url_for_login_page(req) self.assertEqual(next_page, u'/dashboard')
def login_and_registration_form(request, initial_mode="login"): """Render the combined login/registration form, defaulting to login This relies on the JS to asynchronously load the actual form from the user_api. Keyword Args: initial_mode (string): Either "login" or "register". """ # Determine the URL to redirect to following login/registration/third_party_auth redirect_to = get_next_url_for_login_page(request) # If we're already logged in, redirect to the dashboard if request.user.is_authenticated(): return redirect(redirect_to) # Retrieve the form descriptions from the user API form_descriptions = _get_form_descriptions(request) # Our ?next= URL may itself contain a parameter 'tpa_hint=x' that we need to check. # If present, we display a login page focused on third-party auth with that provider. third_party_auth_hint = None if '?' in redirect_to: try: next_args = urlparse.parse_qs(urlparse.urlparse(redirect_to).query) provider_id = next_args['tpa_hint'][0] tpa_hint_provider = third_party_auth.provider.Registry.get( provider_id=provider_id) if tpa_hint_provider: if tpa_hint_provider.skip_hinted_login_dialog: # Forward the user directly to the provider's login URL when the provider is configured # to skip the dialog. return redirect( pipeline.get_login_url(provider_id, pipeline.AUTH_ENTRY_LOGIN, redirect_url=redirect_to)) third_party_auth_hint = provider_id initial_mode = "hinted_login" except (KeyError, ValueError, IndexError): pass # If this is a themed site, revert to the old login/registration pages. # We need to do this for now to support existing themes. # Themed sites can use the new logistration page by setting # 'ENABLE_COMBINED_LOGIN_REGISTRATION' in their # configuration settings. if is_request_in_themed_site() and not configuration_helpers.get_value( 'ENABLE_COMBINED_LOGIN_REGISTRATION', False): if initial_mode == "login": return old_login_view(request) elif initial_mode == "register": return old_register_view(request) # Allow external auth to intercept and handle the request ext_auth_response = _external_auth_intercept(request, initial_mode) if ext_auth_response is not None: return ext_auth_response # Account activation message account_activation_messages = [{ 'message': message.message, 'tags': message.tags } for message in messages.get_messages(request) if 'account-activation' in message.tags] # Otherwise, render the combined login/registration page context = { 'data': { 'login_redirect_url': redirect_to, 'initial_mode': initial_mode, 'third_party_auth': _third_party_auth_context(request, redirect_to, third_party_auth_hint), 'third_party_auth_hint': third_party_auth_hint or '', 'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME), 'support_link': configuration_helpers.get_value('SUPPORT_SITE_LINK', settings.SUPPORT_SITE_LINK), 'password_reset_support_link': configuration_helpers.get_value( 'PASSWORD_RESET_SUPPORT_LINK', settings.PASSWORD_RESET_SUPPORT_LINK) or settings.SUPPORT_SITE_LINK, 'account_activation_messages': account_activation_messages, # Include form descriptions retrieved from the user API. # We could have the JS client make these requests directly, # but we include them in the initial page load to avoid # the additional round-trip to the server. 'login_form_desc': json.loads(form_descriptions['login']), 'registration_form_desc': json.loads(form_descriptions['registration']), 'password_reset_form_desc': json.loads(form_descriptions['password_reset']), 'account_creation_allowed': configuration_helpers.get_value( 'ALLOW_PUBLIC_ACCOUNT_CREATION', settings.FEATURES.get('ALLOW_PUBLIC_ACCOUNT_CREATION', True)) }, 'login_redirect_url': redirect_to, # This gets added to the query string of the "Sign In" button in header 'responsive': True, 'allow_iframing': True, 'disable_courseware_js': True, 'combined_login_and_register': True, 'disable_footer': not configuration_helpers.get_value( 'ENABLE_COMBINED_LOGIN_REGISTRATION_FOOTER', settings.FEATURES['ENABLE_COMBINED_LOGIN_REGISTRATION_FOOTER']), } context = update_context_for_enterprise(request, context) response = render_to_response('student_account/login_and_register.html', context) # Remove enterprise cookie so that subsequent requests show default login page. response.delete_cookie( configuration_helpers.get_value( "ENTERPRISE_CUSTOMER_COOKIE_NAME", settings.ENTERPRISE_CUSTOMER_COOKIE_NAME), domain=configuration_helpers.get_value("BASE_COOKIE_DOMAIN", settings.BASE_COOKIE_DOMAIN), ) return response
def login_and_registration_form(request, initial_mode="login"): """Render the combined login/registration form, defaulting to login This relies on the JS to asynchronously load the actual form from the user_api. Keyword Args: initial_mode (string): Either "login" or "register". """ # Determine the URL to redirect to following login/registration/third_party_auth redirect_to = get_next_url_for_login_page(request) # If we're already logged in, redirect to the dashboard if request.user.is_authenticated: return redirect(redirect_to) # Retrieve the form descriptions from the user API form_descriptions = _get_form_descriptions(request) # Our ?next= URL may itself contain a parameter 'tpa_hint=x' that we need to check. # If present, we display a login page focused on third-party auth with that provider. third_party_auth_hint = None if '?' in redirect_to: try: next_args = urlparse.parse_qs(urlparse.urlparse(redirect_to).query) provider_id = next_args['tpa_hint'][0] tpa_hint_provider = third_party_auth.provider.Registry.get(provider_id=provider_id) if tpa_hint_provider: if tpa_hint_provider.skip_hinted_login_dialog: # Forward the user directly to the provider's login URL when the provider is configured # to skip the dialog. if initial_mode == "register": auth_entry = pipeline.AUTH_ENTRY_REGISTER else: auth_entry = pipeline.AUTH_ENTRY_LOGIN return redirect( pipeline.get_login_url(provider_id, auth_entry, redirect_url=redirect_to) ) third_party_auth_hint = provider_id initial_mode = "hinted_login" except (KeyError, ValueError, IndexError) as ex: log.error("Unknown tpa_hint provider: %s", ex) # If this is a themed site, revert to the old login/registration pages. # We need to do this for now to support existing themes. # Themed sites can use the new logistration page by setting # 'ENABLE_COMBINED_LOGIN_REGISTRATION' in their # configuration settings. if is_request_in_themed_site() and not configuration_helpers.get_value('ENABLE_COMBINED_LOGIN_REGISTRATION', False): if initial_mode == "login": return old_login_view(request) elif initial_mode == "register": return old_register_view(request) # Allow external auth to intercept and handle the request ext_auth_response = _external_auth_intercept(request, initial_mode) if ext_auth_response is not None: return ext_auth_response # Account activation message account_activation_messages = [ { 'message': message.message, 'tags': message.tags } for message in messages.get_messages(request) if 'account-activation' in message.tags ] # Otherwise, render the combined login/registration page context = { 'data': { 'login_redirect_url': redirect_to, 'initial_mode': initial_mode, 'third_party_auth': _third_party_auth_context(request, redirect_to, third_party_auth_hint), 'third_party_auth_hint': third_party_auth_hint or '', 'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME), 'support_link': configuration_helpers.get_value('SUPPORT_SITE_LINK', settings.SUPPORT_SITE_LINK), 'password_reset_support_link': configuration_helpers.get_value( 'PASSWORD_RESET_SUPPORT_LINK', settings.PASSWORD_RESET_SUPPORT_LINK ) or settings.SUPPORT_SITE_LINK, 'account_activation_messages': account_activation_messages, # Include form descriptions retrieved from the user API. # We could have the JS client make these requests directly, # but we include them in the initial page load to avoid # the additional round-trip to the server. 'login_form_desc': json.loads(form_descriptions['login']), 'registration_form_desc': json.loads(form_descriptions['registration']), 'password_reset_form_desc': json.loads(form_descriptions['password_reset']), 'account_creation_allowed': configuration_helpers.get_value( 'ALLOW_PUBLIC_ACCOUNT_CREATION', settings.FEATURES.get('ALLOW_PUBLIC_ACCOUNT_CREATION', True)) }, 'login_redirect_url': redirect_to, # This gets added to the query string of the "Sign In" button in header 'responsive': True, 'allow_iframing': True, 'disable_courseware_js': True, 'combined_login_and_register': True, 'disable_footer': not configuration_helpers.get_value( 'ENABLE_COMBINED_LOGIN_REGISTRATION_FOOTER', settings.FEATURES['ENABLE_COMBINED_LOGIN_REGISTRATION_FOOTER'] ), } enterprise_customer = enterprise_customer_for_request(request) update_logistration_context_for_enterprise(request, context, enterprise_customer) response = render_to_response('student_account/login_and_register.html', context) handle_enterprise_cookies_for_logistration(request, response, context) return response
def login_and_registration_form(request, initial_mode="login"): """Render the combined login/registration form, defaulting to login This relies on the JS to asynchronously load the actual form from the user_api. Keyword Args: initial_mode (string): Either "login" or "register". """ # Determine the URL to redirect to following login/registration/third_party_auth redirect_to = get_next_url_for_login_page(request) # If we're already logged in, redirect to the dashboard if request.user.is_authenticated(): return redirect(redirect_to) # Retrieve the form descriptions from the user API form_descriptions = _get_form_descriptions(request) # If this is a themed site, revert to the old login/registration pages. # We need to do this for now to support existing themes. # Themed sites can use the new logistration page by setting # 'ENABLE_COMBINED_LOGIN_REGISTRATION' in their theme/microsite # configuration settings. if is_request_in_themed_site() and not get_themed_value('ENABLE_COMBINED_LOGIN_REGISTRATION', False): if initial_mode == "login": return old_login_view(request) elif initial_mode == "register": return old_register_view(request) # Allow external auth to intercept and handle the request ext_auth_response = _external_auth_intercept(request, initial_mode) if ext_auth_response is not None: return ext_auth_response # Our ?next= URL may itself contain a parameter 'tpa_hint=x' that we need to check. # If present, we display a login page focused on third-party auth with that provider. third_party_auth_hint = None if '?' in redirect_to: try: next_args = urlparse.parse_qs(urlparse.urlparse(redirect_to).query) provider_id = next_args['tpa_hint'][0] if third_party_auth.provider.Registry.get(provider_id=provider_id): third_party_auth_hint = provider_id initial_mode = "hinted_login" except (KeyError, ValueError, IndexError): pass # Otherwise, render the combined login/registration page context = { 'data': { 'login_redirect_url': redirect_to, 'initial_mode': initial_mode, 'third_party_auth': _third_party_auth_context(request, redirect_to), 'third_party_auth_hint': third_party_auth_hint or '', 'platform_name': get_themed_value('PLATFORM_NAME', settings.PLATFORM_NAME), # Include form descriptions retrieved from the user API. # We could have the JS client make these requests directly, # but we include them in the initial page load to avoid # the additional round-trip to the server. 'login_form_desc': json.loads(form_descriptions['login']), 'registration_form_desc': json.loads(form_descriptions['registration']), 'password_reset_form_desc': json.loads(form_descriptions['password_reset']), }, 'login_redirect_url': redirect_to, # This gets added to the query string of the "Sign In" button in header 'responsive': True, 'allow_iframing': True, 'disable_courseware_js': True, 'disable_footer': True, } return render_to_response('student_account/login_and_register.html', context)
def login_and_registration_form(request, initial_mode="login"): """Render the combined login/registration form, defaulting to login This relies on the JS to asynchronously load the actual form from the user_api. Keyword Args: initial_mode (string): Either "login" or "register". """ # Determine the URL to redirect to following login/registration/third_party_auth redirect_to = get_next_url_for_login_page(request) # If we're already logged in, redirect to the dashboard # Note: We check for the existence of login-related cookies in addition to is_authenticated # since Django's SessionAuthentication middleware auto-updates session cookies but not # the other login-related cookies. See ARCH-282. if request.user.is_authenticated and are_logged_in_cookies_set(request): return redirect(redirect_to) # Retrieve the form descriptions from the user API form_descriptions = _get_form_descriptions(request) # Our ?next= URL may itself contain a parameter 'tpa_hint=x' that we need to check. # If present, we display a login page focused on third-party auth with that provider. third_party_auth_hint = None if '?' in redirect_to: try: next_args = six.moves.urllib.parse.parse_qs(six.moves.urllib.parse.urlparse(redirect_to).query) provider_id = next_args['tpa_hint'][0] tpa_hint_provider = third_party_auth.provider.Registry.get(provider_id=provider_id) if tpa_hint_provider: if tpa_hint_provider.skip_hinted_login_dialog: # Forward the user directly to the provider's login URL when the provider is configured # to skip the dialog. if initial_mode == "register": auth_entry = pipeline.AUTH_ENTRY_REGISTER else: auth_entry = pipeline.AUTH_ENTRY_LOGIN return redirect( pipeline.get_login_url(provider_id, auth_entry, redirect_url=redirect_to) ) third_party_auth_hint = provider_id initial_mode = "hinted_login" except (KeyError, ValueError, IndexError) as ex: log.exception(u"Unknown tpa_hint provider: %s", ex) # We are defaulting to true because all themes should now be using the newer page. if is_request_in_themed_site() and not configuration_helpers.get_value('ENABLE_COMBINED_LOGIN_REGISTRATION', True): if initial_mode == "login": return old_login_view(request) elif initial_mode == "register": return old_register_view(request) # Account activation message account_activation_messages = [ { 'message': message.message, 'tags': message.tags } for message in messages.get_messages(request) if 'account-activation' in message.tags ] account_recovery_messages = [ { 'message': message.message, 'tags': message.tags } for message in messages.get_messages(request) if 'account-recovery' in message.tags ] # Otherwise, render the combined login/registration page context = { 'data': { 'login_redirect_url': redirect_to, 'initial_mode': initial_mode, 'third_party_auth': _third_party_auth_context(request, redirect_to, third_party_auth_hint), 'third_party_auth_hint': third_party_auth_hint or '', 'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME), 'support_link': configuration_helpers.get_value('SUPPORT_SITE_LINK', settings.SUPPORT_SITE_LINK), 'password_reset_support_link': configuration_helpers.get_value( 'PASSWORD_RESET_SUPPORT_LINK', settings.PASSWORD_RESET_SUPPORT_LINK ) or settings.SUPPORT_SITE_LINK, 'account_activation_messages': account_activation_messages, 'account_recovery_messages': account_recovery_messages, # Include form descriptions retrieved from the user API. # We could have the JS client make these requests directly, # but we include them in the initial page load to avoid # the additional round-trip to the server. 'login_form_desc': json.loads(form_descriptions['login']), 'registration_form_desc': json.loads(form_descriptions['registration']), 'password_reset_form_desc': json.loads(form_descriptions['password_reset']), 'account_creation_allowed': configuration_helpers.get_value( 'ALLOW_PUBLIC_ACCOUNT_CREATION', settings.FEATURES.get('ALLOW_PUBLIC_ACCOUNT_CREATION', True)), 'is_account_recovery_feature_enabled': is_secondary_email_feature_enabled() }, 'login_redirect_url': redirect_to, # This gets added to the query string of the "Sign In" button in header 'responsive': True, 'allow_iframing': True, 'disable_courseware_js': True, 'combined_login_and_register': True, 'disable_footer': not configuration_helpers.get_value( 'ENABLE_COMBINED_LOGIN_REGISTRATION_FOOTER', settings.FEATURES['ENABLE_COMBINED_LOGIN_REGISTRATION_FOOTER'] ), } enterprise_customer = enterprise_customer_for_request(request) update_logistration_context_for_enterprise(request, context, enterprise_customer) response = render_to_response('student_account/login_and_register.html', context) handle_enterprise_cookies_for_logistration(request, response, context) return response
def login_and_registration_form(request, initial_mode="login"): """Render the combined login/registration form, defaulting to login This relies on the JS to asynchronously load the actual form from the user_api. Keyword Args: initial_mode (string): Either "login" or "register". """ # Determine the URL to redirect to following login/registration/third_party_auth redirect_to = get_next_url_for_login_page(request) # If we're already logged in, redirect to the dashboard if UserProfile.has_registered(request.user): return redirect(redirect_to) if third_party_auth.is_enabled(): force_provider_id = settings.FORCED_TPA_PROVIDER_ID if force_provider_id: force_provider = third_party_auth.provider.Registry.get( provider_id=force_provider_id, ) if force_provider and force_provider.display_for_login: running_pipeline = third_party_auth.pipeline.get(request) if not running_pipeline: if initial_mode in [pipeline.AUTH_ENTRY_LOGIN, pipeline.AUTH_ENTRY_REGISTER]: tpa_url = pipeline.get_login_url( force_provider_id, initial_mode, redirect_url=redirect_to, ) return redirect(tpa_url) # Retrieve the form descriptions from the user API form_descriptions = _get_form_descriptions(request) # Our ?next= URL may itself contain a parameter 'tpa_hint=x' that we need to check. # If present, we display a login page focused on third-party auth with that provider. third_party_auth_hint = None if '?' in redirect_to: try: next_args = urlparse.parse_qs(urlparse.urlparse(redirect_to).query) provider_id = next_args['tpa_hint'][0] if third_party_auth.provider.Registry.get(provider_id=provider_id): third_party_auth_hint = provider_id initial_mode = "hinted_login" except (KeyError, ValueError, IndexError): pass set_enterprise_branding_filter_param(request=request, provider_id=third_party_auth_hint) # If this is a themed site, revert to the old login/registration pages. # We need to do this for now to support existing themes. # Themed sites can use the new logistration page by setting # 'ENABLE_COMBINED_LOGIN_REGISTRATION' in their # configuration settings. if is_request_in_themed_site() and not configuration_helpers.get_value('ENABLE_COMBINED_LOGIN_REGISTRATION', False): if initial_mode == "login": return old_login_view(request) elif initial_mode == "register": return old_register_view(request) # Allow external auth to intercept and handle the request ext_auth_response = _external_auth_intercept(request, initial_mode) if ext_auth_response is not None: return ext_auth_response # Otherwise, render the combined login/registration page context = { 'data': { 'login_redirect_url': redirect_to, 'initial_mode': initial_mode, 'third_party_auth': _third_party_auth_context(request, redirect_to), 'third_party_auth_hint': third_party_auth_hint or '', 'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME), 'support_link': configuration_helpers.get_value('SUPPORT_SITE_LINK', settings.SUPPORT_SITE_LINK), 'privacy_policy_url': marketing_link('PRIVACY'), # Include form descriptions retrieved from the user API. # We could have the JS client make these requests directly, # but we include them in the initial page load to avoid # the additional round-trip to the server. 'login_form_desc': json.loads(form_descriptions['login']), 'registration_form_desc': json.loads(form_descriptions['registration']), 'password_reset_form_desc': json.loads(form_descriptions['password_reset']), }, 'login_redirect_url': redirect_to, # This gets added to the query string of the "Sign In" button in header 'responsive': True, 'allow_iframing': True, 'disable_courseware_js': True, 'disable_footer': not configuration_helpers.get_value( 'ENABLE_COMBINED_LOGIN_REGISTRATION_FOOTER', settings.FEATURES['ENABLE_COMBINED_LOGIN_REGISTRATION_FOOTER'] ), } return render_to_response('student_account/login_and_register.html', context)
def login_user(request): """ AJAX request to log in the user. Arguments: request (HttpRequest) Required params: email, password Optional params: analytics: a JSON-encoded object with additional info to include in the login analytics event. The only supported field is "enroll_course_id" to indicate that the user logged in while enrolling in a particular course. Returns: HttpResponse: 200 if successful. Ex. {'success': true} HttpResponse: 400 if the request failed. Ex. {'success': false, 'value': '{'success': false, 'value: 'Email or password is incorrect.'} HttpResponse: 403 if successful authentication with a third party provider but does not have a linked account. Ex. {'success': false, 'error_code': 'third-party-auth-with-no-linked-account'} Example Usage: POST /login_ajax with POST params `email`, `password` 200 {'success': true} """ _parse_analytics_param_for_course_id(request) third_party_auth_requested = third_party_auth.is_enabled( ) and pipeline.running(request) first_party_auth_requested = bool(request.POST.get('email')) or bool( request.POST.get('password')) is_user_third_party_authenticated = False set_custom_metric('login_user_course_id', request.POST.get('course_id')) try: if third_party_auth_requested and not first_party_auth_requested: # The user has already authenticated via third-party auth and has not # asked to do first party auth by supplying a username or password. We # now want to put them through the same logging and cookie calculation # logic as with first-party auth. # This nested try is due to us only returning an HttpResponse in this # one case vs. JsonResponse everywhere else. try: user = _do_third_party_auth(request) is_user_third_party_authenticated = True set_custom_metric('login_user_tpa_success', True) except AuthFailedError as e: set_custom_metric('login_user_tpa_success', False) set_custom_metric('login_user_tpa_failure_msg', e.value) # user successfully authenticated with a third party provider, but has no linked Open edX account response_content = e.get_response() response_content[ 'error_code'] = 'third-party-auth-with-no-linked-account' return JsonResponse(response_content, status=403) else: user = _get_user_by_email(request) _check_excessive_login_attempts(user) possibly_authenticated_user = user if not is_user_third_party_authenticated: 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) if possibly_authenticated_user is None or not possibly_authenticated_user.is_active: _handle_failed_authentication(user, possibly_authenticated_user) _handle_successful_authentication_and_login( possibly_authenticated_user, request) redirect_url = None # The AJAX method calling should know the default destination upon success if is_user_third_party_authenticated: running_pipeline = pipeline.get(request) redirect_url = pipeline.get_complete_url( backend_name=running_pipeline['backend']) elif settings.FEATURES.get('ENABLE_LOGIN_MICROFRONTEND'): redirect_url = get_next_url_for_login_page(request) response = JsonResponse({ 'success': True, 'redirect_url': redirect_url, }) # Ensure that the external marketing site can # detect that the user is logged in. response = set_logged_in_cookies(request, response, possibly_authenticated_user) set_custom_metric('login_user_auth_failed_error', False) set_custom_metric('login_user_response_status', response.status_code) set_custom_metric('login_user_redirect_url', redirect_url) return response except AuthFailedError as error: log.exception(error.get_response()) response = JsonResponse(error.get_response(), status=400) set_custom_metric('login_user_auth_failed_error', True) set_custom_metric('login_user_response_status', response.status_code) return response