diff --git a/Gemfile.lock b/Gemfile.lock
index 9d9bfd0d..fb95fba8 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -15,7 +15,7 @@ GIT
GIT
remote: https://github.com/OpenSourcePolitics/omniauth-france_connect
- revision: 4665875c94d45a71dac163889e0eedd21b2ba41d
+ revision: 14a53ad31928c8a83742360cfbdb90938d0a057e
specs:
omniauth-france_connect (0.1.0)
omniauth_openid_connect (~> 0.4.0)
diff --git a/OVERLOADS.md b/OVERLOADS.md
index a64b3b68..27aec3fb 100644
--- a/OVERLOADS.md
+++ b/OVERLOADS.md
@@ -1,5 +1,9 @@
# Overrides
+## Update France Connect with requirements
+* `app/views/decidim/devise/passwords/new.html.erb`
+* `app/views/decidim/shared/_login_modal.html.erb`
+
## Load decidim-awesome assets only if dependencie is present
* `app/views/layouts/decidim/_head.html.erb:33`
diff --git a/app/packs/images/FCboutons-10.png b/app/packs/images/FCboutons-10.png
index bb5adf7b..5a179f87 100644
Binary files a/app/packs/images/FCboutons-10.png and b/app/packs/images/FCboutons-10.png differ
diff --git a/app/packs/images/FCboutons-10@2x.png b/app/packs/images/FCboutons-10@2x.png
index c7461283..9668728b 100644
Binary files a/app/packs/images/FCboutons-10@2x.png and b/app/packs/images/FCboutons-10@2x.png differ
diff --git a/app/views/decidim/devise/passwords/new.html.erb b/app/views/decidim/devise/passwords/new.html.erb
new file mode 100644
index 00000000..ebf006d7
--- /dev/null
+++ b/app/views/decidim/devise/passwords/new.html.erb
@@ -0,0 +1,37 @@
+<% add_decidim_page_title(t("devise.passwords.new.forgot_your_password")) %>
+
+
-
+
<%- current_organization.enabled_omniauth_providers.keys.each do |provider| %>
<% if provider.match?("france") %>
<%= t("devise.shared.links.sign_in_with_provider", provider: normalize_full_provider_name(provider).titleize) %>
+
<%= sso_provider_image(provider, decidim.send("user_#{provider}_omniauth_authorize_path")) %>
+
+ <% if I18n.exists?("decidim.omniauth.france_connect.external.link") %>
+
+ <%= link_to t("link", scope: "decidim.omniauth.france_connect.external"), class: "primary", target: "_blank" do %>
+
+ <%= t("text", scope: "decidim.omniauth.france_connect.external") %>
+
+ <% end %>
+
+ <% end %>
<% else %>
+
<%== sso_provider_button(provider, decidim.send("user_#{provider}_omniauth_authorize_path")).html_safe %>
- <% end %>
-
- <% if I18n.exists?("decidim.omniauth.france_connect.external.link") %>
- <%= link_to t("link", scope: "decidim.omniauth.france_connect.external"), class: "primary", target: "_blank" do %>
-
- <%= t("text", scope: "decidim.omniauth.france_connect.external") %>
-
- <% end %>
+
<% end %>
<% end %>
@@ -28,4 +33,4 @@
<%- end %>
-<% end %>
+<% end %>
\ No newline at end of file
diff --git a/app/views/decidim/shared/_login_modal.html.erb b/app/views/decidim/shared/_login_modal.html.erb
new file mode 100644
index 00000000..ddd14236
--- /dev/null
+++ b/app/views/decidim/shared/_login_modal.html.erb
@@ -0,0 +1,60 @@
+
+
+ <% if current_organization.sign_in_enabled? %>
+
+ <% cache current_organization do %>
+ <%= render "decidim/devise/shared/omniauth_buttons" %>
+ <% end %>
+
+
+
+ <%
+ path = if content_for(:redirect_after_login)
+ session_path(:user, redirect_url: content_for(:redirect_after_login))
+ else
+ session_path(:user)
+ end
+ %>
+ <%= decidim_form_for(Decidim::User.new, namespace: "login", as: :user, url: path, html: { class: "register-form new_user" }) do |f| %>
+
+
+ <%= f.email_field :email %>
+
+
+ <%= f.password_field :password, autocomplete: "off" %>
+
+
+
+ <%= f.submit t("devise.sessions.new.sign_in"), class: "button expanded" %>
+
+ <% end %>
+ <% if current_organization.sign_up_enabled? %>
+
+ <%= link_to t(".sign_up"), decidim.new_user_registration_path, class: "sign-up-link" %>
+
+ <% end %>
+
+ <%= link_to t("devise.shared.links.forgot_your_password"), new_password_path(:user) %>
+
+
+
+
+ <% else %>
+
+
+
+ <%= t("sign_in_disabled", scope: "decidim.devise.sessions.new") %>
+
+
+
+ <% cache current_organization do %>
+ <%= render "decidim/devise/shared/omniauth_buttons" %>
+ <% end %>
+ <% end %>
+
\ No newline at end of file
diff --git a/config/initializers/extends.rb b/config/initializers/extends.rb
index 01c10eab..eb1d5bc1 100644
--- a/config/initializers/extends.rb
+++ b/config/initializers/extends.rb
@@ -2,3 +2,4 @@
require "extends/controllers/decidim/devise/sessions_controller_extends"
require "extends/queries/decidim/participatory_processes/group_participatory_processes_extends"
+require "extends/controllers/decidim/devise/account_controller_extends"
diff --git a/config/initializers/omniauth_france_connect.rb b/config/initializers/omniauth_france_connect.rb
index f0caf0a4..f450eb37 100644
--- a/config/initializers/omniauth_france_connect.rb
+++ b/config/initializers/omniauth_france_connect.rb
@@ -1,18 +1,16 @@
# frozen_string_literal: true
-if Rails.application.secrets.dig(:omniauth, :france_connect).present?
- Rails.application.config.middleware.use OmniAuth::Builder do
- provider(
- :france_connect,
- setup: lambda { |env|
- request = Rack::Request.new(env)
- organization = Decidim::Organization.find_by(host: request.host)
- provider_config = organization.enabled_omniauth_providers[:france_connect]
- env["omniauth.strategy"].options[:client_id] = provider_config[:client_id]
- env["omniauth.strategy"].options[:client_secret] = provider_config[:client_secret]
- env["omniauth.strategy"].options[:site] = provider_config[:site_url]
- env["omniauth.strategy"].options[:scope] = provider_config[:scope]&.split(" ")
- }
- )
- end
+Rails.application.config.middleware.use OmniAuth::Builder do
+ provider(
+ :france_connect,
+ setup: lambda { |env|
+ request = Rack::Request.new(env)
+ organization = env["decidim.current_organization"].presence || Decidim::Organization.find_by(host: request.host)
+ provider_config = organization.enabled_omniauth_providers[:france_connect]
+ env["omniauth.strategy"].options[:client_id] = provider_config[:client_id]
+ env["omniauth.strategy"].options[:client_secret] = provider_config[:client_secret]
+ env["omniauth.strategy"].options[:site] = provider_config[:site_url]
+ env["omniauth.strategy"].options[:scope] = provider_config[:scope]&.split(" ")
+ }
+ )
end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 71bf7942..b713c9db 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -9,11 +9,6 @@ en:
participatory_process:
private_space: Private space
decidim:
- omniauth:
- france_connect:
- external:
- link: https://franceconnect.gouv.fr/
- text: Qu'est-ce-que FranceConnect ?
accessibility:
skip_button: Skip button
admin:
@@ -37,6 +32,10 @@ en:
name: Identity Verification Form
osp_authorization_workflow:
name: Authorization procedure
+ devise:
+ sessions:
+ new:
+ sign_in_disabled: Sign in disabled
events:
budgets:
pending_order:
@@ -57,11 +56,7 @@ en:
email_subject: Failed verification attempt against a managed participant
notification_title: The participant
%{resource_title} has tried to verify themself with the data of the managed participant
%{managed_user_name}.
newsletter_templates:
- mel_template_settings_form:
- interpolations_hint: Interpolations hint
- body: Body
mel_template:
- name: Template
alt_banner_image: Alt banner image
mel_template_settings_form:
show:
@@ -79,6 +74,17 @@ en:
icon: Icon
link: Link
text: Text
+ name: Template
+ mel_template_settings_form:
+ body: Body
+ interpolations_hint: Interpolations hint
+ omniauth:
+ france_connect:
+ external:
+ link: https://franceconnect.gouv.fr/
+ text: What is FranceConnect ?
+ forgot_password:
+ ok_text: Warning, this password is the one of your local account and in no case the one of the account you use through FranceConnect. It will only be used when you log in with your email address rather than via FranceConnect.
proposals:
create:
error: There was a problem saving
@@ -101,6 +107,11 @@ en:
withdraw:
errors:
has_supports: This proposal can not be withdrawn because it already has supports.
+ shared:
+ login_modal:
+ close_modal: Fermer
+ please_sign_in: Veuillez vous connecter
+ sign_up: Créer un compte
system:
organizations:
omniauth_settings:
@@ -133,6 +144,17 @@ en:
actions:
osp_authorization_handler: Verify with the identity verification form
osp_authorization_workflow: Verify with the identity verification form
+ devise:
+ passwords:
+ new:
+ forgot_your_password: Forgot your password
+ send_me_reset_password_instructions: Send me reset password instructions
+ sessions:
+ new:
+ sign_in: Log in
+ shared:
+ links:
+ forgot_your_password: Forgot your password
faker:
address:
country_code:
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index e081ee43..a8428761 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -9,11 +9,6 @@ fr:
participatory_process:
private_space: Espace privé
decidim:
- omniauth:
- france_connect:
- external:
- link: https://franceconnect.gouv.fr/
- text: Qu'est-ce-que FranceConnect ?
accessibility:
skip_button: Passer au contenu principal
admin:
@@ -37,6 +32,10 @@ fr:
name: Formulaire de vérification d'identité
osp_authorization_workflow:
name: Procédure d'autorisation
+ devise:
+ sessions:
+ new:
+ sign_in_disabled: Vous pouvez accéder avec un compte externe
events:
budgets:
pending_order:
@@ -77,6 +76,13 @@ fr:
show:
body: Contenu principal
interpolations_hint: Interpolation
+ omniauth:
+ france_connect:
+ external:
+ link: https://franceconnect.gouv.fr/
+ text: Qu'est-ce-que FranceConnect ?
+ forgot_password:
+ ok_text: Warning, this password is the one of your local account and in no case the one of the account you use through FranceConnect. It will only be used when you log in with your email address rather than via FranceConnect.
proposals:
create:
error: Il y a eu des erreurs lors de la sauvegarde de la proposition.
@@ -99,15 +105,20 @@ fr:
withdraw:
errors:
has_supports: Cette proposition ne peut pas être retirée car elle dispose déjà de supports.
+ shared:
+ login_modal:
+ close_modal: Close modal
+ please_sign_in: Please sign in
+ sign_up: Sign up
system:
organizations:
omniauth_settings:
france_connect:
client_id: Client ID
client_secret: Client secret
- scope: Périmètre de données
provider: FranceConnect
provider_name: FranceConnect
+ scope: Périmètre de données
site_url: Site URL
france_connect_profile:
button_path: Chemin du bouton
@@ -131,6 +142,17 @@ fr:
actions:
osp_authorization_handler: Vérifier avec le formulaire de vérification de l'identité
osp_authorization_workflow: Vérifier avec le formulaire de vérification de l'identité
+ devise:
+ passwords:
+ new:
+ forgot_your_password: Mot de passe oublié ?
+ send_me_reset_password_instructions: Envoyez-moi les instructions de réinitialisation du mot de passe
+ sessions:
+ new:
+ sign_in: S'identifier
+ shared:
+ links:
+ forgot_your_password: Mot de passe oublié ?
faker:
address:
country_code:
diff --git a/lib/extends/controllers/decidim/devise/account_controller_extends.rb b/lib/extends/controllers/decidim/devise/account_controller_extends.rb
new file mode 100644
index 00000000..de4a8d98
--- /dev/null
+++ b/lib/extends/controllers/decidim/devise/account_controller_extends.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module AccountControllerExtends
+ def destroy
+ enforce_permission_to :delete, :user, current_user: current_user
+ @form = form(Decidim::DeleteAccountForm).from_params(params)
+
+ Decidim::DestroyAccount.call(current_user, @form) do
+ on(:ok) do
+ sign_out(current_user)
+ if active_france_connect_session?
+ destroy_france_connect_session(session["omniauth.france_connect.end_session_uri"])
+ else
+ redirect_to decidim.root_path
+ end
+ flash[:notice] = t("account.destroy.success", scope: "decidim")
+ end
+
+ on(:invalid) do
+ flash[:alert] = t("account.destroy.error", scope: "decidim")
+ redirect_to decidim.root_path
+ end
+ end
+ end
+
+ private
+
+ def destroy_france_connect_session(fc_logout_path)
+ session.delete("omniauth.france_connect.end_session_uri")
+
+ redirect_to fc_logout_path
+ end
+
+ def active_france_connect_session?
+ current_organization.enabled_omniauth_providers.include?(:france_connect) && session["omniauth.france_connect.end_session_uri"].present?
+ end
+end
+
+Decidim::AccountController.class_eval do
+ prepend(AccountControllerExtends)
+end
diff --git a/lib/extends/controllers/decidim/devise/sessions_controller_extends.rb b/lib/extends/controllers/decidim/devise/sessions_controller_extends.rb
index 38166b22..deaa01dd 100644
--- a/lib/extends/controllers/decidim/devise/sessions_controller_extends.rb
+++ b/lib/extends/controllers/decidim/devise/sessions_controller_extends.rb
@@ -1,6 +1,17 @@
# frozen_string_literal: true
module SessionControllerExtends
+ def destroy
+ current_user.invalidate_all_sessions!
+ if active_france_connect_session?
+ destroy_france_connect_session(session["omniauth.france_connect.end_session_uri"])
+ elsif params[:translation_suffix].present?
+ super { set_flash_message! :notice, params[:translation_suffix], { scope: "decidim.devise.sessions" } }
+ else
+ super
+ end
+ end
+
def after_sign_in_path_for(user)
return super if user.is_a? Decidim::System::Admin
@@ -19,6 +30,20 @@ def after_sign_in_path_for(user)
def skip_authorization_handler?
ENV["SKIP_FIRST_LOGIN_AUTHORIZATION"] ? ActiveRecord::Type::Boolean.new.cast(ENV["SKIP_FIRST_LOGIN_AUTHORIZATION"]) : true
end
+
+ def destroy_france_connect_session(fc_logout_path)
+ signed_out = (::Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
+ if signed_out
+ set_flash_message! :notice, :signed_out
+ session.delete("omniauth.france_connect.end_session_uri")
+ end
+
+ redirect_to fc_logout_path
+ end
+
+ def active_france_connect_session?
+ current_organization.enabled_omniauth_providers.include?(:france_connect) && session["omniauth.france_connect.end_session_uri"].present?
+ end
end
Devise::SessionsController.class_eval do
diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb
index 9f34bc3d..4da6dc37 100644
--- a/spec/controllers/sessions_controller_spec.rb
+++ b/spec/controllers/sessions_controller_spec.rb
@@ -97,10 +97,11 @@ module Devise
end
describe "DELETE destroy" do
- let(:user) { create(:user, :confirmed) }
+ let(:organization) { create(:organization) }
+ let(:user) { create(:user, :confirmed, organization: organization) }
before do
- request.env["decidim.current_organization"] = user.organization
+ request.env["decidim.current_organization"] = organization
request.env["devise.mapping"] = ::Devise.mappings[:user]
sign_in user
@@ -111,6 +112,43 @@ module Devise
expect(controller.current_user).to be_nil
end
+
+ context "when France Connect is enabled" do
+ let(:organization) { create(:organization, omniauth_settings: omniauth_settings) }
+ let(:omniauth_settings) do
+ { "omniauth_settings_france_connect_enabled": true }
+ end
+
+ before do
+ stub_request(:get, /test-france-connect.fr/)
+ .with(headers: { "Accept" => "*/*", "User-Agent" => "Ruby" })
+ .to_return(status: 200, body: "", headers: {})
+
+ request.env["decidim.current_organization"] = user.organization
+ request.env["devise.mapping"] = ::Devise.mappings[:user]
+
+ sign_in user
+ end
+
+ it "logout user from France Connect" do
+ delete :destroy, session: { "omniauth.france_connect.end_session_uri" => "http://test-france-connect.fr/" }
+
+ expect(controller.current_user).to be_nil
+ expect(controller).to redirect_to("http://test-france-connect.fr/")
+ expect(session["flash"]["flashes"]["notice"]).to eq("Signed out successfully.")
+ end
+
+ context "and France Connect logout session is not present" do
+ it "logout user from application" do
+ delete :destroy
+
+ expect(controller.current_user).to be_nil
+ expect(controller).not_to redirect_to("http://test-france-connect.fr/")
+ expect(controller).to redirect_to("http://test.host/")
+ expect(session["flash"]["flashes"]["notice"]).to eq("Signed out successfully.")
+ end
+ end
+ end
end
end
end