Add FranceConnect Particulier

Reactivate FranceConnect Entreprise
This commit is contained in:
Xavier J 2015-12-24 10:12:23 +01:00
parent 2099095c34
commit 9e0dfb593e
21 changed files with 548 additions and 115 deletions

View file

@ -0,0 +1,5 @@
#france_connect_particulier_email{
width: 300px;
margin-left:auto;
margin-right:auto;
}

View file

@ -7,7 +7,7 @@
padding: 20px;
#btn_fc {
.btn_fc {
margin-top:8%;
background-image: image-url('logo_FC_02.png');
background-repeat: no-repeat;

View file

@ -23,7 +23,7 @@ class FranceConnect::EntrepriseController < ApplicationController
sign_in @user
@user.loged_in_with_france_connect = true
@user.loged_in_with_france_connect = 'entreprise'
@user.save
redirect_to stored_location_for(current_user) || signed_in_root_path(current_user)

View file

@ -0,0 +1,67 @@
class FranceConnect::ParticulierController < ApplicationController
def login
client = FranceConnectParticulierClient.new
session[:state] = SecureRandom.hex(16)
session[:nonce] = SecureRandom.hex(16)
authorization_uri = client.authorization_uri(
scope: [:profile, :email],
state: session[:state],
nonce: session[:nonce]
)
redirect_to authorization_uri
end
def new
@user = (User.new create_user_params).decorate
end
def create
user = User.new create_user_params
user.password = Devise.friendly_token[0, 20]
unless user.valid?
flash.alert = 'Email non valide'
return redirect_to france_connect_particulier_new_path user: params[:user]
end
user.save
connect_france_connect_particulier user
end
def callback
return redirect_to new_user_session_path unless params.has_key?(:code)
user_infos = FranceConnectService.retrieve_user_informations_particulier(params[:code])
unless user_infos.nil?
user = User.find_for_france_connect_particulier user_infos
if user.nil?
return redirect_to france_connect_particulier_new_path(user: user_infos)
end
connect_france_connect_particulier user
end
rescue Rack::OAuth2::Client::Error => e
Rails.logger.error e.message
flash.alert = t('errors.messages.france_connect.connexion')
redirect_to(new_user_session_path)
end
private
def create_user_params
params.require(:user).permit(:france_connect_particulier_id, :gender, :given_name, :family_name, :birthdate, :birthplace, :email)
end
def connect_france_connect_particulier user
sign_in user
user.loged_in_with_france_connect = 'particulier'
user.save
redirect_to stored_location_for(current_user) || signed_in_root_path(current_user)
end
end

View file

@ -10,20 +10,22 @@ class Users::SessionsController < Sessions::SessionsController
def create
super
current_user.update_attributes(loged_in_with_france_connect: false)
current_user.update_attributes(loged_in_with_france_connect: '')
end
# DELETE /resource/sign_out
def destroy
connected_with_france_connect = current_user.loged_in_with_france_connect
current_user.update_attributes(loged_in_with_france_connect: false)
current_user.update_attributes(loged_in_with_france_connect: '')
signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
set_flash_message :notice, :signed_out if signed_out && is_flashing_format?
yield if block_given?
if connected_with_france_connect
if connected_with_france_connect == 'entreprise'
redirect_to FRANCE_CONNECT.entreprise_logout_endpoint
elsif connected_with_france_connect == 'particulier'
redirect_to FRANCE_CONNECT.particulier_logout_endpoint
else
respond_to_on_destroy
end

View file

@ -0,0 +1,12 @@
class UserDecorator < Draper::Decorator
delegate_all
def gender_fr
return 'Mr' if gender == 'male'
return 'Mme' if gender == 'female'
end
def birthdate_fr
birthdate.strftime('%d/%m/%Y')
end
end

View file

@ -0,0 +1,17 @@
class FranceConnectParticulierClient < OpenIDConnect::Client
def initialize params={}
super(
identifier: FRANCE_CONNECT.particulier_identifier,
secret: FRANCE_CONNECT.particulier_secret,
redirect_uri: FRANCE_CONNECT.particulier_redirect_uri,
authorization_endpoint: FRANCE_CONNECT.particulier_authorization_endpoint,
token_endpoint: FRANCE_CONNECT.particulier_token_endpoint,
userinfo_endpoint: FRANCE_CONNECT.particulier_userinfo_endpoint,
logout_endpoint: FRANCE_CONNECT.particulier_logout_endpoint
)
self.authorization_code = params[:code] if params.has_key? :code
end
end

View file

@ -1,4 +1,7 @@
class User < ActiveRecord::Base
enum loged_in_with_france_connect: {particulier: 'particulier',
entreprise: 'entreprise'}
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
@ -6,6 +9,11 @@ class User < ActiveRecord::Base
has_many :dossiers
def self.find_for_france_connect_particulier user_info
User.find_by(france_connect_particulier_id: user_info[:france_connect_particulier_id])
end
def self.find_for_france_connect email, siret
user = User.find_by_email(email)
if user.nil?
@ -15,4 +23,8 @@ class User < ActiveRecord::Base
user
end
end
def loged_in_with_france_connect?
!loged_in_with_france_connect.nil?
end
end

View file

@ -6,4 +6,15 @@ class FranceConnectService
user_info = access_token.userinfo!
Hashie::Mash.new user_info.raw_attributes
end
def self.retrieve_user_informations_particulier code
client = FranceConnectParticulierClient.new code: code
access_token = client.access_token!(client_auth_method: :secret)
user_info = access_token.userinfo!
hash = Hashie::Mash.new user_info.raw_attributes
hash.france_connect_particulier_id = hash.sub
hash
end
end

View file

@ -0,0 +1,36 @@
%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 correctement 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: :create}, method: :post do |f|
.form-group.form-group-lg
= f.text_field :email, class: "form-control", placeholder: "Entrez votre email"
= f.hidden_field :gender
= f.hidden_field :given_name
= f.hidden_field :family_name
= f.hidden_field :birthdate
= f.hidden_field :birthplace
= f.hidden_field :france_connect_particulier_id
= f.submit 'Terminer', class: %w(btn btn-lg btn-success), style: 'margin-top:20px;', id: 'valid_new_fcp'

