Merge pull request #10570 from demarches-simplifiees/feat/10425

ETQ Usager se connectant par FC, je dois confirmer mon mail
This commit is contained in:
LeSim 2024-09-05 08:07:23 +00:00 committed by GitHub
commit 9fd53b182a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 660 additions and 332 deletions

View file

@ -2,8 +2,9 @@
class FranceConnect::ParticulierController < ApplicationController
before_action :redirect_to_login_if_fc_aborted, only: [:callback]
before_action :securely_retrieve_fci, only: [:merge, :merge_with_existing_account, :merge_with_new_account, :resend_and_renew_merge_confirmation]
before_action :securely_retrieve_fci_from_email_merge_token, only: [:mail_merge_with_existing_account]
before_action :securely_retrieve_fci, only: [:merge_using_fc_email, :merge_using_password, :send_email_merge_request]
before_action :securely_retrieve_fci_from_email_merge_token, only: [:merge_using_email_link]
before_action :set_user_by_confirmation_token, only: [:confirm_email]
def login
if FranceConnectService.enabled?
@ -14,112 +15,136 @@ class FranceConnect::ParticulierController < ApplicationController
end
def callback
fci = FranceConnectService.find_or_retrieve_france_connect_information(params[:code])
@fci = FranceConnectService.find_or_retrieve_france_connect_information(params[:code])
if fci.user.nil?
preexisting_unlinked_user = User.find_by(email: sanitize(fci.email_france_connect))
if @fci.user.nil?
preexisting_unlinked_user = User.find_by(email: sanitize(@fci.email_france_connect))
if preexisting_unlinked_user.nil?
fci.associate_user!(fci.email_france_connect)
connect_france_connect_particulier(fci.user)
elsif !preexisting_unlinked_user.can_france_connect?
fci.destroy
redirect_to new_user_session_path, alert: t('errors.messages.france_connect.forbidden_html', reset_link: new_user_password_path)
@fci.create_merge_token!
render :choose_email
elsif preexisting_unlinked_user.can_france_connect?
@fci.create_merge_token!
render :merge
else
merge_token = fci.create_merge_token!
redirect_to france_connect_particulier_merge_path(merge_token)
destroy_fci_and_redirect_to_login(@fci)
end
else
user = fci.user
if user.can_france_connect?
fci.update(updated_at: Time.zone.now)
connect_france_connect_particulier(user)
else # same behaviour as redirect nicely with message when instructeur/administrateur
fci.destroy
redirect_to new_user_session_path, alert: t('errors.messages.france_connect.forbidden_html', reset_link: new_user_password_path)
if @fci.user.can_france_connect?
@fci.update(updated_at: Time.zone.now)
connect_france_connect_particulier(@fci.user)
else
destroy_fci_and_redirect_to_login(@fci)
end
end
rescue Rack::OAuth2::Client::Error => e
Rails.logger.error e.message
redirect_france_connect_error_connection
redirect_to(new_user_session_path, alert: t('errors.messages.france_connect.connexion'))
end
def merge
end
def send_email_merge_request
@fci.update(requested_email: sanitized_email_params)
def merge_with_existing_account
user = User.find_by(email: sanitized_email_params)
if user.present? && user.valid_for_authentication? { user.valid_password?(password_params) }
if !user.can_france_connect?
flash.alert = t('errors.messages.france_connect.forbidden_html', reset_link: new_user_password_path)
redirect_to root_path
else
@fci.update(user: user)
@fci.delete_merge_token!
@fci.delete_email_merge_token!
flash.notice = t('france_connect.particulier.flash.connection_done', application_name: Current.application_name)
connect_france_connect_particulier(user)
end
else
flash.alert = t('france_connect.particulier.flash.invalid_password')
end
end
def mail_merge_with_existing_account
user = User.find_by(email: sanitize(@fci.email_france_connect.downcase))
if user.can_france_connect?
@fci.update(user: user)
@fci.delete_merge_token!
flash.notice = t('france_connect.particulier.flash.connection_done', application_name: Current.application_name)
connect_france_connect_particulier(user)
else # same behaviour as redirect nicely with message when instructeur/administrateur
@fci.destroy
redirect_to new_user_session_path, alert: t('errors.messages.france_connect.forbidden_html', reset_link: new_user_password_path)
end
end
def merge_with_new_account
user = User.find_by(email: sanitized_email_params)
if user.nil?
@fci.associate_user!(sanitized_email_params)
@fci.delete_merge_token!
flash.notice = t('france_connect.particulier.flash.connection_done', application_name: Current.application_name)
connect_france_connect_particulier(@fci.user)
else
@email = sanitized_email_params
@merge_token = merge_token_params
end
end
def resend_and_renew_merge_confirmation
@fci.create_email_merge_token!
UserMailer.france_connect_merge_confirmation(
@fci.email_france_connect,
sanitized_email_params,
@fci.email_merge_token,
@fci.email_merge_token_created_at
)
.deliver_later
merge_token = @fci.create_merge_token!
redirect_to france_connect_particulier_merge_path(merge_token),
notice: t('france_connect.particulier.flash.confirmation_mail_sent')
redirect_to root_path, notice: t('france_connect.particulier.flash.confirmation_mail_sent')
end
def merge_using_fc_email
@fci.safely_associate_user!(@fci.email_france_connect)
sign_in(@fci.user)
@fci.send_custom_confirmation_instructions
render :confirmation_sent, locals: { email: @fci.email_france_connect, destination_path: destination_path(@fci.user) }
end
def merge_using_password
user = User.find_by(email: sanitize(@fci.email_france_connect))
if user.present? && !user.can_france_connect?
return destroy_fci_and_redirect_to_login(@fci)
end
if user.present? && user.valid_for_authentication? { user.valid_password?(params[:password]) }
@fci.safely_update_user(user:)
flash.notice = t('france_connect.particulier.flash.connection_done', application_name: Current.application_name)
connect_france_connect_particulier(user)
else
flash.alert = t('france_connect.particulier.flash.invalid_password')
end
end
def merge_using_email_link
user = User.find_by(email: @fci.requested_email)
if user.present? && !user.can_france_connect?
return destroy_fci_and_redirect_to_login(@fci)
end
if user.nil?
@fci.safely_associate_user!(@fci.requested_email)
else
@fci.safely_update_user(user:)
end
@fci.user.update(email_verified_at: Time.zone.now)
flash.notice = t('france_connect.particulier.flash.connection_done', application_name: Current.application_name)
connect_france_connect_particulier(@fci.user)
end
# TODO mutualiser avec le controller Users::ActivateController
# pour toute la partie de confirmation de compte
def confirm_email
if @user.confirmation_sent_at && 2.days.ago < @user.confirmation_sent_at
@user.update(email_verified_at: Time.zone.now, confirmation_token: nil)
@user.after_confirmation
redirect_to destination_path(@user), notice: I18n.t('france_connect.particulier.flash.email_confirmed')
return
end
fci = FranceConnectInformation.find_by(user: @user)
if fci
fci.send_custom_confirmation_instructions
redirect_to root_path, notice: I18n.t('france_connect.particulier.flash.confirmation_mail_resent')
else
redirect_to root_path, alert: I18n.t('france_connect.particulier.flash.confirmation_mail_resent_error')
end
end
private
def set_user_by_confirmation_token
@user = User.find_by(confirmation_token: params[:token])
if @user.nil?
return redirect_to root_path, alert: I18n.t('france_connect.particulier.flash.user_not_found')
end
if user_signed_in? && current_user != @user
sign_out :user
redirect_to new_user_session_path, alert: I18n.t('france_connect.particulier.flash.redirect_new_user_session')
end
end
def destination_path(user) = stored_location_for(user) || root_path(user)
def securely_retrieve_fci_from_email_merge_token
@fci = FranceConnectInformation.find_by(email_merge_token: email_merge_token_params)
@fci = FranceConnectInformation.find_by(email_merge_token: params[:email_merge_token])
if @fci.nil? || !@fci.valid_for_email_merge?
flash.alert = t('france_connect.particulier.flash.merger_token_expired', application_name: Current.application_name)
flash.alert = I18n.t('france_connect.particulier.flash.merger_token_expired', application_name: Current.application_name)
redirect_to root_path
else
@ -128,10 +153,10 @@ class FranceConnect::ParticulierController < ApplicationController
end
def securely_retrieve_fci
@fci = FranceConnectInformation.find_by(merge_token: merge_token_params)
@fci = FranceConnectInformation.find_by(merge_token: params[:merge_token])
if @fci.nil? || !@fci.valid_for_merge?
flash.alert = t('france_connect.particulier.flash.merger_token_expired', application_name: Current.application_name)
flash.alert = I18n.t('france_connect.particulier.flash.merger_token_expired', application_name: Current.application_name)
redirect_to root_path
end
@ -143,33 +168,18 @@ class FranceConnect::ParticulierController < ApplicationController
end
end
def connect_france_connect_particulier(user)
if user_signed_in?
sign_out :user
end
def destroy_fci_and_redirect_to_login(fci)
fci.destroy
redirect_to new_user_session_path, alert: t('errors.messages.france_connect.forbidden_html', reset_link: new_user_password_path)
end
def connect_france_connect_particulier(user)
sign_out :user if user_signed_in?
sign_in user
user.update_attribute('loged_in_with_france_connect', User.loged_in_with_france_connects.fetch(:particulier))
redirect_to stored_location_for(current_user) || root_path(current_user)
end
def redirect_france_connect_error_connection
flash.alert = t('errors.messages.france_connect.connexion')
redirect_to(new_user_session_path)
end
def merge_token_params
params[:merge_token]
end
def email_merge_token_params
params[:email_merge_token]
end
def password_params
params[:password]
redirect_to destination_path(current_user)
end
def sanitized_email_params

