Пример #1
0
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)
Пример #2
0
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)
Пример #3
0
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)
Пример #4
0
    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)
Пример #5
0
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)))
Пример #6
0
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.'))
Пример #7
0
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')
Пример #8
0
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
Пример #9
0
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
Пример #10
0
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')
Пример #11
0
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.
Пример #12
0
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