View file

@ -25,7 +25,7 @@
= render partial: 'administrateurs/login_banner'
- elsif user_signed_in?
%div.user
-if current_user.loged_in_with_france_connect
-if current_user.loged_in_with_france_connect?
= image_tag('logo_FC_02_small.png', class: 'logo_fc_small')
-else
%i.fa.fa-user

View file

@ -3,14 +3,23 @@
%br
%h2#login_user Connexion
-#%a.btn.btn-default.btn-lg.btn-primary#btn_fc{href: '/france_connect'}
-# %div{style:'margin-left:35px'}
-# S'identifier avec
-# France Connect
-#%hr
%br
%h3.text-info
S'identifier grâce à
%strong
France Connect
%br
%br
.row
.col-md-6.col-lg-6
%a.btn.btn-default.btn-lg.btn-primary.btn_fc#btn_fcp{href: '/france_connect/particulier'}
%div{style:'margin-left:35px'}
Particulier
.col-md-6.col-lg-6
%a.btn.btn-default.btn-lg.btn-danger.btn_fc#btn_fce{href: '/france_connect/entreprise'}
%div{style:'margin-left:35px'}
Entreprise
%hr
= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
%h4
= f.label :email

View file

@ -30,8 +30,8 @@ describe FranceConnect::EntrepriseController, type: :controller do
get :callback, code: code
end
it 'current user have attribut loged_in_with_france_connect at true' do
expect(current_user.loged_in_with_france_connect).to be_truthy
it 'current user have attribut loged_in_with_france_connect at enterprise' do
expect(current_user.loged_in_with_france_connect).to eq 'entreprise'
end
let(:stored_location) { '/plip/plop' }
it 'redirect to stored location' do

View file

@ -0,0 +1,107 @@
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) { '' }
let(:user_info) { Hashie::Mash.new(france_connect_particulier_id: france_connect_particulier_id, given_name: given_name, family_name: family_name, birthdate: birthdate, birthplace: birthplace, gender: gender, email: email) }
describe '.login' do
it 'redirect to france connect serveur' do
get :login
expect(response.status).to eq(302)
end
end
describe '.callback' do
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
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(user_info)
get :callback, code: code
end
context 'when france_connect_particulier_id exist in database' do
before do
create(:user, france_connect_particulier_id: france_connect_particulier_id, email: email, given_name: given_name, family_name: family_name, birthdate: birthdate, gender: gender, birthplace: birthplace)
get :callback, code: code
end
let(:email) { 'plop@plop.com' }
let(:current_user) { User.find_by_email(email) }
let(:stored_location) { '/plip/plop' }
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, code: code
expect(response).to redirect_to(stored_location)
end
end
context 'when france_connect_particulier_id does not exist in database' do
it 'redirects to check email FC page' do
expect(response).to redirect_to(france_connect_particulier_new_path(user: user_info))
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, code: code
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
end
end
end
describe 'POST #create' do
let(:email) { 'plop@gmail.com' }
subject { post :create, user: user_info }
context 'when email is filled' do
it { expect { subject }.to change { User.count }.by(1) }
it 'redirects user root page' do
subject
expect(response).to redirect_to(root_path)
end
end
context 'when email is incorrect' do
let(:email) { '' }
it { expect { subject }.to change { User.count }.by(0) }
it 'redirect to check email FC page' do
subject
expect(response).to redirect_to(france_connect_particulier_new_path(user: user_info))
end
end
end
end