View file

@ -0,0 +1,33 @@
import { ApplicationController } from './application_controller';
export class EmailFranceConnectController extends ApplicationController {
static targets = ['useFranceConnectEmail', 'emailField'];
emailFieldTarget!: HTMLElement;
useFranceConnectEmailTargets!: HTMLInputElement[];
connect() {
this.triggerEmailField();
}
triggerEmailField() {
const checkedTarget = this.useFranceConnectEmailTargets.find(
(target) => target.checked
);
const inputElement = this.emailFieldTarget.querySelector(
'input[type="email"]'
) as HTMLInputElement;
if (checkedTarget && checkedTarget.value === 'false') {
this.emailFieldTarget.classList.remove('hidden');
this.emailFieldTarget.setAttribute('aria-hidden', 'false');
inputElement.setAttribute('required', '');
} else {
this.emailFieldTarget.classList.add('hidden');
this.emailFieldTarget.setAttribute('aria-hidden', 'true');
inputElement.removeAttribute('required');
inputElement.value = '';
}
}
}

View file

@ -36,6 +36,12 @@ class UserMailer < ApplicationMailer
mail(to: email, subject: @subject)
end
def custom_confirmation_instructions(user, token)
@user = user
@token = token
mail(to: @user.email, subject: 'Confirmez votre email')
end
def invite_instructeur(user, reset_password_token)
@reset_password_token = reset_password_token
@user = user
@ -139,7 +145,8 @@ class UserMailer < ApplicationMailer
'france_connect_merge_confirmation',
"new_account_warning",
"ask_for_merge",
"invite_instructeur"
"invite_instructeur",
"custom_confirmation_instructions"
].include?(action_name)
end
end

View file

@ -2,28 +2,41 @@
class FranceConnectInformation < ApplicationRecord
MERGE_VALIDITY = 15.minutes
CONFIRMATION_EMAIL_VALIDITY = 2.days
belongs_to :user, optional: true
validates :france_connect_particulier_id, presence: true, allow_blank: false, allow_nil: false
def associate_user!(email)
def safely_associate_user!(email)
begin
user = User.create!(
email: email.downcase,
password: Devise.friendly_token[0, 20],
confirmed_at: Time.zone.now
)
user.after_confirmation
rescue ActiveRecord::RecordNotUnique
# ignore this exception because we check before is user is nil.
# ignore this exception because we check before if user is nil.
# exception can be raised in race conditions, when FranceConnect calls callback 2 times.
# At the 2nd call, user is nil but exception is raised at the creation of the user
# because the first call has already created a user
end
clean_tokens_and_requested_email
update_attribute('user_id', user.id)
touch # needed to update updated_at column
save!
end
def safely_update_user(user:)
self.user = user
clean_tokens_and_requested_email
save!
end
def send_custom_confirmation_instructions
token = SecureRandom.hex(10)
user.update!(confirmation_token: token, confirmation_sent_at: Time.zone.now)
UserMailer.custom_confirmation_instructions(user, token).deliver_later
end
def create_merge_token!
@ -48,14 +61,18 @@ class FranceConnectInformation < ApplicationRecord
(MERGE_VALIDITY.ago < email_merge_token_created_at) && user_id.nil?
end
def delete_merge_token!
update(merge_token: nil, merge_token_created_at: nil)
end
def delete_email_merge_token!
update(email_merge_token: nil, email_merge_token_created_at: nil)
end
def clean_tokens_and_requested_email
self.merge_token = nil
self.merge_token_created_at = nil
self.email_merge_token = nil
self.email_merge_token_created_at = nil
self.requested_email = nil
end
def full_name
[given_name, family_name].compact.join(" ")
end

