Merge pull request #10595 from demarches-simplifiees/email-mandant-need-confirmation-ldu

ETQ Mandant, je dois confirmer mon mail avant de recevoir des notifs
This commit is contained in:
Lisa Durand 2024-07-15 14:00:38 +00:00 committed by GitHub
commit 7630cc39fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 153 additions and 8 deletions

View file

@ -30,6 +30,24 @@ class Users::ActivateController < ApplicationController
end end
end end
def confirm_email
user = User.find_by(confirmation_token: params[:token])
if user && user.email_verified_at
flash[:notice] = "Votre email est déjà vérifié"
elsif user && 2.days.ago < user.confirmation_sent_at
user.update!(email_verified_at: Time.zone.now)
flash[:notice] = 'Votre email a bien été vérifié'
else
if user.present?
flash[:alert] = "Ce lien n'est plus valable, un nouveau lien a été envoyé à l'adresse #{user.email}"
User.create_or_promote_to_tiers(user.email, SecureRandom.hex)
else
flash[:alert] = "Un problème est survenu, vous pouvez nous contacter sur #{Current.contact_email}"
end
end
redirect_to root_path(user)
end
private private
def user_params def user_params

View file

@ -147,10 +147,14 @@ module Users
def update_identite def update_identite
@dossier = dossier @dossier = dossier
@no_description = true @no_description = true
email = dossier_params.dig('individual_attributes', 'email')
if @dossier.update(dossier_params) && @dossier.individual.valid? if @dossier.update(dossier_params) && @dossier.individual.valid?
# TODO: remove this after proper mandat email validation # verify for_tiers email
@dossier.individual.update!(email_verified_at: Time.zone.now) if email.present?
User.create_or_promote_to_tiers(email, SecureRandom.hex, @dossier)
end
@dossier.update!(autorisation_donnees: true, identity_updated_at: Time.zone.now) @dossier.update!(autorisation_donnees: true, identity_updated_at: Time.zone.now)
flash.notice = t('.identity_saved') flash.notice = t('.identity_saved')

View file

@ -48,6 +48,21 @@ class UserMailer < ApplicationMailer
reply_to: Current.contact_email) reply_to: Current.contact_email)
end end
def invite_tiers(user, token, dossier)
@token = token
@user = user
@dossier = dossier
subject = "Vérification de votre mail"
configure_defaults_for_user(user)
bypass_unverified_mail_protection!
mail(to: user.email,
subject: subject,
reply_to: Current.contact_email)
end
def invite_gestionnaire(user, reset_password_token, groupe_gestionnaire) def invite_gestionnaire(user, reset_password_token, groupe_gestionnaire)
@reset_password_token = reset_password_token @reset_password_token = reset_password_token
@user = user @user = user

View file

@ -83,6 +83,12 @@ class User < ApplicationRecord
UserMailer.invite_instructeur(self, set_reset_password_token).deliver_later UserMailer.invite_instructeur(self, set_reset_password_token).deliver_later
end end
def invite_tiers!(dossier)
token = SecureRandom.hex(10)
self.update!(confirmation_token: token, confirmation_sent_at: Time.zone.now)
UserMailer.invite_tiers(self, token, dossier).deliver_later
end
def invite_gestionnaire!(groupe_gestionnaire) def invite_gestionnaire!(groupe_gestionnaire)
UserMailer.invite_gestionnaire(self, set_reset_password_token, groupe_gestionnaire).deliver_later UserMailer.invite_gestionnaire(self, set_reset_password_token, groupe_gestionnaire).deliver_later
end end
@ -130,6 +136,17 @@ class User < ApplicationRecord
user user
end end
def self.create_or_promote_to_tiers(email, password, dossier = nil)
user = User
.create_with(password: password, confirmed_at: Time.zone.now)
.find_or_create_by(email: email)
if user.valid? && user.unverified_email?
user.invite_tiers!(dossier)
end
user
end
def self.create_or_promote_to_administrateur(email, password) def self.create_or_promote_to_administrateur(email, password)
user = User.create_or_promote_to_instructeur(email, password) user = User.create_or_promote_to_instructeur(email, password)

View file

