feat(email.validation): expand email validation to Avis, ContactInformation, Invite, DossierTransfert

This commit is contained in:
Martin 2024-02-13 10:37:03 +01:00 committed by mfo
parent 05193e1d1e
commit 5f77c0cd06
18 changed files with 45 additions and 31 deletions

View file

@ -22,7 +22,7 @@ class SimpleFormatComponent < ApplicationComponent
} }
SIMPLE_URL_REGEX = %r{https?://[^\s<>]+} SIMPLE_URL_REGEX = %r{https?://[^\s<>]+}
EMAIL_IN_TEXT_REGEX = Regexp.new(Devise.email_regexp.source.gsub(/\\A|\\z/, '\b')) EMAIL_IN_TEXT_REGEX = Regexp.new(StrictEmailValidator::REGEXP.source.gsub(/\\A|\\z/, '\b'))
def initialize(text, allow_a: true, allow_autolink: true, class_names_map: {}) def initialize(text, allow_a: true, allow_autolink: true, class_names_map: {})
@allow_a = allow_a @allow_a = allow_a

View file

@ -29,7 +29,7 @@ class Users::SessionsController < Devise::SessionsController
end end
def link_sent def link_sent
if Devise.email_regexp.match?(params[:email]) if StrictEmailValidator::REGEXP.match?(params[:email])
@email = params[:email] @email = params[:email]
else else
redirect_to root_path redirect_to root_path

View file

@ -21,7 +21,7 @@ class Avis < ApplicationRecord
content_type: AUTHORIZED_CONTENT_TYPES, content_type: AUTHORIZED_CONTENT_TYPES,
size: { less_than: FILE_MAX_SIZE } size: { less_than: FILE_MAX_SIZE }
validates :email, format: { with: Devise.email_regexp, message: "n'est pas valide" }, allow_nil: true validates :email, strict_email: true, allow_nil: true
validates :question_answer, inclusion: { in: [true, false] }, on: :update, if: -> { question_label.present? } validates :question_answer, inclusion: { in: [true, false] }, on: :update, if: -> { question_label.present? }
validates :piece_justificative_file, size: { less_than: FILE_MAX_SIZE } validates :piece_justificative_file, size: { less_than: FILE_MAX_SIZE }
validates :introduction_file, size: { less_than: FILE_MAX_SIZE } validates :introduction_file, size: { less_than: FILE_MAX_SIZE }

View file

@ -11,7 +11,7 @@ module UserFindByConcern
end end
def self.find_all_by_identifier_with_emails(ids: [], emails: []) def self.find_all_by_identifier_with_emails(ids: [], emails: [])
valid_emails, invalid_emails = emails.partition { Devise.email_regexp.match?(_1) } valid_emails, invalid_emails = emails.partition { StrictEmailValidator::REGEXP.match?(_1) }
[ [
where(id: ids).or(where(users: { email: valid_emails })).distinct(:id), where(id: ids).or(where(users: { email: valid_emails })).distinct(:id),

View file

@ -3,7 +3,7 @@ class ContactInformation < ApplicationRecord
validates :nom, presence: { message: 'doit être renseigné' }, allow_nil: false validates :nom, presence: { message: 'doit être renseigné' }, allow_nil: false
validates :nom, uniqueness: { scope: :groupe_instructeur, message: 'existe déjà' } validates :nom, uniqueness: { scope: :groupe_instructeur, message: 'existe déjà' }
validates :email, format: { with: Devise.email_regexp, message: "n'est pas valide" }, presence: { message: 'doit être renseigné' }, allow_nil: false validates :email, strict_email: true, presence: { message: 'doit être renseigné' }, allow_nil: false
validates :telephone, phone: { possible: true, allow_blank: false } validates :telephone, phone: { possible: true, allow_blank: false }
validates :horaires, presence: { message: 'doivent être renseignés' }, allow_nil: false validates :horaires, presence: { message: 'doivent être renseignés' }, allow_nil: false
validates :adresse, presence: { message: 'doit être renseignée' }, allow_nil: false validates :adresse, presence: { message: 'doit être renseignée' }, allow_nil: false

View file

@ -4,7 +4,7 @@ class DossierTransfer < ApplicationRecord
EXPIRATION_LIMIT = 2.weeks EXPIRATION_LIMIT = 2.weeks
validates :email, format: { with: Devise.email_regexp } validates :email, strict_email: true, presence: true
before_validation -> { sanitize_email(:email) } before_validation -> { sanitize_email(:email) }
scope :pending, -> { where('created_at > ?', (Time.zone.now - EXPIRATION_LIMIT)) } scope :pending, -> { where('created_at > ?', (Time.zone.now - EXPIRATION_LIMIT)) }

View file

@ -11,8 +11,7 @@ class Invite < ApplicationRecord
validates :email, presence: true validates :email, presence: true
validates :email, uniqueness: { scope: :dossier_id } validates :email, uniqueness: { scope: :dossier_id }
validates :email, strict_email: true, allow_nil: true
validates :email, format: { with: Devise.email_regexp, message: "n'est pas valide" }, allow_nil: true
scope :with_dossiers, -> { joins(:dossier).merge(Dossier.visible_by_user) } scope :with_dossiers, -> { joins(:dossier).merge(Dossier.visible_by_user) }

View file

@ -37,10 +37,8 @@ class User < ApplicationRecord
before_validation -> { sanitize_email(:email) } before_validation -> { sanitize_email(:email) }
validate :does_not_merge_on_self, if: :requested_merge_into_id_changed? validate :does_not_merge_on_self, if: :requested_merge_into_id_changed?
with_options if: :elligible_to_new_validation? do before_validation :remove_devise_email_validator
before_validation :remove_devise_email_validator validates :email, strict_email: true
validates :email, strict_email: true
end
def validate_password_complexity? def validate_password_complexity?
administrateur? administrateur?
@ -274,10 +272,6 @@ class User < ApplicationRecord
Invite.where(email: email).update_all(user_id: id) Invite.where(email: email).update_all(user_id: id)
end end
def elligible_to_new_validation?
StrictEmailValidator.elligible_to_new_validation?(self)
end
def remove_devise_email_validator def remove_devise_email_validator
_validators[:email]&.reject! { _1.is_a?(ActiveModel::Validations::FormatValidator) } _validators[:email]&.reject! { _1.is_a?(ActiveModel::Validations::FormatValidator) }
_validate_callbacks.each do |callback| _validate_callbacks.each do |callback|

View file

@ -605,7 +605,7 @@ en:
not_a_phone: 'Invalid phone number' not_a_phone: 'Invalid phone number'
not_a_rna: 'Invalid RNA number' not_a_rna: 'Invalid RNA number'
url: 'is not a valid link' url: 'is not a valid link'
invalid_email_format: "is not a valid email" invalid_email_format: "is invalid. Please fill in a valid email ex: john.doe@exemple.fr"
models: models:
attestation_template: attestation_template:
attributes: attributes:

View file

@ -608,7 +608,7 @@ fr:
not_a_phone: 'Numéro de téléphone invalide' not_a_phone: 'Numéro de téléphone invalide'
not_a_rna: 'Numéro RNA invalide' not_a_rna: 'Numéro RNA invalide'
url: 'nest pas un lien valide' url: 'nest pas un lien valide'
invalid_email_format: "n'est pas un email valide" invalid_email_format: "est invalide. Saisir une adresse électronique valide, exemple : john.doe@exemple.fr"
models: models:
attestation_template: attestation_template:
attributes: attributes:

View file

@ -346,8 +346,9 @@ describe Administrateurs::GroupeInstructeursController, type: :controller do
end end
context 'when the admin wants to assign an instructor who is already assigned on this procedure' do context 'when the admin wants to assign an instructor who is already assigned on this procedure' do
let(:emails) { ['instructeur_1@ministere_a.gouv.fr'].to_json } let(:instructeur) { create(:instructeur) }
it { expect(subject.request.flash[:alert]).to be_present } before { procedure_non_routee.groupe_instructeurs.first.add_instructeurs(emails: [instructeur.user.email]) }
let(:emails) { [instructeur.email].to_json }
it { expect(subject).to redirect_to admin_procedure_groupe_instructeurs_path(procedure_non_routee) } it { expect(subject).to redirect_to admin_procedure_groupe_instructeurs_path(procedure_non_routee) }
end end

View file

@ -414,7 +414,7 @@ describe Experts::AvisController, type: :controller do
it do it do
expect(response).to render_template :instruction expect(response).to render_template :instruction
expect(flash.alert).to eq(["toto.fr : Le champ « Email » n'est pas valide"]) expect(flash.alert).to eq(["toto.fr : Le champ « Email » est invalide. Saisir une adresse électronique valide, exemple : john.doe@exemple.fr"])
expect(Avis.last).to eq(previous_avis) expect(Avis.last).to eq(previous_avis)
expect(dossier.last_avis_updated_at).to eq(nil) expect(dossier.last_avis_updated_at).to eq(nil)
end end
@ -445,7 +445,7 @@ describe Experts::AvisController, type: :controller do
it do it do
expect(response).to render_template :instruction expect(response).to render_template :instruction
expect(flash.alert).to eq(["toto.fr : Le champ « Email » n'est pas valide"]) expect(flash.alert).to eq(["toto.fr : Le champ « Email » est invalide. Saisir une adresse électronique valide, exemple : john.doe@exemple.fr"])
expect(flash.notice).to eq("Une demande davis a été envoyée à titi@titimail.com") expect(flash.notice).to eq("Une demande davis a été envoyée à titi@titimail.com")
expect(Avis.count).to eq(old_avis_count + 1) expect(Avis.count).to eq(old_avis_count + 1)
end end

View file

@ -798,7 +798,7 @@ describe Instructeurs::DossiersController, type: :controller do
before { subject } before { subject }
it { expect(response).to render_template :avis } it { expect(response).to render_template :avis }
it { expect(flash.alert).to eq(["emaila.com : Le champ « Email » n'est pas valide"]) } it { expect(flash.alert).to eq(["emaila.com : Le champ « Email » est invalide. Saisir une adresse électronique valide, exemple : john.doe@exemple.fr"]) }
it { expect { subject }.not_to change(Avis, :count) } it { expect { subject }.not_to change(Avis, :count) }
it { expect(dossier.last_avis_updated_at).to eq(nil) } it { expect(dossier.last_avis_updated_at).to eq(nil) }
end end
@ -820,7 +820,7 @@ describe Instructeurs::DossiersController, type: :controller do
before { subject } before { subject }
it { expect(response).to render_template :avis } it { expect(response).to render_template :avis }
it { expect(flash.alert).to eq(["toto.fr : Le champ « Email » n'est pas valide"]) } it { expect(flash.alert).to eq(["toto.fr : Le champ « Email » est invalide. Saisir une adresse électronique valide, exemple : john.doe@exemple.fr"]) }
it { expect(flash.notice).to eq("Une demande davis a été envoyée à titi@titimail.com") } it { expect(flash.notice).to eq("Une demande davis a été envoyée à titi@titimail.com") }
it { expect(Avis.count).to eq(old_avis_count + 1) } it { expect(Avis.count).to eq(old_avis_count + 1) }
it { expect(saved_avis.expert.email).to eq("titi@titimail.com") } it { expect(saved_avis.expert.email).to eq("titi@titimail.com") }

View file

@ -54,7 +54,7 @@ describe Manager::DossiersController, type: :controller do
it do it do
expect { subject }.not_to have_enqueued_mail expect { subject }.not_to have_enqueued_mail
expect(flash[:alert]).to eq("Ladresse email est invalide") expect(flash[:alert]).to eq("Ladresse email est invalide. Saisir une adresse électronique valide, exemple : john.doe@exemple.fr")
end end
end end
end end

View file

@ -134,7 +134,7 @@ describe Users::ProfilController, type: :controller do
it "should not transfer to an empty email" do it "should not transfer to an empty email" do
expect { subject }.not_to change { DossierTransfer.count } expect { subject }.not_to change { DossierTransfer.count }
expect(flash.alert).to eq(["Ladresse email est invalide"]) expect(flash.alert).to eq(["Ladresse email doit être rempli"])
end end
end end
end end

View file

@ -84,18 +84,22 @@ describe Users::TransfersController, type: :controller do
shared_examples 'email error' do shared_examples 'email error' do
it { expect { subject }.not_to change { DossierTransfer.count } } it { expect { subject }.not_to change { DossierTransfer.count } }
it { expect(flash.alert).to match([/invalide/]) } it { expect(flash.alert).to include(expected_error) }
it { is_expected.to redirect_to transferer_dossier_path(dossier.id) } it { is_expected.to redirect_to transferer_dossier_path(dossier.id) }
end end
context "when email is empty" do context "when email is empty" do
let(:email) { "" } let(:email) { "" }
it_behaves_like 'email error' it_behaves_like 'email error' do
let(:expected_error) { 'Ladresse email doit être rempli' }
end
end end
context "when email is invalid" do context "when email is invalid" do
let(:email) { "not-an-email" } let(:email) { "not-an-email" }
it_behaves_like 'email error' it_behaves_like 'email error' do
let(:expected_error) { "Ladresse email est invalide. Saisir une adresse électronique valide, exemple : john.doe@exemple.fr" }
end
end end
end end
end end

View file

@ -39,7 +39,7 @@ RSpec.describe Avis, type: :model do
before do before do
avis.reload avis.reload
end end
it { expect(avis.valid?).to be_truthy }
it { expect(avis.email).to be_nil } it { expect(avis.email).to be_nil }
it { expect(avis.experts_procedure).to eq(experts_procedure) } it { expect(avis.experts_procedure).to eq(experts_procedure) }
end end
@ -75,6 +75,22 @@ RSpec.describe Avis, type: :model do
end end
end end
describe 'email validation' do
let(:now_invalid_email) { "toto@tps" }
context 'new avis' do
before { allow(StrictEmailValidator).to receive(:elligible_to_new_validation?).and_return(true) }
it { expect(build(:avis, email: now_invalid_email).valid?).to be_falsey }
it { expect(build(:avis, email: nil).valid?).to be_truthy }
end
context 'old avis' do
before { allow(StrictEmailValidator).to receive(:elligible_to_new_validation?).and_return(false) }
it { expect(build(:avis, email: now_invalid_email).valid?).to be_truthy }
it { expect(build(:avis, email: nil).valid?).to be_truthy }
end
end
describe ".revoke_by!" do describe ".revoke_by!" do
let(:claimant) { create(:instructeur) } let(:claimant) { create(:instructeur) }

View file

@ -31,7 +31,7 @@ describe Invite do
it do it do
expect(invite.save).to be false expect(invite.save).to be false
expect(invite.errors.full_messages).to eq(["Le champ « Email » n'est pas valide"]) expect(invite.errors.full_messages).to eq(["Le champ « Email » est invalide. Saisir une adresse électronique valide, exemple : john.doe@exemple.fr"])
end end
context 'when an email is empty' do context 'when an email is empty' do