View file

@ -65,7 +65,6 @@ class User < ApplicationRecord
# Callback provided by Devise
def after_confirmation
update!(email_verified_at: Time.zone.now)
link_invites!
end

View file

@ -1,16 +1,7 @@
%p
= t('.already_exists', email: email, application_name: Current.application_name)
%br
= t('.fill_in_password')
= form_tag france_connect_particulier_merge_using_password_path, data: { turbo: true }, class: 'mt-2 form fconnect-form', id: 'merge_using_password' do
= hidden_field_tag :merge_token, fci.merge_token, id: dom_id(fci, :fusion_merge_token)
.fr-input-group{ class: class_names('fr-input-group--error': wrong_password) }
= label_tag :password, t('views.registrations.new.password_label', min_length: 8), class: 'fr-label'
= password_field_tag :password, nil, autocomplete: 'current-password', class: 'mb-1 fr-input'
= form_tag france_connect_particulier_merge_with_existing_account_path, data: { turbo: true, turbo_force: :server }, class: 'mt-2 form fconnect-form' do
= hidden_field_tag :merge_token, merge_token
= hidden_field_tag :email, email
= label_tag :password, t('views.registrations.new.password_label', min_length: 8)
= password_field_tag :password, nil, autocomplete: 'current-password', id: 'password-for-another-account'
.mb-2
= t('views.users.sessions.new.reset_password')
= link_to france_connect_particulier_resend_and_renew_merge_confirmation_path(merge_token: merge_token), method: :post do
= t('france_connect.particulier.merge.link_confirm_by_email')
= button_tag t('.back'), type: 'button', class: 'button secondary', onclick: 'DS.showNewAccount(event);'
= submit_tag t('france_connect.particulier.merge.button_merge'), class: 'button primary'
= submit_tag t('france_connect.particulier.merge.button_merge'), class: 'fr-btn'

View file

@ -0,0 +1,40 @@
.fr-container
%h1.text-center.mt-1= t('.choose_email_contact')
%p= t('.intro_html', email: @fci.email_france_connect)
%p= t('.use_email_for_notifications')
.fr-fieldset.fr-w-30v.fr-mt-2w
= form_with url: france_connect_particulier_merge_using_fc_email_path(merge_token: @fci.merge_token), method: :post, data: { controller: 'email-france-connect' } do |f|
= hidden_field_tag :merge_token, @fci.merge_token
%fieldset.fr-fieldset
%legend.fr-fieldset__legend
.fr-fieldset__element
.fr-radio-group
= f.radio_button :use_france_connect_email, true, id: 'use_france_connect_email_yes', class: 'fr-radio', required: true, data: { action: "email-france-connect#triggerEmailField", email_france_connect_target: "useFranceConnectEmail" }
%label.fr-label.fr-text--wrap{ for: 'use_france_connect_email_yes' }
= t('.keep_fc_email_html', email: h(@fci.email_france_connect)).html_safe
.fr-fieldset__element
.fr-radio-group
= f.radio_button :use_france_connect_email, false, id: 'use_france_connect_email_no', class: 'fr-radio', required: true, data: { action: "email-france-connect#triggerEmailField", email_france_connect_target: "useFranceConnectEmail" }
%label.fr-label.fr-text--wrap{ for: 'use_france_connect_email_no' }
= t('.use_another_email')
.fr-fieldset__element.fr-fieldset__element--inline.hidden{ aria: { hidden: true }, data: { email_france_connect_target: "emailField", controller: 'email-input', email_input_url_value: show_email_suggestions_path } }
= f.label :email, t('.alternative_email'), class: "fr-label"
%span.fr-hint-text.mb-1= t('activerecord.attributes.user.hints.email')
= f.email_field :email, class: "fr-input"
.suspect-email.hidden{ data: { "email-input-target": 'ariaRegion'}, aria: { live: 'off' } }
= render Dsfr::AlertComponent.new(title: t('utils.email_suggest.wanna_say'), state: :info, heading_level: :div) do |c|
- c.with_body do
%p{ data: { "email-input-target": 'suggestion'} } exemple@gmail.com &nbsp;?
%p
= button_tag type: 'button', class: 'fr-btn fr-btn--sm fr-mr-3w', data: { action: 'click->email-input#accept'} do
= t('utils.yes')
= button_tag type: 'button', class: 'fr-btn fr-btn--sm', data: { action: 'click->email-input#discard'} do
= t('utils.no')
%div
= f.submit t('.confirm'), class: 'fr-btn'

View file

@ -0,0 +1,12 @@
.fr-container
.fr-col-12.fr-col-md-6.fr-col-offset-md-3
%h1.fr-mt-6w.fr-h2.center= t('.confirmation_sent_by_email')
%p.center{ aria: { hidden: true } }= image_tag("user/confirmation-email.svg", alt: t('views.confirmation.new.image_alt'))
= render Dsfr::AlertComponent.new(title: '', state: :info, heading_level: 'h2', extra_class_names: 'fr-mt-6w fr-mb-3w') do |c|
- c.with_body do
%p= t('.intro_html', email: h(email)).html_safe
%p= t('.click_the_link_in_the_email')
%p.center= link_to t('.continue'), destination_path, class: 'fr-btn'

View file

