Example #1
0
def password_reset_logistration(request, **kwargs):
    """Reset learner password using passed token and new credentials"""

    reset_status = False
    uidb36 = kwargs.get('uidb36')
    token = kwargs.get('token')

    has_required_values, uid_int = _check_token_has_required_values(uidb36, token)
    if not has_required_values:
        AUDIT_LOG.exception("Invalid password reset confirm token")
        return JsonResponse({'reset_status': reset_status})

    request.POST = request.POST.copy()
    request.POST['new_password1'] = normalize_password(request.POST['new_password1'])
    request.POST['new_password2'] = normalize_password(request.POST['new_password2'])

    password = request.POST['new_password1']
    try:
        user = User.objects.get(id=uid_int)
        if not default_token_generator.check_token(user, token):
            AUDIT_LOG.exception("Token validation failed")
            return JsonResponse({'reset_status': reset_status})

        validate_password(password, user=user)
        form = SetPasswordForm(user, request.POST)
        if form.is_valid():
            form.save()
            reset_status = True

            if 'is_account_recovery' in request.GET:
                try:
                    old_primary_email = user.email
                    user.email = user.account_recovery.secondary_email
                    user.account_recovery.delete()
                    # emit an event that the user changed their secondary email to the primary email
                    tracker.emit(
                        SETTING_CHANGE_INITIATED,
                        {
                            "setting": "email",
                            "old": old_primary_email,
                            "new": user.email,
                            "user_id": user.id,
                        }
                    )
                    user.save()
                    send_password_reset_success_email(user, request)
                except ObjectDoesNotExist:
                    log.error('Account recovery process initiated without AccountRecovery instance for user {username}'
                              .format(username=user.username))
    except ValidationError as err:
        AUDIT_LOG.exception("Password validation failed")
        error_status = {
            'reset_status': reset_status,
            'err_msg': ' '.join(err.messages)
        }
        return JsonResponse(error_status)
    except Exception:   # pylint: disable=broad-except
        AUDIT_LOG.exception("Setting new password failed")

    return JsonResponse({'reset_status': reset_status})
Example #2
0
def _authenticate_first_party(request, unauthenticated_user,
                              third_party_auth_requested):
    """
    Use Django authentication on the given request, using rate limiting if configured
    """

    # If the user doesn't exist, we want to set the username to an invalid username so that authentication is guaranteed
    # to fail and we can take advantage of the ratelimited backend
    username = unauthenticated_user.username if unauthenticated_user else ""

    # First time when a user login through third_party_auth account then user needs to link
    # third_party account with the platform account by login through email and password that's
    # why we need to by-pass this check when user is already authenticated by third_party_auth.
    if not third_party_auth_requested:
        _check_user_auth_flow(request.site, unauthenticated_user)

    try:
        password = normalize_password(request.POST['password'])
        return authenticate(username=username,
                            password=password,
                            request=request)

    # This occurs when there are too many attempts from the same IP address
    except RateLimitException:
        raise AuthFailedError(
            _('Too many failed login attempts. Try again later.'))
Example #3
0
    def post(self, request, *args, **kwargs):
        # We have to make a copy of request.POST because it is a QueryDict object which is immutable until copied.
        # We have to use request.POST because the password_reset_confirm method takes in the request and a user's
        # password is set to the request.POST['new_password1'] field. We have to also normalize the new_password2
        # field so it passes the equivalence check that new_password1 == new_password2
        # In order to switch out of having to do this copy, we would want to move the normalize_password code into
        # a custom User model's set_password method to ensure it is always happening upon calling set_password.
        request.POST = request.POST.copy()
        request.POST['new_password1'] = normalize_password(
            request.POST['new_password1'])
        request.POST['new_password2'] = normalize_password(
            request.POST['new_password2'])
        is_account_recovery = 'is_account_recovery' in request.GET

        password = request.POST['new_password1']
        response = self._validate_password(password, request)
        if response:
            return response

        response = self._process_password_reset_success(
            request, self.token, self.uidb64, extra_context=self.platform_name)

        # If password reset was unsuccessful a template response is returned (status_code 200).
        # Check if form is invalid then show an error to the user.
        # Note if password reset was successful we get response redirect (status_code 302).
        password_reset_successful = response.status_code == 302
        if not password_reset_successful:
            return self._handle_password_reset_failure(response)

        updated_user = User.objects.get(id=self.uid_int)
        if is_account_recovery:
            self._handle_primary_email_update(updated_user)

        updated_user.save()
        if password_reset_successful and is_account_recovery:
            self._handle_password_creation(request, updated_user)

        # Handles clearing the failed login counter upon password reset.
        if LoginFailures.is_feature_enabled():
            LoginFailures.clear_lockout_counter(updated_user)

        update_session_auth_hash(request, updated_user)
        send_password_reset_success_email(updated_user, request)
        return response
