From 2d9854dc01b111b7b8bf9c1af89ee90bbe6f2964 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 24 Sep 2024 14:08:06 +0200 Subject: [PATCH 01/17] [#10799] Declare api_entreprise_token_expires_at attribute and feed it on save --- app/models/api_entreprise_token.rb | 4 +++ app/models/procedure.rb | 5 +++ ...treprise_token_expires_at_to_procedures.rb | 7 ++++ db/schema.rb | 3 +- spec/models/api_entreprise_token_spec.rb | 30 ++++++++++++++++ spec/models/procedure_spec.rb | 36 +++++++++++++++++++ 6 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20240924112458_add_api_entreprise_token_expires_at_to_procedures.rb diff --git a/app/models/api_entreprise_token.rb b/app/models/api_entreprise_token.rb index 65cbfa491..fac81acda 100644 --- a/app/models/api_entreprise_token.rb +++ b/app/models/api_entreprise_token.rb @@ -17,6 +17,10 @@ class APIEntrepriseToken decoded_token.key?("exp") && decoded_token["exp"] <= Time.zone.now.to_i end + def expiration + Time.zone.at(decoded_token["exp"]) + end + def role?(role) roles.include?(role) end diff --git a/app/models/procedure.rb b/app/models/procedure.rb index d959e4b39..792d457c6 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -288,6 +288,7 @@ class Procedure < ApplicationRecord validates :api_particulier_token, format: { with: /\A[A-Za-z0-9\-_=.]{15,}\z/ }, allow_blank: true validate :validate_auto_archive_on_in_the_future, if: :will_save_change_to_auto_archive_on? + before_save :set_api_entreprise_token_expires_at, if: :will_save_change_to_api_entreprise_token? before_save :update_juridique_required after_save :extend_conservation_for_dossiers @@ -973,6 +974,10 @@ class Procedure < ApplicationRecord monavis_embed.gsub('nd_source=button', "nd_source=#{source}").gsub(' Date: Tue, 24 Sep 2024 16:26:36 +0200 Subject: [PATCH 02/17] [#10799] Display a warning about token expiration on token form page --- ...ntreprise_token_expiration_alert.html.haml | 14 +++++++ .../procedures/jeton.html.haml | 2 + ...e_token_expiration_alert.html.haml_spec.rb | 37 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 app/views/administrateurs/procedures/_api_entreprise_token_expiration_alert.html.haml create mode 100644 spec/views/administrateurs/procedures/_api_entreprise_token_expiration_alert.html.haml_spec.rb diff --git a/app/views/administrateurs/procedures/_api_entreprise_token_expiration_alert.html.haml b/app/views/administrateurs/procedures/_api_entreprise_token_expiration_alert.html.haml new file mode 100644 index 000000000..56ce657d5 --- /dev/null +++ b/app/views/administrateurs/procedures/_api_entreprise_token_expiration_alert.html.haml @@ -0,0 +1,14 @@ +- if procedure.api_entreprise_token_expires_at.present? + - if procedure.api_entreprise_token_expires_at < Time.zone.now + = render Dsfr::AlertComponent.new(state: :error, size: :sm, extra_class_names: 'fr-mb-2w') do |c| + - c.with_body do + %p + Votre jeton API Entreprise est expiré. + Merci de le renouveler. + - else + = render Dsfr::AlertComponent.new(state: :warning, size: :sm, extra_class_names: 'fr-mb-2w') do |c| + - c.with_body do + %p + Votre jeton API Entreprise expirera le + = procedure.api_entreprise_token_expires_at.strftime('%d/%m/%Y à %H:%M.') + Merci de le renouveler avant cette date. \ No newline at end of file diff --git a/app/views/administrateurs/procedures/jeton.html.haml b/app/views/administrateurs/procedures/jeton.html.haml index 35d92ab90..57dfa4534 100644 --- a/app/views/administrateurs/procedures/jeton.html.haml +++ b/app/views/administrateurs/procedures/jeton.html.haml @@ -18,6 +18,8 @@ = link_to 'API Entreprise', "https://api.gouv.fr/les-api/api-entreprise/demande-acces" propre à votre démarche. + = render partial: 'administrateurs/procedures/api_entreprise_token_expiration_alert', locals: { procedure: @procedure } + .fr-input-group = f.label :api_entreprise_token, "Jeton", class: 'fr-label' = f.password_field :api_entreprise_token, value: @procedure.read_attribute(:api_entreprise_token), class: 'fr-input' diff --git a/spec/views/administrateurs/procedures/_api_entreprise_token_expiration_alert.html.haml_spec.rb b/spec/views/administrateurs/procedures/_api_entreprise_token_expiration_alert.html.haml_spec.rb new file mode 100644 index 000000000..79bb64352 --- /dev/null +++ b/spec/views/administrateurs/procedures/_api_entreprise_token_expiration_alert.html.haml_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +RSpec.describe 'administrateurs/procedures/_api_entreprise_token_expiration_alert', type: :view do + let(:procedure) { create(:procedure, api_entreprise_token:) } + + subject { render 'administrateurs/procedures/api_entreprise_token_expiration_alert', procedure: procedure } + + context "when there is no token" do + let(:api_entreprise_token) { nil } + + it "does not render anything" do + subject + expect(rendered).to be_empty + end + end + + context "when the token is expired" do + let(:api_entreprise_token) { JWT.encode({ exp: 2.days.ago.to_i }, nil, "none") } + + it "should display an error" do + subject + + expect(rendered).to have_content("Votre jeton API Entreprise est expiré") + end + end + + context "when the token is valid it should display the expiration date" do + let(:expiration) { 2.days.from_now } + let(:api_entreprise_token) { JWT.encode({ exp: expiration.to_i }, nil, "none") } + + it "should display an error" do + subject + + expect(rendered).to have_content("Votre jeton API Entreprise expirera le\n#{expiration.strftime('%d/%m/%Y à %H:%M')}") + end + end +end From 7009eed9d78c92c13193f1ff372b0f6f52539002 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 24 Sep 2024 17:03:18 +0200 Subject: [PATCH 03/17] [#10799] Move api entreprise token logic in a concern --- .../concerns/api_entreprise_token_concern.rb | 32 +++++++++++++++++++ app/models/procedure.rb | 19 +---------- ...ntreprise_token_expiration_alert.html.haml | 2 +- .../api_entreprise_token_concern_spec.rb | 27 ++++++++++++++++ ...e_token_expiration_alert.html.haml_spec.rb | 12 ++++++- 5 files changed, 72 insertions(+), 20 deletions(-) create mode 100644 app/models/concerns/api_entreprise_token_concern.rb create mode 100644 spec/models/concerns/api_entreprise_token_concern_spec.rb diff --git a/app/models/concerns/api_entreprise_token_concern.rb b/app/models/concerns/api_entreprise_token_concern.rb new file mode 100644 index 000000000..e12d00048 --- /dev/null +++ b/app/models/concerns/api_entreprise_token_concern.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module APIEntrepriseTokenConcern + extend ActiveSupport::Concern + + SOON_TO_EXPIRE_DELAY = 1.month + + included do + validates :api_entreprise_token, jwt_token: true, allow_blank: true + before_save :set_api_entreprise_token_expires_at, if: :will_save_change_to_api_entreprise_token? + + def api_entreprise_role?(role) + APIEntrepriseToken.new(api_entreprise_token).role?(role) + end + + def api_entreprise_token + self[:api_entreprise_token].presence || Rails.application.secrets.api_entreprise[:key] + end + + def api_entreprise_token_expired? + APIEntrepriseToken.new(api_entreprise_token).expired? + end + + def api_entreprise_token_expires_soon? + api_entreprise_token_expires_at && api_entreprise_token_expires_at <= SOON_TO_EXPIRE_DELAY.from_now + end + + def set_api_entreprise_token_expires_at + self.api_entreprise_token_expires_at = APIEntrepriseToken.new(api_entreprise_token).expiration + end + end +end diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 792d457c6..9a0c9d42b 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Procedure < ApplicationRecord + include APIEntrepriseTokenConcern include ProcedureStatsConcern include EncryptableConcern include InitiationProcedureConcern @@ -284,11 +285,9 @@ class Procedure < ApplicationRecord size: { less_than: LOGO_MAX_SIZE }, if: -> { new_record? || created_at > Date.new(2020, 11, 13) } - validates :api_entreprise_token, jwt_token: true, allow_blank: true validates :api_particulier_token, format: { with: /\A[A-Za-z0-9\-_=.]{15,}\z/ }, allow_blank: true validate :validate_auto_archive_on_in_the_future, if: :will_save_change_to_auto_archive_on? - before_save :set_api_entreprise_token_expires_at, if: :will_save_change_to_api_entreprise_token? before_save :update_juridique_required after_save :extend_conservation_for_dossiers @@ -756,18 +755,6 @@ class Procedure < ApplicationRecord "Procedure;#{id}" end - def api_entreprise_role?(role) - APIEntrepriseToken.new(api_entreprise_token).role?(role) - end - - def api_entreprise_token - self[:api_entreprise_token].presence || Rails.application.secrets.api_entreprise[:key] - end - - def api_entreprise_token_expired? - APIEntrepriseToken.new(api_entreprise_token).expired? - end - def create_new_revision(revision = nil) transaction do new_revision = (revision || draft_revision) @@ -974,10 +961,6 @@ class Procedure < ApplicationRecord monavis_embed.gsub('nd_source=button', "nd_source=#{source}").gsub(' Date: Tue, 24 Sep 2024 18:33:35 +0200 Subject: [PATCH 04/17] [#10799] Handle the case when api_entreprise_token is not nil then set to nil --- .../concerns/api_entreprise_token_concern.rb | 6 ++- spec/models/procedure_spec.rb | 51 ++++++++++++------- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/app/models/concerns/api_entreprise_token_concern.rb b/app/models/concerns/api_entreprise_token_concern.rb index e12d00048..308e73249 100644 --- a/app/models/concerns/api_entreprise_token_concern.rb +++ b/app/models/concerns/api_entreprise_token_concern.rb @@ -17,6 +17,10 @@ module APIEntrepriseTokenConcern self[:api_entreprise_token].presence || Rails.application.secrets.api_entreprise[:key] end + def has_custom_api_entreprise_token? + self[:api_entreprise_token].present? + end + def api_entreprise_token_expired? APIEntrepriseToken.new(api_entreprise_token).expired? end @@ -26,7 +30,7 @@ module APIEntrepriseTokenConcern end def set_api_entreprise_token_expires_at - self.api_entreprise_token_expires_at = APIEntrepriseToken.new(api_entreprise_token).expiration + self.api_entreprise_token_expires_at = has_custom_api_entreprise_token? ? APIEntrepriseToken.new(api_entreprise_token).expiration : nil end end end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 0e2ad73c0..ae0179513 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -1861,7 +1861,7 @@ describe Procedure do end describe '#set_api_entreprise_token_expires_at (before_save)' do - let(:procedure) { create(:procedure) } + let(:procedure) { create(:procedure, api_entreprise_token: initial_api_entreprise_token) } before do procedure.api_entreprise_token = api_entreprise_token @@ -1869,29 +1869,44 @@ describe Procedure do subject { procedure.save } - context 'when the api_entreprise_token is nil' do - let(:api_entreprise_token) { nil } + context "when procedure had no api_entreprise_token" do + let(:initial_api_entreprise_token) { nil } - it 'does not set the api_entreprise_token_expires_at' do - expect { subject }.not_to change { procedure.api_entreprise_token_expires_at }.from(nil) + context 'when the api_entreprise_token is nil' do + let(:api_entreprise_token) { nil } + + it 'does not set the api_entreprise_token_expires_at' do + expect { subject }.not_to change { procedure.api_entreprise_token_expires_at }.from(nil) + end + end + + context 'when the api_entreprise_token is not valid' do + let(:api_entreprise_token) { "not a token" } + + it do + expect { subject }.not_to change { procedure.api_entreprise_token_expires_at }.from(nil) + end + end + + context 'when the api_entreprise_token is valid' do + let(:expiration_date) { Time.zone.now.beginning_of_minute } + let(:api_entreprise_token) { JWT.encode({ exp: expiration_date.to_i }, nil, 'none') } + + it do + expect { subject }.to change { procedure.api_entreprise_token_expires_at }.from(nil).to(expiration_date) + end end end - context 'when the api_entreprise_token is not valid' do - let(:api_entreprise_token) { "not a token" } + context "when procedure had an api_entreprise_token" do + let(:initial_api_entreprise_token) { JWT.encode({ exp: 2.months.from_now.to_i }, nil, "none") } - it do - expect { subject }.not_to change { procedure.api_entreprise_token_expires_at }.from(nil) - end - end + context 'when the api_entreprise_token is set to nil' do + let(:api_entreprise_token) { nil } - context 'when the api_entreprise_token is valid' do - let(:expiration_date) { Time.zone.now.beginning_of_minute } - let(:api_entreprise_token) { JWT.encode({ exp: expiration_date.to_i }, nil, 'none') } - - it do - puts "expiration_date: #{expiration_date.to_i}" - expect { subject }.to change { procedure.api_entreprise_token_expires_at }.from(nil).to(expiration_date) + it do + expect { subject }.to change { procedure.api_entreprise_token_expires_at }.to(nil) + end end end end From ec2c913ab4edb2e68647255c3b8d3cea469a0333 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 24 Sep 2024 18:59:18 +0200 Subject: [PATCH 05/17] [#10799] Display token error on related card --- .../api_entreprise_component.html.haml | 7 +++-- .../concerns/api_entreprise_token_concern.rb | 2 +- ...ntreprise_token_expiration_alert.html.haml | 2 +- .../card/api_entreprise_component_spec.rb | 27 +++++++++++++++++++ .../api_entreprise_token_concern_spec.rb | 10 +++++-- 5 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 spec/components/procedure/card/api_entreprise_component_spec.rb diff --git a/app/components/procedure/card/api_entreprise_component/api_entreprise_component.html.haml b/app/components/procedure/card/api_entreprise_component/api_entreprise_component.html.haml index 8c5ed09f9..32797f8bd 100644 --- a/app/components/procedure/card/api_entreprise_component/api_entreprise_component.html.haml +++ b/app/components/procedure/card/api_entreprise_component/api_entreprise_component.html.haml @@ -1,8 +1,11 @@ .fr-col-6.fr-col-md-4.fr-col-lg-3 = link_to jeton_admin_procedure_path(@procedure), class: 'fr-tile fr-enlarge-link' do .fr-tile__body.flex.column.align-center.justify-between - - if @procedure.api_entreprise_token.present? - %p.fr-badge.fr-badge--success Validé + - if @procedure.has_custom_api_entreprise_token? + - if @procedure.api_entreprise_token_expired_or_expires_soon? + %p.fr-badge.fr-badge--error À renouveler + - else + %p.fr-badge.fr-badge--success Validé - else %p.fr-badge.fr-badge--info À configurer %div diff --git a/app/models/concerns/api_entreprise_token_concern.rb b/app/models/concerns/api_entreprise_token_concern.rb index 308e73249..3962b72f6 100644 --- a/app/models/concerns/api_entreprise_token_concern.rb +++ b/app/models/concerns/api_entreprise_token_concern.rb @@ -25,7 +25,7 @@ module APIEntrepriseTokenConcern APIEntrepriseToken.new(api_entreprise_token).expired? end - def api_entreprise_token_expires_soon? + def api_entreprise_token_expired_or_expires_soon? api_entreprise_token_expires_at && api_entreprise_token_expires_at <= SOON_TO_EXPIRE_DELAY.from_now end diff --git a/app/views/administrateurs/procedures/_api_entreprise_token_expiration_alert.html.haml b/app/views/administrateurs/procedures/_api_entreprise_token_expiration_alert.html.haml index c83fb1cec..877fb464b 100644 --- a/app/views/administrateurs/procedures/_api_entreprise_token_expiration_alert.html.haml +++ b/app/views/administrateurs/procedures/_api_entreprise_token_expiration_alert.html.haml @@ -5,7 +5,7 @@ %p Votre jeton API Entreprise est expiré. Merci de le renouveler. - - elsif procedure.api_entreprise_token_expires_soon? + - elsif procedure.api_entreprise_token_expired_or_expires_soon? = render Dsfr::AlertComponent.new(state: :warning, size: :sm, extra_class_names: 'fr-mb-2w') do |c| - c.with_body do %p diff --git a/spec/components/procedure/card/api_entreprise_component_spec.rb b/spec/components/procedure/card/api_entreprise_component_spec.rb new file mode 100644 index 000000000..382d425ad --- /dev/null +++ b/spec/components/procedure/card/api_entreprise_component_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe Procedure::Card::APIEntrepriseComponent, type: :component do + subject { render_inline(described_class.new(procedure:)) } + + let(:procedure) { create(:procedure, api_entreprise_token:) } + + context "Token is not configured" do + let(:api_entreprise_token) { nil } + + it { is_expected.to have_css('p.fr-badge.fr-badge--info', text: "À configurer") } + end + + context "Token expires soon" do + let(:api_entreprise_token) { JWT.encode({ exp: 2.days.from_now.to_i }, nil, "none") } + + it { is_expected.to have_css('p.fr-badge.fr-badge--error', text: "À renouveler") } + end + + context "Token expires in a long time" do + let(:api_entreprise_token) { JWT.encode({ exp: 2.months.from_now.to_i }, nil, "none") } + + it { is_expected.to have_css('p.fr-badge.fr-badge--success', text: "Validé") } + end +end diff --git a/spec/models/concerns/api_entreprise_token_concern_spec.rb b/spec/models/concerns/api_entreprise_token_concern_spec.rb index 3eedc3c4d..7a6040af6 100644 --- a/spec/models/concerns/api_entreprise_token_concern_spec.rb +++ b/spec/models/concerns/api_entreprise_token_concern_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true describe APIEntrepriseTokenConcern do - describe "#api_entreprise_token_expires_soon?" do - subject { procedure.api_entreprise_token_expires_soon? } + describe "#api_entreprise_token_expired_or_expires_soon?" do + subject { procedure.api_entreprise_token_expired_or_expires_soon? } let(:procedure) { create(:procedure, api_entreprise_token:) } @@ -23,5 +23,11 @@ describe APIEntrepriseTokenConcern do it { is_expected.to be_truthy } end + + context "when the token is expired" do + let(:api_entreprise_token) { JWT.encode({ exp: 1.day.ago.to_i }, nil, "none") } + + it { is_expected.to be_truthy } + end end end From 2bf773b0b7919eeed45c523b02d4a54224aa4824 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Thu, 26 Sep 2024 11:27:29 +0200 Subject: [PATCH 06/17] [#10799] Add warning badges when token is expiring --- app/views/administrateurs/_breadcrumbs.html.haml | 5 +++++ .../administrateurs/procedures/_procedures_list.html.haml | 4 ++++ config/locales/views/layouts/_breadcrumb.en.yml | 1 + config/locales/views/layouts/_breadcrumb.fr.yml | 1 + 4 files changed, 11 insertions(+) diff --git a/app/views/administrateurs/_breadcrumbs.html.haml b/app/views/administrateurs/_breadcrumbs.html.haml index d19911f36..41a20952a 100644 --- a/app/views/administrateurs/_breadcrumbs.html.haml +++ b/app/views/administrateurs/_breadcrumbs.html.haml @@ -28,6 +28,11 @@ - elsif @procedure.locked? = link_to commencer_url(@procedure.path), commencer_url(@procedure.path), class: "fr-link" .flex.fr-mt-1w + + - if @procedure.api_entreprise_token_expired_or_expires_soon? + %span.fr-badge.fr-badge--warning.fr-mr-1w + = t('to_modify', scope: [:layouts, :breadcrumb]) + %span.fr-badge.fr-badge--success.fr-mr-1w = t('published', scope: [:layouts, :breadcrumb]) = t('since', scope: [:layouts, :breadcrumb], number: @procedure.id, date: l(@procedure.published_at.to_date)) diff --git a/app/views/administrateurs/procedures/_procedures_list.html.haml b/app/views/administrateurs/procedures/_procedures_list.html.haml index 535c76267..dcf4d7c9c 100644 --- a/app/views/administrateurs/procedures/_procedures_list.html.haml +++ b/app/views/administrateurs/procedures/_procedures_list.html.haml @@ -54,11 +54,15 @@ .text-right %p.fr-mb-0.width-max-content N° #{number_with_html_delimiter(procedure.id)} + - if procedure.close? || procedure.depubliee? %span.fr-badge.fr-badge--sm.fr-badge--warning = t('closed', scope: [:layouts, :breadcrumb]) - elsif procedure.publiee? + - if procedure.api_entreprise_token_expired_or_expires_soon? + %span.fr-badge.fr-badge--sm.fr-badge--warning + = t('to_modify', scope: [:layouts, :breadcrumb]) %span.fr-badge.fr-badge--sm.fr-badge--success = t('published', scope: [:layouts, :breadcrumb]) diff --git a/config/locales/views/layouts/_breadcrumb.en.yml b/config/locales/views/layouts/_breadcrumb.en.yml index f0a4ff07d..129028235 100644 --- a/config/locales/views/layouts/_breadcrumb.en.yml +++ b/config/locales/views/layouts/_breadcrumb.en.yml @@ -11,6 +11,7 @@ en: closed: "Closed" published: "Published" draft: "Draft" + to_modify: "To modify" more_info_on_test: "For more information on test stage" go_to_FAQ: "read FAQ" url_FAQ: "/faq#accordion-administrateur-2" diff --git a/config/locales/views/layouts/_breadcrumb.fr.yml b/config/locales/views/layouts/_breadcrumb.fr.yml index 2cb0ed409..cc60f2133 100644 --- a/config/locales/views/layouts/_breadcrumb.fr.yml +++ b/config/locales/views/layouts/_breadcrumb.fr.yml @@ -11,6 +11,7 @@ fr: closed: "Close" published: "Publiée" draft: "En test" + to_modify: "À modifier" more_info_on_test: "Pour plus d’information sur la phase de test" go_to_FAQ: "consulter la FAQ" url_FAQ: "/faq#accordion-administrateur-2" From 99a1b681852989638d0f75be78c30ede21e8fe0c Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Thu, 26 Sep 2024 15:13:51 +0200 Subject: [PATCH 07/17] [#10799] reorder methods A->Z --- app/models/concerns/api_entreprise_token_concern.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/models/concerns/api_entreprise_token_concern.rb b/app/models/concerns/api_entreprise_token_concern.rb index 3962b72f6..ad1d0a41e 100644 --- a/app/models/concerns/api_entreprise_token_concern.rb +++ b/app/models/concerns/api_entreprise_token_concern.rb @@ -7,6 +7,7 @@ module APIEntrepriseTokenConcern included do validates :api_entreprise_token, jwt_token: true, allow_blank: true + before_save :set_api_entreprise_token_expires_at, if: :will_save_change_to_api_entreprise_token? def api_entreprise_role?(role) @@ -17,10 +18,6 @@ module APIEntrepriseTokenConcern self[:api_entreprise_token].presence || Rails.application.secrets.api_entreprise[:key] end - def has_custom_api_entreprise_token? - self[:api_entreprise_token].present? - end - def api_entreprise_token_expired? APIEntrepriseToken.new(api_entreprise_token).expired? end @@ -29,6 +26,10 @@ module APIEntrepriseTokenConcern api_entreprise_token_expires_at && api_entreprise_token_expires_at <= SOON_TO_EXPIRE_DELAY.from_now end + def has_custom_api_entreprise_token? + self[:api_entreprise_token].present? + end + def set_api_entreprise_token_expires_at self.api_entreprise_token_expires_at = has_custom_api_entreprise_token? ? APIEntrepriseToken.new(api_entreprise_token).expiration : nil end From 64297f9ee83c86a11d5c162f0085e6e62bb6fa70 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Thu, 26 Sep 2024 16:15:31 +0200 Subject: [PATCH 08/17] [#10799] Add error on procedure#show if api entreprise token is expiring --- app/components/procedure/errors_summary.rb | 2 ++ app/models/concerns/api_entreprise_token_concern.rb | 7 +++++++ config/locales/models/procedure/en.yml | 1 + config/locales/models/procedure/fr.yml | 1 + 4 files changed, 11 insertions(+) diff --git a/app/components/procedure/errors_summary.rb b/app/components/procedure/errors_summary.rb index ff1dfc8aa..6eb065052 100644 --- a/app/components/procedure/errors_summary.rb +++ b/app/components/procedure/errors_summary.rb @@ -47,6 +47,8 @@ class Procedure::ErrorsSummary < ApplicationComponent when :initiated_mail, :received_mail, :closed_mail, :refused_mail, :without_continuation_mail, :re_instructed_mail klass = "Mails::#{error.attribute.to_s.classify}".constantize edit_admin_procedure_mail_template_path(@procedure, klass.const_get(:SLUG)) + when :api_entreprise_token + jeton_admin_procedure_path(@procedure) end end diff --git a/app/models/concerns/api_entreprise_token_concern.rb b/app/models/concerns/api_entreprise_token_concern.rb index ad1d0a41e..debb559c6 100644 --- a/app/models/concerns/api_entreprise_token_concern.rb +++ b/app/models/concerns/api_entreprise_token_concern.rb @@ -7,6 +7,7 @@ module APIEntrepriseTokenConcern included do validates :api_entreprise_token, jwt_token: true, allow_blank: true + validate :api_entreprise_token_expiration_valid?, on: [:publication] before_save :set_api_entreprise_token_expires_at, if: :will_save_change_to_api_entreprise_token? @@ -18,6 +19,12 @@ module APIEntrepriseTokenConcern self[:api_entreprise_token].presence || Rails.application.secrets.api_entreprise[:key] end + def api_entreprise_token_expiration_valid? + if api_entreprise_token_expired_or_expires_soon? + errors.add(:api_entreprise_token, "expiré ou expirant bientôt") + end + end + def api_entreprise_token_expired? APIEntrepriseToken.new(api_entreprise_token).expired? end diff --git a/config/locales/models/procedure/en.yml b/config/locales/models/procedure/en.yml index 1328196b5..489d0baa0 100644 --- a/config/locales/models/procedure/en.yml +++ b/config/locales/models/procedure/en.yml @@ -48,6 +48,7 @@ en: personne_morale: 'Legal entity' declarative_with_state/en_instruction: Instruction declarative_with_state/accepte: Accepted + api_entreprise_token: Token API Entreprise api_particulier_token: Token API Particulier initiated_mail: File sorted for processing notification email received_mail: File submitted notification email diff --git a/config/locales/models/procedure/fr.yml b/config/locales/models/procedure/fr.yml index 35713d133..6a020049f 100644 --- a/config/locales/models/procedure/fr.yml +++ b/config/locales/models/procedure/fr.yml @@ -54,6 +54,7 @@ fr: personne_morale: 'Personne morale' declarative_with_state/en_instruction: En instruction declarative_with_state/accepte: Accepté + api_entreprise_token: Jeton API Entreprise api_particulier_token: Jeton API Particulier initiated_mail: L’email de notification de passage de dossier en instruction received_mail: L’email de notification de dépôt de dossier From 687617cb084ae8eca3706e55fb04fe86f94b3d3f Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Thu, 26 Sep 2024 17:36:12 +0200 Subject: [PATCH 09/17] [#10799] Add test --- .../_api_entreprise_token_expiration_alert.html.haml | 2 +- app/views/administrateurs/procedures/jeton.html.haml | 2 +- spec/components/procedures/errors_summary_spec.rb | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/views/administrateurs/procedures/_api_entreprise_token_expiration_alert.html.haml b/app/views/administrateurs/procedures/_api_entreprise_token_expiration_alert.html.haml index 877fb464b..d37e14142 100644 --- a/app/views/administrateurs/procedures/_api_entreprise_token_expiration_alert.html.haml +++ b/app/views/administrateurs/procedures/_api_entreprise_token_expiration_alert.html.haml @@ -11,4 +11,4 @@ %p Votre jeton API Entreprise expirera le = procedure.api_entreprise_token_expires_at.strftime('%d/%m/%Y à %H:%M.') - Merci de le renouveler avant cette date. \ No newline at end of file + Merci de le renouveler avant cette date. diff --git a/app/views/administrateurs/procedures/jeton.html.haml b/app/views/administrateurs/procedures/jeton.html.haml index 57dfa4534..862a989a0 100644 --- a/app/views/administrateurs/procedures/jeton.html.haml +++ b/app/views/administrateurs/procedures/jeton.html.haml @@ -19,7 +19,7 @@ propre à votre démarche. = render partial: 'administrateurs/procedures/api_entreprise_token_expiration_alert', locals: { procedure: @procedure } - + .fr-input-group = f.label :api_entreprise_token, "Jeton", class: 'fr-label' = f.password_field :api_entreprise_token, value: @procedure.read_attribute(:api_entreprise_token), class: 'fr-input' diff --git a/spec/components/procedures/errors_summary_spec.rb b/spec/components/procedures/errors_summary_spec.rb index 9ff3edadb..e673244f9 100644 --- a/spec/components/procedures/errors_summary_spec.rb +++ b/spec/components/procedures/errors_summary_spec.rb @@ -83,7 +83,8 @@ describe Procedure::ErrorsSummary, type: :component do include Logic let(:validation_context) { :publication } - let(:procedure) { create(:procedure, attestation_template:, initiated_mail:) } + let(:expired_token) { JWT.encode({ exp: 2.days.ago.to_i }, nil, 'none') } + let(:procedure) { create(:procedure, attestation_template:, initiated_mail:, api_entreprise_token: expired_token) } let(:attestation_template) { build(:attestation_template) } let(:initiated_mail) { build(:initiated_mail) } @@ -97,6 +98,7 @@ describe Procedure::ErrorsSummary, type: :component do expect(page).to have_selector("a", text: "Les règles d’inéligibilité") expect(page).to have_selector("a", text: "Le modèle d’attestation") expect(page).to have_selector("a", text: "L’email de notification de passage de dossier en instruction") + expect(page).to have_selector("a", text: "Jeton API Entreprise") expect(page).to have_text("n'est pas valide", count: 2) end end From 6b326b634e75a3a0ab08a036aef1effd250982a0 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Fri, 27 Sep 2024 11:55:14 +0200 Subject: [PATCH 10/17] [#10799] Modifications after Marlene's comments --- .../api_entreprise_component.fr.yml | 2 +- .../api_entreprise_component.html.haml | 2 +- app/components/procedure/errors_summary.rb | 2 -- .../concerns/api_entreprise_token_concern.rb | 7 ------- app/views/administrateurs/_breadcrumbs.html.haml | 2 +- .../procedures/_procedures_list.html.haml | 2 +- .../administrateurs/procedures/jeton.html.haml | 16 +++++++++------- .../administrateurs/procedures/show.html.haml | 8 ++++++++ config/locales/models/procedure/en.yml | 1 + config/locales/models/procedure/fr.yml | 1 + .../views/administrateurs/procedures/en.yml | 1 + .../views/administrateurs/procedures/fr.yml | 1 + .../components/procedures/errors_summary_spec.rb | 4 +--- 13 files changed, 26 insertions(+), 23 deletions(-) diff --git a/app/components/procedure/card/api_entreprise_component/api_entreprise_component.fr.yml b/app/components/procedure/card/api_entreprise_component/api_entreprise_component.fr.yml index 688cb12c0..1dc20806f 100644 --- a/app/components/procedure/card/api_entreprise_component/api_entreprise_component.fr.yml +++ b/app/components/procedure/card/api_entreprise_component/api_entreprise_component.fr.yml @@ -1,3 +1,3 @@ --- fr: - title: Jeton Entreprise + title: Jeton API Entreprise diff --git a/app/components/procedure/card/api_entreprise_component/api_entreprise_component.html.haml b/app/components/procedure/card/api_entreprise_component/api_entreprise_component.html.haml index 32797f8bd..39fdeb27f 100644 --- a/app/components/procedure/card/api_entreprise_component/api_entreprise_component.html.haml +++ b/app/components/procedure/card/api_entreprise_component/api_entreprise_component.html.haml @@ -10,5 +10,5 @@ %p.fr-badge.fr-badge--info À configurer %div %h3.fr-h6.fr-mt-10v= t('.title') - %p.fr-tile-subtitle Configurer le jeton API entreprise + %p.fr-tile-subtitle Configurer le jeton API Entreprise %p.fr-btn.fr-btn--tertiary= t('views.shared.actions.edit') diff --git a/app/components/procedure/errors_summary.rb b/app/components/procedure/errors_summary.rb index 6eb065052..ff1dfc8aa 100644 --- a/app/components/procedure/errors_summary.rb +++ b/app/components/procedure/errors_summary.rb @@ -47,8 +47,6 @@ class Procedure::ErrorsSummary < ApplicationComponent when :initiated_mail, :received_mail, :closed_mail, :refused_mail, :without_continuation_mail, :re_instructed_mail klass = "Mails::#{error.attribute.to_s.classify}".constantize edit_admin_procedure_mail_template_path(@procedure, klass.const_get(:SLUG)) - when :api_entreprise_token - jeton_admin_procedure_path(@procedure) end end diff --git a/app/models/concerns/api_entreprise_token_concern.rb b/app/models/concerns/api_entreprise_token_concern.rb index debb559c6..ad1d0a41e 100644 --- a/app/models/concerns/api_entreprise_token_concern.rb +++ b/app/models/concerns/api_entreprise_token_concern.rb @@ -7,7 +7,6 @@ module APIEntrepriseTokenConcern included do validates :api_entreprise_token, jwt_token: true, allow_blank: true - validate :api_entreprise_token_expiration_valid?, on: [:publication] before_save :set_api_entreprise_token_expires_at, if: :will_save_change_to_api_entreprise_token? @@ -19,12 +18,6 @@ module APIEntrepriseTokenConcern self[:api_entreprise_token].presence || Rails.application.secrets.api_entreprise[:key] end - def api_entreprise_token_expiration_valid? - if api_entreprise_token_expired_or_expires_soon? - errors.add(:api_entreprise_token, "expiré ou expirant bientôt") - end - end - def api_entreprise_token_expired? APIEntrepriseToken.new(api_entreprise_token).expired? end diff --git a/app/views/administrateurs/_breadcrumbs.html.haml b/app/views/administrateurs/_breadcrumbs.html.haml index 41a20952a..9264a128e 100644 --- a/app/views/administrateurs/_breadcrumbs.html.haml +++ b/app/views/administrateurs/_breadcrumbs.html.haml @@ -30,7 +30,7 @@ .flex.fr-mt-1w - if @procedure.api_entreprise_token_expired_or_expires_soon? - %span.fr-badge.fr-badge--warning.fr-mr-1w + %span.fr-badge.fr-badge--error.fr-mr-1w = t('to_modify', scope: [:layouts, :breadcrumb]) %span.fr-badge.fr-badge--success.fr-mr-1w diff --git a/app/views/administrateurs/procedures/_procedures_list.html.haml b/app/views/administrateurs/procedures/_procedures_list.html.haml index dcf4d7c9c..99eecd912 100644 --- a/app/views/administrateurs/procedures/_procedures_list.html.haml +++ b/app/views/administrateurs/procedures/_procedures_list.html.haml @@ -61,7 +61,7 @@ - elsif procedure.publiee? - if procedure.api_entreprise_token_expired_or_expires_soon? - %span.fr-badge.fr-badge--sm.fr-badge--warning + %span.fr-badge.fr-badge--sm.fr-badge--error = t('to_modify', scope: [:layouts, :breadcrumb]) %span.fr-badge.fr-badge--sm.fr-badge--success = t('published', scope: [:layouts, :breadcrumb]) diff --git a/app/views/administrateurs/procedures/jeton.html.haml b/app/views/administrateurs/procedures/jeton.html.haml index 862a989a0..f51fbaba7 100644 --- a/app/views/administrateurs/procedures/jeton.html.haml +++ b/app/views/administrateurs/procedures/jeton.html.haml @@ -1,10 +1,10 @@ = render partial: 'administrateurs/breadcrumbs', locals: { steps: [['Démarches', admin_procedures_back_path(@procedure)], [@procedure.libelle.truncate_words(10), admin_procedure_path(@procedure)], - ['Jeton Entreprise']] } + ['Jeton API Entreprise']] } .fr-container - %h1.fr-h2 Jeton Entreprise + %h1.fr-h2 Jeton API Entreprise = form_with model: @procedure, url: url_for({ controller: 'administrateurs/procedures', action: :update_jeton }) do |f| .fr-container @@ -14,14 +14,16 @@ Démarches Simplifiées utilise = link_to 'API Entreprise', "https://entreprise.api.gouv.fr/" qui permet de récupérer les informations administratives des entreprises et des associations. - Si votre démarche nécessite des autorisations spécifiques que Démarches Simplifiées n’a pas par défaut, merci de renseigner ici le jeton - = link_to 'API Entreprise', "https://api.gouv.fr/les-api/api-entreprise/demande-acces" + Si votre démarche nécessite des autorisations spécifiques que Démarches Simplifiées n’a pas par défaut, merci de renseigner ci dessous + %strong le jeton API Entreprise propre à votre démarche. + %p + Si besoin, vous pouvez demander une habilitation API Entreprise sur le site + = link_to 'api.gouv.fr.', "https://api.gouv.fr/les-api/api-entreprise/demande-acces" + = render partial: 'administrateurs/procedures/api_entreprise_token_expiration_alert', locals: { procedure: @procedure } - .fr-input-group - = f.label :api_entreprise_token, "Jeton", class: 'fr-label' - = f.password_field :api_entreprise_token, value: @procedure.read_attribute(:api_entreprise_token), class: 'fr-input' + = render Dsfr::InputComponent.new(form: f, attribute: :api_entreprise_token, input_type: :password_field, required: false, opts: { value: @procedure.read_attribute(:api_entreprise_token)}) = render Procedure::FixedFooterComponent.new(procedure: @procedure, form: f) diff --git a/app/views/administrateurs/procedures/show.html.haml b/app/views/administrateurs/procedures/show.html.haml index 8cdfc01c2..4724df9ff 100644 --- a/app/views/administrateurs/procedures/show.html.haml +++ b/app/views/administrateurs/procedures/show.html.haml @@ -27,6 +27,14 @@ = link_to 'Clore', admin_procedure_close_path(procedure_id: @procedure.id), class: 'fr-btn fr-btn--tertiary fr-btn--icon-left fr-icon-calendar-close-fill', id: "close-procedure-link" .fr-container + - if @procedure.api_entreprise_token_expired_or_expires_soon? + = render Dsfr::AlertComponent.new(state: :error, title: t(:technical_issues, scope: [:administrateurs, :procedures]), extra_class_names: 'fr-mb-2w') do |c| + - c.with_body do + %ul.fr-mb-0 + %li + = link_to "Jeton API Entreprise", jeton_admin_procedure_path(@procedure), class: 'error-anchor' + est expiré ou va expirer prochainement + - if @procedure.draft_changed? = render Dsfr::CalloutComponent.new(title: t(:has_changes, scope: [:administrateurs, :revision_changes]), icon: "fr-fi-information-line") do |c| - c.with_body do diff --git a/config/locales/models/procedure/en.yml b/config/locales/models/procedure/en.yml index 489d0baa0..98d27a1f4 100644 --- a/config/locales/models/procedure/en.yml +++ b/config/locales/models/procedure/en.yml @@ -7,6 +7,7 @@ en: attributes: procedure: hints: + api_entreprise_token: 'For example: eyJhbGciOiJIUzI1NiJ9.eyJ1...' description: Describe in a few lines the context, the aim etc. description_target_audience: Describe in a few lines the final recipients of the process, the eligibility criteria if there are any, the prerequisites, etc. description_pj: Describe the required attachments list if there is any diff --git a/config/locales/models/procedure/fr.yml b/config/locales/models/procedure/fr.yml index 6a020049f..9cc851e1f 100644 --- a/config/locales/models/procedure/fr.yml +++ b/config/locales/models/procedure/fr.yml @@ -7,6 +7,7 @@ fr: attributes: procedure: hints: + api_entreprise_token: 'Exemple : eyJhbGciOiJIUzI1NiJ9.eyJ1...' description: Décrivez en quelques lignes le contexte, la finalité, etc. description_target_audience: Décrivez en quelques lignes les destinataires finaux de la démarche, les conditions d’éligibilité s’il y en a, les pré-requis, etc. description_pj: Décrivez la liste des pièces jointes à fournir s’il y en a diff --git a/config/locales/views/administrateurs/procedures/en.yml b/config/locales/views/administrateurs/procedures/en.yml index c1776e841..2f471930d 100644 --- a/config/locales/views/administrateurs/procedures/en.yml +++ b/config/locales/views/administrateurs/procedures/en.yml @@ -67,6 +67,7 @@ en: dpd_part_4: How to do ? You can either send him the link to the procedure on test stage by email, or name him "administrator". In any case, publish your approach only after having had his opinion. back_to_procedure: 'Cancel and return to the procedure page' submit: Publish + technical_issues: "Issues are affecting the proper functioning of the process" check_path: path_not_available: owner: This URL is identical to another of your published procedures. If you publish this procedure, the old one will be unpublished and will no longer be accessible to the public. diff --git a/config/locales/views/administrateurs/procedures/fr.yml b/config/locales/views/administrateurs/procedures/fr.yml index f86a93276..036a90daa 100644 --- a/config/locales/views/administrateurs/procedures/fr.yml +++ b/config/locales/views/administrateurs/procedures/fr.yml @@ -67,6 +67,7 @@ fr: dpd_part_4: Comment faire ? Vous pouvez soit lui communiquer par email le lien vers la démarche en test, ou bien le nommer « administrateur ». Dans tous les cas, ne publiez votre démarche qu’après avoir eu son avis. back_to_procedure: 'Annuler et revenir à la page de la démarche' submit: Publier + technical_issues: Des problèmes impactent le bon fonctionnement de la démarche check_path: path_not_available: owner: Cette url est identique à celle d’une autre de vos démarches publiées. Si vous publiez cette démarche, l’ancienne sera dépubliée et ne sera plus accessible au public. diff --git a/spec/components/procedures/errors_summary_spec.rb b/spec/components/procedures/errors_summary_spec.rb index e673244f9..9ff3edadb 100644 --- a/spec/components/procedures/errors_summary_spec.rb +++ b/spec/components/procedures/errors_summary_spec.rb @@ -83,8 +83,7 @@ describe Procedure::ErrorsSummary, type: :component do include Logic let(:validation_context) { :publication } - let(:expired_token) { JWT.encode({ exp: 2.days.ago.to_i }, nil, 'none') } - let(:procedure) { create(:procedure, attestation_template:, initiated_mail:, api_entreprise_token: expired_token) } + let(:procedure) { create(:procedure, attestation_template:, initiated_mail:) } let(:attestation_template) { build(:attestation_template) } let(:initiated_mail) { build(:initiated_mail) } @@ -98,7 +97,6 @@ describe Procedure::ErrorsSummary, type: :component do expect(page).to have_selector("a", text: "Les règles d’inéligibilité") expect(page).to have_selector("a", text: "Le modèle d’attestation") expect(page).to have_selector("a", text: "L’email de notification de passage de dossier en instruction") - expect(page).to have_selector("a", text: "Jeton API Entreprise") expect(page).to have_text("n'est pas valide", count: 2) end end From f26ff3053880d7e16a56ff3b2b75e78843063205 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Fri, 27 Sep 2024 16:31:41 +0200 Subject: [PATCH 11/17] [#10799] Add maintenance task to fill api_entreprise_token_expires_at for previous data --- ...te_api_entreprise_token_expires_at_task.rb | 14 +++++++++++++ ...i_entreprise_token_expires_at_task_spec.rb | 20 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 app/tasks/maintenance/update_api_entreprise_token_expires_at_task.rb create mode 100644 spec/tasks/maintenance/update_api_entreprise_token_expires_at_task_spec.rb diff --git a/app/tasks/maintenance/update_api_entreprise_token_expires_at_task.rb b/app/tasks/maintenance/update_api_entreprise_token_expires_at_task.rb new file mode 100644 index 000000000..4c111d7dc --- /dev/null +++ b/app/tasks/maintenance/update_api_entreprise_token_expires_at_task.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Maintenance + class UpdateAPIEntrepriseTokenExpiresAtTask < MaintenanceTasks::Task + def collection + Procedure.where.not(api_entreprise_token: nil) + end + + def process(procedure) + procedure.set_api_entreprise_token_expires_at + procedure.save! + end + end +end diff --git a/spec/tasks/maintenance/update_api_entreprise_token_expires_at_task_spec.rb b/spec/tasks/maintenance/update_api_entreprise_token_expires_at_task_spec.rb new file mode 100644 index 000000000..87a78b147 --- /dev/null +++ b/spec/tasks/maintenance/update_api_entreprise_token_expires_at_task_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Maintenance + RSpec.describe UpdateAPIEntrepriseTokenExpiresAtTask do + describe "#process" do + subject(:process) { described_class.process(procedure) } + + let(:expiration) { 1.month.from_now.beginning_of_minute } + let(:procedure) { create(:procedure) } + + before do + procedure.update_column(:api_entreprise_token, JWT.encode({ exp: expiration.to_i }, nil, "none")) + end + + it do + expect { process }.to change { procedure.reload.api_entreprise_token_expires_at }.from(nil).to(expiration) + end + end + end +end From e172f3ed6cae59017daa88f7ec4e5544a7b51500 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Fri, 27 Sep 2024 16:49:23 +0200 Subject: [PATCH 12/17] [#10799] Fix tests --- app/models/api_entreprise_token.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/api_entreprise_token.rb b/app/models/api_entreprise_token.rb index fac81acda..b3c1e5b7b 100644 --- a/app/models/api_entreprise_token.rb +++ b/app/models/api_entreprise_token.rb @@ -18,7 +18,7 @@ class APIEntrepriseToken end def expiration - Time.zone.at(decoded_token["exp"]) + decoded_token.key?("exp") && Time.zone.at(decoded_token["exp"]) end def role?(role) From c897893e8a2315398d1bc2340888d1ca17a0839d Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Fri, 27 Sep 2024 17:02:56 +0200 Subject: [PATCH 13/17] [#10799] Remove dead code --- .../concerns/api_entreprise_token_concern.rb | 4 --- spec/models/procedure_spec.rb | 25 ------------------- 2 files changed, 29 deletions(-) diff --git a/app/models/concerns/api_entreprise_token_concern.rb b/app/models/concerns/api_entreprise_token_concern.rb index ad1d0a41e..2cfacb346 100644 --- a/app/models/concerns/api_entreprise_token_concern.rb +++ b/app/models/concerns/api_entreprise_token_concern.rb @@ -18,10 +18,6 @@ module APIEntrepriseTokenConcern self[:api_entreprise_token].presence || Rails.application.secrets.api_entreprise[:key] end - def api_entreprise_token_expired? - APIEntrepriseToken.new(api_entreprise_token).expired? - end - def api_entreprise_token_expired_or_expires_soon? api_entreprise_token_expires_at && api_entreprise_token_expires_at <= SOON_TO_EXPIRE_DELAY.from_now end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index ae0179513..2453f7749 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -616,31 +616,6 @@ describe Procedure do end end - describe 'api_entreprise_token_expired?' do - let(:token) { "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" } - let(:procedure) { create(:procedure, api_entreprise_token: token) } - let(:payload) { - [ - { "exp" => expiration_time } - ] - } - let(:subject) { procedure.api_entreprise_token_expired? } - - before do - allow(JWT).to receive(:decode).with(token, nil, false).and_return(payload) - end - - context "with token expired" do - let(:expiration_time) { (1.day.ago).to_i } - it { is_expected.to be_truthy } - end - - context "with token not expired" do - let(:expiration_time) { (1.day.from_now).to_i } - it { is_expected.to be_falsey } - end - end - describe 'clone' do let(:service) { create(:service) } let(:procedure) do From 4266a76db612b0f973dfec8bd9d76bffe65d6ffb Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Fri, 27 Sep 2024 17:32:39 +0200 Subject: [PATCH 14/17] [#10799] Move test in right file --- .../api_entreprise_token_concern_spec.rb | 51 +++++++++++++++++++ spec/models/procedure_spec.rb | 51 ------------------- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/spec/models/concerns/api_entreprise_token_concern_spec.rb b/spec/models/concerns/api_entreprise_token_concern_spec.rb index 7a6040af6..ce58d2b15 100644 --- a/spec/models/concerns/api_entreprise_token_concern_spec.rb +++ b/spec/models/concerns/api_entreprise_token_concern_spec.rb @@ -30,4 +30,55 @@ describe APIEntrepriseTokenConcern do it { is_expected.to be_truthy } end end + + describe '#set_api_entreprise_token_expires_at (before_save)' do + let(:procedure) { create(:procedure, api_entreprise_token: initial_api_entreprise_token) } + + before do + procedure.api_entreprise_token = api_entreprise_token + end + + subject { procedure.save } + + context "when procedure had no api_entreprise_token" do + let(:initial_api_entreprise_token) { nil } + + context 'when the api_entreprise_token is nil' do + let(:api_entreprise_token) { nil } + + it 'does not set the api_entreprise_token_expires_at' do + expect { subject }.not_to change { procedure.api_entreprise_token_expires_at }.from(nil) + end + end + + context 'when the api_entreprise_token is not valid' do + let(:api_entreprise_token) { "not a token" } + + it do + expect { subject }.not_to change { procedure.api_entreprise_token_expires_at }.from(nil) + end + end + + context 'when the api_entreprise_token is valid' do + let(:expiration_date) { Time.zone.now.beginning_of_minute } + let(:api_entreprise_token) { JWT.encode({ exp: expiration_date.to_i }, nil, 'none') } + + it do + expect { subject }.to change { procedure.api_entreprise_token_expires_at }.from(nil).to(expiration_date) + end + end + end + + context "when procedure had an api_entreprise_token" do + let(:initial_api_entreprise_token) { JWT.encode({ exp: 2.months.from_now.to_i }, nil, "none") } + + context 'when the api_entreprise_token is set to nil' do + let(:api_entreprise_token) { nil } + + it do + expect { subject }.to change { procedure.api_entreprise_token_expires_at }.to(nil) + end + end + end + end end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 2453f7749..78f5a45d1 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -1835,57 +1835,6 @@ describe Procedure do end end - describe '#set_api_entreprise_token_expires_at (before_save)' do - let(:procedure) { create(:procedure, api_entreprise_token: initial_api_entreprise_token) } - - before do - procedure.api_entreprise_token = api_entreprise_token - end - - subject { procedure.save } - - context "when procedure had no api_entreprise_token" do - let(:initial_api_entreprise_token) { nil } - - context 'when the api_entreprise_token is nil' do - let(:api_entreprise_token) { nil } - - it 'does not set the api_entreprise_token_expires_at' do - expect { subject }.not_to change { procedure.api_entreprise_token_expires_at }.from(nil) - end - end - - context 'when the api_entreprise_token is not valid' do - let(:api_entreprise_token) { "not a token" } - - it do - expect { subject }.not_to change { procedure.api_entreprise_token_expires_at }.from(nil) - end - end - - context 'when the api_entreprise_token is valid' do - let(:expiration_date) { Time.zone.now.beginning_of_minute } - let(:api_entreprise_token) { JWT.encode({ exp: expiration_date.to_i }, nil, 'none') } - - it do - expect { subject }.to change { procedure.api_entreprise_token_expires_at }.from(nil).to(expiration_date) - end - end - end - - context "when procedure had an api_entreprise_token" do - let(:initial_api_entreprise_token) { JWT.encode({ exp: 2.months.from_now.to_i }, nil, "none") } - - context 'when the api_entreprise_token is set to nil' do - let(:api_entreprise_token) { nil } - - it do - expect { subject }.to change { procedure.api_entreprise_token_expires_at }.to(nil) - end - end - end - end - describe "#parsed_latest_zone_labels" do let!(:draft_procedure) { create(:procedure) } let!(:published_procedure) { create(:procedure_with_dossiers, :published, dossiers_count: 2) } From dfa3276cb646c00f9d8e3958a28faf3244ff6ed5 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Fri, 27 Sep 2024 17:42:56 +0200 Subject: [PATCH 15/17] [#10799] Missing test --- ...ate_api_entreprise_token_expires_at_task_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spec/tasks/maintenance/update_api_entreprise_token_expires_at_task_spec.rb b/spec/tasks/maintenance/update_api_entreprise_token_expires_at_task_spec.rb index 87a78b147..4fca851f8 100644 --- a/spec/tasks/maintenance/update_api_entreprise_token_expires_at_task_spec.rb +++ b/spec/tasks/maintenance/update_api_entreprise_token_expires_at_task_spec.rb @@ -2,6 +2,19 @@ module Maintenance RSpec.describe UpdateAPIEntrepriseTokenExpiresAtTask do + describe '#collection' do + subject(:collection) { described_class.collection } + + let!(:procedures_with_token) { create_list(:procedure, 3, api_entreprise_token: JWT.encode({}, nil, 'none')) } + let!(:procedure_without_token) { create(:procedure, api_entreprise_token: nil) } + + it 'returns procedures with api_entreprise_token present' do + expect(collection).to match_array(procedures_with_token) + + expect(collection).not_to include(procedure_without_token) + end + end + describe "#process" do subject(:process) { described_class.process(procedure) } From 6bdc641cdfa654ce81a25c7649d5e16f8d095526 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Fri, 27 Sep 2024 18:08:09 +0200 Subject: [PATCH 16/17] =?UTF-8?q?[#10799]=20Fix=20after=20Marl=C3=A8ne's?= =?UTF-8?q?=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/views/administrateurs/procedures/jeton.html.haml | 4 ++-- app/views/administrateurs/procedures/show.html.haml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/views/administrateurs/procedures/jeton.html.haml b/app/views/administrateurs/procedures/jeton.html.haml index f51fbaba7..036bffba4 100644 --- a/app/views/administrateurs/procedures/jeton.html.haml +++ b/app/views/administrateurs/procedures/jeton.html.haml @@ -18,8 +18,8 @@ %strong le jeton API Entreprise propre à votre démarche. %p - Si besoin, vous pouvez demander une habilitation API Entreprise sur le site - = link_to 'api.gouv.fr.', "https://api.gouv.fr/les-api/api-entreprise/demande-acces" + Si besoin, vous pouvez demander une habilitation API Entreprise en cliquant sur le lien suivant : + = link_to "https://api.gouv.fr/les-api/api-entreprise/demande-acces.", "https://api.gouv.fr/les-api/api-entreprise/demande-acces" = render partial: 'administrateurs/procedures/api_entreprise_token_expiration_alert', locals: { procedure: @procedure } diff --git a/app/views/administrateurs/procedures/show.html.haml b/app/views/administrateurs/procedures/show.html.haml index 4724df9ff..4414f921f 100644 --- a/app/views/administrateurs/procedures/show.html.haml +++ b/app/views/administrateurs/procedures/show.html.haml @@ -32,6 +32,7 @@ - c.with_body do %ul.fr-mb-0 %li + Le = link_to "Jeton API Entreprise", jeton_admin_procedure_path(@procedure), class: 'error-anchor' est expiré ou va expirer prochainement From 029a75404d607844ea3f43c4e0c786d637a5ac58 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 8 Oct 2024 09:48:19 +0200 Subject: [PATCH 17/17] [#10799] Fixes after @E-L-T review's --- .../api_entreprise_component.html.haml | 2 +- app/models/concerns/api_entreprise_token_concern.rb | 4 ++-- .../update_api_entreprise_token_expires_at_task.rb | 2 +- app/views/administrateurs/procedures/jeton.html.haml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/components/procedure/card/api_entreprise_component/api_entreprise_component.html.haml b/app/components/procedure/card/api_entreprise_component/api_entreprise_component.html.haml index 39fdeb27f..e7eda1d7c 100644 --- a/app/components/procedure/card/api_entreprise_component/api_entreprise_component.html.haml +++ b/app/components/procedure/card/api_entreprise_component/api_entreprise_component.html.haml @@ -1,7 +1,7 @@ .fr-col-6.fr-col-md-4.fr-col-lg-3 = link_to jeton_admin_procedure_path(@procedure), class: 'fr-tile fr-enlarge-link' do .fr-tile__body.flex.column.align-center.justify-between - - if @procedure.has_custom_api_entreprise_token? + - if @procedure.has_api_entreprise_token? - if @procedure.api_entreprise_token_expired_or_expires_soon? %p.fr-badge.fr-badge--error À renouveler - else diff --git a/app/models/concerns/api_entreprise_token_concern.rb b/app/models/concerns/api_entreprise_token_concern.rb index 2cfacb346..3750d35ef 100644 --- a/app/models/concerns/api_entreprise_token_concern.rb +++ b/app/models/concerns/api_entreprise_token_concern.rb @@ -22,12 +22,12 @@ module APIEntrepriseTokenConcern api_entreprise_token_expires_at && api_entreprise_token_expires_at <= SOON_TO_EXPIRE_DELAY.from_now end - def has_custom_api_entreprise_token? + def has_api_entreprise_token? self[:api_entreprise_token].present? end def set_api_entreprise_token_expires_at - self.api_entreprise_token_expires_at = has_custom_api_entreprise_token? ? APIEntrepriseToken.new(api_entreprise_token).expiration : nil + self.api_entreprise_token_expires_at = has_api_entreprise_token? ? APIEntrepriseToken.new(api_entreprise_token).expiration : nil end end end diff --git a/app/tasks/maintenance/update_api_entreprise_token_expires_at_task.rb b/app/tasks/maintenance/update_api_entreprise_token_expires_at_task.rb index 4c111d7dc..206a270ec 100644 --- a/app/tasks/maintenance/update_api_entreprise_token_expires_at_task.rb +++ b/app/tasks/maintenance/update_api_entreprise_token_expires_at_task.rb @@ -3,7 +3,7 @@ module Maintenance class UpdateAPIEntrepriseTokenExpiresAtTask < MaintenanceTasks::Task def collection - Procedure.where.not(api_entreprise_token: nil) + Procedure.with_discarded.where.not(api_entreprise_token: nil) end def process(procedure) diff --git a/app/views/administrateurs/procedures/jeton.html.haml b/app/views/administrateurs/procedures/jeton.html.haml index 036bffba4..8172136d5 100644 --- a/app/views/administrateurs/procedures/jeton.html.haml +++ b/app/views/administrateurs/procedures/jeton.html.haml @@ -14,7 +14,7 @@ Démarches Simplifiées utilise = link_to 'API Entreprise', "https://entreprise.api.gouv.fr/" qui permet de récupérer les informations administratives des entreprises et des associations. - Si votre démarche nécessite des autorisations spécifiques que Démarches Simplifiées n’a pas par défaut, merci de renseigner ci dessous + Si votre démarche nécessite des autorisations spécifiques que Démarches Simplifiées n’a pas par défaut, merci de renseigner ci-dessous %strong le jeton API Entreprise propre à votre démarche. %p