2024-04-29 00:17:15 +02:00
|
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
2015-09-23 10:02:01 +02:00
|
|
|
|
describe User, type: :model do
|
2018-09-19 11:56:05 +02:00
|
|
|
|
describe '#after_confirmation' do
|
|
|
|
|
let(:email) { 'mail@beta.gouv.fr' }
|
2019-06-20 00:36:50 +02:00
|
|
|
|
let!(:invite) { create(:invite, email: email) }
|
2018-09-19 11:56:05 +02:00
|
|
|
|
let!(:invite2) { create(:invite, email: email) }
|
|
|
|
|
let(:user) do
|
|
|
|
|
create(:user,
|
|
|
|
|
email: email,
|
2023-03-03 14:16:15 +01:00
|
|
|
|
password: SECURE_PASSWORD,
|
2018-09-19 11:56:05 +02:00
|
|
|
|
confirmation_token: '123',
|
|
|
|
|
confirmed_at: nil)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'when confirming a user, it links the pending invitations to this user' do
|
|
|
|
|
expect(user.invites.size).to eq(0)
|
|
|
|
|
user.confirm
|
|
|
|
|
expect(user.reload.invites.size).to eq(2)
|
|
|
|
|
end
|
2024-05-27 09:59:48 +02:00
|
|
|
|
|
|
|
|
|
it 'verifies its email' do
|
|
|
|
|
expect(user.email_verified_at).to be_nil
|
|
|
|
|
user.confirm
|
|
|
|
|
expect(user.email_verified_at).to be_present
|
|
|
|
|
end
|
2018-09-19 11:56:05 +02:00
|
|
|
|
end
|
|
|
|
|
|
2018-05-30 18:26:23 +02:00
|
|
|
|
describe '#owns?' do
|
|
|
|
|
let(:owner) { create(:user) }
|
|
|
|
|
let(:dossier) { create(:dossier, user: owner) }
|
|
|
|
|
let(:invite_user) { create(:user) }
|
2019-08-06 11:02:54 +02:00
|
|
|
|
let(:invite_instructeur) { create(:user) }
|
2018-05-30 18:26:23 +02:00
|
|
|
|
|
|
|
|
|
subject { user.owns?(dossier) }
|
|
|
|
|
|
|
|
|
|
context 'when user is owner' do
|
|
|
|
|
let(:user) { owner }
|
|
|
|
|
|
|
|
|
|
it { is_expected.to be_truthy }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when user was invited by user' do
|
|
|
|
|
before do
|
2018-10-10 09:23:08 +02:00
|
|
|
|
create(:invite, dossier: dossier, user: invite_user)
|
2018-05-30 18:26:23 +02:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:user) { invite_user }
|
|
|
|
|
|
|
|
|
|
it { is_expected.to be_falsy }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when user is quidam' do
|
|
|
|
|
let(:user) { create(:user) }
|
|
|
|
|
|
|
|
|
|
it { is_expected.to be_falsey }
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2016-03-22 17:36:36 +01:00
|
|
|
|
describe '#invite?' do
|
|
|
|
|
let(:dossier) { create :dossier }
|
|
|
|
|
let(:user) { dossier.user }
|
|
|
|
|
|
2023-04-03 20:00:52 +02:00
|
|
|
|
subject { user.invite? dossier }
|
2016-03-22 17:36:36 +01:00
|
|
|
|
|
|
|
|
|
context 'when user is invite at the dossier' do
|
|
|
|
|
before do
|
|
|
|
|
create :invite, dossier_id: dossier.id, user: user
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it { is_expected.to be_truthy }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when user is not invite at the dossier' do
|
|
|
|
|
it { is_expected.to be_falsey }
|
|
|
|
|
end
|
|
|
|
|
end
|
2016-10-11 10:31:32 +02:00
|
|
|
|
|
2018-05-30 18:31:02 +02:00
|
|
|
|
describe '#owns_or_invite?' do
|
|
|
|
|
let(:owner) { create(:user) }
|
|
|
|
|
let(:dossier) { create(:dossier, user: owner) }
|
|
|
|
|
let(:invite_user) { create(:user) }
|
2019-08-06 11:02:54 +02:00
|
|
|
|
let(:invite_instructeur) { create(:user) }
|
2018-05-30 18:31:02 +02:00
|
|
|
|
|
|
|
|
|
subject { user.owns_or_invite?(dossier) }
|
|
|
|
|
|
|
|
|
|
context 'when user is owner' do
|
|
|
|
|
let(:user) { owner }
|
|
|
|
|
|
|
|
|
|
it { is_expected.to be_truthy }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when user was invited by user' do
|
|
|
|
|
before do
|
2018-10-10 09:23:08 +02:00
|
|
|
|
create(:invite, dossier: dossier, user: invite_user)
|
2018-05-30 18:31:02 +02:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:user) { invite_user }
|
|
|
|
|
|
|
|
|
|
it { is_expected.to be_truthy }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when user is quidam' do
|
|
|
|
|
let(:user) { create(:user) }
|
|
|
|
|
|
|
|
|
|
it { is_expected.to be_falsey }
|
|
|
|
|
end
|
|
|
|
|
end
|
2019-08-16 18:15:05 +02:00
|
|
|
|
|
|
|
|
|
describe '.create_or_promote_to_instructeur' do
|
|
|
|
|
let(:email) { 'inst1@gmail.com' }
|
|
|
|
|
let(:password) { 'un super password !' }
|
|
|
|
|
let(:admins) { [] }
|
|
|
|
|
|
|
|
|
|
subject { User.create_or_promote_to_instructeur(email, password, administrateurs: admins) }
|
|
|
|
|
|
|
|
|
|
context 'without an existing user' do
|
|
|
|
|
it do
|
|
|
|
|
user = subject
|
|
|
|
|
expect(user.valid_password?(password)).to be true
|
|
|
|
|
expect(user.confirmed_at).to be_present
|
2024-06-25 15:14:26 +02:00
|
|
|
|
expect(user.email_verified_at).not_to be_present
|
2019-08-16 18:15:05 +02:00
|
|
|
|
expect(user.instructeur).to be_present
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'with an administrateur' do
|
2024-06-01 22:50:48 +02:00
|
|
|
|
let(:admins) { [administrateurs(:default_admin)] }
|
2019-08-16 18:15:05 +02:00
|
|
|
|
|
|
|
|
|
it do
|
|
|
|
|
user = subject
|
|
|
|
|
expect(user.instructeur.administrateurs).to eq(admins)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'with an existing user' do
|
2023-03-03 14:16:15 +01:00
|
|
|
|
before { create(:user, email: email, password: SECURE_PASSWORD) }
|
2019-08-16 18:15:05 +02:00
|
|
|
|
|
|
|
|
|
it 'keeps the previous password' do
|
|
|
|
|
user = subject
|
2023-03-03 14:16:15 +01:00
|
|
|
|
expect(user.valid_password?(SECURE_PASSWORD)).to be true
|
2019-08-16 18:15:05 +02:00
|
|
|
|
expect(user.instructeur).to be_present
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'with an existing instructeur' do
|
2024-06-01 22:50:48 +02:00
|
|
|
|
let(:old_admins) { [administrateurs(:default_admin)] }
|
2019-08-16 18:15:05 +02:00
|
|
|
|
let(:admins) { [create(:administrateur)] }
|
2019-10-16 14:15:47 +02:00
|
|
|
|
let!(:instructeur) { create(:instructeur, email: 'i@mail.com', administrateurs: old_admins) }
|
2019-08-16 18:15:05 +02:00
|
|
|
|
|
|
|
|
|
before do
|
|
|
|
|
User
|
|
|
|
|
.find_by(email: email)
|
|
|
|
|
.update!(instructeur: instructeur)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'keeps the existing instructeurs and adds administrateur' do
|
|
|
|
|
user = subject
|
|
|
|
|
expect(user.instructeur).to eq(instructeur)
|
2019-11-18 17:26:28 +01:00
|
|
|
|
expect(user.instructeur.administrateurs).to match_array(old_admins + admins)
|
2019-08-16 18:15:05 +02:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'with an invalid email' do
|
|
|
|
|
let(:email) { 'invalid' }
|
|
|
|
|
|
|
|
|
|
it 'does not build an instructeur' do
|
|
|
|
|
user = subject
|
|
|
|
|
expect(user.valid?).to be false
|
|
|
|
|
expect(user.instructeur).to be_nil
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2019-11-05 09:12:14 +01:00
|
|
|
|
|
2021-01-15 16:33:36 +01:00
|
|
|
|
describe '.create_or_promote_to_expert' do
|
|
|
|
|
let(:email) { 'exp1@gmail.com' }
|
|
|
|
|
let(:password) { 'un super expert !' }
|
|
|
|
|
|
|
|
|
|
subject { User.create_or_promote_to_expert(email, password) }
|
|
|
|
|
|
|
|
|
|
context 'with an invalid email' do
|
|
|
|
|
let(:email) { 'invalid' }
|
|
|
|
|
|
|
|
|
|
it 'does not build an expert' do
|
|
|
|
|
user = subject
|
|
|
|
|
expect(user.valid?).to be false
|
|
|
|
|
expect(user.expert).to be_nil
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'without an existing user' do
|
|
|
|
|
it do
|
|
|
|
|
user = subject
|
|
|
|
|
expect(user.valid_password?(password)).to be true
|
|
|
|
|
expect(user.confirmed_at).to be_present
|
2024-05-27 10:01:14 +02:00
|
|
|
|
expect(user.email_verified_at).to be_present
|
2021-01-15 16:33:36 +01:00
|
|
|
|
expect(user.expert).to be_present
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'with an existing user' do
|
2023-03-03 14:16:15 +01:00
|
|
|
|
before { create(:user, email: email, password: SECURE_PASSWORD) }
|
2021-01-15 16:33:36 +01:00
|
|
|
|
|
|
|
|
|
it 'keeps the previous password' do
|
|
|
|
|
user = subject
|
2023-03-03 14:16:15 +01:00
|
|
|
|
expect(user.valid_password?(SECURE_PASSWORD)).to be true
|
2021-01-15 16:33:36 +01:00
|
|
|
|
expect(user.expert).to be_present
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'with an existing expert' do
|
|
|
|
|
let!(:expert) { Expert.create }
|
|
|
|
|
|
|
|
|
|
before do
|
|
|
|
|
User
|
|
|
|
|
.find_by(email: email)
|
|
|
|
|
.update!(expert: expert)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it 'keeps the existing experts' do
|
|
|
|
|
user = subject
|
|
|
|
|
expect(user.expert).to eq(expert)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2024-05-27 10:01:14 +02:00
|
|
|
|
describe '.create_or_promote_to_gestionnaire' do
|
|
|
|
|
let(:email) { 'inst1@gmail.com' }
|
2024-06-25 15:56:37 +02:00
|
|
|
|
let(:password) { 'un super p1ssw0rd !' }
|
2024-05-27 10:01:14 +02:00
|
|
|
|
|
|
|
|
|
subject { User.create_or_promote_to_gestionnaire(email, password) }
|
|
|
|
|
|
2024-06-25 15:56:37 +02:00
|
|
|
|
it 'creates a gestionnaire with unverified email' do
|
2024-05-27 10:01:14 +02:00
|
|
|
|
user = subject
|
2024-06-25 15:56:37 +02:00
|
|
|
|
expect(user.email_verified_at).to be_nil
|
|
|
|
|
expect(user.reload.gestionnaire?).to be true
|
2024-05-27 10:01:14 +02:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2019-11-05 09:12:14 +01:00
|
|
|
|
describe 'invite_administrateur!' do
|
2020-11-05 15:09:11 +01:00
|
|
|
|
let(:super_admin) { create(:super_admin) }
|
2024-06-01 22:50:48 +02:00
|
|
|
|
let(:administrateur) { administrateurs(:default_admin) }
|
2019-11-05 09:12:14 +01:00
|
|
|
|
let(:user) { administrateur.user }
|
|
|
|
|
|
2019-11-05 09:52:25 +01:00
|
|
|
|
let(:mailer_double) { double('mailer', deliver_later: true) }
|
|
|
|
|
|
|
|
|
|
before { allow(AdministrationMailer).to receive(:invite_admin).and_return(mailer_double) }
|
|
|
|
|
|
2024-03-19 17:17:44 +01:00
|
|
|
|
subject { user.invite_administrateur! }
|
2019-11-05 09:12:14 +01:00
|
|
|
|
|
|
|
|
|
context 'when the user is inactif' do
|
2019-11-05 09:52:25 +01:00
|
|
|
|
before { subject }
|
2019-11-05 09:12:14 +01:00
|
|
|
|
|
2024-03-19 17:17:44 +01:00
|
|
|
|
it { expect(AdministrationMailer).to have_received(:invite_admin).with(user, kind_of(String)) }
|
2019-11-05 09:12:14 +01:00
|
|
|
|
end
|
2019-11-05 09:37:53 +01:00
|
|
|
|
|
|
|
|
|
context 'when the user is actif' do
|
|
|
|
|
before do
|
|
|
|
|
user.update(last_sign_in_at: Time.zone.now)
|
|
|
|
|
subject
|
|
|
|
|
end
|
|
|
|
|
|
2020-09-17 15:35:40 +02:00
|
|
|
|
it 'receives an invitation to update its password' do
|
2024-03-19 17:17:44 +01:00
|
|
|
|
expect(AdministrationMailer).to have_received(:invite_admin).with(user, kind_of(String))
|
2020-09-17 15:35:40 +02:00
|
|
|
|
end
|
2019-11-05 09:37:53 +01:00
|
|
|
|
end
|
2019-11-05 09:12:14 +01:00
|
|
|
|
end
|
2019-11-05 10:01:07 +01:00
|
|
|
|
|
|
|
|
|
describe '#active?' do
|
|
|
|
|
let!(:user) { create(:user) }
|
|
|
|
|
|
|
|
|
|
subject { user.active? }
|
|
|
|
|
|
|
|
|
|
context 'when the user has never signed in' do
|
|
|
|
|
before { user.update(last_sign_in_at: nil) }
|
|
|
|
|
|
|
|
|
|
it { is_expected.to be false }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when the user has already signed in' do
|
|
|
|
|
before { user.update(last_sign_in_at: Time.zone.now) }
|
|
|
|
|
|
|
|
|
|
it { is_expected.to be true }
|
|
|
|
|
end
|
|
|
|
|
end
|
2020-01-06 17:33:09 +01:00
|
|
|
|
|
|
|
|
|
describe '#can_be_deleted?' do
|
|
|
|
|
let(:user) { create(:user) }
|
2024-06-01 22:50:48 +02:00
|
|
|
|
let(:administrateur) { administrateurs(:default_admin) }
|
2021-05-01 12:20:24 +02:00
|
|
|
|
let(:instructeur) { create(:instructeur) }
|
|
|
|
|
let(:expert) { create(:expert) }
|
2020-01-06 17:33:09 +01:00
|
|
|
|
|
|
|
|
|
subject { user.can_be_deleted? }
|
|
|
|
|
|
|
|
|
|
context 'when the user has a dossier in instruction' do
|
|
|
|
|
let!(:dossier) { create(:dossier, :en_instruction, user: user) }
|
|
|
|
|
|
2021-05-01 12:20:24 +02:00
|
|
|
|
it { is_expected.to be true }
|
2020-01-06 17:33:09 +01:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when the user has no dossier in instruction' do
|
|
|
|
|
it { is_expected.to be true }
|
|
|
|
|
end
|
2020-01-30 10:48:28 +01:00
|
|
|
|
|
|
|
|
|
context 'when the user is an administrateur' do
|
|
|
|
|
it 'cannot be deleted' do
|
2021-05-01 12:20:24 +02:00
|
|
|
|
expect(administrateur.user.can_be_deleted?).to be_falsy
|
2020-01-30 10:48:28 +01:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when the user is an instructeur' do
|
|
|
|
|
it 'cannot be deleted' do
|
2021-05-01 12:20:24 +02:00
|
|
|
|
expect(instructeur.user.can_be_deleted?).to be_falsy
|
|
|
|
|
end
|
|
|
|
|
end
|
2020-01-30 10:48:28 +01:00
|
|
|
|
|
2021-05-01 12:20:24 +02:00
|
|
|
|
context 'when the user is an expert' do
|
|
|
|
|
it 'cannot be deleted' do
|
|
|
|
|
expect(expert.user.can_be_deleted?).to be_falsy
|
2020-01-30 10:48:28 +01:00
|
|
|
|
end
|
|
|
|
|
end
|
2020-01-06 17:33:09 +01:00
|
|
|
|
end
|
2020-01-08 10:50:16 +01:00
|
|
|
|
|
2022-11-14 16:45:32 +01:00
|
|
|
|
describe '#delete_and_keep_track_dossiers_also_delete_user' do
|
2020-11-05 15:09:11 +01:00
|
|
|
|
let(:super_admin) { create(:super_admin) }
|
2020-01-08 10:50:16 +01:00
|
|
|
|
let(:user) { create(:user) }
|
2023-11-17 10:22:34 +01:00
|
|
|
|
let(:reason) { :user_rmoved }
|
2021-05-01 12:20:24 +02:00
|
|
|
|
context 'without a dossier with processing strted' do
|
2020-01-08 10:50:16 +01:00
|
|
|
|
let!(:dossier_en_construction) { create(:dossier, :en_construction, user: user) }
|
|
|
|
|
let!(:dossier_brouillon) { create(:dossier, user: user) }
|
|
|
|
|
|
2020-02-05 16:09:03 +01:00
|
|
|
|
context 'without a discarded dossier' do
|
2020-01-30 10:48:28 +01:00
|
|
|
|
it "keep track of dossiers and delete user" do
|
2023-11-17 10:22:34 +01:00
|
|
|
|
user.delete_and_keep_track_dossiers_also_delete_user(super_admin, reason:)
|
2020-01-08 10:50:16 +01:00
|
|
|
|
|
2022-03-09 10:29:16 +01:00
|
|
|
|
expect(DeletedDossier.find_by(dossier_id: dossier_en_construction)).to be_present
|
2020-03-19 11:53:25 +01:00
|
|
|
|
expect(DeletedDossier.find_by(dossier_id: dossier_brouillon)).to be_nil
|
2020-01-30 10:48:28 +01:00
|
|
|
|
expect(User.find_by(id: user.id)).to be_nil
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-09 10:29:16 +01:00
|
|
|
|
context 'with a deleted dossier' do
|
|
|
|
|
let(:dossier_to_delete) { create(:dossier, :en_construction, user: user) }
|
2021-05-01 12:20:24 +02:00
|
|
|
|
let!(:dossier_from_another_user) { create(:dossier, :en_construction, user: create(:user)) }
|
2020-01-30 10:48:28 +01:00
|
|
|
|
|
|
|
|
|
it "keep track of dossiers and delete user" do
|
2022-03-09 10:29:16 +01:00
|
|
|
|
dossier_to_delete.hide_and_keep_track!(user, :user_request)
|
2023-11-17 10:22:34 +01:00
|
|
|
|
user.delete_and_keep_track_dossiers_also_delete_user(super_admin, reason:)
|
2020-01-30 10:48:28 +01:00
|
|
|
|
|
2022-03-09 10:29:16 +01:00
|
|
|
|
expect(DeletedDossier.find_by(dossier_id: dossier_en_construction)).to be_present
|
2020-03-19 11:53:25 +01:00
|
|
|
|
expect(DeletedDossier.find_by(dossier_id: dossier_brouillon)).to be_nil
|
2021-05-01 12:20:24 +02:00
|
|
|
|
expect(Dossier.find_by(id: dossier_from_another_user.id)).to be_present
|
2020-01-30 10:48:28 +01:00
|
|
|
|
expect(User.find_by(id: user.id)).to be_nil
|
|
|
|
|
end
|
2021-05-01 12:20:24 +02:00
|
|
|
|
end
|
|
|
|
|
end
|
2020-01-30 11:18:01 +01:00
|
|
|
|
|
2021-05-11 17:49:29 +02:00
|
|
|
|
context 'with dossiers with processing started' do
|
2021-05-01 12:20:24 +02:00
|
|
|
|
let!(:dossier_en_instruction) { create(:dossier, :en_instruction, user: user) }
|
|
|
|
|
let!(:dossier_termine) { create(:dossier, :accepte, user: user) }
|
2020-01-30 11:18:01 +01:00
|
|
|
|
|
2021-05-01 12:20:24 +02:00
|
|
|
|
it "keep track of dossiers and delete user" do
|
2023-11-17 10:22:34 +01:00
|
|
|
|
user.delete_and_keep_track_dossiers_also_delete_user(super_admin, reason:)
|
2021-05-01 12:20:24 +02:00
|
|
|
|
|
|
|
|
|
expect(dossier_en_instruction.reload).to be_present
|
|
|
|
|
expect(dossier_en_instruction.user).to be_nil
|
|
|
|
|
expect(dossier_en_instruction.user_email_for(:display)).to eq(user.email)
|
|
|
|
|
expect { dossier_en_instruction.user_email_for(:notification) }.to raise_error(RuntimeError)
|
|
|
|
|
|
|
|
|
|
expect(dossier_termine.reload).to be_present
|
|
|
|
|
expect(dossier_termine.user).to be_nil
|
|
|
|
|
expect(dossier_termine.user_email_for(:display)).to eq(user.email)
|
2021-05-11 17:49:29 +02:00
|
|
|
|
expect(dossier_termine.valid?).to be_truthy
|
2021-05-01 12:20:24 +02:00
|
|
|
|
expect { dossier_termine.user_email_for(:notification) }.to raise_error(RuntimeError)
|
|
|
|
|
|
|
|
|
|
expect(User.find_by(id: user.id)).to be_nil
|
2024-03-21 17:57:57 +01:00
|
|
|
|
expect(FranceConnectInformation.where(user_id: user.id)).to be_empty
|
2020-01-08 10:50:16 +01:00
|
|
|
|
end
|
|
|
|
|
end
|
2024-03-05 11:15:54 +01:00
|
|
|
|
|
|
|
|
|
context 'with fci' do
|
2024-03-05 14:00:54 +01:00
|
|
|
|
let!(:user) { create(:user, france_connect_informations: [build(:france_connect_information), build(:france_connect_information)]) }
|
2024-03-05 11:15:54 +01:00
|
|
|
|
let(:reason) { :user_expired }
|
|
|
|
|
subject { user.delete_and_keep_track_dossiers_also_delete_user(super_admin, reason:) }
|
|
|
|
|
|
|
|
|
|
it { expect { subject }.not_to raise_error }
|
2024-03-05 14:00:54 +01:00
|
|
|
|
it { expect { subject }.to change { FranceConnectInformation.count }.from(2).to(0) }
|
2024-06-01 22:50:48 +02:00
|
|
|
|
it { expect { subject }.to change { User.count }.by(-1) }
|
2024-03-05 11:15:54 +01:00
|
|
|
|
end
|
2020-01-08 10:50:16 +01:00
|
|
|
|
end
|
2020-09-17 15:53:03 +02:00
|
|
|
|
|
|
|
|
|
describe '#password_complexity' do
|
2020-09-17 16:08:08 +02:00
|
|
|
|
# This password list is sorted by password complexity, according to zxcvbn (used for complexity evaluation)
|
|
|
|
|
# 0 - too guessable: risky password. (guesses < 10^3)
|
|
|
|
|
# 1 - very guessable: protection from throttled online attacks. (guesses < 10^6)
|
|
|
|
|
# 2 - somewhat guessable: protection from unthrottled online attacks. (guesses < 10^8)
|
|
|
|
|
# 3 - safely unguessable: moderate protection from offline slow-hash scenario. (guesses < 10^10)
|
|
|
|
|
# 4 - very unguessable: strong protection from offline slow-hash scenario. (guesses >= 10^10)
|
2021-09-09 16:40:23 +02:00
|
|
|
|
passwords = ['password', '12pass23', 'démarches ', 'démarches-simple', '{My-$3cure-p4ssWord}']
|
2020-09-17 16:08:08 +02:00
|
|
|
|
min_complexity = PASSWORD_COMPLEXITY_FOR_ADMIN
|
|
|
|
|
|
2021-09-09 16:40:23 +02:00
|
|
|
|
subject do
|
|
|
|
|
user.valid?
|
|
|
|
|
user.errors.full_messages
|
|
|
|
|
end
|
2020-09-17 16:08:08 +02:00
|
|
|
|
|
2021-09-09 16:40:23 +02:00
|
|
|
|
context 'for administrateurs' do
|
2022-03-15 17:28:22 +01:00
|
|
|
|
let(:user) { build(:user, email: 'admin@exemple.fr', password: password, administrateur: build(:administrateur, user: nil)) }
|
2020-09-17 15:53:03 +02:00
|
|
|
|
|
2021-09-09 16:40:23 +02:00
|
|
|
|
context 'when the password is too short' do
|
2020-09-17 16:08:08 +02:00
|
|
|
|
let(:password) { 's' * (PASSWORD_MIN_LENGTH - 1) }
|
2020-09-17 15:53:03 +02:00
|
|
|
|
|
2021-09-09 16:40:23 +02:00
|
|
|
|
it 'reports an error about password length (but not about complexity)' do
|
2022-12-20 17:51:36 +01:00
|
|
|
|
expect(subject).to eq(["Le champ « Mot de passe » est trop court. Saisir un mot de passe avec au moins 8 caractères"])
|
2021-09-09 16:40:23 +02:00
|
|
|
|
end
|
2020-09-17 16:08:08 +02:00
|
|
|
|
end
|
2020-09-17 15:53:03 +02:00
|
|
|
|
|
2021-09-09 16:40:23 +02:00
|
|
|
|
passwords[0..(min_complexity - 1)].each do |simple_password|
|
|
|
|
|
context 'when the password is long enough, but too simple' do
|
|
|
|
|
let(:password) { simple_password }
|
2020-09-17 16:08:08 +02:00
|
|
|
|
|
2022-12-20 17:51:36 +01:00
|
|
|
|
it { expect(subject).to eq(["Le champ « Mot de passe » n’est pas assez complexe. Saisir un mot de passe plus complexe"]) }
|
2020-09-17 16:08:08 +02:00
|
|
|
|
end
|
|
|
|
|
end
|
2020-09-17 15:53:03 +02:00
|
|
|
|
|
2021-09-09 16:40:23 +02:00
|
|
|
|
context 'when the password is long and complex' do
|
2020-09-17 16:08:08 +02:00
|
|
|
|
let(:password) { passwords[min_complexity] }
|
2020-09-17 15:53:03 +02:00
|
|
|
|
|
2021-09-09 16:40:23 +02:00
|
|
|
|
it { expect(subject).to be_empty }
|
2020-09-17 16:08:08 +02:00
|
|
|
|
end
|
2020-09-17 15:53:03 +02:00
|
|
|
|
end
|
|
|
|
|
|
2021-09-09 16:40:23 +02:00
|
|
|
|
context 'for simple users' do
|
|
|
|
|
let(:user) { build(:user, email: 'user@exemple.fr', password: password) }
|
|
|
|
|
|
|
|
|
|
context 'when the password is too short' do
|
|
|
|
|
let(:password) { 's' * (PASSWORD_MIN_LENGTH - 1) }
|
|
|
|
|
|
|
|
|
|
it 'reports an error about password length (but not about complexity)' do
|
2022-12-20 17:51:36 +01:00
|
|
|
|
expect(subject).to eq(["Le champ « Mot de passe » est trop court. Saisir un mot de passe avec au moins 8 caractères"])
|
2021-09-09 16:40:23 +02:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when the password is long enough, but simple' do
|
|
|
|
|
let(:password) { 'simple-password' }
|
|
|
|
|
|
|
|
|
|
it 'doesn’t enforce the password complexity' do
|
|
|
|
|
expect(subject).to be_empty
|
2020-09-17 16:08:08 +02:00
|
|
|
|
end
|
|
|
|
|
end
|
2020-09-17 15:53:03 +02:00
|
|
|
|
end
|
|
|
|
|
end
|
2021-10-22 15:17:25 +02:00
|
|
|
|
|
|
|
|
|
describe '#merge' do
|
|
|
|
|
let(:old_user) { create(:user) }
|
|
|
|
|
let(:targeted_user) { create(:user) }
|
|
|
|
|
|
|
|
|
|
subject { targeted_user.merge(old_user) }
|
2022-10-13 16:44:20 +02:00
|
|
|
|
context 'merge myself' do
|
|
|
|
|
it 'fails' do
|
|
|
|
|
expect { old_user.merge(old_user) }.to raise_error 'Merging same user, no way'
|
|
|
|
|
end
|
|
|
|
|
end
|
2021-10-28 12:09:35 +02:00
|
|
|
|
context 'and the old account has some stuff' do
|
2021-10-22 15:17:25 +02:00
|
|
|
|
let!(:dossier) { create(:dossier, user: old_user) }
|
2022-03-09 10:29:16 +01:00
|
|
|
|
let!(:hidden_dossier) { create(:dossier, user: old_user, hidden_by_user_at: 1.hour.ago) }
|
2021-10-28 12:09:35 +02:00
|
|
|
|
let!(:invite) { create(:invite, user: old_user) }
|
2021-10-26 14:41:30 +02:00
|
|
|
|
let!(:merge_log) { MergeLog.create(user: old_user, from_user_id: 1, from_user_email: 'a') }
|
2021-10-22 15:17:25 +02:00
|
|
|
|
|
|
|
|
|
it 'transfers the dossier' do
|
|
|
|
|
subject
|
|
|
|
|
|
2022-03-09 10:29:16 +01:00
|
|
|
|
expect(targeted_user.dossiers).to contain_exactly(dossier, hidden_dossier)
|
2021-10-28 12:09:35 +02:00
|
|
|
|
expect(targeted_user.invites).to match([invite])
|
2021-10-26 14:41:30 +02:00
|
|
|
|
expect(targeted_user.merge_logs.first).to eq(merge_log)
|
|
|
|
|
|
|
|
|
|
added_merge_log = targeted_user.merge_logs.last
|
|
|
|
|
expect(added_merge_log.from_user_id).to eq(old_user.id)
|
|
|
|
|
expect(added_merge_log.from_user_email).to eq(old_user.email)
|
2021-10-22 15:17:25 +02:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'and the old account belongs to an instructeur, expert and administrateur' do
|
|
|
|
|
let!(:expert) { create(:expert, user: old_user) }
|
|
|
|
|
let!(:administrateur) { create(:administrateur, user: old_user) }
|
|
|
|
|
let!(:instructeur) { old_user.instructeur }
|
|
|
|
|
|
|
|
|
|
it 'transfers instructeur account' do
|
|
|
|
|
subject
|
|
|
|
|
targeted_user.reload
|
|
|
|
|
|
|
|
|
|
expect(targeted_user.instructeur).to match(instructeur)
|
|
|
|
|
expect(targeted_user.administrateur).to match(administrateur)
|
2022-03-15 17:28:22 +01:00
|
|
|
|
expect(targeted_user.expert).to match(expert)
|
2021-10-22 15:17:25 +02:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'and the targeted account owns an instructeur and expert as well' do
|
|
|
|
|
let!(:targeted_administrateur) { create(:administrateur, user: targeted_user) }
|
|
|
|
|
let!(:targeted_instructeur) { targeted_user.instructeur }
|
|
|
|
|
let!(:targeted_expert) { create(:expert, user: targeted_user) }
|
|
|
|
|
|
|
|
|
|
it 'merge the account' do
|
|
|
|
|
expect(targeted_instructeur).to receive(:merge).with(instructeur)
|
|
|
|
|
expect(targeted_expert).to receive(:merge).with(expert)
|
|
|
|
|
expect(targeted_administrateur).to receive(:merge).with(administrateur)
|
|
|
|
|
|
|
|
|
|
subject
|
2021-10-28 12:09:30 +02:00
|
|
|
|
|
|
|
|
|
expect { instructeur.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
|
|
|
|
expect { expert.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
|
|
|
|
expect { administrateur.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
|
|
|
|
expect { old_user.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
2021-10-22 15:17:25 +02:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2022-05-23 15:09:22 +02:00
|
|
|
|
|
|
|
|
|
context 'and the old account had targeted_user_links' do
|
|
|
|
|
let(:expert) { create(:expert, user: old_user) }
|
|
|
|
|
let(:expert_procedure) { create(:experts_procedure, expert: expert) }
|
|
|
|
|
let!(:targeted_user_link) { create(:targeted_user_link, user: old_user, target_model: create(:avis, experts_procedure: expert_procedure)) }
|
|
|
|
|
|
|
|
|
|
it 'transfers the targeted_user_link' do
|
|
|
|
|
subject
|
|
|
|
|
targeted_user.reload
|
|
|
|
|
expect(targeted_user.targeted_user_links).to include(targeted_user_link)
|
|
|
|
|
end
|
|
|
|
|
end
|
2021-10-22 15:17:25 +02:00
|
|
|
|
end
|
2024-02-12 12:39:43 +01:00
|
|
|
|
|
|
|
|
|
describe 'discard default devise validation when needed' do
|
|
|
|
|
let(:now) { Time.zone.now }
|
|
|
|
|
let(:before) { now - 1.day }
|
|
|
|
|
let(:after) { now + 1.day }
|
|
|
|
|
subject { user.valid? }
|
|
|
|
|
|
|
|
|
|
shared_examples_for "validation of users.email was flacky" do
|
|
|
|
|
context 'when value is username' do
|
|
|
|
|
let(:email) { 'username' }
|
|
|
|
|
it { is_expected.to be_falsey }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when value does not contain extension' do
|
|
|
|
|
let(:email) { 'username@mailserver' }
|
|
|
|
|
# what we allowed but was a mistake
|
|
|
|
|
it { is_expected.to be_truthy }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when value include an alias' do
|
|
|
|
|
let(:email) { 'username+alias@mailserver.fr' }
|
|
|
|
|
it { is_expected.to be_truthy }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when value includes accents' do
|
|
|
|
|
let(:email) { 'tech@démarches.gouv.fr' }
|
|
|
|
|
it { is_expected.to be_falsey }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when value is the classic standard user@domain.ext' do
|
|
|
|
|
let(:email) { 'username@mailserver.domain' }
|
|
|
|
|
it { is_expected.to be_truthy }
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when env var is not present' do
|
|
|
|
|
let(:user) { build(:user, email: email) }
|
|
|
|
|
before { allow(StrictEmailValidator).to receive(:strict_validation_enabled?).and_return(false).at_least(1) }
|
|
|
|
|
it_behaves_like "validation of users.email was flacky"
|
|
|
|
|
end
|
|
|
|
|
|
2024-02-15 09:45:20 +01:00
|
|
|
|
context "record.created_at < ENV['STRICT_EMAIL_VALIDATION_STARTS_ON']" do
|
2024-02-12 12:39:43 +01:00
|
|
|
|
let(:user) { build(:user, email: email, created_at: before) }
|
|
|
|
|
before do
|
|
|
|
|
allow(StrictEmailValidator).to receive(:strict_validation_enabled?).and_return(true).at_least(1)
|
2024-02-15 09:45:20 +01:00
|
|
|
|
stub_const("StrictEmailValidator::DATE_SINCE_STRICT_EMAIL_VALIDATION", now)
|
2024-02-12 12:39:43 +01:00
|
|
|
|
end
|
|
|
|
|
it_behaves_like "validation of users.email was flacky"
|
|
|
|
|
end
|
|
|
|
|
|
2024-02-15 09:45:20 +01:00
|
|
|
|
context "record.created_at > ENV['STRICT_EMAIL_VALIDATION_STARTS_ON']" do
|
2024-02-12 12:39:43 +01:00
|
|
|
|
let(:user) { build(:user, email: email, created_at: after) }
|
|
|
|
|
before do
|
|
|
|
|
allow(StrictEmailValidator).to receive(:strict_validation_enabled?).and_return(true).at_least(1)
|
2024-02-15 09:45:20 +01:00
|
|
|
|
stub_const("StrictEmailValidator::DATE_SINCE_STRICT_EMAIL_VALIDATION", now)
|
2024-02-12 12:39:43 +01:00
|
|
|
|
end
|
|
|
|
|
context 'when value is username' do
|
|
|
|
|
let(:email) { 'username' }
|
|
|
|
|
it { is_expected.to be_falsey }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when value does not contain extension' do
|
|
|
|
|
let(:email) { 'username@mailserver' }
|
|
|
|
|
it { is_expected.to be_falsey }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when value include an alias' do
|
|
|
|
|
let(:email) { 'username+alias@mailserver.fr' }
|
|
|
|
|
it { is_expected.to be_truthy }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when value includes accents' do
|
|
|
|
|
let(:email) { 'tech@démarches.gouv.fr' }
|
|
|
|
|
it { is_expected.to be_truthy }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context 'when value is the classic standard user@domain.ext' do
|
|
|
|
|
let(:email) { 'username@mailserver.domain' }
|
|
|
|
|
it { is_expected.to be_truthy }
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2015-09-23 10:02:01 +02:00
|
|
|
|
end
|