Example #4
0
def authenticate_new_user(request, username, password):
    """
    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.
    """
    password = normalize_password(password)
    backend = load_backend(NEW_USER_AUTH_BACKEND)
    user = backend.authenticate(request=request, username=username, password=password)
    if not user:
        log.warning(f"Unable to authenticate user: {username}")
    user.backend = NEW_USER_AUTH_BACKEND
    return user
Example #5
0
def _authenticate_first_party(request, unauthenticated_user,
                              third_party_auth_requested):
    """
    Use Django authentication on the given request, using rate limiting if configured
    """
    should_be_rate_limited = getattr(request, 'limited', False)
    if should_be_rate_limited:
        raise AuthFailedError(
            _('Too many failed login attempts. Try again later.'))  # lint-amnesty, pylint: disable=raise-missing-from

    # If the user doesn't exist, we want to set the username to an invalid username so that authentication is guaranteed
    # to fail and we can take advantage of the ratelimited backend
    username = unauthenticated_user.username if unauthenticated_user else ""

    # First time when a user login through third_party_auth account then user needs to link
    # third_party account with the platform account by login through email and password that's
    # why we need to by-pass this check when user is already authenticated by third_party_auth.
    if not third_party_auth_requested:
        _check_user_auth_flow(request.site, unauthenticated_user)

    password = normalize_password(request.POST['password'])
    return authenticate(username=username, password=password, request=request)
Example #6
0
def create_user_through_db_models(data):
    """
    It Registers User through database models

    Arguments:
    data (dict) - User account details

    Returns:
    context (dict) - context to be passed to templates having information about success and
        failure of account creation
    """
    context = {}
    try:
        if not User.objects.filter(email=data["email"]).exists():
            user = User(username=data["username"],
                        email=data["email"],
                        is_active=True)
            password = normalize_password(data["password"])
            user.set_password(password)
            user.save()

            profile = UserProfile(user=user)
            profile.name = data.get("name")
            profile.save()

            context[
                "success_message"] = f"{_('A new account has been registered for user')}: {data['username']}"
        else:
            context[
                "error_message"] = f"{_('An account already exists with email')}: {data['email']}"
            return context
    except Exception as err:  # pylint: disable=broad-except
        context[
            "error_message"] = f"{_('Account could not be created due to following error')}: {err}"

    return context