@ -1,46 +1,42 @@
= content_for :title, "Fusion des comptes FC et #{Current.application_name}"
.container
.fr-container
%h1.page-title= t('.title', application_name: Current.application_name)
%p= t('.subtitle_html', email: @fci.email_france_connect, application_name: Current.application_name)
.form.mt-2
%label= t('.label_select_merge_flow', email: @fci.email_france_connect)
%fieldset.radios
%label{ onclick: "DS.showFusion(event);" }
= radio_button_tag :value, true, false, autocomplete: "off", id: 'it-is-mine'
= t('utils.yes')
%fieldset.fr-fieldset{ aria: { labelledby: 'merge-account' } }
%legend.fr-fieldset__legend#merge-account= t('.label_select_merge_flow', email: @fci.email_france_connect)
.fr-fieldset__element.fr-fieldset__element--inline
.fr-radio-group
%input{ type: 'radio', id: 'it-is-mine', name: 'value', value: 'true', autocomplete: "off", onclick: "DS.showFusion(event);" }
%label{ for: 'it-is-mine' }= t('utils.yes')
.fr-fieldset__element.fr-fieldset__element--inline
.fr-radio-group
%input{ type: 'radio', id: 'it-is-not-mine', name: 'value', value: 'false', autocomplete: "off", onclick: "DS.showNewAccount(event);" }
%label{ for: 'it-is-not-mine' }= t('utils.no')
%label{ onclick: "DS.showNewAccount(event);" }
= radio_button_tag :value, false, false, autocomplete: "off", id: 'it-is-not-mine'
= t('utils.no')
.fusion.hidden
%p= t('.title_fill_in_password')
= form_tag france_connect_particulier_merge_with_existing_account_path, data: { turbo: true }, class: 'mt-2 form fconnect-form' do
= hidden_field_tag :merge_token, @fci.merge_token, id: dom_id(@fci, :fusion_merge_token)
= hidden_field_tag :email, @fci.email_france_connect, id: dom_id(@fci, :fusion_email)
.fr-input-group
= label_tag :password, t('views.registrations.new.password_label', min_length: 8), class: 'fr-label'
= password_field_tag :password, nil, autocomplete: 'current-password', class: 'mb-1 fr-input'
.mb-2
= t('views.users.sessions.new.reset_password')
= link_to france_connect_particulier_resend_and_renew_merge_confirmation_path(merge_token: @fci.merge_token), method: :post do
= t('.link_confirm_by_email')
= render partial: 'password_confirmation', locals: { fci: @fci, wrong_password: @wrong_password }
= submit_tag t('.button_merge'), class: 'fr-btn'
.mt-2
= button_to t('.link_confirm_by_email'),
france_connect_particulier_send_email_merge_request_path,
params: { email: @fci.email_france_connect, merge_token: @fci.merge_token },
class: 'fr-btn fr-btn--secondary'
.new-account.hidden
%p= t('.title_fill_in_email', application_name: Current.application_name)
= form_tag france_connect_particulier_merge_with_new_account_path, data: { turbo: true }, class: 'mt-2 form' do
= form_tag france_connect_particulier_send_email_merge_request_path, class: 'mt-2 form' do
= hidden_field_tag :merge_token, @fci.merge_token, id: dom_id(@fci, :new_account_merge_token)
= label_tag :email, t('views.registrations.new.email_label'), for: dom_id(@fci, :new_account_email)
= email_field_tag :email, "", required: true, id: dom_id(@fci, :new_account_email)
= submit_tag t('.button_use_this_email'), class: 'button primary'
= label_tag :email, t('views.registrations.new.email_label'), for: dom_id(@fci, :new_account_email), class: 'fr-label'
= email_field_tag :email, "", required: true, id: dom_id(@fci, :new_account_email), class: 'mb-1 fr-input'
= submit_tag t('.button_use_this_email'), class: 'fr-btn'
#new-account-password-confirmation.hidden

View file

@ -0,0 +1 @@
= turbo_stream.replace('merge_using_password', partial: 'password_confirmation', locals: { fci: @fci, wrong_password: true })

View file

@ -1,4 +0,0 @@
= turbo_stream.update 'new-account-password-confirmation', partial: 'password_confirmation', locals: { email: @email, merge_token: @merge_token }
= turbo_stream.hide_all '.fusion'
= turbo_stream.hide_all '.new-account'
= turbo_stream.show 'new-account-password-confirmation'

View file

@ -0,0 +1,22 @@
- content_for(:title, 'Confirmez votre email')
%p
Bonjour
= @user.email
!
%p
Veuillez confirmer votre email en cliquant sur le lien ci-dessous:
= round_button 'Je confirme', france_connect_confirm_email_url(@token), :primary
%p Ce lien est valide #{distance_of_time_in_words(FranceConnectInformation::CONFIRMATION_EMAIL_VALIDITY)}.
%p
Tant que vous n'aurez pas confirmé votre email, vous ne recevrez aucune notification sur l'avancement de vos dossiers.
%p
Si vous nêtes pas à lorigine de cette demande, vous pouvez ignorer ce message. Et si vous avez besoin dassistance, nhésitez pas à nous contacter à
= succeed '.' do
= mail_to CONTACT_EMAIL
= render partial: "layouts/mailers/signature"

View file

@ -1,5 +1,5 @@
- content_for(:title, @subject)
- merge_link = france_connect_particulier_mail_merge_with_existing_account_url(email_merge_token: @email_merge_token)
- merge_link = france_connect_particulier_merge_using_email_link_url(email_merge_token: @email_merge_token)
%p
Bonjour,

View file

@ -47,6 +47,9 @@ en:
utils:
'yes': 'Yes'
'no': 'No'
email_suggest:
wanna_say: 'Do you mean to say ?'
deconnexion: "Log out"
pj: "Attachments"
asterisk_html: "Fields marked by an asterisk ( <svg aria-label='required' class='icon mandatory' height='10' role='img' viewBox='0 0 1200 1200' width='10' xml:space='preserve' xmlns='http://www.w3.org/2000/svg'><desc>required</desc><path d='M489.838 29.354v443.603L68.032 335.894 0 545.285l421.829 137.086-260.743 358.876 178.219 129.398L600.048 811.84l260.673 358.806 178.146-129.398-260.766-358.783L1200 545.379l-68.032-209.403-421.899 137.07V29.443H489.84l-.002-.089z'></path></svg> ) are mandatory."
@ -150,6 +153,8 @@ en:
subtitle_two: "Additional notes"
content_html: "<p class=\"fr-mb-2w\">The documentation pages are managed by a third-party tool. They are not fully accessible.</p>
<p class=\"fr-mb-2w\">FAQ management was delegated to a third-party tool. It was reintegrated into the platform in May 2024 and has not yet been audited.</p>"
preparation:
title: "Preparation of this accessibility declaration"
intro: "This declaration was drawn up on 27 April 2022. It was updated on 14 June 2024."
@ -746,6 +751,7 @@ en:
# # etablissement_fail: 'Désolé, nous navons pas réussi à enregistrer létablissement correspondant à ce numéro SIRET'
france_connect:
connexion: "Error trying to connect to France Connect."
forbidden_html: "Only citizen can use FranceConnect. As an instructor or administrator, you should <a href='%{reset_link}'>reset your password</a>."
evil_regexp: The regular expression you have entered is potentially dangerous and could lead to performance issues.
mismatch_regexp: The provided example must match the regular expression
syntax_error_regexp: The syntax of the regular expression is invalid
@ -879,10 +885,19 @@ en:
to_follow: to follow
france_connect:
particulier:
password_confirmation:
back: 'back to previous step'
already_exists: An account with %{email} already existis on %{application_name}
fill_in_password: fill in your password to merge your accounts
choose_email:
intro_html: "Your FranceConnect account uses <span class='fr-badge fr-badge--info fr-badge--sm'>%{email}</span> as the contact email."
use_email_for_notifications: "Would you like to use it to receive notifications regarding the progress of your cases?"
confirm: "Confirm"
choose_email_contact: "Choose your contact email"
alternative_email: "Please provide the email to use for contacting you."
keep_fc_email_html: Yes, use <b class='bold'>%{email}</b> as contact email.
use_another_email: No, use another address.
confirmation_sent:
confirmation_sent_by_email: "Confirm your email"
intro_html: "A confirmation email has been sent to your address <span class='fr-badge fr-badge--info fr-badge--sm'>%{email}</span>"
click_the_link_in_the_email: "Please click the link in the email to confirm your account and connect with France Connect in the future."
continue: "Continue"
merge:
title: "Merge your account FranceConnect and %{application_name}"
subtitle_html: "Hello,<br /><br />Your account FranceConnect uses <b class='bold'>%{email}</b> as contact email.<br />But there is an existing %{application_name} account using this email."
@ -894,6 +909,11 @@ en:
link_confirm_by_email: Confirm by receiving an email
flash:
confirmation_mail_sent: "An email with the confirmation link has been sent, please click on the link."
confirmation_mail_resent: "Confirmation link expired. A new link has been sent by email."
confirmation_mail_resent_error: "An unexpected error has occurred. Please contact support if the problem persists."
redirect_new_user_session: "You have been disconnected from your previous account. Please click on the confirmation link again."
email_confirmed: "Your email is confirmed"
user_not_found: "User not found"
invalid_password: "The password is not correct."
connection_done: "The accounts for FranceConnect and %{application_name} are now merged."
merger_token_expired: "Le delay to merge your FranceConnect and %{application_name} accounts is expired. Please retry."

