merge with an existing account by using the password

This commit is contained in:
simon lehericey 2021-10-13 09:23:40 +02:00
parent 218e4633a9
commit 19f81b594b
8 changed files with 153 additions and 14 deletions

View file

@ -85,6 +85,10 @@ class ApplicationController < ActionController::Base
end
end
def ajax_redirect(path)
"window.location.href='#{path}'"
end
protected
def feature_enabled?(feature_name)

View file

@ -1,6 +1,6 @@
class FranceConnect::ParticulierController < ApplicationController
before_action :redirect_to_login_if_fc_aborted, only: [:callback]
before_action :securely_retrieve_fci, only: [:merge]
before_action :securely_retrieve_fci, only: [:merge, :merge_with_existing_account]
def login
if FranceConnectService.enabled?
@ -41,6 +41,28 @@ class FranceConnect::ParticulierController < ApplicationController
def merge
end
def merge_with_existing_account
user = User.find_by(email: sanitized_email_params)
if user.valid_for_authentication? { user.valid_password?(password_params) }
if !user.can_france_connect?
flash.alert = "#{user.email} ne peut utiliser FranceConnect"
render js: ajax_redirect(root_path)
else
@fci.update(user: user)
@fci.delete_merge_token!
flash.notice = "Les comptes FranceConnect et #{APPLICATION_NAME} sont à présent fusionnés"
connect_france_connect_particulier(user)
end
else
flash.alert = 'Mauvais mot de passe'
render js: helpers.render_flash
end
end
private
def securely_retrieve_fci
@ -49,7 +71,10 @@ class FranceConnect::ParticulierController < ApplicationController
if @fci.nil? || !@fci.valid_for_merge?
flash.alert = 'Votre compte FranceConnect a expiré, veuillez recommencer.'
redirect_to root_path
respond_to do |format|
format.html { redirect_to root_path }
format.js { render js: ajax_redirect(root_path) }
end
end
end
@ -68,7 +93,12 @@ class FranceConnect::ParticulierController < ApplicationController
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)
redirection_location = stored_location_for(current_user) || root_path(current_user)
respond_to do |format|
format.html { redirect_to redirection_location }
format.js { render js: ajax_redirect(root_path) }
end
end
def redirect_france_connect_error_connection
@ -79,4 +109,12 @@ class FranceConnect::ParticulierController < ApplicationController
def merge_token_params
params[:merge_token]
end
def password_params
params[:password]
end
def sanitized_email_params
params[:email]&.gsub(/[[:space:]]/, ' ')&.strip&.downcase
end
end

View file

@ -0,0 +1,5 @@
import { show, hide } from '@utils';
export function showFusion() {
show(document.querySelector('.fusion'));
}

View file

@ -41,6 +41,9 @@ import {
acceptEmailSuggestion,
discardEmailSuggestionBox
} from '../new_design/user-sign_up';
import {
showFusion
} from '../new_design/fc-fusion';
// This is the global application namespace where we expose helpers used from rails views
const DS = {
@ -49,6 +52,7 @@ const DS = {
showMotivation,
motivationCancel,
showImportJustificatif,
showFusion,
replaceSemicolonByComma,
acceptEmailSuggestion,
discardEmailSuggestionBox

View file

@ -52,4 +52,8 @@ class FranceConnectInformation < ApplicationRecord
def valid_for_merge?
(MERGE_VALIDITY.ago < merge_token_created_at) && user_id.nil?
end
def delete_merge_token!
update(merge_token: nil, merge_token_created_at: nil)
end
end

View file

@ -10,3 +10,24 @@
Votre compte FranceConnect utilise <b class='bold'>#{@fci.email_france_connect}</b> comme email de contact.
%br
Or il existe un compte sur #{APPLICATION_NAME} avec cet email.
.form.mt-2
%label Ce compte #{@fci.email_france_connect} vous appartient-il ?
%fieldset.radios
%label{ onclick: "DS.showFusion(event);" }
= radio_button_tag :value, true, false, autocomplete: "off"
Oui
%label
= radio_button_tag :value, false, false, autocomplete: "off"
Non
.fusion.hidden
%p Pour les fusionner, entrez votre mot de passe
= form_tag france_connect_particulier_merge_with_existing_account_path, remote: true, class: 'mt-2 form' do
= hidden_field_tag :merge_token, @fci.merge_token
= hidden_field_tag :email, @fci.email_france_connect
= label_tag :password, 'Mot de passe (8 caractères minimum)'
= password_field_tag :password, nil, autocomplete: 'current-password'
= submit_tag 'Fusionner les comptes', class: 'button primary'

View file

@ -125,6 +125,7 @@ Rails.application.routes.draw do
get 'particulier' => 'particulier#login'
get 'particulier/callback' => 'particulier#callback'
get 'particulier/merge/:merge_token' => 'particulier#merge', as: :particulier_merge
post 'particulier/merge_with_existing_account' => 'particulier#merge_with_existing_account'
end
namespace :champs do

View file

@ -136,16 +136,7 @@ describe FranceConnect::ParticulierController, type: :controller do
end
end
describe '#merge' do
let(:fci) { FranceConnectInformation.create!(user_info) }
let(:merge_token) { fci.create_merge_token! }
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
RSpec.shared_examples "a method that needs a valid merge token" do
context 'when the merge token is invalid' do
before do
merge_token
@ -153,10 +144,29 @@ describe FranceConnect::ParticulierController, type: :controller do
end
it do
expect(subject).to redirect_to root_path
if format == :js
subject
expect(response.body).to eq("window.location.href='/'")
else
expect(subject).to redirect_to root_path
end
expect(flash.alert).to eq('Votre compte FranceConnect a expiré, veuillez recommencer.')
end
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' }
@ -167,4 +177,56 @@ describe FranceConnect::ParticulierController, type: :controller do
end
end
end
describe '#merge_with_existing_account' do
let(:fci) { FranceConnectInformation.create!(user_info) }
let(:merge_token) { fci.create_merge_token! }
let(:email) { 'EXISTING_account@a.com ' }
let(:password) { 'my-s3cure-p4ssword' }
let(:format) { :js }
subject { post :merge_with_existing_account, params: { merge_token: merge_token, email: email, password: password }, format: format }
it_behaves_like "a method that needs a valid merge token"
context 'when the credentials are ok' do
let!(:user) { create(:user, email: email, password: password) }
it 'merges the account, signs in, and delete the merge token' do
subject
fci.reload
expect(fci.user).to eq(user)
expect(fci.merge_token).to be_nil
expect(controller.current_user).to eq(user)
end
context 'but the targeted user is an instructeur' do
let!(:user) { create(:instructeur, email: email, password: password).user }
it 'redirects to the root page' 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
end
end
end
context 'when the credentials are not ok' do
let!(:user) { create(:user, email: email, password: 'another password #$21$%%') }
it 'increases the failed attempts counter' 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(user.reload.failed_attempts).to eq(1)
end
end
end
end