Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configure Devise's password recovery feature #235

Merged
merged 1 commit into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ gem 'jbuilder'
gem 'jsbundling-rails'
gem 'pg', '< 1.5'
gem 'puma'
gem 'mail-notify'
gem 'notifications-ruby-client'
gem 'sprockets-rails'
gem 'stimulus-rails'
Expand Down
8 changes: 8 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,13 @@ GEM
net-imap
net-pop
net-smtp
mail-notify (1.2.0)
actionmailer (>= 5.2.4.6)
actionpack (>= 5.2.7.1)
actionview (>= 5.2.7.1)
activesupport (>= 5.2.4.6)
notifications-ruby-client (~> 5.1)
rack (>= 2.1.4.1)
marcel (1.0.4)
matrix (0.4.2)
method_source (1.1.0)
Expand Down Expand Up @@ -389,6 +396,7 @@ DEPENDENCIES
jbuilder
jsbundling-rails
listen
mail-notify
notifications-ruby-client
pg (< 1.5)
pry
Expand Down
4 changes: 4 additions & 0 deletions app/controllers/admin/passwords_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Admin
class PasswordsController < Devise::PasswordsController
end
end
14 changes: 14 additions & 0 deletions app/mailers/admin/mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module Admin
class Mailer < Devise::Mailer
def devise_mail(record, action, opts = {})
initialize_from_record(record)
view_mail(template_id, headers_for(action, opts))
end

private

def template_id
ENV.fetch("NOTIFY_DEVISE_TEMPLATE_ID")
end
end
end
4 changes: 2 additions & 2 deletions app/mailers/application_mailer.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class ApplicationMailer < ActionMailer::Base
default from: '[email protected]'
class ApplicationMailer < Mail::Notify::Mailer
default from: '[email protected]'
layout 'mailer'
end
6 changes: 5 additions & 1 deletion app/models/user.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
class User < ApplicationRecord
devise :database_authenticatable, :rememberable, :validatable
devise :database_authenticatable, :rememberable, :validatable, :recoverable

def first_name
name.to_s.split(/\s+/).first
end
end
3 changes: 3 additions & 0 deletions app/views/admin/mailer/password_change.text.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Hello <%= @resource.first_name %>,

We're contacting you to notify you that your password has been changed.
9 changes: 9 additions & 0 deletions app/views/admin/mailer/reset_password_instructions.text.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Hello <%= @resource.first_name %>,

Someone has requested a link to change your password. You can do this through the link below.

[Change my password](<%= edit_password_url(@resource, reset_password_token: @token) %>)

If you didn't request this, please ignore this email.

Your password won't change until you access the link above and create a new one.
31 changes: 31 additions & 0 deletions app/views/admin/passwords/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<%= form_for(resource, as: resource_name, url: password_path(resource_name), method: :put) do |f| %>
<%= error_summary resource %>

<%= f.hidden_field :reset_password_token %>

<div class="govuk-form-group">
<fieldset class="govuk-fieldset">
<legend class="govuk-fieldset__legend govuk-fieldset__legend--l">
<h1 class="govuk-fieldset__heading">Change your password</h1>
</legend>

<div class="govuk-form-group">
<%= f.label :password, "New password", class: "govuk-label" %>
<%= f.password_field :password, class: "govuk-input govuk-input--width-30", autofocus: true, autocomplete: "new-password" %>
</div>

<div class="govuk-form-group">
<%= f.label :password_confirmation, "Confirm new password", class: "govuk-label" %>
<%= f.password_field :password_confirmation, class: "govuk-input govuk-input--width-30", autofocus: true, autocomplete: "new-password" %>
</div>

<div class="actions">
<%= f.submit "Change my password", class: "govuk-button", data: { module: "govuk-button" } %>
</div>

<p class="govuk-body">
<%= link_to "Sign in", new_session_path(resource_name), class: "govuk-link" %>
</p>
</fieldset>
</div>
<% end %>
39 changes: 39 additions & 0 deletions app/views/admin/passwords/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<% if flash[:alert] %>
<div class="govuk-error-summary" aria-labelledby="error-summary-title" role="alert" tabindex="-1" data-module="govuk-error-summary">
<h2 class="govuk-error-summary__title" id="error-summary-title">
There was a problem recovering your password
</h2>
<div class="govuk-error-summary__body">
<ul class="govuk-list govuk-error-summary__list">
<li>
<a href="#admin_email"><%= alert %></a>
</li>
</ul>
</div>
</div>
<% end %>

<%= form_for(resource, as: resource_name, url: password_path(resource_name)) do |f| %>
<%= error_summary resource %>

<div class="govuk-form-group">
<fieldset class="govuk-fieldset">
<legend class="govuk-fieldset__legend govuk-fieldset__legend--l">
<h1 class="govuk-fieldset__heading">Forgot your password?</h1>
</legend>

<div class="govuk-form-group">
<%= f.label :email, "Email address", class: "govuk-label" %>
<%= f.text_field :email, class: "govuk-input govuk-input--width-30", spellcheck: false, autofocus: true, inputmode: "email", autocomplete: "email" %>
</div>