View file

@ -1,7 +1,7 @@
require 'spec_helper'
describe Users::SessionsController, type: :controller do
let(:loged_in_with_france_connect) { true }
let(:loged_in_with_france_connect) { 'entreprise' }
let(:user) { create(:user, loged_in_with_france_connect: loged_in_with_france_connect) }
before do
@ -17,7 +17,7 @@ describe Users::SessionsController, type: :controller do
user.reload
end
subject { user.loged_in_with_france_connect }
subject { user.loged_in_with_france_connect? }
it { is_expected.to be_falsey }
end
@ -33,19 +33,27 @@ describe Users::SessionsController, type: :controller do
expect(subject.current_user).to be_nil
end
it 'loged_in_with_france_connect current_user attribut is false' do
it 'loged_in_with_france_connect current_user attribut is nil' do
user.reload
expect(user.loged_in_with_france_connect).to be_falsey
expect(user.loged_in_with_france_connect?).to be_falsey
end
context 'when user is connect with france connect' do
context 'when user is connect with france connect entreprise' do
it 'redirect to france connect logout page' do
expect(response).to redirect_to(FRANCE_CONNECT.entreprise_logout_endpoint)
end
end
context 'when user is connect with france connect entreprise' do
let(:loged_in_with_france_connect) { 'particulier' }
it 'redirect to france connect logout page' do
expect(response).to redirect_to(FRANCE_CONNECT.particulier_logout_endpoint)
end
end
context 'when user is not connect with france connect' do
let(:loged_in_with_france_connect) { false }
let(:loged_in_with_france_connect) { '' }
it 'redirect to root page' do
expect(response).to redirect_to(root_path)

View file

@ -0,0 +1,81 @@
require 'spec_helper'
feature 'France Connect Connexion' do
context 'when user is on login page' do
before do
visit new_user_session_path
end
scenario 'link to France Connect is present' do
expect(page).to have_css('a#btn_fce')
end
context 'and click on france connect link' do
let(:code) { 'plop' }
context 'when authentification is ok' do
before do
allow_any_instance_of(FranceConnectEntrepriseClient).to receive(:authorization_uri).and_return(france_connect_entreprise_callback_path(code: code))
allow(FranceConnectService).to receive(:retrieve_user_informations_entreprise).and_return(Hashie::Mash.new(email: 'patator@cake.com'))
page.find_by_id('btn_fce').click
end
scenario 'he is redirected to france connect' do
expect(page).to have_content('Mes dossiers')
end
end
context 'when authentification is not ok' do
before do
allow_any_instance_of(FranceConnectEntrepriseClient).to receive(:authorization_uri).and_return(france_connect_entreprise_callback_path(code: code))
allow(FranceConnectService).to receive(:retrieve_user_informations_entreprise) { raise Rack::OAuth2::Client::Error.new(500, error: 'Unknown') }
page.find_by_id('btn_fce').click
end
scenario 'he is redirected to login page' do
expect(page).to have_css('a#btn_fce')
end
scenario 'error message is displayed' do
expect(page).to have_content(I18n.t('errors.messages.france_connect.connexion'))
end
end
end
end
feature 'redirection' do
before do
visit initial_path
end
context 'when he use france connect' do
let(:code) { 'my_code' }
let(:email) { 'plop@plop.com' }
let(:siret) { '00000000000000' }
let(:user_infos) { Hashie::Mash.new(email: email, siret: siret) }
before do
allow_any_instance_of(FranceConnectEntrepriseClient).to receive(:authorization_uri).and_return(france_connect_entreprise_callback_path(code: code))
allow(FranceConnectService).to receive(:retrieve_user_informations_entreprise).and_return(user_infos)
page.find_by_id('btn_fce').click
end
context 'when starting page is dossiers list' do
let(:initial_path) { users_dossiers_path }
scenario 'he is redirected to dossier list' do
expect(page).to have_css('#users_index')
end
end
context 'when starting page is procedure' do
let(:procedure) { create(:procedure) }
let(:initial_path) { new_users_dossiers_path(procedure_id: procedure.id ) }
scenario 'he is redirected to siret page' do
expect(page).to have_css('#users_siret_index')
end
scenario 'the siret is already written in form' do
expect(page.find_by_id('dossier_siret').value).to have_content(siret)
end
end
end
end
end