View file

@ -37,6 +37,9 @@ fr:
utils:
'yes': Oui
'no': Non
email_suggest:
wanna_say: 'Voulez-vous dire ?'
i_dont_know: Je ne sais pas
deconnexion: "Déconnexion"
pj: "Pièces jointes"
@ -935,10 +938,21 @@ fr:
ministeres: Ministères
france_connect:
particulier:
password_confirmation:
back: 'revenir en arrière'
already_exists: Le compte %{email} existe déjà sur %{application_name}
fill_in_password: entrez votre mot de passe pour fusionner les comptes
choose_email:
intro_html: "Votre compte FranceConnect utilise <span class='fr-badge fr-badge--info fr-badge--sm'>%{email}</span> comme email de contact."
use_email_for_notifications: Souhaitez-vous l'utiliser pour recevoir les notifications concernant l'avancement de vos dossiers ?
confirm: Confirmer
choose_email_contact: Choisissez votre email de contact pour finaliser votre connexion
alternative_email: Veuillez nous fournir l'email à utiliser pour vous contacter.
keep_fc_email_html: "Oui, utiliser %{email} comme email de contact."
use_another_email: Non, utiliser une autre adresse.
email_suggest:
wanna_say: 'Voulez-vous dire ?'
confirmation_sent:
confirmation_sent_by_email: Confirmez votre email
intro_html: "Un mail de confirmation a été envoyé à votre adresse <span class='fr-badge fr-badge--info fr-badge--sm'>%{email}</span>"
click_the_link_in_the_email: Vous devez impérativement cliquer sur le lien du mail pour activer votre adresse et recevoir les notifications sur l'avancement de vos dossiers.
continue: Continuer
merge:
title: "Fusion des comptes FranceConnect et %{application_name}"
subtitle_html: "Bonjour,<br /><br />Votre compte FranceConnect utilise <b class='bold'>%{email}</b> comme email de contact.<br />Or il existe un compte sur %{application_name} avec cet email."
@ -950,7 +964,12 @@ fr:
link_confirm_by_email: Confirmer mon compte par email
flash:
confirmation_mail_sent: "Nous venons de vous envoyer le mail de confirmation, veuillez cliquer sur le lien contenu dans ce mail pour fusionner vos comptes"
invalid_password: "Mauvais mot de passe"
confirmation_mail_resent: "Le lien de confirmation a expiré. Un nouveau lien de confirmation vous a été envoyé par email."
confirmation_mail_resent_error: "Une erreur inattendue est survenue. Veuillez contacter le support si le problème persiste."
redirect_new_user_session: "Vous avez été déconnecté de votre précédent compte. Veuillez cliquer à nouveau sur le lien de confirmation."
email_confirmed: 'Votre email est bien vérifié'
user_not_found: 'Utilisateur non trouvé'
invalid_password: "Mot de passe incorrect"
connection_done: "Les comptes FranceConnect et %{application_name} sont à présent fusionnés"
merger_token_expired: "Le délai pour fusionner les comptes FranceConnect et %{application_name} est expiré. Veuillez recommencer la procédure pour vous fusionner les comptes."
groupe_gestionnaires:

View file

@ -188,11 +188,14 @@ Rails.application.routes.draw do
namespace :france_connect do
get 'particulier' => 'particulier#login'
get 'particulier/callback' => 'particulier#callback'
get 'particulier/merge/:merge_token' => 'particulier#merge', as: :particulier_merge
get 'particulier/mail_merge_with_existing_account/:email_merge_token' => 'particulier#mail_merge_with_existing_account', as: :particulier_mail_merge_with_existing_account
post 'particulier/resend_and_renew_merge_confirmation' => 'particulier#resend_and_renew_merge_confirmation', as: :particulier_resend_and_renew_merge_confirmation
post 'particulier/merge_with_existing_account' => 'particulier#merge_with_existing_account'
post 'particulier/merge_with_new_account' => 'particulier#merge_with_new_account'
post 'particulier/send_email_merge_request'
post 'particulier/merge_using_fc_email'
post 'particulier/merge_using_password'
get 'particulier/merge_using_email_link/:email_merge_token' => 'particulier#merge_using_email_link', as: :particulier_merge_using_email_link
get 'confirm_email/:token', to: 'particulier#confirm_email', as: :confirm_email
end
namespace :agent_connect do

View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddAlternativeEmailColumnToFranceConnectInformationTable < ActiveRecord::Migration[7.0]
def change
add_column :france_connect_informations, :requested_email, :string
end
end

View file

@ -712,6 +712,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_08_29_141049) do
t.string "given_name"
t.string "merge_token"
t.datetime "merge_token_created_at", precision: nil
t.string "requested_email"
t.datetime "updated_at", precision: nil, null: false
t.integer "user_id"
t.index ["email_merge_token"], name: "index_france_connect_informations_on_email_merge_token"

View file

