Browse Source

Expired password reset token flow (#233)

* Update devise to return a 422 on token error

* Update view to display formatted error

* Add unit test
pull/237/head
baarkerlounger 3 years ago committed by GitHub
parent
commit
59f7e387fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      Gemfile.lock
  2. 4
      app/controllers/users_controller.rb
  3. 11
      app/presenters/error_summary_full_messages_presenter.rb
  4. 2
      app/views/devise/passwords/edit.html.erb
  5. 11
      spec/presenters/error_summary_full_messages_presenter_spec.rb
  6. 1
      spec/requests/case_log_controller_spec.rb
  7. 1
      spec/requests/soft_validations_controller_spec.rb
  8. 30
      spec/requests/user_controller_spec.rb

24
Gemfile.lock

@ -8,7 +8,7 @@ GIT
GIT
remote: https://github.com/baarkerlounger/devise.git
revision: 0f585ea6683a06858863597628b48610731c2613
revision: 223969286e51b02e78309b7dad908d223d6ba7f5
branch: dluhc-fixes
specs:
devise (4.8.1)
@ -322,33 +322,33 @@ GEM
rspec-mocks (~> 3.10)
rspec-support (~> 3.10)
rspec-support (3.10.3)
rubocop (1.23.0)
rubocop (1.25.0)
parallel (~> 1.10)
parser (>= 3.0.0.0)
parser (>= 3.1.0.0)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml
rubocop-ast (>= 1.12.0, < 2.0)
rubocop-ast (>= 1.15.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.13.0)
rubocop-ast (1.15.1)
parser (>= 3.0.1.1)
rubocop-govuk (4.2.0)
rubocop (= 1.23.0)
rubocop-ast (= 1.13.0)
rubocop-rails (= 2.12.4)
rubocop-govuk (4.3.0)
rubocop (= 1.25.0)
rubocop-ast (= 1.15.1)
rubocop-rails (= 2.13.2)
rubocop-rake (= 0.6.0)
rubocop-rspec (= 2.6.0)
rubocop-rspec (= 2.7.0)
rubocop-performance (1.13.2)
rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0)
rubocop-rails (2.12.4)
rubocop-rails (2.13.2)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.7.0, < 2.0)
rubocop-rake (0.6.0)
rubocop (~> 1.0)
rubocop-rspec (2.6.0)
rubocop-rspec (2.7.0)
rubocop (~> 1.19)
ruby-progressbar (1.11.0)
ruby2_keywords (0.0.5)

4
app/controllers/users_controller.rb

@ -12,7 +12,7 @@ class UsersController < ApplicationController
redirect_to user_path(@user)
elsif user_params.key?("password")
format_error_messages
render :edit_password, status: :unprocessable_entity
render "devise/passwords/edit", status: :unprocessable_entity
else
format_error_messages
render :edit, status: :unprocessable_entity
@ -40,7 +40,7 @@ class UsersController < ApplicationController
end
def edit_password
render :edit_password
render "devise/passwords/edit"
end
private

11
app/presenters/error_summary_full_messages_presenter.rb

@ -0,0 +1,11 @@
class ErrorSummaryFullMessagesPresenter
def initialize(error_messages)
@error_messages = error_messages
end
def formatted_error_messages
@error_messages.map do |attribute, messages|
[attribute, [attribute.to_s.humanize, messages.first].join(" ")]
end
end
end

2
app/views/users/edit_password.html.erb → app/views/devise/passwords/edit.html.erb

@ -10,7 +10,7 @@
<%= form_for(@user, as: :user, html: { method: :patch }) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= f.govuk_error_summary %>
<%= f.govuk_error_summary(presenter: ErrorSummaryFullMessagesPresenter) %>
<h1 class="govuk-heading-l">
<%= content_for(:title) %>

11
spec/presenters/error_summary_full_messages_presenter_spec.rb

@ -0,0 +1,11 @@
require "rails_helper"
RSpec.describe ErrorSummaryFullMessagesPresenter do
let(:error_messages) { { reset_password_token: %w[expired] } }
let(:formatted_error_messages) { [[:reset_password_token, "Reset password token expired"]] }
subject { described_class.new(error_messages) }
it "formats messages to include the attribute name" do
expect(subject.formatted_error_messages).to eq(formatted_error_messages)
end
end

1
spec/requests/case_log_controller_spec.rb

@ -1,5 +1,4 @@
require "rails_helper"
require_relative "../request_helper"
RSpec.describe CaseLogsController, type: :request do
let(:owning_organisation) { FactoryBot.create(:organisation) }

1
spec/requests/soft_validations_controller_spec.rb

@ -1,5 +1,4 @@
require "rails_helper"
require_relative "../request_helper"
RSpec.describe SoftValidationsController, type: :request do
let(:params) { { case_log_id: case_log.id } }

30
spec/requests/user_controller_spec.rb

@ -1,7 +1,6 @@
require "rails_helper"
require_relative "../support/devise"
RSpec.describe UsersController, type: :request do
RSpec.describe "password_reset", type: :request do
let(:user) { FactoryBot.create(:user) }
let(:unauthorised_user) { FactoryBot.create(:user) }
let(:headers) { { "Accept" => "text/html" } }
@ -46,6 +45,7 @@ RSpec.describe UsersController, type: :request do
end
context "update password" do
context "valid reset token" do
let(:params) do
{
id: user.id, user: { password: new_value, password_confirmation: "something_else" }
@ -63,6 +63,32 @@ RSpec.describe UsersController, type: :request do
expect(page).to have_content("Password confirmation doesn't match Password")
end
end
context "reset token more than 3 hours old" do
let(:raw) { user.send_reset_password_instructions }
let(:params) do
{
id: user.id,
user: {
password: new_value,
password_confirmation: new_value,
reset_password_token: raw,
},
}
end
before do
allow_any_instance_of(User).to receive(:reset_password_sent_at).and_return(4.hours.ago)
put "/users/password", headers: headers, params: params
end
it "shows an error" do
expect(response).to have_http_status(:unprocessable_entity)
expect(page).to have_selector("#error-summary-title")
expect(page).to have_content("Reset password token has expired, please request a new one")
end
end
end
end
end

Loading…
Cancel
Save