diff --git a/app/controllers/manager/administrateur_confirmations_controller.rb b/app/controllers/manager/administrateur_confirmations_controller.rb new file mode 100644 index 000000000..9f3f15d4a --- /dev/null +++ b/app/controllers/manager/administrateur_confirmations_controller.rb @@ -0,0 +1,52 @@ +module Manager + class AdministrateurConfirmationsController < Manager::ApplicationController + before_action :set_procedure + before_action :decrypt_params + before_action :ensure_not_inviter, unless: -> { Rails.env.development? } + before_action :ensure_not_invited, unless: -> { Rails.env.development? } + + def new + @inviter = SuperAdmin.find(@inviter_id) + end + + def create + administrateur = Administrateur.by_email(@invited_email) + AdministrateursProcedure.create!(procedure: @procedure, administrateur: administrateur) + flash[:notice] = "L’administrateur \"#{administrateur.email}\" a été ajouté à la démarche." + redirect_to manager_procedure_path(@procedure) + end + + private + + def ensure_not_inviter + redirect_unallowed if @inviter_id.to_i == current_super_admin.id + end + + def ensure_not_invited + redirect_unallowed if @invited_email == current_super_admin.email + end + + def redirect_unallowed + flash[:alert] = "Veuillez partager ce lien avec un autre super administrateur pour qu'il confirme votre action" + redirect_to manager_procedure_path(@procedure) + end + + def decrypt_params + @inviter_id = decrypted_params[:inviter_id] + @invited_email = decrypted_params[:email] + rescue ActiveSupport::MessageVerifier::InvalidSignature, ArgumentError + flash[:error] = "Le lien que vous avez utilisé est invalide. Veuillez contacter la personne qui vous l'a envoyé." + redirect_to manager_procedure_path(@procedure) + end + + def decrypted_params + @decrypted_params ||= ActiveSupport::MessageVerifier.new( + Rails.application.key_generator.generate_key("confirm_adding_administrateur") + ).verify(Base64.urlsafe_decode64(params[:q])) + end + + def set_procedure + @procedure = Procedure.with_discarded.find(params[:procedure_id]) + end + end +end diff --git a/app/controllers/manager/confirmation_urls_controller.rb b/app/controllers/manager/confirmation_urls_controller.rb new file mode 100644 index 000000000..5f8bc0a73 --- /dev/null +++ b/app/controllers/manager/confirmation_urls_controller.rb @@ -0,0 +1,46 @@ +module Manager + class ConfirmationUrlsController < Manager::ApplicationController + before_action :ensure_administrateur_exists + before_action :ensure_not_already_added + + def new + @url = new_manager_procedure_administrateur_confirmation_url( + procedure.id, + q: encrypt({ email: params[:email], inviter_id: current_super_admin.id }) + ) + end + + private + + def ensure_administrateur_exists + redirect("Cet administrateur n'existe pas. Veuillez réessayer.") unless administrateur + end + + def ensure_not_already_added + redirect("Cet administrateur a déjà été ajouté à cette démarche.") if already_added? + end + + def redirect(alert) + flash[:alert] = alert + redirect_to manager_procedure_path(procedure) + end + + def already_added? + AdministrateursProcedure.exists?(procedure: procedure, administrateur: administrateur) + end + + def administrateur + @administrateur ||= Administrateur.by_email(params[:email]) + end + + def procedure + @procedure ||= Procedure.with_discarded.find(params[:procedure_id]) + end + + def encrypt(parameters) + key = Rails.application.key_generator.generate_key("confirm_adding_administrateur") + verifier = ActiveSupport::MessageVerifier.new(key) + Base64.urlsafe_encode64(verifier.generate(parameters)) + end + end +end diff --git a/app/controllers/manager/procedures_controller.rb b/app/controllers/manager/procedures_controller.rb index a8058d5c3..5ebacbdc0 100644 --- a/app/controllers/manager/procedures_controller.rb +++ b/app/controllers/manager/procedures_controller.rb @@ -65,22 +65,7 @@ module Manager end def add_administrateur_with_confirmation - confirmation_url = confirm_add_administrateur_manager_procedure_url(id: procedure.id, email: current_super_admin.email) - - flash[:notice] = "Veuillez partager ce lien : #{confirmation_url} avec un autre super admin pour que l'operation soit effectuée" - redirect_to manager_procedure_path(procedure) - end - - def confirm_add_administrateur - administrateur_email = params[:email] - if administrateur_email != current_super_admin.email - administrateur = Administrateur.by_email(params[:email]) - AdministrateursProcedure.create!(procedure: procedure, administrateur: administrateur) - flash[:notice] = "L’administrateur \"#{administrateur.email}\" a été ajouté à la démarche." - else - flash[:alert] = "Veuillez partager ce lien avec un autre super administrateur pour qu'il confirme votre action" - end - redirect_to manager_procedure_path(procedure) + redirect_to new_manager_procedure_confirmation_url_path(procedure, email: params[:email]) end def delete_administrateur diff --git a/app/views/manager/administrateur_confirmations/new.html.erb b/app/views/manager/administrateur_confirmations/new.html.erb new file mode 100644 index 000000000..83321e600 --- /dev/null +++ b/app/views/manager/administrateur_confirmations/new.html.erb @@ -0,0 +1,35 @@ +<% content_for(:title) { "Confirmation d'ajout d'un administrateur" } %> + + + +
+