@ -89,39 +89,25 @@ describe FranceConnect::ParticulierController, type: :controller do
let(:fc_user) { nil }
context 'and no user with the same email exists' do
it 'creates an user with the same email and log in' do
expect { subject }.to change { User.count }.by(1)
user = User.last
expect(user.email).to eq(email.downcase)
expect(controller.current_user).to eq(user)
expect(response).to redirect_to(root_path)
end
context 'when invites are pending' do
let!(:invite) { create(:invite, email: email, user: nil) }
it 'links pending invites' do
expect(invite.reload.user).to eq(nil)
subject
expect(invite.reload.user).to eq(User.last)
end
it 'render the choose email template to select good email' do
expect { subject }.to change { User.count }.by(0)
expect(subject).to render_template(:choose_email)
end
end
context 'and an user with the same email exists' do
let!(:preexisting_user) { create(:user, email: email) }
it 'redirects to the merge process' do
it 'renders the merge page' do
expect { subject }.not_to change { User.count }
expect(response).to redirect_to(france_connect_particulier_merge_path(fci.reload.merge_token))
expect(response).to render_template(:merge)
end
end
context 'and an instructeur with the same email exists' do
let!(:preexisting_user) { create(:instructeur, email: email) }
it 'redirects to the merge process' do
it 'redirects to the login path' do
expect { subject }.not_to change { User.count }
expect(response).to redirect_to(new_user_session_path)
@ -134,15 +120,7 @@ describe FranceConnect::ParticulierController, type: :controller do
context 'when france_connect_particulier_id does not exist in database' do
it { expect { subject }.to change { FranceConnectInformation.count }.by(1) }
describe 'FranceConnectInformation attributs' do
let(:stored_fci) { FranceConnectInformation.last }
before { subject }
it { expect(stored_fci).to have_attributes(user_info.merge(birthdate: Time.zone.parse(birthdate).to_datetime)) }
end
it { is_expected.to redirect_to(root_path) }
it { is_expected.to render_template(:choose_email) }
end
end
@ -158,6 +136,167 @@ describe FranceConnect::ParticulierController, type: :controller do
end
end
describe '#merge_using_fc_email' do
subject { post :merge_using_fc_email, params: { merge_token: merge_token } }
let!(:fci) { FranceConnectInformation.create!(user_info) }
let(:merge_token) { fci.create_merge_token! }
before do
allow(UserMailer).to receive_message_chain(:custom_confirmation_instructions, :deliver_later)
end
context 'when the merge token is valid' do
it do
expect(User.last.email).not_to eq(email.downcase)
subject
user = User.last
expect(user.email).to eq(email.downcase)
expect(UserMailer).to have_received(:custom_confirmation_instructions).with(user, user.confirmation_token)
expect(user.email_verified_at).to be_nil
expect(fci.reload.merge_token).to be_nil
expect(response).to render_template(:confirmation_sent)
end
end
context 'when the merge token is invalid' do
let(:merge_token) { 'invalid_token' }
it 'redirects to root_path with an alert' do
subject
expect(response).to redirect_to(root_path)
expect(flash[:alert]).to eq("Le délai pour fusionner les comptes FranceConnect et demarches-simplifiees.fr est expiré. Veuillez recommencer la procédure pour vous fusionner les comptes.")
end
end
context 'when @fci is not valid for merge' do
before do
merge_token
fci.update!(merge_token_created_at: 2.years.ago)
end
it 'redirects to root_path with an alert' do
subject
expect(response).to redirect_to(root_path)
expect(flash[:alert]).to eq('Le délai pour fusionner les comptes FranceConnect et demarches-simplifiees.fr est expiré. Veuillez recommencer la procédure pour vous fusionner les comptes.')
end
end
end
describe '#confirm_email' do
let!(:user) { create(:user) }
let!(:fci) { create(:france_connect_information, user: user) }
before { fci.send_custom_confirmation_instructions }
context 'when the confirmation token is valid' do
before do
get :confirm_email, params: { token: user.confirmation_token }
user.reload
end
it do
expect(user.email_verified_at).to be_present
expect(user.confirmation_token).to be_nil
expect(response).to redirect_to(root_path(user))
expect(flash[:notice]).to eq('Votre email est bien vérifié')
end
end
context 'when invites are pending' do
let!(:invite) { create(:invite, email: user.email, user: nil) }
it 'links pending invites' do
get :confirm_email, params: { token: user.confirmation_token }
invite.reload
expect(invite.user).to eq(user)
end
end
context 'when the confirmation token is expired' do
let!(:expired_user_confirmation) do
create(:user, confirmation_token: 'expired_token', confirmation_sent_at: 3.days.ago)
end
it 'redirects to root path with an alert when FranceConnectInformation is not found' do
get :confirm_email, params: { token: 'expired_token' }
expect(response).to redirect_to(root_path)
expect(flash[:alert]).to eq(I18n.t('france_connect.particulier.flash.confirmation_mail_resent_error'))
end
context 'when FranceConnectInformation exists' do
let!(:france_connect_information) do
create(:france_connect_information, user: expired_user_confirmation)
end
before do
allow(UserMailer).to receive_message_chain(:custom_confirmation_instructions, :deliver_later)
end
it 'resends the confirmation email and redirects to root path with a notice' do
get :confirm_email, params: { token: 'expired_token' }
expect(UserMailer).to have_received(:custom_confirmation_instructions)
.with(expired_user_confirmation, expired_user_confirmation.reload.confirmation_token)
expect(response).to redirect_to(root_path)
expect(flash[:notice]).to eq(I18n.t('france_connect.particulier.flash.confirmation_mail_resent'))
end
end
end
context 'when a different user is signed in' do
let!(:expired_user_confirmation) do
create(:user, confirmation_token: 'expired_token', confirmation_sent_at: 3.days.ago)
end
let(:another_user) { create(:user) }
before { sign_in(another_user) }
it 'signs out the current user and redirects to sign in path' do
expect_any_instance_of(FranceConnectInformation).not_to receive(:send_custom_confirmation_instructions)
expect(controller).to receive(:sign_out).with(:user)
get :confirm_email, params: { token: 'expired_token' }
expect(response).to redirect_to(new_user_session_path)
expect(flash[:alert]).to eq(I18n.t('france_connect.particulier.flash.redirect_new_user_session'))
end
end
end
describe '#set_user_by_confirmation_token' do
let(:current_user) { create(:user) }
let!(:confirmation_user) { create(:user, confirmation_token: 'valid_token') }
before { sign_in current_user }
it 'signs out current user and redirects to new session path when users do not match' do
expect(controller).to receive(:sign_out).with(:user)
get :confirm_email, params: { token: 'valid_token' }
expect(response).to redirect_to(new_user_session_path)
expect(flash[:alert]).to eq(I18n.t('france_connect.particulier.flash.redirect_new_user_session'))
end
context 'when user is not found' do
it 'redirects to root path with user not found alert' do
get :confirm_email, params: { token: 'invalid_token' }
expect(response).to redirect_to(root_path)
expect(flash[:alert]).to eq(I18n.t('france_connect.particulier.flash.user_not_found'))
end
end
end
RSpec.shared_examples "a method that needs a valid merge token" do
context 'when the merge token is invalid' do
before do
@ -178,41 +317,14 @@ describe FranceConnect::ParticulierController, type: :controller do
end
end
describe '#merge' do
let(:fci) { FranceConnectInformation.create!(user_info) }
let(:merge_token) { fci.create_merge_token! }
let(:format) { :html }
subject { get :merge, params: { merge_token: merge_token } }
context 'when the merge token is valid' do
it { expect(subject).to have_http_status(:ok) }
end
it_behaves_like "a method that needs a valid merge token"
context 'when the merge token does not exist' do
let(:merge_token) { 'i do not exist' }
before do
allow(Current).to receive(:application_name).and_return('demarches-simplifiees.fr')
end
it do
expect(subject).to redirect_to root_path
expect(flash.alert).to eq("Le délai pour fusionner les comptes FranceConnect et demarches-simplifiees.fr est expiré. Veuillez recommencer la procédure pour vous fusionner les comptes.")
end
end
end
describe '#merge_with_existing_account' do
describe '#merge_using_password' do
let(:fci) { FranceConnectInformation.create!(user_info) }
let(:merge_token) { fci.create_merge_token! }
let(:email) { 'EXISTING_account@a.com ' }
let(:password) { SECURE_PASSWORD }
let(:format) { :turbo_stream }
subject { post :merge_with_existing_account, params: { merge_token: merge_token, email: email, password: password }, format: format }
subject { post :merge_using_password, params: { merge_token: merge_token, password: password }, format: format }
it_behaves_like "a method that needs a valid merge token"
@ -244,9 +356,9 @@ describe FranceConnect::ParticulierController, type: :controller do
it 'redirects to the root page' do
subject
fci.reload
expect(fci.user).to be_nil
expect { fci.reload }.to raise_error(ActiveRecord::RecordNotFound)
expect(fci.merge_token).not_to be_nil
expect(controller.current_user).to be_nil
end
@ -268,18 +380,21 @@ describe FranceConnect::ParticulierController, type: :controller do
end
end
describe '#mail_merge_with_existing_account' do
describe '#merge_using_email_link' do
let(:fci) { FranceConnectInformation.create!(user_info) }
let!(:email_merge_token) { fci.create_email_merge_token! }
context 'when the merge_token is ok and the user is found' do
subject { post :mail_merge_with_existing_account, params: { email_merge_token: } }
subject do
post :merge_using_email_link, params: { email_merge_token: }
end
before do
allow(Current).to receive(:application_name).and_return('demarches-simplifiees.fr')
fci.update!(requested_email: email.downcase)
end
let!(:user) { create(:user, email: email, password: 'abcdefgh') }
let!(:user) { create(:user, email:, password: 'abcdefgh') }
it 'merges the account, signs in, and delete the merge token' do
subject
@ -304,82 +419,26 @@ describe FranceConnect::ParticulierController, type: :controller do
end
end
end
context 'when the email_merge_token is not ok' do
subject { post :mail_merge_with_existing_account, params: { email_merge_token: 'ko' } }
let!(:user) { create(:user, email: email) }
it 'increases the failed attempts counter' do
subject
fci.reload
expect(fci.user).to be_nil
expect(fci.email_merge_token).not_to be_nil
expect(controller.current_user).to be_nil
expect(response).to redirect_to(root_path)
end
end
end
describe '#merge_with_new_account' do
describe '#send_email_merge_request' do
let(:fci) { FranceConnectInformation.create!(user_info) }
let(:merge_token) { fci.create_merge_token! }
let(:email) { ' Account@a.com ' }
let(:format) { :turbo_stream }
let(:email) { 'requested_email@.a.com' }
subject { post :merge_with_new_account, params: { merge_token: merge_token, email: email }, format: format }
subject { post :send_email_merge_request, params: { merge_token: merge_token, email: } }
it_behaves_like "a method that needs a valid merge token"
context 'when the email does not belong to any user' do
it 'creates the account, signs in, and delete the merge token' do
subject
fci.reload
expect(fci.user.email).to eq(email.downcase.strip)
expect(fci.merge_token).to be_nil
expect(controller.current_user).to eq(fci.user)
expect(response).to redirect_to(root_path)
end
end
context 'when an account with the same email exists' do
let!(:user) { create(:user, email: email) }
before { allow(controller).to receive(:sign_in).and_call_original }
render_views
it 'asks for the corresponding password' do
subject
fci.reload
expect(fci.user).to be_nil
expect(fci.merge_token).not_to be_nil
expect(controller.current_user).to be_nil
expect(response.body).to include('entrez votre mot de passe')
end
it 'cannot use the merge token in the email confirmation route' do
subject
fci.reload
get :mail_merge_with_existing_account, params: { email_merge_token: fci.merge_token }
expect(controller).not_to have_received(:sign_in)
expect(flash[:alert]).to be_present
end
end
end
describe '#resend_and_renew_merge_confirmation' do
let(:fci) { FranceConnectInformation.create!(user_info) }
let(:merge_token) { fci.create_merge_token! }
it 'renew token' do
expect { post :resend_and_renew_merge_confirmation, params: { merge_token: merge_token } }.to change { fci.reload.merge_token }
allow(UserMailer).to receive_message_chain(:france_connect_merge_confirmation, :deliver_later)
subject
fci.reload
expect(fci.requested_email).to eq(email)
expect(fci.email_merge_token).to be_present
expect(response).to redirect_to(france_connect_particulier_merge_path(fci.reload.merge_token))
expect(UserMailer).to have_received(:france_connect_merge_confirmation).with(email, fci.email_merge_token, fci.email_merge_token_created_at)
expect(response).to redirect_to(root_path)
end
end
end

