Merge pull request #1264 from betagouv/partially_clean_fc_part2

Partially clean fc part2
This commit is contained in:
LeSim 2018-01-17 16:55:30 +01:00 committed by GitHub
commit 154181d793
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 92 additions and 394 deletions

View file

@ -1,87 +1,39 @@
class FranceConnect::ParticulierController < ApplicationController
before_action :redirect_to_login_if_fc_aborted, only: [:callback]
def login
redirect_to FranceConnectService.authorization_uri
end
def callback
if params[:code].nil?
return redirect_to new_user_session_path
fetched_fci = FranceConnectService.retrieve_user_informations_particulier(params[:code])
fci = FranceConnectInformation
.find_by(france_connect_particulier_id: fetched_fci[:france_connect_particulier_id]) ||
fetched_fci.tap { |object| object.save }
if fci.user.nil?
user = User.find_or_create_by(email: fci.email_france_connect) do |new_user|
new_user.password = Devise.friendly_token[0, 20]
end
fci.update_attribute('user_id', user.id)
end
fetched_fc_information = FranceConnectService.retrieve_user_informations_particulier(params[:code])
france_connect_information = FranceConnectInformation
.find_by(france_connect_particulier_id: fetched_fc_information[:france_connect_particulier_id])
if france_connect_information.nil?
fetched_fc_information.save
france_connect_information = fetched_fc_information
end
user = france_connect_information.user
salt = FranceConnectSaltService.new(france_connect_information).salt
if user.nil?
redirect_to france_connect_particulier_new_path(fci_id: france_connect_information.id, salt: salt)
else
connect_france_connect_particulier(user)
end
connect_france_connect_particulier(fci.user)
rescue Rack::OAuth2::Client::Error => e
Rails.logger.error e.message
redirect_france_connect_error_connection
end
def new
return redirect_france_connect_error_connection if !valid_salt_and_fci_id_params?
france_connect_information = FranceConnectInformation.find(params[:fci_id])
@user = User.new(france_connect_information: france_connect_information).decorate
rescue ActiveRecord::RecordNotFound
redirect_france_connect_error_connection
end
def check_email
return redirect_france_connect_error_connection if !valid_salt_and_fci_id_params?
user = User.find_by_email(params[:user][:email_france_connect])
return create if user.nil?
if params[:user][:password].present?
if user.valid_password?(params[:user][:password])
user.france_connect_information = FranceConnectInformation.find(params[:fci_id])
return connect_france_connect_particulier user
else
flash.now.alert = 'Mot de passe invalide'
end
end
france_connect_information = FranceConnectInformation.find(params[:fci_id])
france_connect_information.update_attribute(:email_france_connect, params[:user][:email_france_connect])
@user = User.new(france_connect_information: france_connect_information).decorate
end
def create
user = User.new email: params[:user][:email_france_connect]
user.password = Devise.friendly_token[0, 20]
if !user.valid?
flash.alert = 'Email non valide'
return redirect_to france_connect_particulier_new_path fci_id: params[:fci_id], salt: params[:salt], user: {email_france_connect: params[:user]['email_france_connect']}
end
user.save
FranceConnectInformation.find(params[:fci_id]).update_attribute(:user, user)
connect_france_connect_particulier user
end
private
def redirect_to_login_if_fc_aborted
if params[:code].empty?
redirect_to new_user_session_path
end
end
def connect_france_connect_particulier user
sign_out :user if user_signed_in?
sign_out :gestionnaire if gestionnaire_signed_in?
@ -89,19 +41,13 @@ class FranceConnect::ParticulierController < ApplicationController
sign_in user
user.loged_in_with_france_connect = 'particulier'
user.save
user.update_attribute('loged_in_with_france_connect', 'particulier')
redirect_to stored_location_for(current_user) || signed_in_root_path(current_user)
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 valid_salt_and_fci_id_params?
france_connect_information = FranceConnectInformation.find(params[:fci_id])
FranceConnectSaltService.new(france_connect_information).valid? params[:salt]
end
end

View file

@ -1,16 +0,0 @@
class FranceConnectSaltService
attr_reader :model
def initialize france_connect_information
raise 'Not a FranceConnectInformation class' if france_connect_information.class != FranceConnectInformation
@model = france_connect_information
end
def valid? test_salt
salt == test_salt
end
def salt
Digest::MD5.hexdigest(model.france_connect_particulier_id + model.given_name + model.family_name + FRANCE_CONNECT[:particulier][:secret] + DateTime.now.to_date.to_s)
end
end

