Merge pull request #10068 from mfo/US/user-has-many-fcis

tech(User.FranceConnect): un usager peut avoir plusieurs profils FranceConnect, pas uniquement un
This commit is contained in:
mfo 2024-03-20 16:30:39 +00:00 committed by GitHub
commit 086cc1537b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 52 additions and 27 deletions

View file

@ -212,7 +212,7 @@ module Types
private
def user_loader
Loaders::Record.for(User, includes: :france_connect_information).load(object.user_id)
Loaders::Record.for(User, includes: :france_connect_informations).load(object.user_id)
end
end
end

View file

@ -129,7 +129,7 @@ class Dossier < ApplicationRecord
belongs_to :batch_operation, optional: true
has_many :dossier_batch_operations, dependent: :destroy
has_many :batch_operations, through: :dossier_batch_operations
has_one :france_connect_information, through: :user
has_many :france_connect_informations, through: :user
has_one :procedure, through: :revision
has_one :attestation_template, through: :procedure
@ -439,8 +439,7 @@ class Dossier < ApplicationRecord
scope :not_having_batch_operation, -> { where(batch_operation_id: nil) }
delegate :siret, :siren, to: :etablissement, allow_nil: true
delegate :france_connect_information, to: :user, allow_nil: true
delegate :france_connected_with_one_identity?, to: :user, allow_nil: true
before_save :build_default_champs_for_new_dossier, if: Proc.new { revision_id_was.nil? && parent_dossier_id.nil? && editing_fork_origin_id.nil? }
after_save :send_web_hook
@ -518,8 +517,8 @@ class Dossier < ApplicationRecord
def build_default_individual
if procedure.for_individual? && individual.blank?
self.individual = if france_connect_information.present?
Individual.from_france_connect(france_connect_information)
self.individual = if france_connected_with_one_identity?
Individual.from_france_connect(france_connect_informations.first)
else
Individual.new
end
@ -1381,7 +1380,7 @@ class Dossier < ApplicationRecord
def user_from_france_connect?
return false if user_deleted?
user.france_connect_information.present?
user.france_connected_with_one_identity?
end
def champs_for_revision(scope: nil, root: false)

View file

@ -22,15 +22,15 @@ class User < ApplicationRecord
has_many :deleted_dossiers
has_many :merge_logs, dependent: :destroy
has_many :requested_merge_from, class_name: 'User', dependent: :nullify, inverse_of: :requested_merge_into, foreign_key: :requested_merge_into_id
has_many :france_connect_informations, dependent: :destroy
has_one :france_connect_information, dependent: :destroy
has_one :instructeur, dependent: :destroy
has_one :administrateur, dependent: :destroy
has_one :gestionnaire, dependent: :destroy
has_one :expert, dependent: :destroy
belongs_to :requested_merge_into, class_name: 'User', optional: true
accepts_nested_attributes_for :france_connect_information
accepts_nested_attributes_for :france_connect_informations
default_scope { eager_load(:instructeur, :administrateur, :expert) }
@ -103,7 +103,7 @@ class User < ApplicationRecord
if user.valid?
if user.instructeur.nil?
user.create_instructeur!
user.update(france_connect_information: nil)
user.france_connect_informations.delete_all
end
user.instructeur.administrateurs << administrateurs
@ -127,7 +127,7 @@ class User < ApplicationRecord
if user.valid? && user.administrateur.nil?
user.create_administrateur!
user.update(france_connect_information: nil)
user.france_connect_informations.delete_all
AdminUpdateDefaultZonesJob.perform_later(user.administrateur)
end
@ -176,6 +176,10 @@ class User < ApplicationRecord
!administrateur? && !instructeur?
end
def france_connected_with_one_identity?
france_connect_informations.size == 1
end
def can_be_deleted?
!administrateur? && !instructeur? && !expert?
end
@ -190,7 +194,6 @@ class User < ApplicationRecord
Invite.where(dossier: dossiers).destroy_all
delete_and_keep_track_dossiers(super_admin, reason: :user_removed)
destroy!
end
end

View file

@ -38,7 +38,7 @@ class PiecesJustificativesService
pdfs = []
procedure = dossiers.first.procedure
dossiers = dossiers.includes(:individual, :traitement, :etablissement, user: :france_connect_information, avis: :expert, commentaires: [:instructeur, :expert])
dossiers = dossiers.includes(:individual, :traitement, :etablissement, user: :france_connect_informations, avis: :expert, commentaires: [:instructeur, :expert])
dossiers = DossierPreloader.new(dossiers).in_batches
dossiers.each do |dossier|
dossier.association(:procedure).target = procedure

View file

@ -336,8 +336,8 @@ prawn_document(page_size: "A4") do |pdf|
add_title(pdf, "Identité du demandeur")
if @dossier.france_connect_information.present?
format_in_2_columns(pdf, 'Informations FranceConnect', france_connect_informations(@dossier.france_connect_information))
if @dossier.france_connected_with_one_identity?
format_in_2_columns(pdf, 'Informations FranceConnect', france_connect_information(@dossier.france_connect_informations.first))
end
format_in_2_columns(pdf, "Email", @dossier.user_email_for(:display))

View file

@ -2,7 +2,7 @@
.fr-nav__item
%button.account-btn.fr-translate__btn.fr-btn{ "aria-controls" => "account", "aria-expanded" => "false", :title => t('my_account', scope: [:layouts]) }
%span= current_email
- if dossier.present? && dossier&.france_connect_information.present?
- if dossier.present? && dossier&.france_connected_with_one_identity?
%span
&nbsp;via FranceConnect
%span{ class: "fr-badge fr-badge--sm fr-ml-1w #{color_by_role(nav_bar_profile)}" }