View file

@ -81,7 +81,7 @@ RSpec.describe UserMailer, type: :mailer do
it 'sends to correct email with merge link' do
expect(subject.to).to eq([email])
expect(subject.body).to include(france_connect_particulier_mail_merge_with_existing_account_url(email_merge_token: code))
expect(subject.body).to include(france_connect_particulier_merge_using_email_link_url(email_merge_token: code))
end
context 'without SafeMailer configured' do
@ -101,6 +101,31 @@ RSpec.describe UserMailer, type: :mailer do
end
end
describe '#custom_confirmation_instructions' do
let(:user) { create(:user, email: 'user@example.com') }
let(:token) { 'confirmation_token_123' }
let(:mail) { UserMailer.custom_confirmation_instructions(user, token) }
it 'renders the headers' do
expect(mail.subject).to eq('Confirmez votre email')
expect(mail.to).to eq([user.email])
expect(mail.from).to eq(['contact@demarches-simplifiees.fr'])
end
it 'renders the body' do
expect(mail.body.encoded).to match(user.email)
expect(mail.body.encoded).to match(token)
end
it 'assigns @user' do
expect(mail.body.encoded).to match(user.email)
end
it 'assigns @token' do
expect(mail.body.encoded).to include(token)
end
end
describe '.send_archive' do
let(:procedure) { create(:procedure) }
let(:archive) { create(:archive) }