@ -0,0 +1,27 @@
- content_for(:title, "Vérification de votre mail sur #{Current.application_name}")
%p
Bonjour,
%p
- if @dossier.present?
Un dossier sur la démarche : #{@dossier.procedure.libelle} a été démarré en votre nom par #{@dossier.user.email}.
- else
Un dossier a été démarré en votre nom sur #{Current.application_name}"
%p
Pour continuer à recevoir les mails concernant votre dossier, vous devez confirmer votre adresse email en cliquant sur ce bouton :
= round_button 'Je confirme', users_confirm_email_url(token: @token), :primary
%p
Vous pouvez aussi utiliser ce lien :
= link_to(users_confirm_email_url(token: @token), users_confirm_email_url(token: @token))
%p
- if @dossier.present?
Pour en savoir plus, veuillez vous rapprocher de #{@dossier.user.email}.
- else
Nous restons à votre disposition si vous avez besoin daccompagnement à l'adresse #{link_to CONTACT_EMAIL, "mailto:#{CONTACT_EMAIL}"}.
= render partial: "layouts/mailers/signature"

View file

@ -277,6 +277,7 @@ Rails.application.routes.draw do
get 'activate' => '/users/activate#new' get 'activate' => '/users/activate#new'
patch 'activate' => '/users/activate#create' patch 'activate' => '/users/activate#create'
get 'confirm_email/:token' => '/users/activate#confirm_email', as: :confirm_email
end end
# order matters: we don't want those routes to match /admin/procedures/:id # order matters: we don't want those routes to match /admin/procedures/:id

View file

@ -37,4 +37,54 @@ describe Users::ActivateController, type: :controller do
it { expect(response).to redirect_to(users_activate_path(token: token)) } it { expect(response).to redirect_to(users_activate_path(token: token)) }
end end
end end
describe '#confirm_email' do
let(:user) { create(:user) }
let(:dossier) { create(:dossier, user: user) }
before { user.invite_tiers!(dossier) }
context 'when the confirmation token is valid' do
before do
get :confirm_email, params: { token: user.confirmation_token }
user.reload
end
it 'updates the email_verified_at' do
expect(user.email_verified_at).to be_present
expect(user.confirmation_token).to be_present
end
it 'redirects to root path with a success notice' do
expect(response).to redirect_to(root_path(user))
expect(flash[:notice]).to eq('Votre email a bien été vérifié')
end
end
context 'when the confirmation token is valid but already used' do
before do
get :confirm_email, params: { token: user.confirmation_token }
get :confirm_email, params: { token: user.confirmation_token }
end
it 'redirects to root path with an explanation notice' do
expect(response).to redirect_to(root_path(user))
expect(flash[:notice]).to eq('Votre email est déjà vérifié')
end
end
context 'when the confirmation token is too old or not valid' do
subject { get :confirm_email, params: { token: user.confirmation_token } }
before do
user.update!(confirmation_sent_at: 3.days.ago)
end
it 'redirects to root path with an explanation notice and it send a new link if user present' do
expect { subject }.to have_enqueued_mail(UserMailer, :invite_tiers)
expect(response).to redirect_to(root_path(user))
expect(flash[:alert]).to eq("Ce lien n'est plus valable, un nouveau lien a été envoyé à l'adresse #{user.email}")
end
end
end
end end

View file

