Merge pull request #6462 from betagouv/admin_can_add_api_part_token
Un administrateur peut ajouter un jeton api particulier à une procédure
This commit is contained in:
commit
86147ec165
18 changed files with 447 additions and 2 deletions
|
@ -0,0 +1,45 @@
|
||||||
|
module NewAdministrateur
|
||||||
|
class JetonParticulierController < AdministrateurController
|
||||||
|
before_action :retrieve_procedure
|
||||||
|
|
||||||
|
def api_particulier
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@procedure.api_particulier_token = token
|
||||||
|
|
||||||
|
if @procedure.invalid?
|
||||||
|
flash.now.alert = @procedure.errors.full_messages
|
||||||
|
render :show
|
||||||
|
elsif scopes.empty?
|
||||||
|
flash.now.alert = t('.no_scopes_token')
|
||||||
|
render :show
|
||||||
|
else
|
||||||
|
@procedure.save
|
||||||
|
|
||||||
|
redirect_to admin_procedure_api_particulier_jeton_path(procedure_id: @procedure.id),
|
||||||
|
notice: t('.token_ok')
|
||||||
|
end
|
||||||
|
|
||||||
|
rescue APIParticulier::Error::Unauthorized
|
||||||
|
flash.now.alert = t('.not_found_token')
|
||||||
|
render :show
|
||||||
|
rescue APIParticulier::Error::HttpError
|
||||||
|
flash.now.alert = t('.network_error')
|
||||||
|
render :show
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def scopes
|
||||||
|
@scopes ||= APIParticulier::API.new(token).scopes
|
||||||
|
end
|
||||||
|
|
||||||
|
def token
|
||||||
|
params[:procedure][:api_particulier_token]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
34
app/lib/api_particulier/api.rb
Normal file
34
app/lib/api_particulier/api.rb
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
class APIParticulier::API
|
||||||
|
include APIParticulier::Error
|
||||||
|
|
||||||
|
INTROSPECT_RESOURCE_NAME = "introspect"
|
||||||
|
|
||||||
|
TIMEOUT = 20
|
||||||
|
|
||||||
|
def initialize(token)
|
||||||
|
@token = token
|
||||||
|
end
|
||||||
|
|
||||||
|
def scopes
|
||||||
|
get(INTROSPECT_RESOURCE_NAME)[:scopes]
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def get(resource_name, params = {})
|
||||||
|
url = [API_PARTICULIER_URL, resource_name].join("/")
|
||||||
|
|
||||||
|
response = Typhoeus.get(url,
|
||||||
|
headers: { accept: "application/json", "X-API-Key": @token },
|
||||||
|
params: params,
|
||||||
|
timeout: TIMEOUT)
|
||||||
|
|
||||||
|
if response.success?
|
||||||
|
JSON.parse(response.body, symbolize_names: true)
|
||||||
|
elsif response.code == 401
|
||||||
|
raise Unauthorized.new(response)
|
||||||
|
else
|
||||||
|
raise RequestFailed.new(response)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
32
app/lib/api_particulier/error.rb
Normal file
32
app/lib/api_particulier/error.rb
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
module APIParticulier
|
||||||
|
module Error
|
||||||
|
class HttpError < ::StandardError
|
||||||
|
def initialize(response)
|
||||||
|
connect_time = response.connect_time
|
||||||
|
curl_message = response.return_message
|
||||||
|
http_error_code = response.code
|
||||||
|
datetime = response.headers.fetch('Date', DateTime.current.inspect)
|
||||||
|
total_time = response.total_time
|
||||||
|
|
||||||
|
uri = URI.parse(response.effective_url)
|
||||||
|
url = "#{uri.host}#{uri.path}"
|
||||||
|
|
||||||
|
msg = <<~TEXT
|
||||||
|
url: #{url}
|
||||||
|
HTTP error code: #{http_error_code}
|
||||||
|
#{response.body}
|
||||||
|
curl message: #{curl_message}
|
||||||
|
total time: #{total_time}
|
||||||
|
connect time: #{connect_time}
|
||||||
|
datetime: #{datetime}
|
||||||
|
TEXT
|
||||||
|
|
||||||
|
super(msg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class RequestFailed < HttpError; end
|
||||||
|
|
||||||
|
class Unauthorized < HttpError; end
|
||||||
|
end
|
||||||
|
end
|
|
@ -267,7 +267,7 @@ class Procedure < ApplicationRecord
|
||||||
if: -> { new_record? || created_at > Date.new(2020, 11, 13) }
|
if: -> { new_record? || created_at > Date.new(2020, 11, 13) }
|
||||||
|
|
||||||
validates :api_entreprise_token, jwt_token: true, allow_blank: true
|
validates :api_entreprise_token, jwt_token: true, allow_blank: true
|
||||||
validates :api_particulier_token, format: { with: /\A[A-Za-z0-9\-_=.]{15,}\z/, message: "n'est pas un jeton valide" }, allow_blank: true
|
validates :api_particulier_token, format: { with: /\A[A-Za-z0-9\-_=.]{15,}\z/ }, allow_blank: true
|
||||||
|
|
||||||
before_save :update_juridique_required
|
before_save :update_juridique_required
|
||||||
after_initialize :ensure_path_exists
|
after_initialize :ensure_path_exists
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
= render partial: 'new_administrateur/breadcrumbs',
|
||||||
|
locals: { steps: [link_to('Démarches', admin_procedures_path),
|
||||||
|
link_to(@procedure.libelle, admin_procedure_path(@procedure)),
|
||||||
|
Procedure.human_attribute_name(:jeton_api_particulier)] }
|
||||||
|
|
||||||
|
.container
|
||||||
|
.flex
|
||||||
|
= link_to admin_procedure_api_particulier_jeton_path, class: 'card-admin' do
|
||||||
|
- if @procedure.api_particulier_token.blank?
|
||||||
|
%div
|
||||||
|
%span.icon.clock
|
||||||
|
%p.card-admin-status-todo= t('.needs_configuration')
|
||||||
|
- else
|
||||||
|
%div
|
||||||
|
%span.icon.accept
|
||||||
|
%p.card-admin-status-accept= t('.already_configured')
|
||||||
|
%div
|
||||||
|
%p.card-admin-title
|
||||||
|
= Procedure.human_attribute_name(:jeton_api_particulier)
|
||||||
|
%p.button= t('views.shared.actions.edit')
|
|
@ -0,0 +1,22 @@
|
||||||
|
= render partial: 'new_administrateur/breadcrumbs',
|
||||||
|
locals: { steps: [link_to('Démarches', admin_procedures_path),
|
||||||
|
link_to(@procedure.libelle, admin_procedure_path(@procedure)),
|
||||||
|
link_to(Procedure.human_attribute_name(:jeton_api_particulier), admin_procedure_api_particulier_path(@procedure)),
|
||||||
|
'Jeton'] }
|
||||||
|
|
||||||
|
.container
|
||||||
|
%h1.page-title
|
||||||
|
= t('.configure_token')
|
||||||
|
|
||||||
|
.container
|
||||||
|
%h1
|
||||||
|
= form_with model: @procedure, url: admin_procedure_api_particulier_jeton_path, local: true, html: { class: 'form' } do |f|
|
||||||
|
%p.explication
|
||||||
|
= t('.api_particulier_description_html', app_name: APPLICATION_NAME)
|
||||||
|
|
||||||
|
= f.label :api_particulier_token
|
||||||
|
.desc.mb-2
|
||||||
|
%p= t('.token_description')
|
||||||
|
= f.password_field :api_particulier_token, class: 'form-control', required: :required
|
||||||
|
.text-right
|
||||||
|
= f.button t('views.shared.actions.save'), class: 'button primary send'
|
|
@ -194,10 +194,25 @@
|
||||||
%span.icon.clock
|
%span.icon.clock
|
||||||
%p.card-admin-status-todo À configurer
|
%p.card-admin-status-todo À configurer
|
||||||
%div
|
%div
|
||||||
%p.card-admin-title Jeton
|
%p.card-admin-title Jeton Entreprise
|
||||||
%p.card-admin-subtitle Configurer le jeton API entreprise
|
%p.card-admin-subtitle Configurer le jeton API entreprise
|
||||||
%p.button Modifier
|
%p.button Modifier
|
||||||
|
|
||||||
|
- if feature_enabled?(:api_particulier)
|
||||||
|
= link_to admin_procedure_api_particulier_path(@procedure), class: 'card-admin' do
|
||||||
|
- if @procedure.api_particulier_token.present?
|
||||||
|
%div
|
||||||
|
%span.icon.accept
|
||||||
|
%p.card-admin-status-accept= t('.ready')
|
||||||
|
- else
|
||||||
|
%div
|
||||||
|
%span.icon.clock
|
||||||
|
%p.card-admin-status-todo= t('.needs_configuration')
|
||||||
|
%div
|
||||||
|
%p.card-admin-title= Procedure.human_attribute_name(:api_particulier_token)
|
||||||
|
%p.card-admin-subtitle= t('.configure_api_particulier_token')
|
||||||
|
%p.button= t('views.shared.actions.edit')
|
||||||
|
|
||||||
= link_to monavis_admin_procedure_path(@procedure), class: 'card-admin' do
|
= link_to monavis_admin_procedure_path(@procedure), class: 'card-admin' do
|
||||||
- if @procedure.monavis_embed.present?
|
- if @procedure.monavis_embed.present?
|
||||||
%div
|
%div
|
||||||
|
|
|
@ -75,3 +75,6 @@ DS_ENV="staging"
|
||||||
|
|
||||||
# Désactivé l'OTP pour SuperAdmin
|
# Désactivé l'OTP pour SuperAdmin
|
||||||
# SUPER_ADMIN_OTP_ENABLED = "disabled" # "enabled" par défaut
|
# SUPER_ADMIN_OTP_ENABLED = "disabled" # "enabled" par défaut
|
||||||
|
|
||||||
|
# API Particulier https://api.gouv.fr/les-api/api-particulier
|
||||||
|
# API_PARTICULIER_URL="https://particulier.api.gouv.fr/api"
|
||||||
|
|
|
@ -27,6 +27,7 @@ end
|
||||||
features = [
|
features = [
|
||||||
:administrateur_routage,
|
:administrateur_routage,
|
||||||
:administrateur_web_hook,
|
:administrateur_web_hook,
|
||||||
|
:api_particulier,
|
||||||
:dossier_pdf_vide,
|
:dossier_pdf_vide,
|
||||||
:expert_not_allowed_to_invite,
|
:expert_not_allowed_to_invite,
|
||||||
:hide_instructeur_email,
|
:hide_instructeur_email,
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
# API URLs
|
# API URLs
|
||||||
API_ENTREPRISE_URL = ENV.fetch("API_ENTREPRISE_URL", "https://entreprise.api.gouv.fr/v2")
|
API_ENTREPRISE_URL = ENV.fetch("API_ENTREPRISE_URL", "https://entreprise.api.gouv.fr/v2")
|
||||||
API_EDUCATION_URL = ENV.fetch("API_EDUCATION_URL", "https://data.education.gouv.fr/api/records/1.0")
|
API_EDUCATION_URL = ENV.fetch("API_EDUCATION_URL", "https://data.education.gouv.fr/api/records/1.0")
|
||||||
|
API_PARTICULIER_URL = ENV.fetch("API_PARTICULIER_URL", "https://particulier.api.gouv.fr/api")
|
||||||
HELPSCOUT_API_URL = ENV.fetch("HELPSCOUT_API_URL", "https://api.helpscout.net/v2")
|
HELPSCOUT_API_URL = ENV.fetch("HELPSCOUT_API_URL", "https://api.helpscout.net/v2")
|
||||||
PIPEDRIVE_API_URL = ENV.fetch("PIPEDRIVE_API_URL", "https://api.pipedrive.com/v1")
|
PIPEDRIVE_API_URL = ENV.fetch("PIPEDRIVE_API_URL", "https://api.pipedrive.com/v1")
|
||||||
SENDINBLUE_API_URL = ENV.fetch("SENDINBLUE_API_URL", "https://in-automate.sendinblue.com/api/v2")
|
SENDINBLUE_API_URL = ENV.fetch("SENDINBLUE_API_URL", "https://in-automate.sendinblue.com/api/v2")
|
||||||
|
|
|
@ -97,6 +97,9 @@ fr:
|
||||||
first: Premier
|
first: Premier
|
||||||
truncate: '…'
|
truncate: '…'
|
||||||
shared:
|
shared:
|
||||||
|
actions:
|
||||||
|
save: Enregistrer
|
||||||
|
edit: Modifier
|
||||||
greetings:
|
greetings:
|
||||||
hello: Bonjour,
|
hello: Bonjour,
|
||||||
best_regards: Bonne journée,
|
best_regards: Bonne journée,
|
||||||
|
@ -366,3 +369,22 @@ fr:
|
||||||
no_establishment: "Aucun établissement n’est associé à ce dossier"
|
no_establishment: "Aucun établissement n’est associé à ce dossier"
|
||||||
identity_saved: "Identité enregistrée"
|
identity_saved: "Identité enregistrée"
|
||||||
no_longer_available: "L’attestation n'est plus disponible sur ce dossier."
|
no_longer_available: "L’attestation n'est plus disponible sur ce dossier."
|
||||||
|
new_administrateur:
|
||||||
|
jeton_particulier:
|
||||||
|
show:
|
||||||
|
configure_token: "Configurer le jeton API Particulier"
|
||||||
|
api_particulier_description_html: "%{app_name} utilise <a href=\"https://api.gouv.fr/les-api/api-particulier\">API Particulier</a> qui permet de récupérer les données familiales (CAF).<br />Renseignez ici le <a href=\"https://api.gouv.fr/les-api/api-particulier/demande-acces\">jeton API Particulier</a> propre à votre démarche."
|
||||||
|
token_description: "Il doit contenir au minimum 15 caractères."
|
||||||
|
update:
|
||||||
|
token_ok: "Le jeton a bien été mis à jour"
|
||||||
|
no_scopes_token: "Mise à jour impossible : le jeton n'a pas acces aux données.<br /><br />Vérifier le auprès de <a href='https://datapass.api.gouv.fr/'>https://datapass.api.gouv.fr/</a>"
|
||||||
|
not_found_token: "Mise à jour impossible : le jeton n'a pas été trouvé ou n'est pas actif<br /><br />Vérifier le auprès de <a href='https://datapass.api.gouv.fr/'>https://datapass.api.gouv.fr/</a>"
|
||||||
|
network_error: "Mise à jour impossible : une erreur réseau est survenue"
|
||||||
|
procedures:
|
||||||
|
show:
|
||||||
|
ready: "Validé"
|
||||||
|
needs_configuration: "À configurer"
|
||||||
|
configure_api_particulier_token: "Configurer le jeton API particulier"
|
||||||
|
api_particulier:
|
||||||
|
already_configured: "Déjà rempli"
|
||||||
|
needs_configuration: "À remplir"
|
||||||
|
|
|
@ -16,3 +16,11 @@ fr:
|
||||||
aasm_state/hidden: Suprimée
|
aasm_state/hidden: Suprimée
|
||||||
declarative_with_state/en_instruction: En instruction
|
declarative_with_state/en_instruction: En instruction
|
||||||
declarative_with_state/accepte: Accepté
|
declarative_with_state/accepte: Accepté
|
||||||
|
api_particulier_token: Jeton API Particulier
|
||||||
|
errors:
|
||||||
|
models:
|
||||||
|
procedure:
|
||||||
|
attributes:
|
||||||
|
api_particulier_token:
|
||||||
|
invalid: 'n’a pas le bon format'
|
||||||
|
|
||||||
|
|
|
@ -397,6 +397,12 @@ Rails.application.routes.draw do
|
||||||
put :experts_require_administrateur_invitation
|
put :experts_require_administrateur_invitation
|
||||||
end
|
end
|
||||||
|
|
||||||
|
get :api_particulier, controller: 'jeton_particulier'
|
||||||
|
|
||||||
|
resource 'api_particulier', only: [] do
|
||||||
|
resource 'jeton', only: [:show, :update], controller: 'jeton_particulier'
|
||||||
|
end
|
||||||
|
|
||||||
put 'clone'
|
put 'clone'
|
||||||
put 'archive'
|
put 'archive'
|
||||||
get 'publication' => 'procedures#publication', as: :publication
|
get 'publication' => 'procedures#publication', as: :publication
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
describe NewAdministrateur::JetonParticulierController, type: :controller do
|
||||||
|
let(:admin) { create(:administrateur) }
|
||||||
|
let(:procedure) { create(:procedure, administrateur: admin) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_const("API_PARTICULIER_URL", "https://particulier.api.gouv.fr/api")
|
||||||
|
|
||||||
|
sign_in(admin.user)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET #api_particulier" do
|
||||||
|
let(:procedure) { create :procedure, :with_service, administrateur: admin }
|
||||||
|
|
||||||
|
render_views
|
||||||
|
|
||||||
|
subject { get :api_particulier, params: { procedure_id: procedure.id } }
|
||||||
|
|
||||||
|
it { is_expected.to have_http_status(:success) }
|
||||||
|
it { expect(subject.body).to have_content('Jeton API particulier') }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET #show" do
|
||||||
|
subject { get :show, params: { procedure_id: procedure.id } }
|
||||||
|
|
||||||
|
it { is_expected.to have_http_status(:success) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "PATCH #update" do
|
||||||
|
let(:params) { { procedure_id: procedure.id, procedure: { api_particulier_token: token } } }
|
||||||
|
|
||||||
|
subject { patch :update, params: params }
|
||||||
|
|
||||||
|
context "when jeton has a valid shape" do
|
||||||
|
let(:token) { "d7e9c9f4c3ca00caadde31f50fd4521a" }
|
||||||
|
before do
|
||||||
|
VCR.use_cassette(cassette) do
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "and the api response is a success" do
|
||||||
|
let(:cassette) { "api_particulier/success/introspect" }
|
||||||
|
|
||||||
|
it { expect(flash.alert).to be_nil }
|
||||||
|
it { expect(flash.notice).to eq("Le jeton a bien été mis à jour") }
|
||||||
|
it { expect(procedure.reload.api_particulier_token).to eql(token) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "and the api response is a success but with an empty scopes" do
|
||||||
|
let(:cassette) { "api_particulier/success/introspect_empty_scopes" }
|
||||||
|
|
||||||
|
it { expect(flash.alert).to include("le jeton n'a pas acces aux données") }
|
||||||
|
it { expect(flash.notice).to be_nil }
|
||||||
|
it { expect(procedure.reload.api_particulier_token).not_to eql(token) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "and the api response is not unauthorized" do
|
||||||
|
let(:cassette) { "api_particulier/unauthorized/introspect" }
|
||||||
|
|
||||||
|
it { expect(flash.alert).to include("Mise à jour impossible : le jeton n'a pas été trouvé ou n'est pas actif") }
|
||||||
|
it { expect(flash.notice).to be_nil }
|
||||||
|
it { expect(procedure.reload.api_particulier_token).not_to eql(token) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when jeton is invalid and no network call is made" do
|
||||||
|
let(:token) { "jet0n 1nvalide" }
|
||||||
|
|
||||||
|
before { subject }
|
||||||
|
|
||||||
|
it { expect(flash.alert.first).to include("pas le bon format") }
|
||||||
|
it { expect(flash.notice).to be_nil }
|
||||||
|
it { expect(procedure.reload.api_particulier_token).not_to eql(token) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
44
spec/fixtures/cassettes/api_particulier/success/introspect.yml
vendored
Normal file
44
spec/fixtures/cassettes/api_particulier/success/introspect.yml
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
---
|
||||||
|
http_interactions:
|
||||||
|
- request:
|
||||||
|
method: get
|
||||||
|
uri: https://particulier.api.gouv.fr/api/introspect
|
||||||
|
body:
|
||||||
|
encoding: US-ASCII
|
||||||
|
string: ''
|
||||||
|
headers:
|
||||||
|
User-Agent:
|
||||||
|
- demarches-simplifiees.fr
|
||||||
|
Accept:
|
||||||
|
- application/json
|
||||||
|
X-Api-Key:
|
||||||
|
- d7e9c9f4c3ca00caadde31f50fd4521a
|
||||||
|
Expect:
|
||||||
|
- ''
|
||||||
|
response:
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
headers:
|
||||||
|
Date:
|
||||||
|
- Tue, 16 Mar 2021 15:25:24 GMT
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Content-Length:
|
||||||
|
- '228'
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Keep-Alive:
|
||||||
|
- timeout=5
|
||||||
|
X-Gravitee-Request-Id:
|
||||||
|
- 0e4dd327-de40-4052-8dd3-27de401052c4
|
||||||
|
X-Gravitee-Transaction-Id:
|
||||||
|
- cc30bb74-6516-46d9-b0bb-746516d6d904
|
||||||
|
Strict-Transport-Security:
|
||||||
|
- max-age=15552000
|
||||||
|
body:
|
||||||
|
encoding: UTF-8
|
||||||
|
string: '{"_id":"1d99db5a-a099-4314-ad2f-2707c6b505a6","name":"Application de
|
||||||
|
sandbox","scopes":["dgfip_avis_imposition","dgfip_adresse","cnaf_allocataires","cnaf_enfants","cnaf_adresse","cnaf_quotient_familial","mesri_statut_etudiant"]}'
|
||||||
|
recorded_at: Tue, 16 Mar 2021 15:25:24 GMT
|
||||||
|
recorded_with: VCR 6.0.0
|
44
spec/fixtures/cassettes/api_particulier/success/introspect_empty_scopes.yml
vendored
Normal file
44
spec/fixtures/cassettes/api_particulier/success/introspect_empty_scopes.yml
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
---
|
||||||
|
http_interactions:
|
||||||
|
- request:
|
||||||
|
method: get
|
||||||
|
uri: https://particulier.api.gouv.fr/api/introspect
|
||||||
|
body:
|
||||||
|
encoding: US-ASCII
|
||||||
|
string: ''
|
||||||
|
headers:
|
||||||
|
User-Agent:
|
||||||
|
- demarches-simplifiees.fr
|
||||||
|
Accept:
|
||||||
|
- application/json
|
||||||
|
X-Api-Key:
|
||||||
|
- d7e9c9f4c3ca00caadde31f50fd4521a
|
||||||
|
Expect:
|
||||||
|
- ''
|
||||||
|
response:
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
headers:
|
||||||
|
Date:
|
||||||
|
- Tue, 16 Mar 2021 15:25:24 GMT
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Content-Length:
|
||||||
|
- '228'
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Keep-Alive:
|
||||||
|
- timeout=5
|
||||||
|
X-Gravitee-Request-Id:
|
||||||
|
- 0e4dd327-de40-4052-8dd3-27de401052c4
|
||||||
|
X-Gravitee-Transaction-Id:
|
||||||
|
- cc30bb74-6516-46d9-b0bb-746516d6d904
|
||||||
|
Strict-Transport-Security:
|
||||||
|
- max-age=15552000
|
||||||
|
body:
|
||||||
|
encoding: UTF-8
|
||||||
|
string: '{"_id":"1d99db5a-a099-4314-ad2f-2707c6b505a6","name":"Application de
|
||||||
|
sandbox","scopes":[]}'
|
||||||
|
recorded_at: Tue, 16 Mar 2021 15:25:24 GMT
|
||||||
|
recorded_with: VCR 6.0.0
|
44
spec/fixtures/cassettes/api_particulier/unauthorized/introspect.yml
vendored
Normal file
44
spec/fixtures/cassettes/api_particulier/unauthorized/introspect.yml
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
---
|
||||||
|
http_interactions:
|
||||||
|
- request:
|
||||||
|
method: get
|
||||||
|
uri: https://particulier.api.gouv.fr/api/introspect
|
||||||
|
body:
|
||||||
|
encoding: US-ASCII
|
||||||
|
string: ''
|
||||||
|
headers:
|
||||||
|
User-Agent:
|
||||||
|
- demarches-simplifiees.fr
|
||||||
|
Accept:
|
||||||
|
- application/json
|
||||||
|
X-Api-Key:
|
||||||
|
- d7e9c9f4c3ca00caadde31f50fd4521a
|
||||||
|
Expect:
|
||||||
|
- ''
|
||||||
|
response:
|
||||||
|
status:
|
||||||
|
code: 401
|
||||||
|
message: ''
|
||||||
|
headers:
|
||||||
|
Server:
|
||||||
|
- nginx
|
||||||
|
Date:
|
||||||
|
- Wed, 15 Sep 2021 10:02:12 GMT
|
||||||
|
Content-Type:
|
||||||
|
- application/json; charset=utf-8
|
||||||
|
Content-Length:
|
||||||
|
- '134'
|
||||||
|
X-Powered-By:
|
||||||
|
- Express
|
||||||
|
Vary:
|
||||||
|
- Origin
|
||||||
|
Etag:
|
||||||
|
- W/"86-FwFf7uuVKCSJkazn1ZHnY1yVYUo"
|
||||||
|
Strict-Transport-Security:
|
||||||
|
- max-age=15724800; includeSubdomains
|
||||||
|
body:
|
||||||
|
encoding: UTF-8
|
||||||
|
string: >
|
||||||
|
{"error":"acces_denied","reason":"Token not found or inactive","message":"Votre jeton d'API n'a pas été trouvé ou n'est pas actif"}
|
||||||
|
recorded_at: Wed, 15 Sep 2021 10:02:12 GMT
|
||||||
|
recorded_with: VCR 6.0.0
|
28
spec/lib/api_particulier/api_spec.rb
Normal file
28
spec/lib/api_particulier/api_spec.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
describe APIParticulier::API do
|
||||||
|
let(:token) { "d7e9c9f4c3ca00caadde31f50fd4521a" }
|
||||||
|
let(:api) { APIParticulier::API.new(token) }
|
||||||
|
|
||||||
|
before { stub_const("API_PARTICULIER_URL", "https://particulier.api.gouv.fr/api") }
|
||||||
|
|
||||||
|
describe "scopes" do
|
||||||
|
subject { api.scopes }
|
||||||
|
|
||||||
|
it "doit retourner une liste de scopes" do
|
||||||
|
VCR.use_cassette("api_particulier/success/introspect") do
|
||||||
|
expect(subject).to match_array(['dgfip_avis_imposition', 'dgfip_adresse', 'cnaf_allocataires', 'cnaf_enfants', 'cnaf_adresse', 'cnaf_quotient_familial', 'mesri_statut_etudiant'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an unauthorized exception" do
|
||||||
|
VCR.use_cassette("api_particulier/unauthorized/introspect") do
|
||||||
|
begin
|
||||||
|
subject
|
||||||
|
rescue APIParticulier::Error::Unauthorized => e
|
||||||
|
expect(e.message).to include('url: particulier.api.gouv.fr/api/introspect')
|
||||||
|
expect(e.message).to include('HTTP error code: 401')
|
||||||
|
expect(e.message).to include("Votre jeton d'API n'a pas été trouvé ou n'est pas actif")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue