diff --git a/Gemfile b/Gemfile index 7084e1e3e..f818540a3 100644 --- a/Gemfile +++ b/Gemfile @@ -19,6 +19,8 @@ gem "bootsnap", ">= 1.4.4", require: false gem "govuk-components" # GOV UK component form builder DSL gem "govuk_design_system_formbuilder" +# GOV UK Notify +gem "notifications-ruby-client" # Turbo and Stimulus gem "hotwire-rails" # Soft delete ActiveRecords objects diff --git a/Gemfile.lock b/Gemfile.lock index a3ce16ddf..836a7c62a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,6 @@ GIT remote: https://github.com/activeadmin/arbre.git - revision: e4970e683c81c99432b73f0ceb5dff7d3fe50413 + revision: 305c221a2131be8dd0d96955f9b0a328dfa4beba specs: arbre (1.4.0) activesupport (>= 3.0.0, < 7.1) @@ -20,7 +20,7 @@ GIT GIT remote: https://github.com/tagliala/activeadmin.git - revision: 8ccc35b8144482284c90b0af74a01e940765f7a6 + revision: f4fc57251399c0ed452d72ac4b07d0bdd3d047c6 branch: feature/railties-7 specs: activeadmin (2.9.0) @@ -106,7 +106,7 @@ GEM ast (2.4.2) bcrypt (3.1.16) bindex (0.8.1) - bootsnap (1.10.1) + bootsnap (1.10.2) msgpack (~> 1.2) builder (3.2.4) byebug (11.1.3) @@ -158,7 +158,7 @@ GEM activemodel (>= 6.1) railties (>= 6.1) view_component (~> 2.47.0) - govuk_design_system_formbuilder (3.0.0) + govuk_design_system_formbuilder (3.0.1) actionview (>= 6.1) activemodel (>= 6.1) activesupport (>= 6.1) @@ -186,6 +186,7 @@ GEM thor (>= 0.14, < 2.0) json-schema (2.8.1) addressable (>= 2.4) + jwt (2.3.0) kaminari (1.2.2) activesupport (>= 4.1.0) kaminari-actionview (= 1.2.2) @@ -211,7 +212,7 @@ GEM method_source (1.0.0) mini_mime (1.1.2) minitest (5.15.0) - msgpack (1.4.3) + msgpack (1.4.4) net-imap (0.2.3) digest net-protocol @@ -234,6 +235,8 @@ GEM racc (~> 1.4) nokogiri (1.13.1-x86_64-linux) racc (~> 1.4) + notifications-ruby-client (5.3.0) + jwt (>= 1.5, < 3) orm_adapter (0.5.0) overcommit (0.58.0) childprocess (>= 0.6.3, < 5) @@ -242,7 +245,7 @@ GEM parallel (1.21.0) parser (3.1.0.0) ast (~> 2.4.1) - pg (1.2.3) + pg (1.3.0) postcodes_io (0.4.0) excon (~> 0.39) pry (0.13.1) @@ -435,6 +438,7 @@ DEPENDENCIES hotwire-rails json-schema listen (~> 3.3) + notifications-ruby-client overcommit (>= 0.37.0) pg (~> 1.1) postcodes_io diff --git a/app/mailers/devise_notify_mailer.rb b/app/mailers/devise_notify_mailer.rb new file mode 100644 index 000000000..b447e14fb --- /dev/null +++ b/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 diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 49866c93b..d5196b163 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -24,10 +24,11 @@ Devise.setup do |config| # 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 = ENV["CORE_EMAIL_USERNAME"] + # config.mailer_sender = ENV["CORE_EMAIL_USERNAME"] # Configure the class responsible to send e-mails. # config.mailer = 'Devise::Mailer' + config.mailer = "DeviseNotifyMailer" # Configure the parent class responsible to send e-mails. # config.parent_mailer = 'ActionMailer::Base' diff --git a/spec/features/organisation_spec.rb b/spec/features/organisation_spec.rb index 7b7611a2a..0fcb6874c 100644 --- a/spec/features/organisation_spec.rb +++ b/spec/features/organisation_spec.rb @@ -5,8 +5,15 @@ RSpec.describe "User Features" do include Helpers let(:organisation) { user.organisation } 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 + 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 end @@ -39,7 +46,19 @@ RSpec.describe "User Features" do fill_in("user[name]", with: "New User") fill_in("user[email]", with: "new_user@example.com") 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(User.last.role).to eq("data_provider") end diff --git a/spec/features/user_spec.rb b/spec/features/user_spec.rb index 71375f4d2..dd8eacfce 100644 --- a/spec/features/user_spec.rb +++ b/spec/features/user_spec.rb @@ -1,6 +1,17 @@ require "rails_helper" + 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 it " is required to log in" do visit("/logs") @@ -66,10 +77,22 @@ RSpec.describe "User Features" do expect(page).to have_current_path("/confirmations/reset?email=idontexist%40example.com") 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") 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 diff --git a/spec/request_helper.rb b/spec/request_helper.rb index c939e7afa..5d7e502d6 100644 --- a/spec/request_helper.rb +++ b/spec/request_helper.rb @@ -5,5 +5,7 @@ module RequestHelper WebMock.disable_net_connect!(allow_localhost: true) WebMock.stub_request(:get, /api.postcodes.io/) .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 diff --git a/spec/requests/auth/passwords_controller_spec.rb b/spec/requests/auth/passwords_controller_spec.rb index b6fbb8ac1..61f5cc50a 100644 --- a/spec/requests/auth/passwords_controller_spec.rb +++ b/spec/requests/auth/passwords_controller_spec.rb @@ -4,6 +4,12 @@ require_relative "../../support/devise" RSpec.describe Auth::PasswordsController, type: :request do let(:params) { { user: { email: email } } } 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 let(:user) { FactoryBot.create(:user) } @@ -32,19 +38,6 @@ RSpec.describe Auth::PasswordsController, type: :request do 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 let(:user) { FactoryBot.create(:user) } let(:token) { user.send(:set_reset_password_token) } diff --git a/spec/requests/user_controller_spec.rb b/spec/requests/users_controller_spec.rb similarity index 96% rename from spec/requests/user_controller_spec.rb rename to spec/requests/users_controller_spec.rb index 0553aba95..9d93cb92b 100644 --- a/spec/requests/user_controller_spec.rb +++ b/spec/requests/users_controller_spec.rb @@ -1,12 +1,18 @@ require "rails_helper" -RSpec.describe "password_reset", type: :request do +RSpec.describe UsersController, type: :request do let(:user) { FactoryBot.create(:user) } let(:unauthorised_user) { FactoryBot.create(:user) } let(:headers) { { "Accept" => "text/html" } } let(:page) { Capybara::Node::Simple.new(response.body) } let(:new_value) { "new test name" } 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 describe "#show" do