+ Vous avez été invité·e par + + <%= @inviter.email %> + + à confirmer l'ajout de + + <%= @invited_email %> + + à la démarche + + <%= @procedure.libelle %> + (<%= @procedure.id %>). + +

+ +

+ Confirmez-vous cet ajout ? +

+ + <%= form_tag manager_procedure_administrateur_confirmations_path(@procedure) do %> + <%= hidden_field_tag :q, params[:q] %> + <%= submit_tag "Oui, je confirme l'ajout" %> + <%= link_to "Non, je ne confirme pas l'ajout", :back %> + <% end %> +
diff --git a/app/views/manager/application/_navigation.html.erb b/app/views/manager/application/_navigation.html.erb index 063ba6789..6ba4253fe 100644 --- a/app/views/manager/application/_navigation.html.erb +++ b/app/views/manager/application/_navigation.html.erb @@ -12,7 +12,7 @@ as defined by the routes in the `admin/` namespace
- <% Administrate::Namespace.new(namespace).resources.each do |resource| %> + <% Administrate::Namespace.new(namespace).resources.select { |resource| !resource.to_s.in?(%w(confirmation_urls administrateur_confirmations)) }.each do |resource| %> <%= link_to( display_resource_name(resource), diff --git a/app/views/manager/confirmation_urls/new.html.erb b/app/views/manager/confirmation_urls/new.html.erb new file mode 100644 index 000000000..fbcb59819 --- /dev/null +++ b/app/views/manager/confirmation_urls/new.html.erb @@ -0,0 +1,22 @@ +<% content_for(:title) { "Ajout d'un administrateur" } %> + + + +
+

+ Veuillez partager ce lien avec un autre super admin pour que l'opération soit effectuée. +

+
+
+ Lien +
+ +
+ <%= @url %> +
+
+
diff --git a/config/routes.rb b/config/routes.rb index d44e451a1..50093db4d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -18,9 +18,10 @@ Rails.application.routes.draw do put 'delete_administrateur', on: :member post 'add_administrateur_and_instructeur', on: :member post 'add_administrateur_with_confirmation', on: :member - get 'confirm_add_administrateur', on: :member post 'change_piece_justificative_template', on: :member get 'export_mail_brouillons', on: :member + resources :confirmation_urls, only: :new + resources :administrateur_confirmations, only: [:new, :create] end resources :archives, only: [:index, :show] diff --git a/spec/controllers/manager/administrateur_confirmations_controller_spec.rb b/spec/controllers/manager/administrateur_confirmations_controller_spec.rb new file mode 100644 index 000000000..ef629eef1 --- /dev/null +++ b/spec/controllers/manager/administrateur_confirmations_controller_spec.rb @@ -0,0 +1,164 @@ +require 'rails_helper' + +RSpec.describe Manager::AdministrateurConfirmationsController, type: :controller do + let(:inviter_super_admin) { create(:super_admin) } + let(:inviter_administrateur) { create(:administrateur, email: inviter_super_admin.email) } + + let(:invited_super_admin) { create(:super_admin) } + let(:invited_administrateur) { create(:administrateur, email: invited_super_admin.email) } + + let(:confirmer_super_admin) { create(:super_admin) } + + let(:procedure) { create(:procedure, administrateurs: [inviter_administrateur]) } + + describe "GET #new" do + subject(:new_request) do + get :new, params: { + procedure_id: procedure.id, + q: encrypt({ email: invited_administrateur.email, inviter_id: inviter_super_admin.id }) + } + end + + shared_examples "current admin is allowed to confirm adding another one" do + before { new_request } + + it { expect(response).to render_template(:new) } + end + + shared_examples "current admin isn't allowed to confirm adding another one" do + before { new_request } + + it { expect(flash[:alert]).to match(/Veuillez partager ce lien avec un autre super administrateur/) } + + it { expect(response).to redirect_to(manager_procedure_path(procedure)) } + end + + context 'when the current admin is the invited' do + before { sign_in invited_super_admin } + + it_behaves_like "current admin isn't allowed to confirm adding another one" + end + + context 'when the current admin is the inviter' do + before { sign_in inviter_super_admin } + + it_behaves_like "current admin isn't allowed to confirm adding another one" + end + + context 'when the current admin is not the invited nor the inviter' do + before { sign_in confirmer_super_admin } + + it_behaves_like "current admin is allowed to confirm adding another one" + end + + describe 'edge cases' do + context 'when the environment is development' do + before { Rails.env.stub(development?: true) } + + context 'when the current admin is the inviter' do + before { sign_in inviter_super_admin } + + it_behaves_like "current admin is allowed to confirm adding another one" + end + + context 'when the current admin is the invited' do + before { sign_in invited_super_admin } + + it_behaves_like "current admin is allowed to confirm adding another one" + end + end + + context 'when the encrypted params are invalid' do + before { sign_in inviter_super_admin } + before { get :new, params: { procedure_id: procedure.id, q: "something that is invalid" } } + + it { expect(flash[:error]).to match(/Le lien que vous avez utilisé est invalide/) } + end + end + end + + describe "GET #create" do + subject(:create_request) do + post :create, params: { + procedure_id: procedure.id, + q: encrypt({ email: invited_administrateur.email, inviter_id: inviter_super_admin.id }) + } + end + + shared_examples "current admin is allowed to confirm adding another one" do + it "flashes the success message" do + create_request + expect(flash[:notice]).to include(invited_administrateur.email) + expect(flash[:notice]).to match(/ajouté à la démarche/) + end + + it "adds the admin to the procedure" do + expect { create_request }.to change { procedure.administrateurs.count }.by(1) + end + + it "redirects to the procedure" do + create_request + expect(response).to redirect_to(manager_procedure_path(procedure)) + end + end + + shared_examples "current admin isn't allowed to confirm adding another one" do + before { create_request } + + it { expect(flash[:alert]).to match(/Veuillez partager ce lien avec un autre super administrateur/) } + + it { expect(response).to redirect_to(manager_procedure_path(procedure)) } + end + + context 'when the current admin is the invited' do + before { sign_in invited_super_admin } + + it_behaves_like "current admin isn't allowed to confirm adding another one" + end + + context 'when the current admin is the inviter' do + before { sign_in inviter_super_admin } + + it_behaves_like "current admin isn't allowed to confirm adding another one" + end + + context 'when the current admin is not the invited nor the inviter' do + before { sign_in confirmer_super_admin } + + it_behaves_like "current admin is allowed to confirm adding another one" + end + + describe 'edge cases' do + context 'when the environment is development' do + before { Rails.env.stub(development?: true) } + + context 'when the current admin is the inviter' do + before { sign_in inviter_super_admin } + + it_behaves_like "current admin is allowed to confirm adding another one" + end + + context 'when the current admin is the invited' do + before { sign_in invited_super_admin } + + it_behaves_like "current admin is allowed to confirm adding another one" + end + end + + context 'when the encrypted params are invalid' do + before { sign_in inviter_super_admin } + before { post :create, params: { procedure_id: procedure.id, q: "something that is invalid" } } + + it { expect(flash[:error]).to match(/Le lien que vous avez utilisé est invalide/) } + end + end + end + + private + + def encrypt(parameters) + key = Rails.application.key_generator.generate_key("confirm_adding_administrateur") + verifier = ActiveSupport::MessageVerifier.new(key) + Base64.urlsafe_encode64(verifier.generate(parameters)) + end +end diff --git a/spec/controllers/manager/confirmation_urls_controller_spec.rb b/spec/controllers/manager/confirmation_urls_controller_spec.rb new file mode 100644 index 000000000..b0e32a075 --- /dev/null +++ b/spec/controllers/manager/confirmation_urls_controller_spec.rb @@ -0,0 +1,73 @@ +describe Manager::ConfirmationUrlsController, type: :controller do + let(:inviter_super_admin) { create(:super_admin) } + let(:inviter_administrateur) { create(:administrateur, email: inviter_super_admin.email) } + + let(:invited_super_admin) { create(:super_admin) } + let(:invited_administrateur) { create(:administrateur, email: invited_super_admin.email) } + + let(:procedure) { create(:procedure, administrateurs: [inviter_administrateur]) } + + before { sign_in inviter_super_admin } + + describe "#add_administrateur_with_confirmation" do + render_views + + let(:params) do + { + procedure_id: procedure.id, + email: invited_administrateur.email + } + end + + before { get :new, params: params } + + it { expect(response).to render_template(:new) } + + it { expect(response.body).to match(/Veuillez partager ce lien/) } + + it "shows the confirmation url with encrypted parameters" do + expect(response.body).to include( + new_manager_procedure_administrateur_confirmation_url( + procedure, + q: encrypt({ email: invited_administrateur.email, inviter_id: inviter_super_admin.id }) + ) + ) + end + + describe 'edge cases' do + context 'when the administrateur does not exist' do + let(:params) do + { + procedure_id: procedure.id, + email: "wrong@email.com" + } + end + + it { expect(flash[:alert]).to match(/Cet administrateur n'existe pas/) } + + it { expect(response).to redirect_to(manager_procedure_path(procedure)) } + end + + context 'when the administrateur has already been added to the procedure' do + let(:params) do + { + procedure_id: procedure.id, + email: inviter_super_admin.email + } + end + + it { expect(flash[:alert]).to match(/Cet administrateur a déjà été ajouté/) } + + it { expect(response).to redirect_to(manager_procedure_path(procedure)) } + end + end + end + + private + + def encrypt(parameters) + key = Rails.application.key_generator.generate_key("confirm_adding_administrateur") + verifier = ActiveSupport::MessageVerifier.new(key) + Base64.urlsafe_encode64(verifier.generate(parameters)) + end +end