From f63b3696607a34ebe21843c7cf3ce5e713d18e43 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Feldis <5403+jbfeldis@users.noreply.github.com> Date: Mon, 16 Dec 2024 18:30:10 +0100 Subject: [PATCH] =?UTF-8?q?Ajoute=20une=20route=20pour=20setup=20une=20col?= =?UTF-8?q?lectivit=C3=A9=20depuis=20un=20webhook=20datapass?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L'organizer SetupCollectivity va créer les resources nécessaires sur HubEE (organisation et abonnement) puis créer l'entrée de la collectivité sur FQF. Une doc à été ajoutée dans docs/scripts.md pour créer des communes de test en local ou en sandbox. --- Gemfile | 6 + Gemfile.lock | 13 ++ .../api/datapass_webhooks_controller.rb | 57 ++++++++ .../datapass_webhook/create_collectivity.rb | 25 ++++ .../create_hubee_organization.rb | 15 ++ .../create_hubee_subscription.rb | 42 ++++++ app/jobs/setup_collectivity_job.rb | 6 + app/models/organization.rb | 37 +++++ .../datapass_webhook/setup_collectivity.rb | 20 +++ config/credentials.yml.enc | 2 +- config/credentials/development.yml.enc | 2 +- config/credentials/sandbox.yml.enc | 2 +- config/credentials/test.yml.enc | 2 +- config/initializers/inflections.rb | 1 + config/routes.rb | 2 + config/settings.yml | 7 +- config/settings/production.yml | 3 + docs/scripts.md | 33 +++++ lib/hub_signature.rb | 25 ++++ lib/hubee/admin_api.rb | 132 ++++++++++++++++++ lib/hubee/auth.rb | 29 ++++ lib/hubee/base.rb | 23 +++ lib/insee_sirene/api.rb | 17 +++ lib/insee_sirene/auth.rb | 19 +++ lib/insee_sirene/base.rb | 25 ++++ spec/lib/hub_signature_spec.rb | 35 +++++ 26 files changed, 574 insertions(+), 6 deletions(-) create mode 100644 app/controllers/api/datapass_webhooks_controller.rb create mode 100644 app/interactors/datapass_webhook/create_collectivity.rb create mode 100644 app/interactors/datapass_webhook/create_hubee_organization.rb create mode 100644 app/interactors/datapass_webhook/create_hubee_subscription.rb create mode 100644 app/jobs/setup_collectivity_job.rb create mode 100644 app/models/organization.rb create mode 100644 app/organizers/datapass_webhook/setup_collectivity.rb create mode 100644 docs/scripts.md create mode 100644 lib/hub_signature.rb create mode 100644 lib/hubee/admin_api.rb create mode 100644 lib/hubee/auth.rb create mode 100644 lib/hubee/base.rb create mode 100644 lib/insee_sirene/api.rb create mode 100644 lib/insee_sirene/auth.rb create mode 100644 lib/insee_sirene/base.rb create mode 100644 spec/lib/hub_signature_spec.rb diff --git a/Gemfile b/Gemfile index 0db89494..de57b4c4 100644 --- a/Gemfile +++ b/Gemfile @@ -55,6 +55,12 @@ gem "factory_bot_rails" gem "active_model_serializers", github: "rails-api/active_model_serializers", branch: "0-10-stable" gem "activerecord-session_store" +gem "faraday" +gem "faraday-gzip" +gem "faraday-net_http" +gem "faraday-retry" +gem "faraday-encoding" + group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem gem "debug", platforms: %i[mri windows] diff --git a/Gemfile.lock b/Gemfile.lock index 39716e88..379d9b72 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -188,10 +188,17 @@ GEM faraday (2.10.0) faraday-net_http (>= 2.0, < 3.2) logger + faraday-encoding (0.0.6) + faraday faraday-follow_redirects (0.3.0) faraday (>= 1, < 3) + faraday-gzip (3.0.2) + faraday (>= 2.0, < 3) + zlib (~> 3.0) faraday-net_http (3.1.0) net-http + faraday-retry (2.2.1) + faraday (~> 2.0) ffi (1.17.0-arm64-darwin) ffi (1.17.0-x86_64-linux-gnu) formatador (1.1.0) @@ -540,6 +547,7 @@ GEM xpath (3.2.0) nokogiri (~> 1.8) zeitwerk (2.7.1) + zlib (3.2.0) PLATFORMS arm64-darwin-23 @@ -557,6 +565,11 @@ DEPENDENCIES database_cleaner-active_record debug factory_bot_rails + faraday + faraday-encoding + faraday-gzip + faraday-net_http + faraday-retry good_job (~> 3.99) guard guard-cucumber diff --git a/app/controllers/api/datapass_webhooks_controller.rb b/app/controllers/api/datapass_webhooks_controller.rb new file mode 100644 index 00000000..dffd5f13 --- /dev/null +++ b/app/controllers/api/datapass_webhooks_controller.rb @@ -0,0 +1,57 @@ +class Api::DatapassWebhooksController < ActionController::API + before_action :verify_hub_signature!, only: :create + + def create + if event == "approve" + SetupCollectivityJob.perform_later(datapass_id:, collectivity_siret:, collectivity_email:, service_provider:) + end + end + + private + + def collectivity_email + webhook_params.dig("data", "applicant", "email") + end + + def collectivity_siret + webhook_params.dig("data", "organization", "siret") + end + + def datapass_id + webhook_params["model_id"] + end + + def event + webhook_params["event"] + end + + def hub_signature + request.headers["X-Hub-Signature-256"] + end + + def hub_signature_valid? + HubSignature.new(hub_signature, request.raw_post).valid? + end + + def service_provider + webhook_params.dig("data", "service_provider") + end + + def unauthorized + render json: {error: "Unauthorized"}, status: :unauthorized + end + + def webhook_params + @webhook_params ||= params.permit( + :event, + :model_type, + :model_id, + :fired_at, + data: {} + ).to_h + end + + def verify_hub_signature! + unauthorized unless hub_signature_valid? + end +end diff --git a/app/interactors/datapass_webhook/create_collectivity.rb b/app/interactors/datapass_webhook/create_collectivity.rb new file mode 100644 index 00000000..07ff3190 --- /dev/null +++ b/app/interactors/datapass_webhook/create_collectivity.rb @@ -0,0 +1,25 @@ +class DatapassWebhook::CreateCollectivity < BaseInteractor + delegate :organization, to: :context + + def call + context.collectivity = find_or_create_collectivity + end + + private + + def editor_id + Hash(context.service_provider).fetch("id", nil) + end + + def find_or_create_collectivity + collectivity = Collectivity.find_or_initialize_by(siret: organization.siret) + + collectivity.update!( + name: organization.denomination, + code_cog: organization.code_commune_etablissement, + departement: organization.code_postal_etablissement[0..1], + status: :active, + editor: editor_id + ) + end +end diff --git a/app/interactors/datapass_webhook/create_hubee_organization.rb b/app/interactors/datapass_webhook/create_hubee_organization.rb new file mode 100644 index 00000000..4e111b8f --- /dev/null +++ b/app/interactors/datapass_webhook/create_hubee_organization.rb @@ -0,0 +1,15 @@ +class DatapassWebhook::CreateHubEEOrganization < BaseInteractor + def call + context.hubee_organization_payload = find_or_create_organization_on_hubee + end + + private + + def find_or_create_organization_on_hubee + hubee_api_client.find_or_create_organization(context.organization, context.collectivity_email) + end + + def hubee_api_client + @hubee_api_client ||= HubEE::AdminApi.new + end +end diff --git a/app/interactors/datapass_webhook/create_hubee_subscription.rb b/app/interactors/datapass_webhook/create_hubee_subscription.rb new file mode 100644 index 00000000..d070d4ab --- /dev/null +++ b/app/interactors/datapass_webhook/create_hubee_subscription.rb @@ -0,0 +1,42 @@ +class DatapassWebhook::CreateHubEESubscription < BaseInteractor + delegate :collectivity_email, :datapass_id, :hubee_organization_payload, :service_provider, to: :context + + def call + context.hubee_subscription_payload = create_subscription_on_hubee + end + + private + + def create_subscription_on_hubee + hubee_api_client.create_subscription(datapass_id:, collectivity_email:, organization_payload: hubee_organization_payload, process_code:, editor_payload:) + end + + def editor_organization + @editor_organization ||= Organization.new(service_provider["siret"]) + end + + def editor_payload + return {} unless editor_subscription? + + { + delegationActor: { + branchCode: editor_organization.code_commune_etablissement, + companyRegister: editor_organization.siret, + type: "EDT", + }, + accessMode: "API", + } + end + + def editor_subscription? + service_provider["type"] == "editor" + end + + def hubee_api_client + @hubee_api_client ||= HubEE::AdminApi.new + end + + def process_code + "FormulaireQF" + end +end diff --git a/app/jobs/setup_collectivity_job.rb b/app/jobs/setup_collectivity_job.rb new file mode 100644 index 00000000..65e915b2 --- /dev/null +++ b/app/jobs/setup_collectivity_job.rb @@ -0,0 +1,6 @@ +class SetupCollectivityJob < ApplicationJob + def perform(datapass_id:, collectivity_siret:, collectivity_email:, service_provider:) + organization = Organization.new(collectivity_siret) + DatapassWebhook::SetupCollectivity.call(datapass_id:, organization:, collectivity_email:, service_provider:) + end +end diff --git a/app/models/organization.rb b/app/models/organization.rb new file mode 100644 index 00000000..a21c7b4e --- /dev/null +++ b/app/models/organization.rb @@ -0,0 +1,37 @@ +class Organization + attr_reader :siret + + def initialize(siret) + @siret = siret + end + + def denomination + unite_legale_insee_payload["denominationUniteLegale"] + end + + def code_commune_etablissement + adresse_etablissement_insee_payload["codeCommuneEtablissement"] + end + + def code_postal_etablissement + adresse_etablissement_insee_payload["codePostalEtablissement"] + end + + private + + def adresse_etablissement_insee_payload + etablissement_insee_payload["adresseEtablissement"] + end + + def etablissement_insee_payload + insee_payload["etablissement"] + end + + def unite_legale_insee_payload + etablissement_insee_payload["uniteLegale"] + end + + def insee_payload + @insee_payload ||= INSEESirene::Api.new.etablissement(siret:) + end +end diff --git a/app/organizers/datapass_webhook/setup_collectivity.rb b/app/organizers/datapass_webhook/setup_collectivity.rb new file mode 100644 index 00000000..2852b4c2 --- /dev/null +++ b/app/organizers/datapass_webhook/setup_collectivity.rb @@ -0,0 +1,20 @@ +class DatapassWebhook::SetupCollectivity < BaseOrganizer + organize DatapassWebhook::CreateHubEEOrganization, + DatapassWebhook::CreateHubEESubscription, + DatapassWebhook::CreateCollectivity + + around do |interactor| + interactor.call + rescue => e + Sentry.set_context( + "DataPass webhook: create HubEE resources", + payload: { + organization: context.organization.siret, + collectivity_email: context.collectivity_email, + service_provider: context.service_provider, + } + ) + + Sentry.capture_exception(e) + end +end diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc index f67b4824..5a9a51ef 100644 --- a/config/credentials.yml.enc +++ b/config/credentials.yml.enc @@ -1 +1 @@ -2mAuNgte6oJSy+gYfmqaPXiNLTenG/E+boDez2wGBuUaT5hDKSkZ56GcfQ+Ox2pG9xO5CEre/ZdDN0ygb/p9l/spcnM38Ewm3qeBCXYgs8r0Gor4E2tfsJBRo+1R5OsbNjPhHm6uKy1Qc/SYGb2oZIDKjYqsE1iMgqUtL8+RzohzGfb5eWQSJxh1zRwfnFhJXAH0lpwk+Yf/Z4EK0cPma6KTqEwZP29tc/T2NkAA8B5GzPx/xozRd7Jwp+/IYAkMstJbepZifa/F2cqZHXa41P+LaFx/UL/RSvDiBywFpac0/t4hC7TGaY1Cidw9LXXdQ4MT74Un/wEdPjCFIsLrHkTcfI+OSpywgBQSYJXb1arHJAui4SVu/j6dvir8/pPbP4jSPW8YymqcZoqSKFA4kYEni12KSgW3z23lbY7fHvnr7l9IfTqLsGoyBrhQBZhenXrlX7nG/pjJioJAcjNBQACB7b48h3UQCdKuXrensvdyFfjsSBkJwoUInR0CvKBOgLSb1cEW1HG2maIpzoTMftwpsVxdCXuPCRj2Irt2+eRpwfwWkqLO2l1o0/hijWNp7uJgcH7fdF5TYxalB5KJqDo7WmVNyE/R5xzS6OMK40kBnfsyZ9h5vwfVsdi14zTyv4oPdGAIrKxCnbOjdZOghLz6CqltBHecQ1DVp6ZeMgAn09wNIRFpO3zhPe84vk092eodH/Lrsb5Sc8L2pm4JdJLRTVtVUS2bdfX3gJvXww8ncrP5LYWkGeRi3XXVrColdIXoqW1JUFbU1F7Y4NGabMD9QEPCw3I+2mGYWNzbPKu+p01thxEEnD+QwfxR/z208EBxCrytYAftOkMANHyKZ+qLqS3ktyyk3i8/svMECvz4i9NkFNYCuwshxs3k4JQ6gX0AE5rQWtaAJt/5/RnubXyLUsJ71ruFx99YSU8P/EXz2v1m9orfem1RptHCq+5xlGCAFmiE13DiA4i8hzUH/oZ90BtKVwEubRlIAIGrCqt+h7UeJuOQ7hJj53Ey7zQso4fXJ+tTYeo3Y8TeguIxp1QqWVJgi/9swz7pdQ93Q+5ekO/0kk2OEdrm31hE5Z4SzM08pUCCq3C93QlA8AQf33oVSEykQHdYxqSvFQB8FWH6LveiCZEdRPbk+hTYiOqUm5Q6CfjiMPzKYXrWXykuTQDemdVFjbp4YVpt5rl+ICy9UjNUUG+vNkuYKkBUOplStcF4b78y+w47zCWNVi5EQ1jizMKvdDza69gYm4ksbGpnOSSJIAGAZH8OIbKMJqyrbobetRhbSZY2+wBwj8jc2MjQSCj3XzE6eFZbPqKDwz8lcpw84PAy3fYMByonH4AFHkTAVjeO5Cs00OY5Sco6RddCiY5jTyhlP4hCkvziwFwNaRtE/vJTCn9KCuyIjHYic++mEO89owMpB/02xkjifRwM4hhf3QpfDS6FYcKwJKB/LfL0Mvj4x1fZA9fNAIMvLCwhNY2hYYVjIXu9sQTjdlBZDW8DU9QauRnMeDJqctbbzIfZgSSD27VBJ/FSSQb6lzSv744/1IF6yGqu2/09wPeSdMgNAz36tkeHwbM3NZKDZA58hyrJcCX+WsOa--9hUvhkDG/m62mhN/--ESmze2CGI5lwomznqY6wwQ== \ No newline at end of file +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--+O1lTkK2C88CovZ2--MyWfiwCGlQr9Qpr1BioxWQ== \ No newline at end of file diff --git a/config/credentials/development.yml.enc b/config/credentials/development.yml.enc index 7ef4ea07..7e773aed 100644 --- a/config/credentials/development.yml.enc +++ b/config/credentials/development.yml.enc @@ -1 +1 @@ -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--8qCW2ehjT6VTrADj--8N4dNDFGbxOVuiCcl0JV4Q== \ No newline at end of file +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--MEmaoVUZVHN7ifhU--Ek4PsdCVCthpCEjZeJTD0g== \ No newline at end of file diff --git a/config/credentials/sandbox.yml.enc b/config/credentials/sandbox.yml.enc index cea51923..858e9dc8 100644 --- a/config/credentials/sandbox.yml.enc +++ b/config/credentials/sandbox.yml.enc @@ -1 +1 @@ -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--Mc3etfrIm7n+Zyrc--i8R3Ive+JFOOxKVdqXJ1+g== \ No newline at end of file +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--tZ9knVvYgVCppJKv--brn+4i9OXGqib/5kaLrtGQ== \ No newline at end of file diff --git a/config/credentials/test.yml.enc b/config/credentials/test.yml.enc index 8545c674..1179b3b0 100644 --- a/config/credentials/test.yml.enc +++ b/config/credentials/test.yml.enc @@ -1 +1 @@ -UOBgaRHKhUCGM1XD0pjgD3DzV/Uo+zMhhJFdXj2NTt/Q0zvwgMNpicbm1gz5milvfL4/AHIfuMg41wqh2VhNJrXBkEzE+XKyAX/2evnmbpAg0ImOYAw4Sg/QtBxpn5w+EmJCMjFWaYLbzljqKuRVwMhl75A6dzHgYabgjD1LMBfmXO3ByOpk1ghZyDx9lKUxdeOOmzf3jo2S6iXTWNZk8C3Z79GwDH9wFANDoo51JWI1MAwmbfq7pLgchf6CcK3UGIHNDrrPslo8ydCjJt/oBB9g92C4mTrg9nRYiZj8DPjWVKCP0R77KEM3Fxz9TLzf6LlwESwyP1NuLA4h8PwjHixKrMQQONduVVPqVrlvE2SuJtLT0nhGCxzlMceWTJubUWDU2BcJpiL5DKTra5n1wsbFsh35Ik7RdUrEZiSa2a8bqN4fp3kxDmH1PmvTLfaw--uwMHlADbKhszxBQP--qEMKbtD5+dVre5vzEDlyzg== \ No newline at end of file +t7kH37F/v5iFPt9yh+V1Civ7UnKQnpTJF6t3aExOo0/84fTJwP2llHGLGLsgbPVguwm8UoVDEopLbsH/HDmW/vT3WMbBXv+TZv9/K8i1Zj8CrEZ11FO0WZ+I02xRQLRjJwP/9eleeHiTTbhZbTEQe2mCfXOWpfQijwOt9pHzviSvXdWBLXXYG7E8M6D8wq7m+mY8s/KaDF9d5fV2bC0+1npIeifA8ZUKMqAY7KK767o0YZp8VkiDqdtNQ8AYy/k+nSvpSyoHmX7KhVWIs1E4oTCQwt8pZXzSYUx8b2BTitwr7Jv7CK9IHOEwcrxpBOLm1JqcC1TtI8NpDFfVrRDRukN1v82vKFvF/AME+LGLk+9jtVUa0f9khWNQvJ4VhK7fu8KFWvf0M+kMMsOW9MwcZHbTwlj/JE6aRLDLopwPqOyklyV1v0yjP5EJE4ESdwAOgwW2mIb225NgZ5Atfe356nyD1Q3tVrTUq3IljGrNosWdm1f1qUQC+Jj+zQzdiaWizA9czCAypbe6SNoLzJCyZuny65K5Rz/GJGYl5pKthDvaGD/mqorgBL+bj76dJE5faE3WGItIXVfw5ytfpmf22R4thI8V0U1vmqt3vWn1hfoSNvEUApLZI3R1MIN2EZo7v4OCG8Vw7DXNyMRmwEa/Ye02VEjPMhaA23vLjlJuXr7z6WDvFpxa--mbiUptsp/n9zVjTt--WoFu0qIE54NjLI5pICg0vg== \ No newline at end of file diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index 088c4a90..2c986b83 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -16,4 +16,5 @@ # end ActiveSupport::Inflector.inflections do |inflect| inflect.acronym "HubEE" + inflect.acronym "INSEE" end diff --git a/config/routes.rb b/config/routes.rb index b531835a..f2ea2f9c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -44,6 +44,8 @@ resources :collectivities, only: %i[index create], path: "collectivites" resources :frontal, only: :index + + post "/datapass/webhook" => "datapass_webhooks#create" end mount GoodJob::Engine => "good_job" diff --git a/config/settings.yml b/config/settings.yml index fa00d340..dac5406c 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -12,7 +12,10 @@ good_job: hubee: base_url: "https://api.bas.hubee.numerique.gouv.fr" token_url: "https://auth.bas.hubee.numerique.gouv.fr/oauth2/token" - client_id: <%= Rails.application.credentials.hubee.client_id %> - client_secret: <%= Rails.application.credentials.hubee.client_secret %> + client_id: <%= Rails.application.credentials.hubee.osl.client_id %> + client_secret: <%= Rails.application.credentials.hubee.osl.client_secret %> + admin: + base_url: "https://api.basrec.hubee.numerique.gouv.fr" + token_url: "https://auth.basrec.hubee.numerique.gouv.fr/oauth2/token" sentry: dsn: "real_dsn_to_setup_in_env_settings" diff --git a/config/settings/production.yml b/config/settings/production.yml index b90c127c..1ade6169 100644 --- a/config/settings/production.yml +++ b/config/settings/production.yml @@ -6,5 +6,8 @@ france_connect: hubee: base_url: "https://api.hubee.numerique.gouv.fr" token_url: "https://auth.hubee.numerique.gouv.fr/oauth2/token" + admin: + base_url: "https://api.hubee.numerique.gouv.fr" + token_url: "https://auth.hubee.numerique.gouv.fr/oauth2/token" sentry: dsn: <%= Rails.application.credentials.sentry.dsn %> diff --git a/docs/scripts.md b/docs/scripts.md new file mode 100644 index 00000000..ef051b4d --- /dev/null +++ b/docs/scripts.md @@ -0,0 +1,33 @@ +# Scripts utiles + +### Créer une commune de test + +Utile notamment pour le setup en sandbox d'un éditeur. + +```ruby +# On surcharge la dénomination pour éviter la dénomination INSEE officielle +class NamedOrga < SimpleDelegator + def denomination + "Commune test editeur AAA" + end +end + +# Une astuce pratique : utiliser le SIRET d'un établissement de la poste: +# https://annuaire-entreprises.data.gouv.fr/entreprise/la-poste-direction-generale-de-la-poste-356000000#etablissements +organization = NamedOrga.new(Organization.new("35600000038983")) +# Peu importe la valeur tant que c'est rempli +datapass_id = "12345" +# Utiliser son email ou un email jetable (yopmail etc) +collectivity_email = "jean-baptiste.feldis@beta.gouv.fr" +# Pour une commune sans éditeur : +service_provider = {} +# Pour une commune avec éditeur : +service_provider = { + "id" => "identifiant_editeur_datapass", + "siret" => "siret_editeur", + "type" => "editor", +} + +# Créé l'organisation sur HubEE, avec un abonnement FormulaireQF actif, créé la collectivité sur FQF +result = DatapassWebhook::SetupCollectivity.call(datapass_id:, organization:, collectivity_email:, service_provider:) +``` diff --git a/lib/hub_signature.rb b/lib/hub_signature.rb new file mode 100644 index 00000000..ae020ab0 --- /dev/null +++ b/lib/hub_signature.rb @@ -0,0 +1,25 @@ +require "openssl" + +class HubSignature + attr_reader :value, :body + + def initialize(value, body) + @value = value + @body = body + end + + def valid? + value.present? && + Rack::Utils.secure_compare(value, compute_signature) + end + + def compute_signature + "sha256=#{OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), verify_token, body)}" + end + + private + + def verify_token + Rails.application.credentials.webhook_verify_token! + end +end diff --git a/lib/hubee/admin_api.rb b/lib/hubee/admin_api.rb new file mode 100644 index 00000000..f39b425c --- /dev/null +++ b/lib/hubee/admin_api.rb @@ -0,0 +1,132 @@ +# :nocov: +class HubEE::AdminApi < HubEE::Base + class NotFound < StandardError; end + + class AlreadyExists < StandardError; end + + def find_or_create_organization(organization, email_demandeur = nil) + find_organization(organization) + rescue NotFound + create_organization(organization, email_demandeur) + end + + def find_organization(organization) + http_connection.get("#{host}/referential/v1/organizations/SI-#{organization.siret}-#{organization.code_commune_etablissement}").body + rescue Faraday::ResourceNotFound + raise NotFound + end + + def create_organization(organization, email) + http_connection.post( + "#{host}/referential/v1/organizations", + { + type: "SI", + companyRegister: organization.siret, + branchCode: organization.code_commune_etablissement, + email:, + name: organization.denomination, + country: "France", + postalCode: organization.code_postal_etablissement, + territory: organization.code_commune_etablissement, + status: "Actif", + }.to_json, + "Content-Type" => "application/json" + ).body + rescue Faraday::BadRequestError => e + raise AlreadyExists if already_exists_error?(e) + + raise + end + + def create_subscription(datapass_id:, collectivity_email:, organization_payload:, process_code:, editor_payload: {}) + subscription_payload = find_or_create_inactive_subscription(datapass_id:, collectivity_email:, organization_payload:, process_code:) + activate_subscription(subscription_payload, editor_payload) + subscription_payload + end + + def find_subscription(organization_payload, process_code) + request = http_connection { |conn| conn.request :gzip }.get( + "#{host}/referential/v1/subscriptions", + companyRegister: organization_payload["companyRegister"], + processCode: process_code + ) + request.body.first + end + + protected + + def host + Settings.hubee.admin.base_url + end + + def already_exists_error?(faraday_error) + faraday_error.response[:body]["errors"].any? do |error| + error["message"].include?("already exists") + end + end + + def http_connection(&block) + super do |conn| + conn.request :authorization, "Bearer", -> { HubEE::Auth.new.access_token } + yield(conn) if block + end + end + + private + + def activate_subscription(subscription_payload, editor_payload = {}) # rubocop:disable Metrics/AbcSize + subscription_id = subscription_payload["id"] + return if subscription_id.blank? + + payload = subscription_payload.with_indifferent_access.merge({ + status: "Actif", + activateDateTime: DateTime.now.iso8601, + accessMode: "API", + notificationFrequency: "Aucune", + }.with_indifferent_access) + + payload.delete("id") + payload.delete("creationDateTime") + payload.merge!(editor_payload.with_indifferent_access) + + http_connection.put( + "#{host}/referential/v1/subscriptions/#{subscription_id}", + payload.to_json, + "Content-Type" => "application/json" + ).body + end + + def create_inactive_subscription(datapass_id:, collectivity_email:, organization_payload:, process_code:) # rubocop:disable Metrics/AbcSize + http_connection.post( + "#{host}/referential/v1/subscriptions", + { + datapassId: datapass_id.to_i, + notificationFrequency: "Aucune", + processCode: process_code, + subscriber: { + type: "SI", + companyRegister: organization_payload["companyRegister"], + branchCode: organization_payload["branchCode"], + }, + email: collectivity_email, + status: "Inactif", + localAdministrator: { + email: collectivity_email, + }, + validateDateTime: DateTime.now.iso8601, + updateDateTime: DateTime.now.iso8601, + }.to_json, + "Content-Type" => "application/json" + ).body + rescue Faraday::BadRequestError => e + raise AlreadyExists if already_exists_error?(e) + + raise + end + + def find_or_create_inactive_subscription(datapass_id:, collectivity_email:, organization_payload:, process_code:) + create_inactive_subscription(datapass_id:, collectivity_email:, organization_payload:, process_code:) + rescue HubEEAPIClient::AlreadyExists + find_subscription(organization_payload, process_code) + end +end diff --git a/lib/hubee/auth.rb b/lib/hubee/auth.rb new file mode 100644 index 00000000..e66b2a17 --- /dev/null +++ b/lib/hubee/auth.rb @@ -0,0 +1,29 @@ +# :nocov: +class HubEE::Auth < HubEE::Base + def access_token + http_connection.post( + auth_url, + "grant_type=client_credentials&scope=ADMIN", + { + "Authorization" => "Basic #{encoded_client_id_and_secret}", + } + ).body["access_token"] + end + + private + + def auth_url + Settings.hubee.admin.token_url + end + + def encoded_client_id_and_secret + Base64.strict_encode64("#{consumer_key}:#{consumer_secret}") + end + + def http_connection(&block) + @http_connection ||= super do |conn| + conn.response :json + yield(conn) if block + end + end +end diff --git a/lib/hubee/base.rb b/lib/hubee/base.rb new file mode 100644 index 00000000..870e909f --- /dev/null +++ b/lib/hubee/base.rb @@ -0,0 +1,23 @@ +require "faraday" + +class HubEE::Base + protected + + def http_connection(&block) + Faraday.new do |conn| + conn.request :retry, max: 5 + conn.response :raise_error + conn.response :json + conn.options.timeout = 2 + yield(conn) if block + end + end + + def consumer_key + Rails.application.credentials.hubee.admin.client_id + end + + def consumer_secret + Rails.application.credentials.hubee.admin.client_secret + end +end diff --git a/lib/insee_sirene/api.rb b/lib/insee_sirene/api.rb new file mode 100644 index 00000000..b855f689 --- /dev/null +++ b/lib/insee_sirene/api.rb @@ -0,0 +1,17 @@ +module INSEESirene + class Api < Base + def etablissement(siret:) + http_connection.get( + "https://api.insee.fr/entreprises/sirene/V3.11/siret/#{siret}" + ).body + end + + protected + + def http_connection + super do |conn| + conn.request :authorization, "Bearer", -> { Auth.new.access_token } + end + end + end +end diff --git a/lib/insee_sirene/auth.rb b/lib/insee_sirene/auth.rb new file mode 100644 index 00000000..f914995d --- /dev/null +++ b/lib/insee_sirene/auth.rb @@ -0,0 +1,19 @@ +module INSEESirene + class Auth < Base + def access_token + http_connection.post( + "https://api.insee.fr/token", + "grant_type=client_credentials", + { + "Authorization" => "Basic #{encoded_client_id_and_secret}", + } + ).body["access_token"] + end + + private + + def encoded_client_id_and_secret + Base64.strict_encode64("#{consumer_key}:#{consumer_secret}") + end + end +end diff --git a/lib/insee_sirene/base.rb b/lib/insee_sirene/base.rb new file mode 100644 index 00000000..012c4f49 --- /dev/null +++ b/lib/insee_sirene/base.rb @@ -0,0 +1,25 @@ +require "faraday" + +module INSEESirene + class Base + protected + + def http_connection(&block) + @http_connection ||= Faraday.new do |conn| + conn.request :retry, max: 5 + conn.response :raise_error + conn.response :json + conn.options.timeout = 2 + yield(conn) if block + end + end + + def consumer_key + Rails.application.credentials.insee_consumer_key + end + + def consumer_secret + Rails.application.credentials.insee_consumer_secret + end + end +end diff --git a/spec/lib/hub_signature_spec.rb b/spec/lib/hub_signature_spec.rb new file mode 100644 index 00000000..9443674c --- /dev/null +++ b/spec/lib/hub_signature_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe HubSignature do + describe "#valid?" do + subject { described_class.new(value, body) } + + let(:verify_token) { Rails.application.credentials.webhook_verify_token! } + + context "without body nor value" do + let(:body) { nil } + let(:value) { nil } + + it { is_expected.not_to be_valid } + end + + context "with a value well formed" do + let(:value) { "sha256=#{OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), verify_token, payload)}" } + let(:payload) { "whatever" } + + context "when body is the payload used to create the HMAC signature" do + let(:body) { payload } + + it { is_expected.to be_valid } + end + + context "when body is not the payload used to create the HMAC signature" do + let(:body) { "whatever else" } + + it { is_expected.not_to be_valid } + end + end + end +end