Example #7
0
def do_create_account(form, custom_form=None):
    """
    Given cleaned post variables, create the User and UserProfile objects, as well as the
    registration for this user.

    Returns a tuple (User, UserProfile, Registration).

    Note: this function is also used for creating test users.
    """
    # Check if ALLOW_PUBLIC_ACCOUNT_CREATION flag turned off to restrict user account creation
    if not configuration_helpers.get_value(
            'ALLOW_PUBLIC_ACCOUNT_CREATION',
            settings.FEATURES.get('ALLOW_PUBLIC_ACCOUNT_CREATION', True)):
        raise PermissionDenied()

    errors = {}
    errors.update(form.errors)
    if custom_form:
        errors.update(custom_form.errors)

    if errors:
        raise ValidationError(errors)

    proposed_username = form.cleaned_data["username"]
    user = User(username=proposed_username,
                email=form.cleaned_data["email"],
                is_active=False)
    password = normalize_password(form.cleaned_data["password"])
    user.set_password(password)
    registration = Registration()

    # TODO: Rearrange so that if part of the process fails, the whole process fails.
    # Right now, we can have e.g. no registration e-mail sent out and a zombie account
    try:
        with transaction.atomic():
            user.save()
            if custom_form:
                custom_model = custom_form.save(commit=False)
                custom_model.user = user
                custom_model.save()
    except IntegrityError:
        # Figure out the cause of the integrity error
        # TODO duplicate email is already handled by form.errors above as a ValidationError.
        # The checks for duplicate email/username should occur in the same place with an
        # AccountValidationError and a consistent user message returned (i.e. both should
        # return "It looks like {username} belongs to an existing account. Try again with a
        # different username.")
        if username_exists_or_retired(user.username):
            raise AccountValidationError(
                USERNAME_EXISTS_MSG_FMT.format(username=proposed_username),
                field="username")
        elif email_exists_or_retired(user.email):
            raise AccountValidationError(_(
                "An account with the Email '{email}' already exists.").format(
                    email=user.email),
                                         field="email")
        else:
            raise

    registration.register(user)

    profile_fields = [
        "name", "level_of_education", "gender", "mailing_address", "city",
        "country", "goals", "year_of_birth"
    ]
    profile = UserProfile(
        user=user,
        **{key: form.cleaned_data.get(key)
           for key in profile_fields})
    extended_profile = form.cleaned_extended_profile
    if extended_profile:
        profile.meta = json.dumps(extended_profile)
    try:
        profile.save()
    except Exception:
        log.exception(
            "UserProfile creation failed for user {id}.".format(id=user.id))
        raise

    return user, profile, registration
Example #8
0
    def post(self, request, **kwargs):
        """ Reset learner password using passed token and new credentials """

        reset_status = False
        uidb36 = kwargs.get('uidb36')
        token = kwargs.get('token')

        has_required_values, uid_int = self._check_token_has_required_values(
            uidb36, token)
        if not has_required_values:
            AUDIT_LOG.exception("Invalid password reset confirm token")
            return Response({'reset_status': reset_status})

        request.data._mutable = True  # lint-amnesty, pylint: disable=protected-access
        request.data['new_password1'] = normalize_password(
            request.data['new_password1'])
        request.data['new_password2'] = normalize_password(
            request.data['new_password2'])

        password = request.data['new_password1']
        try:
            user = User.objects.get(id=uid_int)
            if not default_token_generator.check_token(user, token):
                AUDIT_LOG.exception("Token validation failed")
                return Response({'reset_status': reset_status})

            validate_password(password, user=user)

            if settings.ENABLE_AUTHN_RESET_PASSWORD_HIBP_POLICY:
                # Checks the Pwned Databases for password vulnerability.
                pwned_response = check_pwned_password(password)
                if pwned_response.get('vulnerability', 'no') == 'yes':
                    error_status = {
                        'reset_status': reset_status,
                        'err_msg': accounts.AUTHN_PASSWORD_COMPROMISED_MSG
                    }
                    return Response(error_status)

            form = SetPasswordForm(user, request.data)
            if form.is_valid():
                form.save()
                reset_status = True

                if 'is_account_recovery' in request.GET:
                    try:
                        old_primary_email = user.email
                        user.email = user.account_recovery.secondary_email
                        user.account_recovery.delete()
                        # emit an event that the user changed their secondary email to the primary email
                        tracker.emit(
                            SETTING_CHANGE_INITIATED, {
                                "setting": "email",
                                "old": old_primary_email,
                                "new": user.email,
                                "user_id": user.id,
                            })
                        user.save()
                    except ObjectDoesNotExist:
                        err = 'Account recovery process initiated without AccountRecovery instance for user {username}'
                        log.error(err.format(username=user.username))

                # Handles clearing the failed login counter upon password reset.
                if LoginFailures.is_feature_enabled():
                    LoginFailures.clear_lockout_counter(user)

                send_password_reset_success_email(user, request)
                update_session_auth_hash(request, user)
        except ValidationError as err:
            AUDIT_LOG.exception("Password validation failed")
            error_status = {
                'reset_status': reset_status,
                'err_msg': ' '.join(err.messages)
            }
            return Response(error_status)
        except Exception:  # pylint: disable=broad-except
            AUDIT_LOG.exception("Setting new password failed")

        return Response({'reset_status': reset_status})