Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions actions/doctor-signature.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use server";

import { createClient } from "@/lib/supabase/server";
import { createClient, createAdminClient } from "@/lib/supabase/server";
import { z } from "zod";

export interface DoctorSignature {
Expand Down Expand Up @@ -89,6 +89,12 @@ export async function deleteDoctorSignature(): Promise<{ success: boolean; error
* de la clinique puisse récupérer l'image de signature d'un confrère sans
* passer par un document réel. Tant que le diagnostic n'est pas validé, aucune
* signature n'est renvoyée.
*
* Depuis la migration 013, plus aucune policy n'autorise un compte à lire la
* signature d'un confrère : la lecture finale passe donc par le client
* d'administration, une fois la clinique de l'appelant et le statut du
* diagnostic vérifiés ici — c'est cette fonction, et non une policy trop large,
* qui porte la décision d'autorisation.
*/
export async function getSignatureForValidatedDiagnostic(
diagnosticId: string
Expand Down Expand Up @@ -125,7 +131,13 @@ export async function getSignatureForValidatedDiagnostic(
return null;
}

const { data } = await db
// Le filtre sur clinic_id reste indispensable : le client d'administration
// ignore la RLS, c'est donc cette contrainte qui garantit qu'on ne lit pas la
// signature d'un praticien extérieur à la clinique de l'appelant.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const adminDb = (await createAdminClient()) as any;

const { data } = await adminDb
.from("doctor_signatures")
.select("*")
.eq("user_id", diagnostic.validated_by_user_id)
Expand Down
27 changes: 27 additions & 0 deletions supabase/migrations/013_signatures_lecture_restreinte.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-- ═══════════════════════════════════════════════════════════════════════════════
-- 013 — Restriction de la lecture des signatures de médecins
-- (audit signature / carnet / ordonnance 2026-08-02)
--
-- CRITICAL : la policy `doctor_signatures_select_clinic` autorisait tout compte
-- partageant la clinique à lire `signature_data_url`. Une secrétaire ou un
-- assistant pouvait donc exécuter, depuis son navigateur :
--
-- supabase.from('doctor_signatures').select('*')
--
-- et récupérer l'image de signature manuscrite de chaque médecin du cabinet,
-- sans ouvrir le moindre document. Or cette image est le seul élément qui
-- authentifie une ordonnance imprimée : sa diffusion permet de fabriquer un
-- document signé hors du produit, sans trace.
--
-- La policy est supprimée. Il ne reste que `doctor_signatures_select_own` :
-- chacun lit sa propre signature, personne ne lit celle d'un confrère.
--
-- L'affichage légitime de la signature du validateur sur un document reste
-- assuré par la Server Action `getSignatureForValidatedDiagnostic`, qui vérifie
-- la clinique de l'appelant et le statut du diagnostic avant de lire la
-- signature avec le client d'administration — même schéma que
-- `importPatientCarnet`. L'autorisation est ainsi décidée par du code relu et
-- testable, au lieu d'être déléguée à une policy trop large.
-- ═══════════════════════════════════════════════════════════════════════════════

DROP POLICY IF EXISTS "doctor_signatures_select_clinic" ON doctor_signatures;
Loading