View file

@ -1,23 +0,0 @@
%h2.text-info
= image_tag('logo_FC_02_small.png', style: 'height: 55px;')
&nbsp;France Connect - Particulier
%h3 Nouvelle connexion
%h4.text-warning{ style: 'margin-left: 20px;' } Email déjà utilisé
%br
%p
%h4.center Nous avons trouvé un compte qui utilise déjà cette adresse email.
%p.center
Afin d'associer ce compte à votre identifiant France Connect, merci de saisir votre mot de passe TPS.
%br
.center
#france-connect-particulier-email
= form_for @user, url: { controller: 'france_connect/particulier', action: :check_email }, method: :post do |f|
.form-group.form-group-lg
= f.text_field :email_france_connect, class: "form-control", readonly: 'readonly'
%br
= f.password_field :password, class: "form-control", placeholder: "Entrez votre mot de passe"
= hidden_field_tag :fci_id, params[:fci_id]
= hidden_field_tag :salt, params[:salt]
= f.submit 'Terminer', class: %w(btn btn-lg btn-success), style: 'margin-top: 20px;', id: 'valid_new_fcp'

View file

@ -1,32 +0,0 @@
%h2.text-info
= image_tag('logo_FC_02_small.png', style: 'height: 55px;')
&nbsp;France Connect - Particulier
%h3 Nouvelle connexion
%br
%p
Nous vous avons identifié comme étant
%h4.text-info.center
%strong
= @user.gender_fr
= @user.given_name
= @user.family_name
né le
%strong
= @user.birthdate_fr
%br
%h4
Afin de finaliser votre première connexion à TPS, merci de saisir un email valide :
%br
.center
#france-connect-particulier-email
= form_for @user, url: { controller: 'france_connect/particulier', action: :check_email }, method: :post do |f|
.form-group.form-group-lg
= f.text_field :email_france_connect, class: "form-control", placeholder: "Entrez votre email"
= hidden_field_tag :fci_id, params[:fci_id]
= hidden_field_tag :salt, params[:salt]
= f.submit 'Terminer', class: %w(btn btn-lg btn-success), style: 'margin-top: 20px;', id: 'valid_new_fcp'

View file

@ -65,10 +65,6 @@ Rails.application.routes.draw do
namespace :france_connect do
get 'particulier' => 'particulier#login'
get 'particulier/callback' => 'particulier#callback'
get 'particulier/new' => 'particulier#new'
post 'particulier/create' => 'particulier#create'
post 'particulier/check_email' => 'particulier#check_email'
end
namespace :users do

View file