<div class="actions">
<%= f.submit "Send me password reset instructions", class: "govuk-button", data: { module: "govuk-button" } %>
</div>

<p class="govuk-body">
<%= link_to "Sign in", new_session_path(resource_name), class: "govuk-link" %>
</p>
</fieldset>
</div>
<% end %>
5 changes: 4 additions & 1 deletion app/views/admin/sessions/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
</legend>
<div class="govuk-form-group">
<%= f.label :email, "Email address", class: "govuk-label" %>
<%= f.email_field :email, class: "govuk-input govuk-input--width-30", spellcheck: false, autofocus: true, autocomplete: "email" %>
<%= f.text_field :email, class: "govuk-input govuk-input--width-30", spellcheck: false, autofocus: true, inputmode: "email", autocomplete: "email" %>
</div>
<div class="govuk-form-group">
<%= f.label :password, class: "govuk-label" %>
Expand All @@ -30,6 +30,9 @@
<div class="actions">
<%= f.submit "Sign in", class: "govuk-button", data: { module: "govuk-button" } %>
</div>
<p class="govuk-body">
<%= link_to "Forgot your password?", new_password_path(resource_name), class: "govuk-link" %>
</p>
</fieldset>
</div>
<% end %>
2 changes: 2 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class Application < Rails::Application

config.time_zone = "Europe/London"

config.action_mailer.delivery_method = :notify
config.action_mailer.notify_settings = { api_key: ENV.fetch("NOTIFY_API_KEY") }
config.action_view.form_with_generates_remote_forms = false
config.action_view.field_error_proc = ->(html_tag, instance) { html_tag }
config.action_view.prefix_partial_path_with_controller_namespace = false
Expand Down
3 changes: 3 additions & 0 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,7 @@
# Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem.
config.file_watcher = ActiveSupport::EventedFileUpdateChecker

# Allow web-console connections into docker
config.web_console.permissions = %w[127.0.0.1 ::1 172.16.0.0/12]
end
12 changes: 6 additions & 6 deletions config/initializers/devise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@
# Configure the e-mail address which will be shown in Devise::Mailer,
# note that it will be overwritten if you use your own mailer class
# with default "from" parameter.
config.mailer_sender = '[email protected]'
config.mailer_sender = '[email protected]'

# Configure the class responsible to send e-mails.
# config.mailer = 'Devise::Mailer'
config.mailer = 'Admin::Mailer'

# Configure the parent class responsible to send e-mails.
# config.parent_mailer = 'ActionMailer::Base'
config.parent_mailer = 'ApplicationMailer'

# ==> ORM configuration
# Load and configure the ORM. Supports :active_record (default) and
Expand Down Expand Up @@ -90,7 +90,7 @@
# It will change confirmation, password recovery and other workflows
# to behave the same regardless if the e-mail provided was right or wrong.
# Does not affect registerable.
# config.paranoid = true
config.paranoid = true

# By default Devise will store the user in session. You can skip storage for
# particular strategies by setting this option.
Expand Down Expand Up @@ -132,7 +132,7 @@
# config.send_email_changed_notification = false

# Send a notification email when the user's password is changed.
# config.send_password_change_notification = false
config.send_password_change_notification = true

# ==> Configuration for :confirmable
# A period that the user is allowed to access the website even without
Expand Down Expand Up @@ -228,7 +228,7 @@

# When set to false, does not sign a user in automatically after their password is
# reset. Defaults to true, so a user is signed in automatically after a reset.
# config.sign_in_after_reset_password = true
config.sign_in_after_reset_password = false

# ==> Configuration for :encryptable
# Allow you to use another hashing or encryption algorithm besides bcrypt (default).
Expand Down
2 changes: 1 addition & 1 deletion config/locales/devise.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ en:
last_attempt: "You have one more attempt before your account is locked."
not_found_in_database: "Invalid %{authentication_keys} or password."
timeout: "Your session expired. Please sign in again to continue."
unauthenticated: "You need to sign in or sign up before continuing."
unauthenticated: "You need to sign in before continuing."
unconfirmed: "You have to confirm your email address before continuing."
mailer:
confirmation_instructions:
Expand Down
12 changes: 12 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -459,3 +459,15 @@ en:
attributes:
uprn:
blank: "Please enter the UPRN of the building"

user:
attributes:
email:
blank: "Please enter your email address"
not_found: "Sorry, we couldn't find that email address"
password:
blank: "Please enter a new password"
password_confirmation:
confirmation: "The password confirmation does not match the password"
reset_password_token:
blank: "Missing password reset token"
2 changes: 1 addition & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
delete "/survey", action: "destroy", as: nil
end

devise_for :admin, only: :sessions, class_name: "User", module: "admin"
devise_for :admin, only: %i[sessions passwords], class_name: "User", module: "admin"

namespace :admin do
root to: "buildings#index"
Expand Down