Ejemplo n.º 1
0
def _validate_password(password,
                       username=None,
                       email=None,
                       reset_password_page=False):
    """Validate the format of the user's password.

    Passwords cannot be the same as the username of the account,
    so we create a temp_user using the username and email to test the password against.
    This user is never saved.

    Arguments:
        password (unicode): The proposed password.
        username (unicode): The username associated with the user's account.
        email (unicode): The email associated with the user's account.
        reset_password_page (bool): The flag that determines the validation page.

    Returns:
        None

    Raises:
        errors.AccountPasswordInvalid

    """
    try:
        _validate_type(password, str, accounts.PASSWORD_BAD_TYPE_MSG)
        temp_user = User(username=username, email=email) if username else None
        validate_password(password, user=temp_user)
    except errors.AccountDataBadType as invalid_password_err:
        raise errors.AccountPasswordInvalid(str(invalid_password_err))
    except ValidationError as validation_err:
        raise errors.AccountPasswordInvalid(' '.join(validation_err.messages))

    if ((settings.ENABLE_AUTHN_RESET_PASSWORD_HIBP_POLICY
         and reset_password_page)
            or (settings.ENABLE_AUTHN_REGISTER_HIBP_POLICY
                and not reset_password_page)):
        pwned_response = check_pwned_password(password)
        if pwned_response.get('vulnerability', 'no') == 'yes':
            if (reset_password_page or pwned_response.get('frequency', 0) >=
                    settings.HIBP_REGISTRATION_PASSWORD_FREQUENCY_THRESHOLD):
                raise errors.AccountPasswordInvalid(
                    accounts.AUTHN_PASSWORD_COMPROMISED_MSG)
Ejemplo n.º 2
0
def check_pwned_password_and_send_track_event(user_id,
                                              password,
                                              internal_user=False,
                                              is_new_user=False):
    """
    Check the Pwned Databases and send its event to Segment.
    """
    try:
        pwned_properties = check_pwned_password(password)
        if pwned_properties:
            pwned_properties['internal_user'] = internal_user
            pwned_properties['new_user'] = is_new_user
            segment.track(user_id, 'edx.bi.user.pwned.password.status',
                          pwned_properties)
    except Exception:  # pylint: disable=W0703
        log.exception(
            'Unable to get response from pwned password api for user_id: "%s"',
            user_id,
        )
        return None  # lint-amnesty, pylint: disable=raise-missing-from
Ejemplo n.º 3
0
def _validate_password(password,
                       username=None,
                       email=None,
                       reset_password_page=False):
    """Validate the format of the user's password.

    Passwords cannot be the same as the username of the account,
    so we create a temp_user using the username and email to test the password against.
    This user is never saved.

    Arguments:
        password (unicode): The proposed password.
        username (unicode): The username associated with the user's account.
        email (unicode): The email associated with the user's account.
        reset_password_page (bool): The flag that determines the validation page.

    Returns:
        None

    Raises:
        errors.AccountPasswordInvalid

    """
    try:
        _validate_type(password, str, accounts.PASSWORD_BAD_TYPE_MSG)
        temp_user = User(username=username, email=email) if username else None
        validate_password(password, user=temp_user)
    except errors.AccountDataBadType as invalid_password_err:
        raise errors.AccountPasswordInvalid(str(invalid_password_err))
    except ValidationError as validation_err:
        raise errors.AccountPasswordInvalid(' '.join(validation_err.messages))

    # TODO: VAN-666 - Restrict this feature to reset password page for now until it is
    #  enabled on account sign in and register.
    if settings.ENABLE_AUTHN_RESET_PASSWORD_HIBP_POLICY and reset_password_page:
        pwned_response = check_pwned_password(password)
        if pwned_response.get('vulnerability', 'no') == 'yes':
            raise errors.AccountPasswordInvalid(
                accounts.AUTHN_PASSWORD_COMPROMISED_MSG)
Ejemplo n.º 4
0
    def clean_password(self):
        """Enforce password policies (if applicable)"""
        password = self.cleaned_data["password"]
        if not self.do_third_party_auth:
            # Creating a temporary user object to test password against username
            # This user should NOT be saved
            username = self.cleaned_data.get('username')
            email = self.cleaned_data.get('email')
            temp_user = User(username=username,
                             email=email) if username else None
            validate_password(password, temp_user)

            if settings.ENABLE_AUTHN_REGISTER_HIBP_POLICY:
                # Checks the Pwned Databases for password vulnerability.
                pwned_response = check_pwned_password(password)

                if (pwned_response.get('vulnerability', 'no') == 'yes'
                        and pwned_response.get('frequency', 0) >=
                        settings.HIBP_REGISTRATION_PASSWORD_FREQUENCY_THRESHOLD
                    ):
                    raise ValidationError(
                        accounts.AUTHN_PASSWORD_COMPROMISED_MSG)
        return password
Ejemplo n.º 5
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})