View file

@ -9,19 +9,67 @@ describe FranceConnectInformation, type: :model do
end
end
describe 'associate_user!' do
context 'when there is no user with same email' do
let(:email) { 'A@email.com' }
let(:fci) { build(:france_connect_information) }
describe 'safely_associate_user!' do
let(:email) { 'A@email.com' }
let(:fci) { build(:france_connect_information) }
subject { fci.associate_user!(email) }
subject { fci.safely_associate_user!(email) }
it { expect { subject }.to change(User, :count).by(1) }
context 'when there is no user with the same email' do
it 'creates a new user' do
expect { subject }.to change(User, :count).by(1)
end
it do
it 'sets the correct attributes on the user' do
subject
expect(fci.user.email).to eq('a@email.com')
expect(fci.user.email_verified_at).to be_present
user = User.find_by(email: email.downcase)
expect(user).not_to be_nil
expect(user.confirmed_at).to be_present
end
it 'associates the user with the FranceConnectInformation' do
subject
expect(fci.reload.user.email).to eq(email.downcase)
end
end
context 'when a user with the same email already exists due to race condition' do
let!(:existing_user) { create(:user, email: email.downcase) }
let!(:fci) { create(:france_connect_information) } # Assurez-vous que fci est créé et sauvegardé
before do
call_count = 0
allow(User).to receive(:create!).and_wrap_original do
call_count += 1
if call_count == 1
raise ActiveRecord::RecordNotUnique
else
existing_user
end
end
allow(fci).to receive(:send_custom_confirmation_instructions)
end
it 'raises an error' do
expect { fci.safely_associate_user!(email) }.to raise_error(NoMethodError)
end
it 'does not create a new user' do
expect {
begin
fci.safely_associate_user!(email)
rescue NoMethodError
end
}.to_not change(User, :count)
end
it 'does not associate with any user' do
expect(fci.user).to be_nil
begin
fci.safely_associate_user!(email)
rescue NoMethodError
end
expect(fci.reload.user).to be_nil
end
end
end

View file

@ -18,12 +18,6 @@ describe User, type: :model do
user.confirm
expect(user.reload.invites.size).to eq(2)
end
it 'verifies its email' do
expect(user.email_verified_at).to be_nil
user.confirm
expect(user.email_verified_at).to be_present
end
end
describe '#owns?' do

View file

@ -43,9 +43,25 @@ describe 'France Connect Particulier Connexion' do
before { page.find('.fr-connect').click }
scenario 'he is redirected to user dossiers page' do
expect(page).to have_content('Dossiers')
expect(page).to have_content("Choisissez votre email de contact pour finaliser votre connexion")
find("#use_france_connect_email_no").click
fill_in("email", with: "exemple@email.com")
page.find("input[type='submit'][name='commit'][value='Confirmer']").click
expect(page).to have_content("Confirmez votre email")
click_on 'Continuer'
expect(User.find_by(email: email)).not_to be nil
end
scenario 'he can choose not to use FranceConnect email and input an alternative email' do
expect(page).to have_content("Choisissez votre email de contact pour finaliser votre connexion")
expect(page).to have_selector("input[name='email']", visible: true, wait: 10)
fill_in 'email', with: 'alternative@example.com'
click_on 'Confirmer'
expect(page).to have_content("Confirmez votre email")
end
end
context 'and an user exists with the same email' do
@ -72,25 +88,23 @@ describe 'France Connect Particulier Connexion' do
fill_in 'email', with: 'new_email@a.com'
click_on 'Utiliser ce mail'
expect(page).to have_content('Dossiers')
expect(page).to have_content('Nous venons de vous envoyer le mail de confirmation')
end
context 'and the user wants an email that belongs to another account', js: true do
let!(:another_user) { create(:user, email: 'an_existing_email@a.com', password: SECURE_PASSWORD) }
scenario 'it uses another email that belongs to another account' do
page.find('#it-is-not-mine').click
fill_in 'email', with: 'an_existing_email@a.com'
click_on 'Utiliser ce mail'
find('label[for="it-is-not-mine"]').click
expect(page).to have_css('#password-for-another-account', visible: true)
expect(page).to have_css('.new-account', visible: true)
within '#new-account-password-confirmation' do
fill_in 'password', with: SECURE_PASSWORD
click_on 'Fusionner les comptes'
within '.new-account' do
fill_in 'email', with: 'an_existing_email@a.com'
click_on 'Utiliser ce mail'
end
expect(page).to have_content('Dossiers')
expect(page).to have_content('Nous venons de vous envoyer le mail de confirmation')
end
end
end

View file

@ -191,7 +191,15 @@ describe 'Prefilling a dossier (with a GET request):', js: true do
page.find('.fr-connect').click
click_on "Poursuivre mon dossier prérempli"
expect(page).to have_content("Choisissez votre email de contact pour finaliser votre connexion")
expect(page).to have_selector("#use_france_connect_email_no", visible: false, wait: 10)
page.execute_script('document.getElementById("use_france_connect_email_no").click()')
fill_in("email", with: "exemple@email.com")
page.find("input[type='submit'][name='commit'][value='Confirmer']").click
expect(page).to have_content("Confirmez votre email")
click_on 'Continuer'
expect(page).to have_content('Vous avez un dossier prérempli')
find('.fr-btn.fr-mb-2w', text: 'Poursuivre mon dossier prérempli', wait: 10).click
end
end
end

View file

@ -142,9 +142,15 @@ describe 'Prefilling a dossier (with a POST request):', js: true do
allow(FranceConnectService).to receive(:retrieve_user_informations_particulier).and_return(build(:france_connect_information))
page.find('.fr-connect').click
expect(page).to have_content("Choisissez votre email de contact pour finaliser votre connexion")
expect(page).to have_selector("#use_france_connect_email_yes", visible: false, wait: 10)
page.execute_script('document.getElementById("use_france_connect_email_yes").click()')
click_on 'Confirmer'
expect(page).to have_content("Confirmez votre email")
click_on 'Continuer'
expect(page).to have_content('Vous avez un dossier prérempli')
click_on 'Poursuivre mon dossier prérempli'
find('.fr-btn.fr-mb-2w', text: 'Poursuivre mon dossier prérempli', wait: 10).click
end
end
end