@ -1,225 +1,103 @@
require 'spec_helper'
describe FranceConnect::ParticulierController, type: :controller do
let(:code) { 'plop' }
let(:given_name) { 'titi' }
let(:family_name) { 'toto' }
let(:birthdate) { '20150821' }
let(:gender) { 'M' }
let(:birthplace) { '1234' }
let(:france_connect_particulier_id) { 'blabla' }
let(:email) { 'test@test.com' }
let(:password) { '' }
let(:user_info) { { france_connect_particulier_id: france_connect_particulier_id, given_name: given_name, family_name: family_name, birthdate: birthdate, birthplace: birthplace, gender: gender, email_france_connect: email } }
describe '.auth' do
it 'redirect to france connect serveur' do
get :login
expect(response.status).to eq(302)
end
let(:user_info) do
{
france_connect_particulier_id: 'blablabla',
given_name: 'titi',
family_name: 'toto',
birthdate: birthdate,
birthplace: '1234',
gender: 'M',
email_france_connect: email
}
end
describe '.callback' do
describe '#auth' do
subject { get :login }
it { is_expected.to have_http_status(:redirect) }
end
describe '#callback' do
let(:code) { 'plop' }
subject { get :callback, params: {code: code} }
context 'when param code is missing' do
it 'redirect to login page' do
get :callback
expect(response).to redirect_to(new_user_session_path)
end
let(:code) { nil }
it { is_expected.to redirect_to(new_user_session_path) }
end
context 'when params code is present' do
context 'when code is correct' do
before do
allow(FranceConnectService).to receive(:retrieve_user_informations_particulier)
.and_return(FranceConnectInformation.new(user_info))
end
context 'when param code is empty' do
let(:code) { '' }
context 'when france_connect_particulier_id exist in database' do
let!(:france_connect_information) { create(:france_connect_information, france_connect_particulier_id: france_connect_particulier_id, given_name: given_name, family_name: family_name, birthdate: birthdate, gender: gender, birthplace: birthplace) }
it { is_expected.to redirect_to(new_user_session_path) }
end
context {
subject { get :callback, params: {code: code} }
context 'when code is correct' do
before do
allow(FranceConnectService).to receive(:retrieve_user_informations_particulier)
.and_return(FranceConnectInformation.new(user_info))
end
it 'does not create a new france_connect_information in database' do
expect { subject }.not_to change { FranceConnectInformation.count }
end
}
context 'when france_connect_particulier_id exist in database' do
let!(:france_connect_information) { create(:france_connect_information, user_info) }
context 'when france_connect_particulier_id have an associate user' do
before do
create(:user, email: email, france_connect_information: france_connect_information)
it { expect { subject }.not_to change { FranceConnectInformation.count } }
get :callback, params: {code: code}
end
context 'when france_connect_particulier_id have an associate user' do
let!(:user) { create(:user, email: 'plop@plop.com', france_connect_information: france_connect_information) }
let(:email) { 'plop@plop.com' }
let(:current_user) { User.find_by_email(email) }
it do
subject
expect(user.reload.loged_in_with_france_connect).to eq('particulier')
end
context 'and the user has a stored location' do
let(:stored_location) { '/plip/plop' }
before { controller.store_location_for(:user, stored_location) }
it 'current user have attribut loged_in_with_france_connect? at true' do
expect(current_user.loged_in_with_france_connect?).to be_truthy
end
it 'redirect to stored location' do
subject.store_location_for(:user, stored_location)
get :callback, params: {code: code}
expect(response).to redirect_to(stored_location)
end
end
context 'when france_connect_particulier_id does not have an associate user' do
let(:salt) { FranceConnectSaltService.new(france_connect_information).salt }
before do
get :callback, params: {code: code}
end
it 'redirects to check email FC page' do
expect(response).to redirect_to(france_connect_particulier_new_path(fci_id: france_connect_information.id, salt: salt))
end
it { is_expected.to redirect_to(stored_location) }
end
end
context 'when france_connect_particulier_id does not exist in database' do
let(:last_france_connect_information) { FranceConnectInformation.last }
let(:salt) { FranceConnectSaltService.new(last_france_connect_information).salt }
subject { get :callback, params: {code: code} }
context 'when france_connect_particulier_id does not have an associate user' do
it { is_expected.to redirect_to(root_path) }
it { expect { subject }.to change { FranceConnectInformation.count }.by(1) }
describe 'FranceConnectInformation attributs' do
before do
get :callback, params: {code: code}
end
subject { last_france_connect_information }
it { expect(subject.gender).to eq gender }
it { expect(subject.given_name).to eq given_name }
it { expect(subject.family_name).to eq family_name }
it { expect(subject.email_france_connect).to eq email }
it { expect(subject.birthdate.to_time.to_i).to eq birthdate.to_time.to_i }
it { expect(subject.birthplace).to eq birthplace }
it { expect(subject.france_connect_particulier_id).to eq france_connect_particulier_id }
end
it 'redirects to check email FC page' do
expect(subject).to redirect_to(france_connect_particulier_new_path(fci_id: last_france_connect_information.id, salt: salt))
it do
subject
expect(User.find_by(email: email)).not_to be_nil
end
end
end
context 'when code is not correct' do
before do
allow(FranceConnectService).to receive(:retrieve_user_informations_particulier) { raise Rack::OAuth2::Client::Error.new(500, error: 'Unknown') }
get :callback, params: {code: code}
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: DateTime.parse(birthdate))) }
end
it 'redirect to login page' do
expect(response).to redirect_to(new_user_session_path)
end
it 'display error message' do
expect(flash[:alert]).to be_present
end
it { is_expected.to redirect_to(root_path) }
end
end
end
describe 'POST #check_email' do
let(:email) { 'plop@gmail.com' }
let!(:france_connect_information) { create(:france_connect_information) }
let(:france_connect_information_id) { france_connect_information.id }
let(:salt) { FranceConnectSaltService.new(france_connect_information).salt }
subject { post :check_email, params: {fci_id: france_connect_information_id, salt: salt, user: {email_france_connect: email}} }
context 'when salt and fci_id does not matches' do
let(:france_connect_information_fake) { create(:france_connect_information, france_connect_particulier_id: 'iugfjh') }
let(:france_connect_information_id) { france_connect_information_fake.id }
it { is_expected.to redirect_to new_user_session_path }
end
context 'when salt and fci_id matches' do
context 'when email is not used' do
context 'when email is valid' do
it { expect { subject }.to change { User.count }.by(1) }
describe 'New user attributs' do
before do
subject
end
let(:user) { User.last }
it { expect(user.email).to eq email }
it { expect(user.france_connect_information).to eq france_connect_information }
end
end
context 'when email is not valid' do
let(:email) { 'kdjizjflk' }
it { expect { subject }.not_to change { User.count } }
it { is_expected.to redirect_to(france_connect_particulier_new_path fci_id: france_connect_information.id, salt: salt, user: {email_france_connect: email}) }
end
context 'when code is not correct' do
before do
allow(FranceConnectService).to receive(:retrieve_user_informations_particulier) { raise Rack::OAuth2::Client::Error.new(500, error: 'Unknown') }
subject
end
context 'when email is used' do
let!(:user) { create(:user, email: france_connect_information.email_france_connect) }
let(:email) { france_connect_information.email_france_connect }
let(:password) { user.password }
it { expect(response).to redirect_to(new_user_session_path) }
before do
subject
end
subject { post :check_email, params: {fci_id: france_connect_information_id, salt: salt, user: {email_france_connect: email, password: password}} }
context 'when email and password couple is valid' do
it { expect { subject }.not_to change { User.count } }
describe 'Update user attributs' do
before do
subject
end
it { expect(user.france_connect_information).to eq france_connect_information }
end
end
context 'when email and password couple is not valid' do
let(:password) { 'fake' }
it { expect(flash.alert).to eq 'Mot de passe invalide' }
end
end
end
end
describe 'POST #create' do
let!(:france_connect_information) { create(:france_connect_information, email_france_connect: email) }
let(:france_connect_information_id) { france_connect_information.id }
let(:salt) { FranceConnectSaltService.new(france_connect_information).salt }
subject { post :create, params: {fci_id: france_connect_information_id, salt: salt, user: {email_france_connect: france_connect_information.email_france_connect}} }
context 'when email is filled' do
let(:email) { 'plop@gmail.com' }
it { expect { subject }.to change { User.count }.by(1) }
it { expect(subject).to redirect_to(root_path) }
end
context 'when email is incorrect' do
let(:email) { '' }
it { expect { subject }.not_to change { User.count } }
it { expect(subject).to redirect_to(france_connect_particulier_new_path(fci_id: france_connect_information_id, salt: salt, user: {email_france_connect: france_connect_information.email_france_connect})) }
it { expect(flash[:alert]).to be_present }
end
end
end