View file

@ -1,6 +1,6 @@
- if dossier.france_connect_information.present? && current_user.instructeur? && !current_user.owns_or_invite?(dossier)
- if dossier.france_connected_with_one_identity? && current_user.instructeur? && !current_user.owns_or_invite?(dossier)
- content_for(:notice_info) do
= render partial: "shared/dossiers/france_connect_informations_notice", locals: { user_information: dossier.france_connect_information }
= render partial: "shared/dossiers/france_connect_informations_notice", locals: { user_information: dossier.france_connect_informations.first }
.fr-container.counter-start-header-section.dossier-show{ class: class_names("dossier-show-instructeur" => profile =="instructeur") }
.fr-grid-row.fr-grid-row--center

View file

@ -1,8 +1,8 @@
- dossier_for_editing = dossier.en_construction? ? dossier.owner_editing_fork : dossier
- if dossier.france_connect_information.present? && current_user.instructeur? && !current_user.owns_or_invite?(dossier)
- if dossier.france_connected_with_one_identity? && current_user.instructeur? && !current_user.owns_or_invite?(dossier)
- content_for(:notice_info) do
= render partial: "shared/dossiers/france_connect_informations_notice", locals: { user_information: dossier.france_connect_information }
= render partial: "shared/dossiers/france_connect_informations_notice", locals: { user_information: dossier.france_connect_informations.first }
.dossier-edit.container.counter-start-header-section

View file

@ -12,5 +12,9 @@ FactoryBot.define do
trait :with_strong_password do
password { '{my-%s3cure[]-p4$$w0rd' }
end
trait :with_fci do
france_connect_informations { [association(:france_connect_information)] }
end
end
end

View file

@ -11,7 +11,7 @@ describe DossierSearchableConcern do
let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private) }
let(:dossier) { create(:dossier, etablissement: etablissement, user: user, procedure: procedure) }
let(:france_connect_information) { build(:france_connect_information, given_name: 'Chris', family_name: 'Harrisson') }
let(:user) { build(:user, france_connect_information: france_connect_information) }
let(:user) { build(:user, france_connect_informations: [france_connect_information]) }
before do
champ_public.update_attribute(:value, "champ public")

View file

@ -295,8 +295,7 @@ describe Dossier, type: :model do
end
context 'and the user signs-in using France Connect' do
let(:france_connect_information) { build(:france_connect_information) }
let(:user) { build(:user, france_connect_information: france_connect_information) }
let(:user) { create(:user, france_connect_informations: [build(:france_connect_information)]) }
it 'fills the individual with the informations from France Connect' do
subject
@ -305,6 +304,16 @@ describe Dossier, type: :model do
expect(dossier.individual.gender).to eq(Individual::GENDER_FEMALE)
end
end
context 'and the user signs-in using France Connect many times' do
let(:user) { create(:user, france_connect_informations: [build(:france_connect_information), build(:france_connect_information)]) }
it 'fills the individual with the informations from France Connect' do
subject
expect(dossier.individual.nom).to eq(nil)
expect(dossier.individual.prenom).to eq(nil)
expect(dossier.individual.gender).to eq(nil)
end
end
end
context 'when the dossier belongs to a procedure for moral personas' do
@ -2095,7 +2104,7 @@ describe Dossier, type: :model do
let(:dossier) { create(:dossier) }
context 'user france connected' do
let(:dossier) { build(:dossier, user: build(:user, france_connect_information: build(:france_connect_information))) }
let(:dossier) { build(:dossier, user: build(:user, france_connect_informations: [build(:france_connect_information)])) }
it { expect(dossier.spreadsheet_columns(types_de_champ: [])).to include(["FranceConnect ?", true]) }
end

View file

@ -348,12 +348,22 @@ describe User, type: :model do
expect(dossier_termine.user).to be_nil
expect(dossier_termine.user_email_for(:display)).to eq(user.email)
expect(dossier_termine.valid?).to be_truthy
expect(dossier_termine.france_connect_information).to be_nil
expect(dossier_termine.france_connect_informations).to be_empty
expect { dossier_termine.user_email_for(:notification) }.to raise_error(RuntimeError)
expect(User.find_by(id: user.id)).to be_nil
end
end
context 'with fci' do
let!(:user) { create(:user, france_connect_informations: [build(:france_connect_information), build(:france_connect_information)]) }
let(:reason) { :user_expired }
subject { user.delete_and_keep_track_dossiers_also_delete_user(super_admin, reason:) }
it { expect { subject }.not_to raise_error }
it { expect { subject }.to change { FranceConnectInformation.count }.from(2).to(0) }
it { expect { subject }.to change { User.count }.from(1).to(0) }
end
end
describe '#password_complexity' do

View file

@ -172,7 +172,7 @@ describe 'instructeurs/dossiers/show', type: :view do
context 'when the user is logged in with france connect' do
let(:france_connect_information) { build(:france_connect_information) }
let(:user) { build(:user, france_connect_information: france_connect_information) }
let(:user) { build(:user, france_connect_informations: [france_connect_information]) }
let(:procedure1) { create(:procedure, :with_type_de_champ, for_individual: true) }
let(:dossier) { create(:dossier, procedure: procedure1, user: user) }

View file

@ -36,7 +36,7 @@ describe 'users/dossiers/demande', type: :view do
context 'when the user is logged in with france connect' do
let(:france_connect_information) { build(:france_connect_information) }
let(:user) { build(:user, france_connect_information: france_connect_information) }
let(:user) { build(:user, france_connect_informations: [france_connect_information]) }
let(:procedure1) { create(:procedure, :with_type_de_champ, for_individual: true) }
let(:dossier) { create(:dossier, procedure: procedure1, user: user) }