Browse Source

Use Notify rather than Gmail for devise emails

pull/241/head
baarkerlounger 3 years ago
parent
commit
d6fbbf4af9
  1. 2
      Gemfile
  2. 16
      Gemfile.lock
  3. 49
      app/mailers/devise_notify_mailer.rb
  4. 3
      config/initializers/devise.rb
  5. 21
      spec/features/organisation_spec.rb
  6. 29
      spec/features/user_spec.rb
  7. 2
      spec/request_helper.rb
  8. 19
      spec/requests/auth/passwords_controller_spec.rb
  9. 8
      spec/requests/users_controller_spec.rb

2
Gemfile

@ -19,6 +19,8 @@ gem "bootsnap", ">= 1.4.4", require: false
gem "govuk-components" gem "govuk-components"
# GOV UK component form builder DSL # GOV UK component form builder DSL
gem "govuk_design_system_formbuilder" gem "govuk_design_system_formbuilder"
# GOV UK Notify
gem "notifications-ruby-client"
# Turbo and Stimulus # Turbo and Stimulus
gem "hotwire-rails" gem "hotwire-rails"
# Soft delete ActiveRecords objects # Soft delete ActiveRecords objects

16
Gemfile.lock

@ -1,6 +1,6 @@
GIT GIT
remote: https://github.com/activeadmin/arbre.git remote: https://github.com/activeadmin/arbre.git
revision: e4970e683c81c99432b73f0ceb5dff7d3fe50413 revision: 305c221a2131be8dd0d96955f9b0a328dfa4beba
specs: specs:
arbre (1.4.0) arbre (1.4.0)
activesupport (>= 3.0.0, < 7.1) activesupport (>= 3.0.0, < 7.1)
@ -20,7 +20,7 @@ GIT
GIT GIT
remote: https://github.com/tagliala/activeadmin.git remote: https://github.com/tagliala/activeadmin.git
revision: 8ccc35b8144482284c90b0af74a01e940765f7a6 revision: f4fc57251399c0ed452d72ac4b07d0bdd3d047c6
branch: feature/railties-7 branch: feature/railties-7
specs: specs:
activeadmin (2.9.0) activeadmin (2.9.0)
@ -106,7 +106,7 @@ GEM
ast (2.4.2) ast (2.4.2)
bcrypt (3.1.16) bcrypt (3.1.16)
bindex (0.8.1) bindex (0.8.1)
bootsnap (1.10.1) bootsnap (1.10.2)
msgpack (~> 1.2) msgpack (~> 1.2)
builder (3.2.4) builder (3.2.4)
byebug (11.1.3) byebug (11.1.3)
@ -158,7 +158,7 @@ GEM
activemodel (>= 6.1) activemodel (>= 6.1)
railties (>= 6.1) railties (>= 6.1)
view_component (~> 2.47.0) view_component (~> 2.47.0)
govuk_design_system_formbuilder (3.0.0) govuk_design_system_formbuilder (3.0.1)
actionview (>= 6.1) actionview (>= 6.1)
activemodel (>= 6.1) activemodel (>= 6.1)
activesupport (>= 6.1) activesupport (>= 6.1)
@ -186,6 +186,7 @@ GEM
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
json-schema (2.8.1) json-schema (2.8.1)
addressable (>= 2.4) addressable (>= 2.4)
jwt (2.3.0)
kaminari (1.2.2) kaminari (1.2.2)
activesupport (>= 4.1.0) activesupport (>= 4.1.0)
kaminari-actionview (= 1.2.2) kaminari-actionview (= 1.2.2)
@ -211,7 +212,7 @@ GEM
method_source (1.0.0) method_source (1.0.0)
mini_mime (1.1.2) mini_mime (1.1.2)
minitest (5.15.0) minitest (5.15.0)
msgpack (1.4.3) msgpack (1.4.4)
net-imap (0.2.3) net-imap (0.2.3)
digest digest
net-protocol net-protocol
@ -234,6 +235,8 @@ GEM
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.13.1-x86_64-linux) nokogiri (1.13.1-x86_64-linux)
racc (~> 1.4) racc (~> 1.4)
notifications-ruby-client (5.3.0)
jwt (>= 1.5, < 3)
orm_adapter (0.5.0) orm_adapter (0.5.0)
overcommit (0.58.0) overcommit (0.58.0)
childprocess (>= 0.6.3, < 5) childprocess (>= 0.6.3, < 5)
@ -242,7 +245,7 @@ GEM
parallel (1.21.0) parallel (1.21.0)
parser (3.1.0.0) parser (3.1.0.0)
ast (~> 2.4.1) ast (~> 2.4.1)
pg (1.2.3) pg (1.3.0)
postcodes_io (0.4.0) postcodes_io (0.4.0)
excon (~> 0.39) excon (~> 0.39)
pry (0.13.1) pry (0.13.1)
@ -435,6 +438,7 @@ DEPENDENCIES
hotwire-rails hotwire-rails
json-schema json-schema
listen (~> 3.3) listen (~> 3.3)
notifications-ruby-client
overcommit (>= 0.37.0) overcommit (>= 0.37.0)
pg (~> 1.1) pg (~> 1.1)
postcodes_io postcodes_io