View file

@ -0,0 +1,84 @@
require 'spec_helper'
feature 'France Connect Particulier Connexion' do
let(:code) { 'plop' }
let(:given_name) { 'titi' }
let(:family_name) { 'toto' }
let(:birthdate) { '20150821' }
let(:gender) { 'M' }
let(:birthplace) { '1234' }
let(:email) { 'plop@plop.com' }
let(:know_france_connect_particulier_id) { 'blabla' }
let(:unknow_france_connect_particulier_id) { 'titi' }
let(:user_info) { Hashie::Mash.new(france_connect_particulier_id: france_connect_particulier_id, given_name: given_name, family_name: family_name, birthdate: birthdate, birthplace: birthplace, gender: gender, email: email) }
context 'when user is on login page' do
before do
visit new_user_session_path
end
scenario 'link to France Connect is present' do
expect(page).to have_css('a#btn_fcp')
end
context 'and click on france connect link' do
let(:code) { 'plop' }
context 'when authentification is ok' do
let!(:user) { create(:user, france_connect_particulier_id: know_france_connect_particulier_id, given_name: given_name, family_name: family_name, birthdate: birthdate, birthplace: birthplace, gender: gender) }
before do
allow_any_instance_of(FranceConnectParticulierClient).to receive(:authorization_uri).and_return(france_connect_particulier_callback_path(code: code))
allow(FranceConnectService).to receive(:retrieve_user_informations_particulier).and_return(user_info)
page.find_by_id('btn_fcp').click
end
context 'when is the first connexion' do
let(:france_connect_particulier_id) { unknow_france_connect_particulier_id }
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').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('Mes dossiers')
end
end
end
context 'when is not the first connexion' do
let(:france_connect_particulier_id) { know_france_connect_particulier_id }
scenario 'he is redirected to user dossiers page' do
expect(page).to have_content('Mes dossiers')
end
end
end
context 'when authentification is not ok' do
before do
allow_any_instance_of(FranceConnectParticulierClient).to receive(:authorization_uri).and_return(france_connect_particulier_callback_path(code: code))
allow(FranceConnectService).to receive(:retrieve_user_informations_particulier) { raise Rack::OAuth2::Client::Error.new(500, error: 'Unknown') }
page.find_by_id('btn_fcp').click
end
scenario 'he is redirected to login page' do
expect(page).to have_css('a#btn_fcp')
end
scenario 'error message is displayed' do
expect(page).to have_content(I18n.t('errors.messages.france_connect.connexion'))
end
end
end
end
end

View file

@ -1,81 +0,0 @@
require 'spec_helper'
feature 'France Connect Connexion' do
# context 'when user is on login page' do
#
# before do
# visit new_user_session_path
# end
#
# scenario 'link to France Connect is present' do
# expect(page).to have_css('a#btn_fc')
# end
#
# context 'and click on france connect link' do
# let(:code) { 'plop' }
#
# context 'when authentification is ok' do
# before do
# allow_any_instance_of(FranceConnectEntrepriseClient).to receive(:authorization_uri).and_return(france_connect_callback_path(code: code))
# allow(FranceConnectService).to receive(:retrieve_user_informations_entreprise).and_return(Hashie::Mash.new(email: 'patator@cake.com'))
# page.find_by_id('btn_fc').click
# end
#
# scenario 'he is redirected to france connect' do
# expect(page).to have_content('Mes dossiers')
# end
# end
#
# context 'when authentification is not ok' do
# before do
# allow_any_instance_of(FranceConnectEntrepriseClient).to receive(:authorization_uri).and_return(france_connect_callback_path(code: code))
# allow(FranceConnectService).to receive(:retrieve_user_informations_entreprise) { raise Rack::OAuth2::Client::Error.new(500, error: 'Unknown') }
# page.find_by_id('btn_fc').click
# end
#
# scenario 'he is redirected to login page' do
# expect(page).to have_css('a#btn_fc')
# end
#
# scenario 'error message is displayed' do
# expect(page).to have_content(I18n.t('errors.messages.france_connect.connexion'))
# end
# end
# end
# end
#
#
# feature 'redirection' do
# before do
# visit initial_path
# end
# context 'when he use france connect' do
# let(:code) { 'my_code' }
# let(:email) { 'plop@plop.com' }
# let(:siret) { '00000000000000' }
# let(:user_infos) { Hashie::Mash.new(email: email, siret: siret) }
# before do
# allow_any_instance_of(FranceConnectEntrepriseClient).to receive(:authorization_uri).and_return(france_connect_callback_path(code: code))
# allow(FranceConnectService).to receive(:retrieve_user_informations_entreprise).and_return(user_infos)
# page.find_by_id('btn_fc').click
# end
# context 'when starting page is dossiers list' do
# let(:initial_path) { users_dossiers_path }
# scenario 'he is redirected to dossier list' do
# expect(page).to have_css('#users_index')
# end
# end
# context 'when starting page is procedure' do
# let(:procedure) { create(:procedure) }
# let(:initial_path) { new_users_dossiers_path(procedure_id: procedure.id ) }
# scenario 'he is redirected to siret page' do
# expect(page).to have_css('#users_siret_index')
# end
#
# scenario 'the siret is already written in form' do
# expect(page.find_by_id('dossier_siret').value).to have_content(siret)
# end
# end
# end
# end
end

