diff --git a/app/controllers/users/activate_controller.rb b/app/controllers/users/activate_controller.rb index 657e2cf7c..4ab7c47ac 100644 --- a/app/controllers/users/activate_controller.rb +++ b/app/controllers/users/activate_controller.rb @@ -30,6 +30,24 @@ class Users::ActivateController < ApplicationController 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 def user_params diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index f22896053..f5f1c974a 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -147,10 +147,14 @@ module Users def update_identite @dossier = dossier @no_description = true + email = dossier_params.dig('individual_attributes', 'email') if @dossier.update(dossier_params) && @dossier.individual.valid? - # TODO: remove this after proper mandat email validation - @dossier.individual.update!(email_verified_at: Time.zone.now) + # verify for_tiers email + 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) flash.notice = t('.identity_saved') diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 58a28f96f..d6fb81b49 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -48,6 +48,21 @@ class UserMailer < ApplicationMailer reply_to: Current.contact_email) 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) @reset_password_token = reset_password_token @user = user diff --git a/app/models/user.rb b/app/models/user.rb index df0beb00f..ee80ce46a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -83,6 +83,12 @@ class User < ApplicationRecord UserMailer.invite_instructeur(self, set_reset_password_token).deliver_later 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) UserMailer.invite_gestionnaire(self, set_reset_password_token, groupe_gestionnaire).deliver_later end @@ -130,6 +136,17 @@ class User < ApplicationRecord user 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) user = User.create_or_promote_to_instructeur(email, password) diff --git a/app/views/user_mailer/invite_tiers.html.haml b/app/views/user_mailer/invite_tiers.html.haml new file mode 100644 index 000000000..3b3e1ce53 --- /dev/null +++ b/app/views/user_mailer/invite_tiers.html.haml @@ -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 d’accompagnement à l'adresse #{link_to CONTACT_EMAIL, "mailto:#{CONTACT_EMAIL}"}. + + += render partial: "layouts/mailers/signature" diff --git a/config/routes.rb b/config/routes.rb index 2c2c86ed0..77991c3d6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -277,6 +277,7 @@ Rails.application.routes.draw do get 'activate' => '/users/activate#new' patch 'activate' => '/users/activate#create' + get 'confirm_email/:token' => '/users/activate#confirm_email', as: :confirm_email end # order matters: we don't want those routes to match /admin/procedures/:id diff --git a/spec/controllers/users/activate_controller_spec.rb b/spec/controllers/users/activate_controller_spec.rb index 01aeda490..c1aa7cc6d 100644 --- a/spec/controllers/users/activate_controller_spec.rb +++ b/spec/controllers/users/activate_controller_spec.rb @@ -37,4 +37,54 @@ describe Users::ActivateController, type: :controller do it { expect(response).to redirect_to(users_activate_path(token: token)) } 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 diff --git a/spec/controllers/users/dossiers_controller_spec.rb b/spec/controllers/users/dossiers_controller_spec.rb index 97462cfe1..8d54295e8 100644 --- a/spec/controllers/users/dossiers_controller_spec.rb +++ b/spec/controllers/users/dossiers_controller_spec.rb @@ -162,19 +162,21 @@ describe Users::DossiersController, type: :controller do describe 'update_identite' do let(:procedure) { create(:procedure, :for_individual) } 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 } } before do sign_in(user) - Timecop.freeze(now) do - subject - end end context 'with correct individual and dossier params' do 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 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 let(:dossier) { create(:dossier, :with_individual, :en_instruction, user: user, procedure: procedure) } let(:dossier_params) { { individual_attributes: { gender: 'M', nom: 'Mouse', prenom: 'Mickey' } } } + before { subject } it 'redirects to the dossiers list' do 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 let(:dossier_params) { { individual_attributes: { gender: '', nom: '', prenom: '' } } } + before { subject } it do expect(response).not_to have_http_status(:redirect) @@ -201,17 +205,20 @@ describe Users::DossiersController, type: :controller do 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_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 + expect { subject }.to have_enqueued_mail(UserMailer, :invite_tiers) + .and change(User, :count).by(1) + dossier.reload individual = dossier.individual.reload expect(individual.errors.full_messages).to be_empty expect(individual.notification_method).to eq('email') 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)) end @@ -219,6 +226,8 @@ describe Users::DossiersController, type: :controller do let(:dossier_params) { { mandataire_first_name: "Jean", mandataire_last_name: "Dupont" } } it 'updates the dossier mandataire first and last name' do + expect { subject }.not_to have_enqueued_mail(UserMailer, :invite_tiers) + dossier.reload individual = dossier.individual.reload expect(dossier.errors.full_messages).to be_empty diff --git a/spec/mailers/previews/user_mailer_preview.rb b/spec/mailers/previews/user_mailer_preview.rb index 39b285c9c..df46a0e06 100644 --- a/spec/mailers/previews/user_mailer_preview.rb +++ b/spec/mailers/previews/user_mailer_preview.rb @@ -24,6 +24,10 @@ class UserMailerPreview < ActionMailer::Preview UserMailer.invite_instructeur(user, 'aedfa0d0') end + def invite_tiers + UserMailer.invite_tiers(user, 'aedfa0d0', Dossier.first) + end + def invite_gestionnaire groupe_gestionnaire = GroupeGestionnaire.new(name: 'Root admins group') UserMailer.invite_gestionnaire(user, 'aedfa0d0', groupe_gestionnaire)