49
app/mailers/devise_notify_mailer.rb

@ -0,0 +1,49 @@
class DeviseNotifyMailer < Devise::Mailer
require "notifications/client"
RESET_PASSWORD_TEMPLATE_ID = "4593417c-500f-452c-8111-0f9d311aad0e".freeze
SET_PASSWORD_TEMPLATE_ID = "00cd7163-4213-4596-b4f9-9e72796e0d76".freeze
def notify_client
@notify_client ||= ::Notifications::Client.new(ENV["GOVUK_NOTIFY_API_KEY"])
end
def host
@host ||= ENV["APP_HOST"]
end
def send_email(email, template_id, personalisation)
notify_client.send_email(
email_address: email,
template_id: template_id,
personalisation: personalisation,
)
end
def reset_password_instructions(record, token, _opts = {})
template_id = record.last_sign_in_at ? RESET_PASSWORD_TEMPLATE_ID : SET_PASSWORD_TEMPLATE_ID
personalisation = {
name: record.name,
email: record.email,
organisation: record.organisation.name,
link: "https://#{host}/users/password/edit?reset_password_token=#{token}",
}
send_email(record.email, template_id, personalisation)
end
# def confirmation_instructions(record, token, _opts = {})
# super
# end
#
# def unlock_instructions(record, token, opts = {})
# super
# end
#
# def email_changed(record, opts = {})
# super
# end
#
# def password_change(record, opts = {})
# super
# end
end

3
config/initializers/devise.rb

@ -24,10 +24,11 @@ Devise.setup do |config|
# Configure the e-mail address which will be shown in Devise::Mailer, # 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 # note that it will be overwritten if you use your own mailer class
# with default "from" parameter. # with default "from" parameter.
config.mailer_sender = ENV["CORE_EMAIL_USERNAME"] # config.mailer_sender = ENV["CORE_EMAIL_USERNAME"]
# Configure the class responsible to send e-mails. # Configure the class responsible to send e-mails.
# config.mailer = 'Devise::Mailer' # config.mailer = 'Devise::Mailer'
config.mailer = "DeviseNotifyMailer"
# Configure the parent class responsible to send e-mails. # Configure the parent class responsible to send e-mails.
# config.parent_mailer = 'ActionMailer::Base' # config.parent_mailer = 'ActionMailer::Base'

21
spec/features/organisation_spec.rb

@ -5,8 +5,15 @@ RSpec.describe "User Features" do
include Helpers include Helpers
let(:organisation) { user.organisation } let(:organisation) { user.organisation }
let(:org_id) { organisation.id } let(:org_id) { organisation.id }
let(:set_password_template_id) { DeviseNotifyMailer::SET_PASSWORD_TEMPLATE_ID }
let(:notify_client) { double(Notifications::Client) }
let(:reset_password_token) { "MCDH5y6Km-U7CFPgAMVS" }
before do before do
allow_any_instance_of(DeviseNotifyMailer).to receive(:notify_client).and_return(notify_client)
allow_any_instance_of(DeviseNotifyMailer).to receive(:host).and_return("test.com")
allow_any_instance_of(User).to receive(:set_reset_password_token).and_return(reset_password_token)
allow(notify_client).to receive(:send_email).and_return(true)
sign_in user sign_in user
end end
@ -39,7 +46,19 @@ RSpec.describe "User Features" do
fill_in("user[name]", with: "New User") fill_in("user[name]", with: "New User")
fill_in("user[email]", with: "new_user@example.com") fill_in("user[email]", with: "new_user@example.com")
choose("user-role-data-provider-field") choose("user-role-data-provider-field")
expect { click_button("Continue") }.to change { ActionMailer::Base.deliveries.count }.by(1) expect(notify_client).to receive(:send_email).with(
{
email_address: "new_user@example.com",
template_id: set_password_template_id,
personalisation: {
name: "New User",
email: "new_user@example.com",
organisation: organisation.name,
link: "https://test.com/users/password/edit?reset_password_token=#{reset_password_token}",
},
},
)
click_button("Continue")
expect(page).to have_current_path("/organisations/#{org_id}/users") expect(page).to have_current_path("/organisations/#{org_id}/users")
expect(User.last.role).to eq("data_provider") expect(User.last.role).to eq("data_provider")
end end

29
spec/features/user_spec.rb

