def send_password_reset_email_for_user(user, request, preferred_email=None): """ Send out a password reset email for the given user. Arguments: user (User): Django User object request (HttpRequest): Django request object preferred_email (str): Send email to this address if present, otherwise fallback to user's email address. """ message_context, user_language_preference = get_user_default_email_params(user) site_name = settings.AUTHN_MICROFRONTEND_DOMAIN if should_redirect_to_authn_microfrontend() \ else configuration_helpers.get_value('SITE_NAME', settings.SITE_NAME) message_context.update({ 'request': request, # Used by google_analytics_tracking_pixel # TODO: This overrides `platform_name` from `get_base_template_context` to make the tests passes 'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME), 'reset_link': '{protocol}://{site}{link}?track=pwreset'.format( protocol='https' if request.is_secure() else 'http', site=site_name, link=reverse('password_reset_confirm', kwargs={ 'uidb36': int_to_base36(user.id), 'token': default_token_generator.make_token(user), }), ) }) msg = PasswordReset().personalize( recipient=Recipient(user.id, preferred_email or user.email), language=user_language_preference, user_context=message_context, ) ace.send(msg)
def _check_user_auth_flow(site, user): """ Check if user belongs to an allowed domain and not whitelisted then ask user to login through allowed domain SSO provider. """ if user and ENABLE_LOGIN_USING_THIRDPARTY_AUTH_ONLY.is_enabled(): allowed_domain = site.configuration.get_value( 'THIRD_PARTY_AUTH_ONLY_DOMAIN', '').lower() email_parts = user.email.split('@') if len(email_parts) != 2: # User has a nonstandard email so we record their id. # we don't record their e-mail in case there is sensitive info accidentally # in there. set_custom_attribute('login_tpa_domain_shortcircuit_user_id', user.id) log.warn( "User %s has nonstandard e-mail. Shortcircuiting THIRD_PART_AUTH_ONLY_DOMAIN check.", user.id) # lint-amnesty, pylint: disable=deprecated-method return user_domain = email_parts[1].strip().lower() # If user belongs to allowed domain and not whitelisted then user must login through allowed domain SSO if user_domain == allowed_domain and not AllowedAuthUser.objects.filter( site=site, email=user.email).exists(): if not should_redirect_to_authn_microfrontend(): msg = _create_message(site, None, allowed_domain) else: root_url = configuration_helpers.get_value( 'LMS_ROOT_URL', settings.LMS_ROOT_URL) msg = _create_message(site, root_url, allowed_domain) raise AuthFailedError(msg)
def send_account_recovery_email_for_user(user, request, email=None): """ Send out a account recovery email for the given user. Arguments: user (User): Django User object request (HttpRequest): Django request object email (str): Send email to this address. """ site = get_current_site() message_context = get_base_template_context(site) site_name = settings.AUTHN_MICROFRONTEND_DOMAIN if should_redirect_to_authn_microfrontend() \ else configuration_helpers.get_value('SITE_NAME', settings.SITE_NAME) message_context.update({ 'request': request, # Used by google_analytics_tracking_pixel 'email': email, 'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME), 'reset_link': '{protocol}://{site}{link}?is_account_recovery=true'.format( protocol='https' if request.is_secure() else 'http', site=site_name, link=reverse('password_reset_confirm', kwargs={ 'uidb36': int_to_base36(user.id), 'token': default_token_generator.make_token(user), }), ) }) msg = AccountRecoveryMessage().personalize( recipient=Recipient(user.username, email), language=get_user_preference(user, LANGUAGE_KEY), user_context=message_context, ) ace.send(msg)
def send_password_reset_email(self, user, site): """ Send email to learner with reset password link :param user: :param site: """ message_context = get_base_template_context(site) email = user.email if should_redirect_to_authn_microfrontend(): site_url = settings.AUTHN_MICROFRONTEND_URL else: site_url = configuration_helpers.get_value('SITE_NAME', settings.SITE_NAME) message_context.update({ 'email': email, 'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME), 'reset_link': '{protocol}://{site_url}{link}?track=pwreset'.format( protocol='http', site_url=site_url, link=reverse('password_reset_confirm', kwargs={ 'uidb36': int_to_base36(user.id), 'token': default_token_generator.make_token(user), }), ) }) with emulate_http_request(site, user): msg = PasswordReset().personalize( recipient=Recipient(user.id, email), language=get_user_preference(user, LANGUAGE_KEY), user_context=message_context, ) ace.send(msg)
def _generate_locked_out_error_message(): """ Helper function to generate error message for users consumed all login attempts. """ locked_out_period_in_sec = settings.MAX_FAILED_LOGIN_ATTEMPTS_LOCKOUT_PERIOD_SECS if not should_redirect_to_authn_microfrontend(): # pylint: disable=no-else-raise raise AuthFailedError(Text(_('To protect your account, it’s been temporarily ' 'locked. Try again in {locked_out_period} minutes.' '{li_start}To be on the safe side, you can reset your ' 'password {link_start}here{link_end} before you try again.')).format( link_start=HTML('<a http="#login" class="form-toggle" data-type="password-reset">'), link_end=HTML('</a>'), li_start=HTML('<li>'), li_end=HTML('</li>'), locked_out_period=int(locked_out_period_in_sec / 60))) else: raise AuthFailedError(Text(_('To protect your account, it’s been temporarily ' 'locked. Try again in {locked_out_period} minutes.\n' 'To be on the safe side, you can reset your ' 'password {link_start}here{link_end} before you try again.\n')).format( link_start=HTML('<a href="/reset" >'), link_end=HTML('</a>'), locked_out_period=int(locked_out_period_in_sec / 60)))
def _handle_failed_authentication(user, authenticated_user): """ Handles updating the failed login count, inactive user notifications, and logging failed authentications. """ if user: if LoginFailures.is_feature_enabled(): LoginFailures.increment_lockout_counter(user) if authenticated_user and not user.is_active: _log_and_raise_inactive_user_auth_error(user) # if we didn't find this username earlier, the account for this email # doesn't exist, and doesn't have a corresponding password if settings.FEATURES['SQUELCH_PII_IN_LOGS']: loggable_id = user.id if user else "<unknown>" AUDIT_LOG.warning(u"Login failed - password for user.id: {0} is invalid".format(loggable_id)) else: AUDIT_LOG.warning(u"Login failed - password for {0} is invalid".format(user.email)) if user and LoginFailures.is_feature_enabled(): blocked_threshold, failure_count = LoginFailures.check_user_reset_password_threshold(user) if blocked_threshold: if not LoginFailures.is_user_locked_out(user): max_failures_allowed = settings.MAX_FAILED_LOGIN_ATTEMPTS_ALLOWED remaining_attempts = max_failures_allowed - failure_count if not should_redirect_to_authn_microfrontend(): # pylint: disable=no-else-raise raise AuthFailedError(Text(_('Email or password is incorrect.' '{li_start}You have {remaining_attempts} more sign-in ' 'attempts before your account is temporarily locked.{li_end}' '{li_start}If you\'ve forgotten your password, click ' '{link_start}here{link_end} to reset.{li_end}' )) .format( link_start=HTML('<a http="#login" class="form-toggle" data-type="password-reset">'), link_end=HTML('</a>'), li_start=HTML('<li>'), li_end=HTML('</li>'), remaining_attempts=remaining_attempts)) else: raise AuthFailedError(Text(_('Email or password is incorrect.\n' 'You have {remaining_attempts} more sign-in ' 'attempts before your account is temporarily locked.\n' 'If you{quote}ve forgotten your password, click ' '{link_start}here{link_end} to reset.\n' )) .format( quote=HTML("'"), link_start=HTML('<a href="/reset" >'), link_end=HTML('</a>'), remaining_attempts=remaining_attempts)) else: _generate_locked_out_error_message() raise AuthFailedError(_('Email or password is incorrect.'))
def activate_account(request, key): """ When link in activation e-mail is clicked """ # If request is in Studio call the appropriate view if theming_helpers.get_project_root_name().lower() == 'cms': monitoring_utils.set_custom_attribute('student_activate_account', 'cms') return activate_account_studio(request, key) # TODO: Use custom attribute to determine if there are any `activate_account` calls for cms in Production. # If not, the templates wouldn't be needed for cms, but we still need a way to activate for cms tests. monitoring_utils.set_custom_attribute('student_activate_account', 'lms') activation_message_type = None invalid_message = HTML(_( '{html_start}Your account could not be activated{html_end}' 'Something went wrong, please <a href="{support_url}">contact support</a> to resolve this issue.' )).format( support_url=configuration_helpers.get_value( 'ACTIVATION_EMAIL_SUPPORT_LINK', settings.ACTIVATION_EMAIL_SUPPORT_LINK ) or settings.SUPPORT_SITE_LINK, html_start=HTML('<p class="message-title">'), html_end=HTML('</p>'), ) try: registration = Registration.objects.get(activation_key=key) except (Registration.DoesNotExist, Registration.MultipleObjectsReturned): activation_message_type = 'error' messages.error( request, invalid_message, extra_tags='account-activation aa-icon' ) else: if request.user.is_authenticated and request.user.id != registration.user.id: activation_message_type = 'error' messages.error( request, invalid_message, extra_tags='account-activation aa-icon' ) elif registration.user.is_active: activation_message_type = 'info' messages.info( request, HTML(_('{html_start}This account has already been activated.{html_end}')).format( html_start=HTML('<p class="message-title">'), html_end=HTML('</p>'), ), extra_tags='account-activation aa-icon', ) else: registration.activate() # Success message for logged in users. message = _('{html_start}Success{html_end} You have activated your account.') tracker.emit( USER_ACCOUNT_ACTIVATED, { "user_id": registration.user.id, "activation_timestamp": registration.activation_timestamp } ) if not request.user.is_authenticated: # Success message for logged out users message = _( '{html_start}Success! You have activated your account.{html_end}' 'You will now receive email updates and alerts from us related to' ' the courses you are enrolled in. Sign In to continue.' ) # Add message for later use. activation_message_type = 'success' messages.success( request, HTML(message).format( html_start=HTML('<p class="message-title">'), html_end=HTML('</p>'), ), extra_tags='account-activation aa-icon', ) # If a (safe) `next` parameter is provided in the request # and it's not the same as the dashboard, redirect there. # The `get_next_url_for_login_page()` function will only return a safe redirect URL. # If the provided `next` URL is not safe, that function will fill `redirect_to` # with a value of `reverse('dashboard')`. if request.GET.get('next'): redirect_to, root_url = get_next_url_for_login_page(request, include_host=True) if redirect_to != reverse('dashboard'): redirect_url = get_redirect_url_with_host(root_url, redirect_to) return redirect(redirect_url) if should_redirect_to_authn_microfrontend() and not request.user.is_authenticated: url_path = f'/login?account_activation_status={activation_message_type}' return redirect(settings.AUTHN_MICROFRONTEND_URL + url_path) return redirect('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 # 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: # lint-amnesty, pylint: disable=too-many-nested-blocks try: next_args = six.moves.urllib.parse.parse_qs(six.moves.urllib.parse.urlparse(redirect_to).query) if 'tpa_hint' in next_args: 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) enterprise_customer = enterprise_customer_for_request(request) # Redirect to authn MFE if it is enabled if should_redirect_to_authn_microfrontend() and not enterprise_customer: # This is to handle a case where a logged-in cookie is not present but the user is authenticated. # Note: If we don't handle this learner is redirected to authn MFE and then back to dashboard # instead of the desired redirect URL (e.g. finish_auth) resulting in learners not enrolling # into the courses. if request.user.is_authenticated and redirect_to: return redirect(redirect_to) query_params = request.GET.urlencode() url_path = '/{}{}'.format( initial_mode, '?' + query_params if query_params else '' ) return redirect(settings.AUTHN_MICROFRONTEND_URL + url_path) # 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(), 'is_multiple_user_enterprises_feature_enabled': is_multiple_user_enterprises_feature_enabled(), 'enterprise_slug_login_url': get_enterprise_slug_login_url(), 'is_require_third_party_auth_enabled': is_require_third_party_auth_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'] ), } 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_user(request, api_version='v1'): """ 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_attribute('login_user_course_id', request.POST.get('course_id')) if is_require_third_party_auth_enabled( ) and not third_party_auth_requested: return HttpResponseForbidden( "Third party authentication is required to login. Username and password were received instead." ) possibly_authenticated_user = None 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_attribute('login_user_tpa_success', True) except AuthFailedError as e: set_custom_attribute('login_user_tpa_success', False) set_custom_attribute('login_user_tpa_failure_msg', e.value) if e.error_code: set_custom_attribute('login_error_code', e.error_code) # user successfully authenticated with a third party provider, but has no linked Open edX account response_content = e.get_response() return JsonResponse(response_content, status=403) else: user = _get_user_by_email_or_username(request, api_version) _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) check_pwned_password_and_send_track_event.delay( user.id, request.POST.get('password'), user.is_staff) if possibly_authenticated_user is None or not ( possibly_authenticated_user.is_active or settings.MARKETING_EMAILS_OPT_IN): _handle_failed_authentication(user, possibly_authenticated_user) _handle_successful_authentication_and_login( possibly_authenticated_user, request) # The AJAX method calling should know the default destination upon success redirect_url, finish_auth_url = None, '' if third_party_auth_requested: running_pipeline = pipeline.get(request) finish_auth_url = pipeline.get_complete_url( backend_name=running_pipeline['backend']) if is_user_third_party_authenticated: redirect_url = finish_auth_url elif should_redirect_to_authn_microfrontend(): next_url, root_url = get_next_url_for_login_page(request, include_host=True) redirect_url = get_redirect_url_with_host( root_url, enterprise_selection_page(request, possibly_authenticated_user, finish_auth_url or next_url)) 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_attribute('login_user_auth_failed_error', False) set_custom_attribute('login_user_response_status', response.status_code) set_custom_attribute('login_user_redirect_url', redirect_url) mark_user_change_as_expected(response, user.id) return response except AuthFailedError as error: response_content = error.get_response() log.exception(response_content) error_code = response_content.get('error_code') if error_code: set_custom_attribute('login_error_code', error_code) email_or_username_key = 'email' if api_version == API_V1 else 'email_or_username' email_or_username = request.POST.get(email_or_username_key, None) email_or_username = possibly_authenticated_user.email \ if possibly_authenticated_user else email_or_username response_content['email'] = email_or_username response = JsonResponse(response_content, status=400) set_custom_attribute('login_user_auth_failed_error', True) set_custom_attribute('login_user_response_status', response.status_code) return response
def activate_account(request, key): """ When link in activation e-mail is clicked """ # If request is in Studio call the appropriate view if theming_helpers.get_project_root_name().lower() == u'cms': monitoring_utils.set_custom_attribute('student_activate_account', 'cms') return activate_account_studio(request, key) # TODO: Use custom attribute to determine if there are any `activate_account` calls for cms in Production. # If not, the templates wouldn't be needed for cms, but we still need a way to activate for cms tests. monitoring_utils.set_custom_attribute('student_activate_account', 'lms') activation_message_type = None try: registration = Registration.objects.get(activation_key=key) except (Registration.DoesNotExist, Registration.MultipleObjectsReturned): activation_message_type = 'error' messages.error( request, HTML(_( '{html_start}Your account could not be activated{html_end}' 'Something went wrong, please <a href="{support_url}">contact support</a> to resolve this issue.' )).format( support_url=configuration_helpers.get_value( 'ACTIVATION_EMAIL_SUPPORT_LINK', settings.ACTIVATION_EMAIL_SUPPORT_LINK ) or settings.SUPPORT_SITE_LINK, html_start=HTML('<p class="message-title">'), html_end=HTML('</p>'), ), extra_tags='account-activation aa-icon' ) else: if registration.user.is_active: activation_message_type = 'info' messages.info( request, HTML(_('{html_start}This account has already been activated.{html_end}')).format( html_start=HTML('<p class="message-title">'), html_end=HTML('</p>'), ), extra_tags='account-activation aa-icon', ) else: registration.activate() # Success message for logged in users. message = _('{html_start}Success{html_end} You have activated your account.') if not request.user.is_authenticated: # Success message for logged out users message = _( '{html_start}Success! You have activated your account.{html_end}' 'You will now receive email updates and alerts from us related to' ' the courses you are enrolled in. Sign In to continue.' ) # Add message for later use. activation_message_type = 'success' messages.success( request, HTML(message).format( html_start=HTML('<p class="message-title">'), html_end=HTML('</p>'), ), extra_tags='account-activation aa-icon', ) if should_redirect_to_authn_microfrontend() and not request.user.is_authenticated: url_path = '/login?account_activation_status={}'.format(activation_message_type) return redirect(settings.AUTHN_MICROFRONTEND_URL + url_path) return redirect('dashboard')
AUTH_ENTRY_REGISTER_API = 'register_api' # AUTH_ENTRY_CUSTOM: Custom auth entry point for post-auth integrations. # This should be a dict where the key is a word passed via ?auth_entry=, and the # value is a dict with an arbitrary 'secret_key' and a 'url'. # This can be used as an extension point to inject custom behavior into the auth # process, replacing the registration/login form that would normally be seen # immediately after the user has authenticated with the third party provider. # If a custom 'auth_entry' query parameter is used, then once the user has # authenticated with a specific backend/provider, they will be redirected to the # URL specified with this setting, rather than to the built-in # registration/login form/logic. AUTH_ENTRY_CUSTOM = getattr(settings, 'THIRD_PARTY_AUTH_CUSTOM_AUTH_FORMS', {}) # If authn MFE is enabled, the redirect should be to MFE instead of FE BASE_URL = settings.AUTHN_MICROFRONTEND_URL if should_redirect_to_authn_microfrontend( ) else '' def is_api(auth_entry): """Returns whether the auth entry point is via an API call.""" return (auth_entry == AUTH_ENTRY_LOGIN_API) or (auth_entry == AUTH_ENTRY_REGISTER_API) # lint-amnesty, pylint: disable=consider-using-in # URLs associated with auth entry points # These are used to request additional user information # (for example, account credentials when logging in), # and when the user cancels the auth process # (e.g., refusing to grant permission on the provider's login page). # We don't use "reverse" here because doing so may cause modules # to load that depend on this module.
def activate_account(request, key): """ When link in activation e-mail is clicked """ # If request is in Studio call the appropriate view if theming_helpers.get_project_root_name().lower() == 'cms': monitoring_utils.set_custom_attribute('student_activate_account', 'cms') return activate_account_studio(request, key) # TODO: Use custom attribute to determine if there are any `activate_account` calls for cms in Production. # If not, the templates wouldn't be needed for cms, but we still need a way to activate for cms tests. monitoring_utils.set_custom_attribute('student_activate_account', 'lms') activation_message_type = None activated_or_confirmed = 'confirmed' if settings.MARKETING_EMAILS_OPT_IN else 'activated' account_or_email = 'email' if settings.MARKETING_EMAILS_OPT_IN else 'account' invalid_message = HTML( _('{html_start}Your {account_or_email} could not be {activated_or_confirmed}{html_end}' 'Something went wrong, please <a href="{support_url}">contact support</a> to resolve this issue.' )).format( account_or_email=account_or_email, activated_or_confirmed=activated_or_confirmed, support_url=configuration_helpers.get_value( 'ACTIVATION_EMAIL_SUPPORT_LINK', settings.ACTIVATION_EMAIL_SUPPORT_LINK) or settings.SUPPORT_SITE_LINK, html_start=HTML('<p class="message-title">'), html_end=HTML('</p>'), ) show_account_activation_popup = None try: registration = Registration.objects.get(activation_key=key) except (Registration.DoesNotExist, Registration.MultipleObjectsReturned): activation_message_type = 'error' messages.error(request, invalid_message, extra_tags='account-activation aa-icon') else: if request.user.is_authenticated and request.user.id != registration.user.id: activation_message_type = 'error' messages.error(request, invalid_message, extra_tags='account-activation aa-icon') elif registration.user.is_active: activation_message_type = 'info' messages.info( request, HTML( _('{html_start}This {account_or_email} has already been {activated_or_confirmed}.{html_end}' )).format( account_or_email=account_or_email, activated_or_confirmed=activated_or_confirmed, html_start=HTML('<p class="message-title">'), html_end=HTML('</p>'), ), extra_tags='account-activation aa-icon', ) else: registration.activate() # Success message for logged in users. message = _( '{html_start}Success{html_end} You have {activated_or_confirmed} your {account_or_email}.' ) tracker.emit( USER_ACCOUNT_ACTIVATED, { "user_id": registration.user.id, "activation_timestamp": registration.activation_timestamp }) if not request.user.is_authenticated: # Success message for logged out users message = _( '{html_start}Success! You have {activated_or_confirmed} your {account_or_email}.{html_end}' 'You will now receive email updates and alerts from us related to' ' the courses you are enrolled in. Sign In to continue.') # Add message for later use. activation_message_type = 'success' messages.success( request, HTML(message).format( account_or_email=account_or_email, activated_or_confirmed=activated_or_confirmed, html_start=HTML('<p class="message-title">'), html_end=HTML('</p>'), ), extra_tags='account-activation aa-icon', ) show_account_activation_popup = request.COOKIES.get( settings.SHOW_ACTIVATE_CTA_POPUP_COOKIE_NAME, None) # If a safe `next` parameter is provided in the request # and it's not the same as the dashboard, redirect there. # The `get_next_url_for_login_page()` function will only return a safe redirect URL. # If the provided `next` URL is not safe, that function will fill `redirect_to` # with a value of `reverse('dashboard')`. redirect_url = None if request.GET.get('next'): redirect_to, root_login_url = get_next_url_for_login_page( request, include_host=True) # Don't automatically redirect authenticated users to the redirect_url # if the `next` value is either: # 1. "/dashboard" or # 2. "https://{LMS_ROOT_URL}/dashboard" (which we might provide as a value from the AuthN MFE) if redirect_to not in (root_login_url + reverse('dashboard'), reverse('dashboard')): redirect_url = get_redirect_url_with_host(root_login_url, redirect_to) if should_redirect_to_authn_microfrontend( ) and not request.user.is_authenticated: params = {'account_activation_status': activation_message_type} if redirect_url: params['next'] = redirect_url url_path = '/login?{}'.format(urllib.parse.urlencode(params)) return redirect(settings.AUTHN_MICROFRONTEND_URL + url_path) response = redirect( redirect_url) if redirect_url and is_enterprise_learner( request.user) else redirect('dashboard') if show_account_activation_popup: response.delete_cookie( settings.SHOW_ACTIVATE_CTA_POPUP_COOKIE_NAME, domain=settings.SESSION_COOKIE_DOMAIN, path='/', ) return response