View file

@ -55,19 +55,9 @@ feature 'France Connect Particulier Connexion' do
before do
page.find('.login-with-fc').click
end
scenario 'he is redirected to france connect particulier page' do
expect(page).to have_content('Nouvelle connexion')
end
context 'when he fill an email and valid' do
before do
page.find_by_id('user_email_france_connect').set email
page.find_by_id('valid_new_fcp').click
end
scenario 'he is redirected to user dossiers page' do
expect(page).to have_content('Dossiers')
end
scenario 'he is redirected to user dossiers page' do
expect(page).to have_content('Dossiers')
end
end

View file

@ -1,41 +0,0 @@
require 'spec_helper'
describe FranceConnectSaltService do
describe '.initialize' do
context 'when args is not a FranceConnectInformation class' do
let(:args) { create(:dossier) }
subject { described_class.new args }
it { expect { subject }.to raise_error 'Not a FranceConnectInformation class' }
end
end
describe '.valid?' do
let(:france_connect_information) { create(:france_connect_information) }
let(:salt_service) { FranceConnectSaltService.new(france_connect_information) }
let(:salt) { salt_service.salt }
context 'when france_connect_information_id is correct' do
let(:france_connect_information_id) { france_connect_information.id }
let(:france_connect_information_get_with_id) { FranceConnectInformation.find(france_connect_information_id) }
let(:salt_service_compare) { FranceConnectSaltService.new france_connect_information_get_with_id }
subject { salt_service_compare.valid? salt }
it { is_expected.to be_truthy }
end
context 'when france_connect_information_id is not correct' do
let(:france_connect_information_fake) { create(:france_connect_information, france_connect_particulier_id: '87515272') }
let(:france_connect_information_id) { france_connect_information_fake.id }
let(:france_connect_information_get_with_id) { FranceConnectInformation.find(france_connect_information_id) }
let(:salt_service_compare) { FranceConnectSaltService.new france_connect_information_get_with_id }
subject { salt_service_compare.valid? salt }
it { is_expected.to be_falsey }
end
end
end