@ -1,6 +1,17 @@
require "rails_helper" require "rails_helper"
RSpec.describe "User Features" do RSpec.describe "User Features" do
let!(:user) { FactoryBot.create(:user) } let!(:user) { FactoryBot.create(:user, last_sign_in_at: Time.zone.now) }
let(:reset_password_template_id) { DeviseNotifyMailer::RESET_PASSWORD_TEMPLATE_ID }
let(:notify_client) { double(Notifications::Client) }
let(:reset_password_token) { "MCDH5y6Km-U7CFPgAMVS" }
before do
allow_any_instance_of(DeviseNotifyMailer).to receive(:notify_client).and_return(notify_client)
allow_any_instance_of(DeviseNotifyMailer).to receive(:host).and_return("test.com")
allow(notify_client).to receive(:send_email).and_return(true)
allow_any_instance_of(User).to receive(:set_reset_password_token).and_return(reset_password_token)
end
context "A user navigating to case logs" do context "A user navigating to case logs" do
it " is required to log in" do it " is required to log in" do
visit("/logs") visit("/logs")
@ -66,10 +77,22 @@ RSpec.describe "User Features" do
expect(page).to have_current_path("/confirmations/reset?email=idontexist%40example.com") expect(page).to have_current_path("/confirmations/reset?email=idontexist%40example.com")
end end
it " is sent a reset password email" do it " is sent a reset password email via Notify" do
expect(notify_client).to receive(:send_email).with(
{
email_address: user.email,
template_id: reset_password_template_id,
personalisation: {
name: user.name,
email: user.email,
organisation: user.organisation.name,
link: "https://test.com/users/password/edit?reset_password_token=#{reset_password_token}",
},
},
)
visit("/users/password/new") visit("/users/password/new")
fill_in("user[email]", with: user.email) fill_in("user[email]", with: user.email)
expect { click_button("Send email") }.to change { ActionMailer::Base.deliveries.count }.by(1) click_button("Send email")
end end
end end

2
spec/request_helper.rb

@ -5,5 +5,7 @@ module RequestHelper
WebMock.disable_net_connect!(allow_localhost: true) WebMock.disable_net_connect!(allow_localhost: true)
WebMock.stub_request(:get, /api.postcodes.io/) WebMock.stub_request(:get, /api.postcodes.io/)
.to_return(status: 200, body: "{\"status\":404,\"error\":\"Postcode not found\"}", headers: {}) .to_return(status: 200, body: "{\"status\":404,\"error\":\"Postcode not found\"}", headers: {})
WebMock.stub_request(:post, /api.notifications.service.gov.uk\/v2\/notifications\/email/)
.to_return(status: 200, body: "", headers: {})
end end
end end

19
spec/requests/auth/passwords_controller_spec.rb

@ -4,6 +4,12 @@ require_relative "../../support/devise"
RSpec.describe Auth::PasswordsController, type: :request do RSpec.describe Auth::PasswordsController, type: :request do
let(:params) { { user: { email: email } } } let(:params) { { user: { email: email } } }
let(:page) { Capybara::Node::Simple.new(response.body) } let(:page) { Capybara::Node::Simple.new(response.body) }
let(:notify_client) { double(Notifications::Client) }
before do
allow_any_instance_of(DeviseNotifyMailer).to receive(:notify_client).and_return(notify_client)
allow(notify_client).to receive(:send_email).and_return(true)
end
context "when a password reset is requested for a valid email" do context "when a password reset is requested for a valid email" do
let(:user) { FactoryBot.create(:user) } let(:user) { FactoryBot.create(:user) }
@ -32,19 +38,6 @@ RSpec.describe Auth::PasswordsController, type: :request do
end end
end end
context "when a password reset is requested the email" do
let(:user) { FactoryBot.create(:user, last_sign_in_at: Time.zone.now) }
let(:email) { user.email }
it "should contain the correct email" do
post "/users/password", params: params
follow_redirect!
email_ascii_content = ActionMailer::Base.deliveries.last.body.raw_source
email_content = email_ascii_content.encode("ASCII", "UTF-8", undef: :replace)
expect(email_content).to match(email)
end
end
context "#Update - reset password" do context "#Update - reset password" do
let(:user) { FactoryBot.create(:user) } let(:user) { FactoryBot.create(:user) }
let(:token) { user.send(:set_reset_password_token) } let(:token) { user.send(:set_reset_password_token) }

8
spec/requests/user_controller_spec.rb → spec/requests/users_controller_spec.rb

@ -1,12 +1,18 @@
require "rails_helper" require "rails_helper"
RSpec.describe "password_reset", type: :request do RSpec.describe UsersController, type: :request do
let(:user) { FactoryBot.create(:user) } let(:user) { FactoryBot.create(:user) }
let(:unauthorised_user) { FactoryBot.create(:user) } let(:unauthorised_user) { FactoryBot.create(:user) }
let(:headers) { { "Accept" => "text/html" } } let(:headers) { { "Accept" => "text/html" } }
let(:page) { Capybara::Node::Simple.new(response.body) } let(:page) { Capybara::Node::Simple.new(response.body) }
let(:new_value) { "new test name" } let(:new_value) { "new test name" }
let(:params) { { id: user.id, user: { name: new_value } } } let(:params) { { id: user.id, user: { name: new_value } } }
let(:notify_client) { double(Notifications::Client) }
before do
allow_any_instance_of(DeviseNotifyMailer).to receive(:notify_client).and_return(notify_client)
allow(notify_client).to receive(:send_email).and_return(true)
end
context "a not signed in user" do context "a not signed in user" do
describe "#show" do describe "#show" do
Loading…
Cancel
Save