Skip to content

Commit

Permalink
Allow read only authentication for visioplainte
Browse files Browse the repository at this point in the history
  • Loading branch information
victormours committed Nov 21, 2024
1 parent 3fd54e6 commit a729518
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 25 deletions.
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ AGENT_CONNECT_RDVAN_CLIENT_SECRET="voir https://vaultwarden.incubateur.net/#/vau
# INCLUSIONCONNECT_DISABLED=true

VISIOPLAINTE_API_KEY="visioplainte-api-test-key-123456"
VISIOPLAINTE_API_KEY_READ_ONLY="visioplainte-api-test-key-read-only-456789"

COOP_MEDIATION_NUMERIQUE_API_KEY="coop-mediation-numerique-api-test-key-123456"

Expand Down
48 changes: 32 additions & 16 deletions app/controllers/api/visioplainte/base_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,6 @@ class Api::Visioplainte::BaseController < ActionController::Base # rubocop:disab

GENDARMERIE_SERVICE_NAME = "Gendarmerie Nationale".freeze

def authenticate_with_api_key
authorized = ActiveSupport::SecurityUtils.secure_compare(
request.headers["X-VISIOPLAINTE-API-KEY"] || "",
ENV.fetch("VISIOPLAINTE_API_KEY")
)

unless authorized
render(
status: :unauthorized,
json: {
errors: ["Authentification invalide"],
}
)
end
end

def reset
# On met plusieurs guard clauses de sécurité pour s'assurer qu'on ne peut appeler cette méthode destructive que sur la staging
return unless ENV["RDV_SOLIDARITES_INSTANCE_NAME"] == "STAGING"
Expand All @@ -47,4 +31,36 @@ def reset
load Rails.root.join("db/seeds/visioplainte.rb")
head :ok
end

protected

def allow_authentication_with_read_only_api_key
@read_only_api_key_allowed = true
end

def authenticate_with_api_key
authorized = ActiveSupport::SecurityUtils.secure_compare(
request.headers["X-VISIOPLAINTE-API-KEY"] || "",
ENV.fetch("VISIOPLAINTE_API_KEY")
) || authorized_with_read_only_api_key?

unless authorized
render(
status: :unauthorized,
json: {
errors: ["Authentification invalide"],
}
)
end
end

def authorized_with_read_only_api_key?
return false unless @read_only_api_key_allowed
return false if ENV["VISIOPLAINTE_API_KEY_READ_ONLY"].blank?

ActiveSupport::SecurityUtils.secure_compare(
request.headers["X-VISIOPLAINTE-API-KEY"],
ENV["VISIOPLAINTE_API_KEY_READ_ONLY"]
)
end
end
2 changes: 2 additions & 0 deletions app/controllers/api/visioplainte/creneaux_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ class Api::Visioplainte::CreneauxController < Api::Visioplainte::BaseController
before_action :validate_date_debut
before_action :validate_date_range, only: [:index]

prepend_before_action :allow_authentication_with_read_only_api_key

