18F/identity-idp

View on GitHub
app/controllers/users/passwords_controller.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
# frozen_string_literal: true

module Users
  class PasswordsController < ApplicationController
    include ReauthenticationRequiredConcern

    before_action :confirm_two_factor_authenticated
    before_action :capture_password_if_pii_present_but_locked
    before_action :confirm_recently_authenticated_2fa

    def edit
      analytics.edit_password_visit
      @update_user_password_form = UpdateUserPasswordForm.new(current_user)
      @forbidden_passwords = current_user.email_addresses.flat_map do |email_address|
        ForbiddenPasswords.new(email_address.email).call
      end
    end

    def update
      @update_user_password_form = UpdateUserPasswordForm.new(current_user, user_session)

      result = @update_user_password_form.submit(user_params)

      analytics.password_changed(**result.to_h)

      if result.success?
        handle_valid_password
      else
        handle_invalid_password
      end
    end

    private

    def capture_password_if_pii_present_but_locked
      return unless current_user.identity_verified? &&
                    !Pii::Cacher.new(current_user, user_session).exists_in_session?
      user_session[:stored_location] = request.url
      redirect_to capture_password_url
    end

    def user_params
      params.require(:update_user_password_form).permit(:password, :password_confirmation)
    end

    def handle_valid_password
      send_password_reset_risc_event
      create_event_and_notify_user_about_password_change
      # Changing the password hash terminates the warden session, and bypass_sign_in ensures
      # that the user remains authenticated.
      bypass_sign_in current_user

      flash[:info] = t('notices.password_changed')
      if @update_user_password_form.personal_key.present?
        user_session[:personal_key] = @update_user_password_form.personal_key
        redirect_to manage_personal_key_url
      else
        redirect_to account_url
      end
    end

    def send_password_reset_risc_event
      event = PushNotification::PasswordResetEvent.new(user: current_user)
      PushNotification::HttpPush.deliver(event)
    end

    def create_event_and_notify_user_about_password_change
      _event, disavowal_token = create_user_event_with_disavowal(:password_changed)
      UserAlerts::AlertUserAboutPasswordChange.call(current_user, disavowal_token)
    end

    def handle_invalid_password
      # If the form is submitted with a password that's too short (based on
      # our Devise config) but that zxcvbn treats as strong enough, then we
      # need to provide our custom forbidden passwords data that zxcvbn needs,
      # otherwise the JS will throw an exception and the password strength
      # meter will not appear.
      @forbidden_passwords = current_user.email_addresses.flat_map do |email_address|
        ForbiddenPasswords.new(email_address.email).call
      end
      render :edit
    end
  end
end