View file

@ -0,0 +1,17 @@
require 'spec_helper'
describe FranceConnectParticulierClient do
describe '.initialize' do
it 'create an openid client' do
expect(described_class).to be < OpenIDConnect::Client
end
context 'when given code in params' do
let(:code) { 'plop' }
subject { described_class.new(code: code) }
it 'set authorisation code' do
expect_any_instance_of(described_class).to receive(:authorization_code=).with(code)
described_class.new(code: code)
end
end
end
end

View file

@ -16,6 +16,13 @@ describe User, type: :model do
it { is_expected.to have_db_column(:updated_at) }
it { is_expected.to have_db_column(:siret) }
it { is_expected.to have_db_column(:loged_in_with_france_connect) }
it { is_expected.to have_db_column(:given_name) }
it { is_expected.to have_db_column(:family_name) }
it { is_expected.to have_db_column(:birthdate) }
it { is_expected.to have_db_column(:gender) }
it { is_expected.to have_db_column(:birthplace) }
it { is_expected.to have_db_column(:france_connect_particulier_id) }
end
describe 'associations' do
it { is_expected.to have_many(:dossiers) }

View file

@ -2,7 +2,6 @@ require 'spec_helper'
describe FranceConnectService do
describe '.retrieve_user_informations_entreprise' do
let(:code) { 'plop' }
let(:access_token) { 'my access_token' }
let(:email) { 'patator@cake.com' }
@ -26,4 +25,44 @@ describe FranceConnectService do
expect(subject.siret).to eq(siret)
end
end
describe '.retrieve_user_informations_particulier' do
let(:code) { 'plop' }
let(:access_token) { 'my access_token' }
let(:given_name) { 'plop1' }
let(:family_name) { 'plop2' }
let(:birthdate) { 'plop3' }
let(:gender) { 'plop4' }
let(:birthplace) { 'plop5' }
let(:email) { 'plop@emaiL.com' }
let(:phone) { '012345678' }
let(:france_connect_particulier_id) { 'izhikziogjuziegj' }
let(:user_info_hash) { {sub: france_connect_particulier_id, given_name: given_name, family_name: family_name, birthdate: birthdate, gender: gender, birthplace: birthplace, email: email, phone: phone} }
let(:user_info) { instance_double('OpenIDConnect::ResponseObject::UserInfo', raw_attributes: user_info_hash) }
subject { described_class.retrieve_user_informations_particulier code }
before do
allow_any_instance_of(FranceConnectParticulierClient).to receive(:access_token!).and_return(access_token)
allow(access_token).to receive(:userinfo!).and_return(user_info)
end
it 'set code for FranceConnectEntrepriseClient' do
expect_any_instance_of(FranceConnectParticulierClient).to receive(:authorization_code=).with(code)
subject
end
it 'returns user informations in a object' do
expect(subject.given_name).to eq(given_name)
expect(subject.family_name).to eq(family_name)
expect(subject.birthdate).to eq(birthdate)
expect(subject.gender).to eq(gender)
expect(subject.email).to eq(email)
expect(subject.phone).to eq(phone)
expect(subject.birthplace).to eq(birthplace)
expect(subject.france_connect_particulier_id).to eq(france_connect_particulier_id)
end
end
end