def index
creneaux = CreneauxSearch::ForUser.new(
motif: motif,
Expand Down
7 changes: 7 additions & 0 deletions docs/interconnexions/visioplainte.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Ils et elles ne peuvent donc pas se connecter à RDV Service Public.

Un autre besoin identifié est un environnement de staging pouvant être réinitialisé sur demande.


## Avancement

En date d’octobre 2024, nous avons fini de développer la première version de l’API.
Expand All @@ -27,3 +28,9 @@ Les équipes de SensioLabs nous indiquent travailler sur l’intégration de leu

L’API est implémenté par des contrôleurs tous regroupés dans `app/controllers/api/visioplainte`.
Les appels à notre API sont authentifiées via un header `X-VISIOPLAINTE-API-KEY`.

## Intégration avec MaSécurité

Pour éviter de démarrer un processus de visioplainte alors qu'il n'y a aucun créneau de disponible, l'équipe qui gère le site MaSécurité a aussi besoin d'accéder à l'application pour savoir s'il existe des créneaux.
Nous avons donc mis en place la possibilité de se connecter à l'api avec une clé avec des permissions en lecture seule, pour avoir le minimum de permissions.
Il y a donc deux clés d'api, celle en lecture-écriture pour Visioplainte, et celle en lecture seule pour MaSécurité.
45 changes: 37 additions & 8 deletions spec/requests/api/visioplainte/authentication_spec.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
RSpec.describe "Authentification" do
let(:creneaux_params) do
{
service: "Gendarmerie",
date_debut: "2024-08-19",
date_fin: "2024-08-25",
}
end

context "when the api key is configured properly" do
stub_env_with(VISIOPLAINTE_API_KEY: "visioplainte-api-test-key-123456")
let(:creneaux_params) do
{
service: "Gendarmerie",
date_debut: "2024-08-19",
date_fin: "2024-08-25",
}
end

context "without the api key header" do
before do
get "/api/visioplainte/creneaux"
Expand Down Expand Up @@ -55,4 +55,33 @@
expect(response).to be_nil
end
end

context "with a read-only api key" do
stub_env_with(VISIOPLAINTE_API_KEY_READ_ONLY: "visioplainte-api-test-key-read-only-456789")
stub_env_with(VISIOPLAINTE_API_KEY: "visioplainte-api-test-key-123456")

context "on an endpoint that only reads data" do
before do
load Rails.root.join("db/seeds/visioplainte.rb")
get "/api/visioplainte/creneaux", headers: { "X-VISIOPLAINTE-API-KEY": "visioplainte-api-test-key-read-only-456789" }, params: creneaux_params
end

it "returns a 200 response" do
expect(response.status).to eq 200
expect(response.parsed_body.keys).to eq ["creneaux"]
end
end

context "on an endpoint that modifies data" do
before do
load Rails.root.join("db/seeds/visioplainte.rb")
post "/api/visioplainte/rdvs", headers: { "X-VISIOPLAINTE-API-KEY": "visioplainte-api-test-key-read-only-456789" }
end

it "returns a 401 response" do
expect(response.status).to eq 401
expect(response.parsed_body).to eq({ "errors" => ["Authentification invalide"] })
end
end
end
end
4 changes: 4 additions & 0 deletions spec/swagger_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
curl --request GET --url "https://demo.rdv.anct.gouv.fr/api/visioplainte/creneaux" --header "X-VISIOPLAINTE-API-KEY: LA_CLE_D_API"
```
## Clé d'api en lecture seule
Il est possible d'avoir une clé d'api qui n'a des permissions qu'en lecture seule sur les créneaux. L'authentification se fait via le même header.
# Réinitialisation des données sur l'environnement de staging
La staging de RDV Service Public a vocation à être utilisée par l'environnement de qualification du téléservice Visioplainte.
Expand Down
2 changes: 1 addition & 1 deletion swagger/visioplainte/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"info": {
"title": "API RDV Service Public pour Visioplainte",
"version": "v1",
"description": "# Authentification\n\nL'authentification à l'api se fait en passant la clé d'api dans le header `X-VISIOPLAINTE-API-KEY`.\n\nPar exemple:\n```\ncurl --request GET --url \"https://demo.rdv.anct.gouv.fr/api/visioplainte/creneaux\" --header \"X-VISIOPLAINTE-API-KEY: LA_CLE_D_API\"\n```\n\n# Réinitialisation des données sur l'environnement de staging\n\nLa staging de RDV Service Public a vocation à être utilisée par l'environnement de qualification du téléservice Visioplainte.\nAfin de faciliter les tests sur l'environnement de staging, un endpoint permet de réinitialiser les données.\nCet endpoint n'est disponible qu'en staging, pas en production, ni en démo, ni en local.\n\nCet appel ce fait avec un POST sur le path /api/visioplainte/reset, par exemple :\n```\ncurl -X POST --url \"https://staging.rdv-service-public.fr/api/visioplainte/reset\" --header \"X-VISIOPLAINTE-API-KEY: LA_CLE_D_API\"\n```\n"
"description": "# Authentification\n\nL'authentification à l'api se fait en passant la clé d'api dans le header `X-VISIOPLAINTE-API-KEY`.\n\nPar exemple:\n```\ncurl --request GET --url \"https://demo.rdv.anct.gouv.fr/api/visioplainte/creneaux\" --header \"X-VISIOPLAINTE-API-KEY: LA_CLE_D_API\"\n```\n\n## Clé d'api en lecture seule\n\nIl est possible d'avoir une clé d'api qui n'a des permissions qu'en lecture seule sur les créneaux. L'authentification se fait via le même header.\n\n# Réinitialisation des données sur l'environnement de staging\n\nLa staging de RDV Service Public a vocation à être utilisée par l'environnement de qualification du téléservice Visioplainte.\nAfin de faciliter les tests sur l'environnement de staging, un endpoint permet de réinitialiser les données.\nCet endpoint n'est disponible qu'en staging, pas en production, ni en démo, ni en local.\n\nCet appel ce fait avec un POST sur le path /api/visioplainte/reset, par exemple :\n```\ncurl -X POST --url \"https://staging.rdv-service-public.fr/api/visioplainte/reset\" --header \"X-VISIOPLAINTE-API-KEY: LA_CLE_D_API\"\n```\n"
},
"paths": {
"/api/visioplainte/creneaux": {
Expand Down

0 comments on commit a729518

Please sign in to comment.