Resourceful routing for passwords

This also matches the routes used by devise
This commit is contained in:
Andy Allan 2023-12-06 18:41:00 +00:00
parent 1d0f588d62
commit 4bca24a7be
7 changed files with 60 additions and 35 deletions

View file

@ -19,7 +19,7 @@ class Ability
can [:confirm, :confirm_resend, :confirm_email], :confirmation can [:confirm, :confirm_resend, :confirm_email], :confirmation
can [:index, :rss, :show, :comments], DiaryEntry can [:index, :rss, :show, :comments], DiaryEntry
can [:index], Note can [:index], Note
can [:lost_password, :reset_password], :password can [:new, :create, :edit, :update], :password
can [:index, :show], Redaction can [:index, :show], Redaction
can [:new, :create, :destroy], :session can [:new, :create, :destroy], :session
can [:index, :show, :data, :georss, :picture, :icon], Trace can [:index, :show, :data, :georss, :picture, :icon], Trace

View file

@ -9,12 +9,30 @@ class PasswordsController < ApplicationController
authorize_resource :class => false authorize_resource :class => false
before_action :check_database_writable, :only => [:lost_password, :reset_password] before_action :check_database_writable
def lost_password def new
@title = t ".title"
end
def edit
@title = t ".title" @title = t ".title"
if request.post? if params[:token]
token = UserToken.find_by(:token => params[:token])
if token
self.current_user = token.user
else
flash[:error] = t ".flash token bad"
redirect_to :action => "new"
end
else
head :bad_request
end
end
def create
user = User.visible.find_by(:email => params[:email]) user = User.visible.find_by(:email => params[:email])
if user.nil? if user.nil?
@ -30,13 +48,11 @@ class PasswordsController < ApplicationController
redirect_to login_path redirect_to login_path
else else
flash.now[:error] = t ".notice email cannot find" flash.now[:error] = t ".notice email cannot find"
end render :new
end end
end end
def reset_password def update
@title = t ".title"
if params[:token] if params[:token]
token = UserToken.find_by(:token => params[:token]) token = UserToken.find_by(:token => params[:token])
@ -54,11 +70,13 @@ class PasswordsController < ApplicationController
session[:fingerprint] = current_user.fingerprint session[:fingerprint] = current_user.fingerprint
flash[:notice] = t ".flash changed" flash[:notice] = t ".flash changed"
successful_login(current_user) successful_login(current_user)
else
render :edit
end end
end end
else else
flash[:error] = t ".flash token bad" flash[:error] = t ".flash token bad"
redirect_to :action => "lost_password" redirect_to :action => "new"
end end
else else
head :bad_request head :bad_request

View file

@ -2,7 +2,7 @@
<h1><%= t ".heading", :user => current_user.display_name %></h1> <h1><%= t ".heading", :user => current_user.display_name %></h1>
<% end %> <% end %>
<%= bootstrap_form_for current_user, :url => { :action => "reset_password" }, :html => { :method => :post } do |f| %> <%= bootstrap_form_for current_user, :url => { :action => "update" }, :html => { :method => :post } do |f| %>
<%= f.hidden_field :token, :name => "token", :value => params[:token] %> <%= f.hidden_field :token, :name => "token", :value => params[:token] %>
<%= f.password_field :pass_crypt, :value => "" %> <%= f.password_field :pass_crypt, :value => "" %>
<%= f.password_field :pass_crypt_confirmation, :value => "" %> <%= f.password_field :pass_crypt_confirmation, :value => "" %>

View file

@ -1732,18 +1732,21 @@ en:
destroy: destroy:
destroyed: "Message deleted" destroyed: "Message deleted"
passwords: passwords:
lost_password: new:
title: "Lost password" title: "Lost password"
heading: "Forgotten Password?" heading: "Forgotten Password?"
email address: "Email Address:" email address: "Email Address:"
new password button: "Reset password" new password button: "Reset password"
help_text: "Enter the email address you used to sign up, we will send a link to it that you can use to reset your password." help_text: "Enter the email address you used to sign up, we will send a link to it that you can use to reset your password."
create:
notice email on way: "Sorry you lost it :-( but an email is on its way so you can reset it soon." notice email on way: "Sorry you lost it :-( but an email is on its way so you can reset it soon."
notice email cannot find: "Could not find that email address, sorry." notice email cannot find: "Could not find that email address, sorry."
reset_password: edit:
title: "Reset password" title: "Reset password"
heading: "Reset Password for %{user}" heading: "Reset Password for %{user}"
reset: "Reset Password" reset: "Reset Password"
flash token bad: "Did not find that token, check the URL maybe?"
update:
flash changed: "Your password has been changed." flash changed: "Your password has been changed."
flash token bad: "Did not find that token, check the URL maybe?" flash token bad: "Did not find that token, check the URL maybe?"
preferences: preferences:

View file

@ -172,8 +172,12 @@ OpenStreetMap::Application.routes.draw do
match "/user/confirm" => "confirmations#confirm", :via => [:get, :post] match "/user/confirm" => "confirmations#confirm", :via => [:get, :post]
match "/user/confirm-email" => "confirmations#confirm_email", :via => [:get, :post] match "/user/confirm-email" => "confirmations#confirm_email", :via => [:get, :post]
post "/user/go_public" => "users#go_public" post "/user/go_public" => "users#go_public"
match "/user/reset-password" => "passwords#reset_password", :via => [:get, :post], :as => :user_reset_password scope :user, :as => "user" do
match "/user/forgot-password" => "passwords#lost_password", :via => [:get, :post], :as => :user_forgot_password get "forgot-password" => "passwords#new"
post "forgot-password" => "passwords#create"
get "reset-password" => "passwords#edit"
post "reset-password" => "passwords#update"
end
get "/user/suspended" => "users#suspended" get "/user/suspended" => "users#suspended"
get "/index.html", :to => redirect(:path => "/") get "/index.html", :to => redirect(:path => "/")

View file

@ -6,19 +6,19 @@ class PasswordsControllerTest < ActionDispatch::IntegrationTest
def test_routes def test_routes
assert_routing( assert_routing(
{ :path => "/user/forgot-password", :method => :get }, { :path => "/user/forgot-password", :method => :get },
{ :controller => "passwords", :action => "lost_password" } { :controller => "passwords", :action => "new" }
) )
assert_routing( assert_routing(
{ :path => "/user/forgot-password", :method => :post }, { :path => "/user/forgot-password", :method => :post },
{ :controller => "passwords", :action => "lost_password" } { :controller => "passwords", :action => "create" }
) )
assert_routing( assert_routing(
{ :path => "/user/reset-password", :method => :get }, { :path => "/user/reset-password", :method => :get },
{ :controller => "passwords", :action => "reset_password" } { :controller => "passwords", :action => "edit" }
) )
assert_routing( assert_routing(
{ :path => "/user/reset-password", :method => :post }, { :path => "/user/reset-password", :method => :post },
{ :controller => "passwords", :action => "reset_password" } { :controller => "passwords", :action => "update" }
) )
end end
@ -26,7 +26,7 @@ class PasswordsControllerTest < ActionDispatch::IntegrationTest
# Test fetching the lost password page # Test fetching the lost password page
get user_forgot_password_path get user_forgot_password_path
assert_response :success assert_response :success
assert_template :lost_password assert_template :new
assert_select "div#notice", false assert_select "div#notice", false
# Test resetting using the address as recorded for a user that has an # Test resetting using the address as recorded for a user that has an
@ -41,7 +41,7 @@ class PasswordsControllerTest < ActionDispatch::IntegrationTest
end end
end end
assert_response :success assert_response :success
assert_template :lost_password assert_template :new
# Resetting with POST should work # Resetting with POST should work
assert_difference "ActionMailer::Base.deliveries.size", 1 do assert_difference "ActionMailer::Base.deliveries.size", 1 do
@ -80,7 +80,7 @@ class PasswordsControllerTest < ActionDispatch::IntegrationTest
end end
end end
assert_response :success assert_response :success
assert_template :lost_password assert_template :new
assert_select ".alert.alert-danger", /^Could not find that email address/ assert_select ".alert.alert-danger", /^Could not find that email address/
# Test resetting using the address as recorded for a user that has an # Test resetting using the address as recorded for a user that has an
@ -124,7 +124,7 @@ class PasswordsControllerTest < ActionDispatch::IntegrationTest
# Test a request with a bogus token # Test a request with a bogus token
get user_reset_password_path, :params => { :token => "made_up_token" } get user_reset_password_path, :params => { :token => "made_up_token" }
assert_response :redirect assert_response :redirect
assert_redirected_to :action => :lost_password assert_redirected_to :action => :new
# Create a valid token for a user # Create a valid token for a user
token = user.tokens.create token = user.tokens.create
@ -132,12 +132,12 @@ class PasswordsControllerTest < ActionDispatch::IntegrationTest
# Test a request with a valid token # Test a request with a valid token
get user_reset_password_path, :params => { :token => token.token } get user_reset_password_path, :params => { :token => token.token }
assert_response :success assert_response :success
assert_template :reset_password assert_template :edit
# Test that errors are reported for erroneous submissions # Test that errors are reported for erroneous submissions
post user_reset_password_path, :params => { :token => token.token, :user => { :pass_crypt => "new_password", :pass_crypt_confirmation => "different_password" } } post user_reset_password_path, :params => { :token => token.token, :user => { :pass_crypt => "new_password", :pass_crypt_confirmation => "different_password" } }
assert_response :success assert_response :success
assert_template :reset_password assert_template :edit
assert_select "div.invalid-feedback" assert_select "div.invalid-feedback"
# Test setting a new password # Test setting a new password