Aller au contenu

PD-16 — Plan d'implémentation


📚 Navigation User Story | Document | | | ---------- | -- | | 📋 [Spécification](PD-16-specification.md) | | | 🛠️ **Plan d'implémentation** | *(ce document)* | | ✅ [Critères d'acceptation](PD-16-acceptability.md) | | | 📝 [Retour d'expérience](PD-16-rex.md) | | [← Retour à backend-core](../PD-186-epic.md) · [↑ Index User Story](index.md)

Objectif

Créer le schéma PostgreSQL vault_secure.documents avec Row-Level Security (RLS) et cycle de vie probatoire.

Choix techniques retenus

  • Schéma : vault_secure (isolation)
  • RLS : Policies par user_id
  • Cycle : PENDING → SEALED → EXPIRED
  • Context : AsyncLocalStorage pour user_id

Architecture ciblée

src/modules/documents/entities/
└── document-secure.entity.ts

src/modules/auth/services/
└── rls-context.service.ts

src/modules/auth/middleware/
└── rls.middleware.ts

src/database/migrations/
├── 1731888050000-CreateVaultSecureSchema.ts
└── 1731888100000-CreateDocumentsTable.ts

Découpage technique

Phase 1 : Schéma et ENUM

  1. Créer migration schema :
CREATE SCHEMA IF NOT EXISTS vault_secure;

DO $$ BEGIN
  CREATE TYPE vault_secure.document_status AS ENUM (
    'PENDING', 'SEALED', 'EXPIRED'
  );
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;

Phase 2 : Table documents

  1. Migration table :
CREATE TABLE vault_secure.documents (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE,
  encrypted_metadata BYTEA NOT NULL,
  keyword_deterministic TEXT[] NULL,
  file_hash BYTEA NOT NULL CHECK (octet_length(file_hash) = 32),
  ovh_path TEXT NOT NULL,
  status vault_secure.document_status DEFAULT 'PENDING',
  sealed_at TIMESTAMPTZ,
  retention_until TIMESTAMPTZ,
  expired_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

Phase 3 : Index

  1. Créer index :
  2. idx_documents_user_id (BTREE)
  3. idx_documents_user_file_hash (UNIQUE)
  4. idx_documents_created_at (DESC)
  5. idx_documents_keywords_gin (GIN)
  6. idx_documents_status
  7. idx_documents_retention_until

Phase 4 : RLS Policies

  1. Activer RLS :
ALTER TABLE vault_secure.documents ENABLE ROW LEVEL SECURITY;
  1. Créer policies :
-- SELECT : propriétaire uniquement
CREATE POLICY documents_select ON vault_secure.documents
  FOR SELECT USING (
    user_id = current_setting('app.current_user_id', true)::uuid
  );

-- INSERT : forcer user_id
CREATE POLICY documents_insert ON vault_secure.documents
  FOR INSERT WITH CHECK (
    user_id = current_setting('app.current_user_id', true)::uuid
  );

-- UPDATE : propriétaire + PENDING
CREATE POLICY documents_update ON vault_secure.documents
  FOR UPDATE USING (
    user_id = current_setting('app.current_user_id', true)::uuid
    AND status = 'PENDING'
  );

-- DELETE : propriétaire + PENDING
CREATE POLICY documents_delete ON vault_secure.documents
  FOR DELETE USING (
    user_id = current_setting('app.current_user_id', true)::uuid
    AND status = 'PENDING'
  );

Phase 5 : Injection contexte

  1. Créer RlsContextService :
  2. Utiliser AsyncLocalStorage
  3. Stocker user_id par requête

  4. Créer RlsMiddleware :

  5. Extraire user_id du JWT
  6. Injecter dans AsyncLocalStorage
  7. SET LOCAL app.current_user_id

Phase 6 : Entité TypeORM

  1. Créer DocumentSecure entity :
  2. Mapper colonnes
  3. Relations user
  4. Enum status

Phase 7 : Tests

  1. Tests RLS isolation (user A ≠ user B)
  2. Tests policies UPDATE/DELETE
  3. Tests contraintes (file_hash 32 bytes)
  4. Tests transitions status

Points de vigilance

  • RLS bypass : Attention aux rôles superuser
  • AsyncLocalStorage : Bien gérer le cycle de vie
  • Transactions : SET LOCAL valide pour transaction courante
  • SEALED : Immutable, prévoir role technique pour transitions

Hors périmètre

  • Upload fichiers OVH (→ PD-5)
  • Calcul hash SHA3 (→ PD-38)
  • API CRUD documents (→ autre US)