@ -162,19 +162,21 @@ describe Users::DossiersController, type: :controller do
describe 'update_identite' do describe 'update_identite' do
let(:procedure) { create(:procedure, :for_individual) } let(:procedure) { create(:procedure, :for_individual) }
let(:dossier) { create(:dossier, user: user, procedure: procedure) } let(:dossier) { create(:dossier, user: user, procedure: procedure) }
let(:now) { Time.zone.parse('01/01/2100') }
subject { post :update_identite, params: { id: dossier.id, dossier: dossier_params } } subject { post :update_identite, params: { id: dossier.id, dossier: dossier_params } }
before do before do
sign_in(user) sign_in(user)
Timecop.freeze(now) do
subject
end
end end
context 'with correct individual and dossier params' do context 'with correct individual and dossier params' do
let(:dossier_params) { { individual_attributes: { gender: 'M', nom: 'Mouse', prenom: 'Mickey' } } } let(:dossier_params) { { individual_attributes: { gender: 'M', nom: 'Mouse', prenom: 'Mickey' } } }
let(:now) { Time.zone.parse('01/01/2100') }
before do
Timecop.freeze(now) do
subject
end
end
it do it do
expect(response).to redirect_to(brouillon_dossier_path(dossier)) expect(response).to redirect_to(brouillon_dossier_path(dossier))
@ -185,6 +187,7 @@ describe Users::DossiersController, type: :controller do
context 'when the identite cannot be updated by the user' do context 'when the identite cannot be updated by the user' do
let(:dossier) { create(:dossier, :with_individual, :en_instruction, user: user, procedure: procedure) } let(:dossier) { create(:dossier, :with_individual, :en_instruction, user: user, procedure: procedure) }
let(:dossier_params) { { individual_attributes: { gender: 'M', nom: 'Mouse', prenom: 'Mickey' } } } let(:dossier_params) { { individual_attributes: { gender: 'M', nom: 'Mouse', prenom: 'Mickey' } } }
before { subject }
it 'redirects to the dossiers list' do it 'redirects to the dossiers list' do
expect(response).to redirect_to(dossier_path(dossier)) expect(response).to redirect_to(dossier_path(dossier))
@ -194,6 +197,7 @@ describe Users::DossiersController, type: :controller do
context 'with incorrect individual and dossier params' do context 'with incorrect individual and dossier params' do
let(:dossier_params) { { individual_attributes: { gender: '', nom: '', prenom: '' } } } let(:dossier_params) { { individual_attributes: { gender: '', nom: '', prenom: '' } } }
before { subject }
it do it do
expect(response).not_to have_http_status(:redirect) expect(response).not_to have_http_status(:redirect)
@ -201,17 +205,20 @@ describe Users::DossiersController, type: :controller do
end end
end end
context 'when a dossier is in broullon, for_tiers and we want to update the individual' do context 'when a dossier is in brouillon, for_tiers and we want to update the individual' do
let(:dossier) { create(:dossier, :for_tiers_without_notification, state: "brouillon", user: user, procedure: procedure) } let(:dossier) { create(:dossier, :for_tiers_without_notification, state: "brouillon", user: user, procedure: procedure) }
let(:dossier_params) { { individual_attributes: { gender: 'M', nom: 'Mouse', prenom: 'Mickey', email: 'mickey@gmail.com', notification_method: 'email' } } } let(:dossier_params) { { individual_attributes: { gender: 'M', nom: 'Mouse', prenom: 'Mickey', email: 'mickey@gmail.com', notification_method: 'email' } } }
it 'updates the individual with valid notification_method' do it 'updates the individual with valid notification_method' do
expect { subject }.to have_enqueued_mail(UserMailer, :invite_tiers)
.and change(User, :count).by(1)
dossier.reload dossier.reload
individual = dossier.individual.reload individual = dossier.individual.reload
expect(individual.errors.full_messages).to be_empty expect(individual.errors.full_messages).to be_empty
expect(individual.notification_method).to eq('email') expect(individual.notification_method).to eq('email')
expect(individual.email).to eq('mickey@gmail.com') expect(individual.email).to eq('mickey@gmail.com')
expect(individual.email_verified_at).to be_present expect(individual.email_verified_at).to eq nil
expect(response).to redirect_to(brouillon_dossier_path(dossier)) expect(response).to redirect_to(brouillon_dossier_path(dossier))
end end
@ -219,6 +226,8 @@ describe Users::DossiersController, type: :controller do
let(:dossier_params) { { mandataire_first_name: "Jean", mandataire_last_name: "Dupont" } } let(:dossier_params) { { mandataire_first_name: "Jean", mandataire_last_name: "Dupont" } }
it 'updates the dossier mandataire first and last name' do it 'updates the dossier mandataire first and last name' do
expect { subject }.not_to have_enqueued_mail(UserMailer, :invite_tiers)
dossier.reload dossier.reload
individual = dossier.individual.reload individual = dossier.individual.reload
expect(dossier.errors.full_messages).to be_empty expect(dossier.errors.full_messages).to be_empty

View file

@ -24,6 +24,10 @@ class UserMailerPreview < ActionMailer::Preview
UserMailer.invite_instructeur(user, 'aedfa0d0') UserMailer.invite_instructeur(user, 'aedfa0d0')
end end
def invite_tiers
UserMailer.invite_tiers(user, 'aedfa0d0', Dossier.first)
end
def invite_gestionnaire def invite_gestionnaire
groupe_gestionnaire = GroupeGestionnaire.new(name: 'Root admins group') groupe_gestionnaire = GroupeGestionnaire.new(name: 'Root admins group')
UserMailer.invite_gestionnaire(user, 'aedfa0d0', groupe_gestionnaire) UserMailer.invite_gestionnaire(user, 'aedfa0d0', groupe_gestionnaire)