Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
-- ============================================================
|
||||
-- Omika Diagnostic Center — base dédiée omika_diagnostics (§14)
|
||||
-- Outil SUPPORT uniquement : jamais utilisé par le métier (§13).
|
||||
-- 100 % Windows / PostgreSQL. Aucune dépendance Linux/Docker.
|
||||
-- PostgreSQL est NON critique : Omika fonctionne sans cette base.
|
||||
-- ============================================================
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- Table principale des événements (§15)
|
||||
-- ------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS diagnostic_event (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
event_id UUID NOT NULL, -- clé de déduplication (§16)
|
||||
timestamp_utc TIMESTAMPTZ NOT NULL,
|
||||
priority TEXT, -- P0..P4
|
||||
level TEXT, -- trace..critical
|
||||
event_code TEXT NOT NULL,
|
||||
component TEXT,
|
||||
trace_id UUID, -- X-Omika-TraceId conservé (§33)
|
||||
session_id UUID,
|
||||
operation_id UUID,
|
||||
app_version TEXT,
|
||||
platform TEXT,
|
||||
error_code TEXT,
|
||||
message TEXT, -- Phase 6 (crash reporting)
|
||||
exception_type TEXT, -- Phase 6
|
||||
stack_trace TEXT, -- Phase 6
|
||||
duration_ms BIGINT,
|
||||
source TEXT, -- mobile/api/worker/collector
|
||||
metadata_json JSONB, -- whitelist sécurité (§18/§19)
|
||||
ingested_at_utc TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
|
||||
-- Déduplication at-least-once : INSERT ... ON CONFLICT (event_id) DO NOTHING (§16)
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_diagnostic_event_event_id
|
||||
ON diagnostic_event (event_id);
|
||||
|
||||
-- Index utiles uniquement (§17) — pas d'index sur chaque colonne
|
||||
CREATE INDEX IF NOT EXISTS ix_diagnostic_event_timestamp_utc ON diagnostic_event (timestamp_utc);
|
||||
CREATE INDEX IF NOT EXISTS ix_diagnostic_event_trace_id ON diagnostic_event (trace_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_diagnostic_event_operation_id ON diagnostic_event (operation_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_diagnostic_event_session_id ON diagnostic_event (session_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_diagnostic_event_event_code ON diagnostic_event (event_code);
|
||||
CREATE INDEX IF NOT EXISTS ix_diagnostic_event_component ON diagnostic_event (component);
|
||||
CREATE INDEX IF NOT EXISTS ix_diagnostic_event_level ON diagnostic_event (level);
|
||||
CREATE INDEX IF NOT EXISTS ix_diagnostic_event_error_code ON diagnostic_event (error_code);
|
||||
CREATE INDEX IF NOT EXISTS ix_diagnostic_event_app_version ON diagnostic_event (app_version);
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- Métriques agrégées horaires (§28) — low-cardinality (§30)
|
||||
-- ------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS telemetry_metric_hourly (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
bucket_utc TIMESTAMPTZ NOT NULL,
|
||||
metric_name TEXT NOT NULL,
|
||||
component TEXT NOT NULL DEFAULT '', -- '' = métrique globale
|
||||
value DOUBLE PRECISION NOT NULL,
|
||||
count BIGINT NOT NULL,
|
||||
min_value DOUBLE PRECISION NOT NULL,
|
||||
max_value DOUBLE PRECISION NOT NULL,
|
||||
tags_json JSONB NOT NULL DEFAULT '{}'::jsonb
|
||||
);
|
||||
|
||||
-- Upsert : un agrégat unique par (bucket, métrique, composant, tags)
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_telemetry_metric_hourly
|
||||
ON telemetry_metric_hourly (bucket_utc, metric_name, component, tags_json);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_telemetry_metric_hourly_bucket
|
||||
ON telemetry_metric_hourly (bucket_utc);
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- Rétention (§45) — valeur configurable (Diagnostics:RetentionDays, défaut 60).
|
||||
-- NON exécutée automatiquement : à planifier en maintenance (purge destructive
|
||||
-- documentée). Exemple (à adapter) :
|
||||
-- DELETE FROM diagnostic_event
|
||||
-- WHERE timestamp_utc < now() - interval '60 days';
|
||||
-- ------------------------------------------------------------
|
||||
@@ -0,0 +1,23 @@
|
||||
-- ============================================================
|
||||
-- Omika Diagnostic Center — 002_actor_key.sql (Phase 5B2A.1)
|
||||
-- Pseudonymisation ActorKey (HMAC-SHA256) pour la recherche support.
|
||||
-- ============================================================
|
||||
-- IDEMPOTENT : ADD COLUMN IF NOT EXISTS + CREATE INDEX IF NOT EXISTS.
|
||||
-- AUCUN DROP, AUCUNE suppression, AUCUNE migration destructive.
|
||||
-- PostgreSQL 18 compatible (syntaxe disponible depuis 9.6).
|
||||
--
|
||||
-- ActorKey = HMAC-SHA256( "v1|tenant:<CodeClient>|collaborateur:<CollaborateurId>" )
|
||||
-- calculé côté SERVEUR (Omika.Api) depuis l'identité AUTHENTIFIÉE (§3).
|
||||
-- Format : hexadécimal minuscule, 64 caractères. NULL = système / sans
|
||||
-- utilisateur / clé HMAC indisponible (fail-open).
|
||||
-- ActorKeyVersion = version de l'algorithme (1 aujourd'hui) → rotation future.
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE diagnostic_event
|
||||
ADD COLUMN IF NOT EXISTS actor_key TEXT,
|
||||
ADD COLUMN IF NOT EXISTS actor_key_version INTEGER;
|
||||
|
||||
-- Recherche support « tous les diagnostics d'un collaborateur » (§12) :
|
||||
-- index composé (actor_key, timestamp_utc DESC).
|
||||
CREATE INDEX IF NOT EXISTS ix_diagnostic_event_actor_key_timestamp
|
||||
ON diagnostic_event (actor_key, timestamp_utc DESC);
|
||||
@@ -0,0 +1,123 @@
|
||||
-- ============================================================
|
||||
-- Omika Diagnostic Center -- 003_diagnostics_roles.sql (Phase 5B2B.3)
|
||||
-- PostgreSQL hardening : least privilege (Worker / Web / Owner).
|
||||
-- ============================================================
|
||||
-- TO RUN ON SRV-OMIKA as PostgreSQL superuser (e.g. psql -U postgres),
|
||||
-- BEFORE Phase 5C. DO NOT run from the development PC.
|
||||
--
|
||||
-- Guarantees :
|
||||
-- - PostgreSQL 18 compatible.
|
||||
-- - Idempotent : re-running is a no-op.
|
||||
-- - Non destructive : NO DROP, no role/object/data removal.
|
||||
-- - NO password, NO secret (LOGIN role passwords are set manually via
|
||||
-- \password, never in this file).
|
||||
--
|
||||
-- Role model :
|
||||
-- omika_diagnostics_owner NOLOGIN : owner of DB/schema/tables/sequences/indexes.
|
||||
-- omika_diagnostics_worker LOGIN : Worker runtime (JSONL import).
|
||||
-- omika_diagnostics_web LOGIN : Web runtime (read only).
|
||||
-- omika_diagnostics_app (existing) : KEPT (controlled transition),
|
||||
-- NOT disabled here.
|
||||
--
|
||||
-- Server apply order : 001 (schema) -> 002 (actor_key) -> 003 (roles).
|
||||
-- ============================================================
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 1. Create roles if absent (NO password here).
|
||||
-- ------------------------------------------------------------
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'omika_diagnostics_owner') THEN
|
||||
CREATE ROLE omika_diagnostics_owner;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'omika_diagnostics_worker') THEN
|
||||
CREATE ROLE omika_diagnostics_worker;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'omika_diagnostics_web') THEN
|
||||
CREATE ROLE omika_diagnostics_web;
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 2. Secure attributes (idempotent).
|
||||
-- All: NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS.
|
||||
-- ------------------------------------------------------------
|
||||
ALTER ROLE omika_diagnostics_owner NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS NOLOGIN;
|
||||
ALTER ROLE omika_diagnostics_worker NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS LOGIN;
|
||||
ALTER ROLE omika_diagnostics_web NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS LOGIN;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 3. Ownership transfer (explicit -- no global REASSIGN OWNED).
|
||||
-- Only Omika Diagnostics objects are targeted.
|
||||
-- ------------------------------------------------------------
|
||||
ALTER DATABASE omika_diagnostics OWNER TO omika_diagnostics_owner;
|
||||
ALTER SCHEMA public OWNER TO omika_diagnostics_owner;
|
||||
|
||||
ALTER TABLE diagnostic_event OWNER TO omika_diagnostics_owner;
|
||||
ALTER TABLE telemetry_metric_hourly OWNER TO omika_diagnostics_owner;
|
||||
|
||||
ALTER SEQUENCE diagnostic_event_id_seq OWNER TO omika_diagnostics_owner;
|
||||
ALTER SEQUENCE telemetry_metric_hourly_id_seq OWNER TO omika_diagnostics_owner;
|
||||
|
||||
-- PostgreSQL 18:
|
||||
-- Index ownership follows table ownership.
|
||||
-- No ALTER INDEX OWNER is required.
|
||||
-- (Sequences are NOT auto-transferred by ALTER TABLE: the ALTER SEQUENCE
|
||||
-- OWNER commands above are therefore kept and required.)
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 4. Schema "public" hardening.
|
||||
-- Decision : YES, REVOKE CREATE -- the database is dedicated to the
|
||||
-- Diagnostic Center; only the owner/admin should create objects.
|
||||
-- (PostgreSQL 15+ already revokes it for new databases; this is an
|
||||
-- idempotent belt-and-suspenders.)
|
||||
-- ------------------------------------------------------------
|
||||
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 5. MINIMUM runtime privileges (from real code audit).
|
||||
--
|
||||
-- WORKER (omika_diagnostics_worker):
|
||||
-- * diagnostic_event : INSERT only
|
||||
-- (INSERT ... ON CONFLICT (event_id) DO NOTHING -- dedup).
|
||||
-- * sequence diagnostic_event_id_seq : USAGE (id BIGSERIAL).
|
||||
-- * NO SELECT/UPDATE/DELETE (60-day retention is NOT implemented;
|
||||
-- telemetry_metric_hourly upsert is registered but has NO caller).
|
||||
--
|
||||
-- WEB (omika_diagnostics_web): SELECT ONLY on diagnostic_event
|
||||
-- (QueryAsync count+page, GetByTraceId, GetByOperationId, IsAvailableAsync).
|
||||
--
|
||||
-- NO privilege granted on telemetry_metric_hourly while no code uses it
|
||||
-- (least privilege).
|
||||
-- ------------------------------------------------------------
|
||||
|
||||
-- Explicit connection intent (PUBLIC keeps default CONNECT; documented only,
|
||||
-- not hardened beyond requested scope).
|
||||
GRANT CONNECT ON DATABASE omika_diagnostics TO omika_diagnostics_worker, omika_diagnostics_web;
|
||||
|
||||
GRANT USAGE ON SCHEMA public TO omika_diagnostics_worker, omika_diagnostics_web;
|
||||
|
||||
-- Worker : JSONL import -> diagnostic_event.
|
||||
GRANT INSERT ON TABLE diagnostic_event TO omika_diagnostics_worker;
|
||||
GRANT USAGE ON SEQUENCE diagnostic_event_id_seq TO omika_diagnostics_worker;
|
||||
|
||||
-- Web : read only.
|
||||
GRANT SELECT ON TABLE diagnostic_event TO omika_diagnostics_web;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 6. Legacy role omika_diagnostics_app : KEPT (no DROP, no NOLOGIN here).
|
||||
-- After transfer it owns no Diagnostics object and has no object privilege;
|
||||
-- disabling it (ALTER ROLE ... NOLOGIN) is an EXPLICIT administrator action
|
||||
-- AFTER validating the new roles.
|
||||
-- ------------------------------------------------------------
|
||||
|
||||
-- ============================================================
|
||||
-- END 003. Manual next steps (outside this script):
|
||||
-- 1. Set passwords: psql -> \password omika_diagnostics_worker
|
||||
-- then \password omika_diagnostics_web (never in script/log/history).
|
||||
-- 2. Point Worker: Username=omika_diagnostics_worker ; Web:
|
||||
-- Username=omika_diagnostics_web (ConnectionStrings__DiagnosticsDatabase).
|
||||
-- 3. Validate Worker (INSERT) and Web (SELECT), then only after,
|
||||
-- disable omika_diagnostics_app (ALTER ROLE ... NOLOGIN) if desired.
|
||||
-- ============================================================
|
||||
@@ -0,0 +1,62 @@
|
||||
-- ============================================================
|
||||
-- Omika Diagnostic Center -- 003_diagnostics_roles_preprod.sql
|
||||
-- VARIANTE PRÉPROD LOCALE de 003_diagnostics_roles.sql (T21).
|
||||
-- ============================================================
|
||||
-- ⚠️ EXCLUSIVEMENT LOCAL / PRÉPROD — jamais en Production.
|
||||
--
|
||||
-- TRANSFORMATION UNIQUE par rapport à l'original 003 :
|
||||
-- « ALTER DATABASE omika_diagnostics OWNER TO ... »
|
||||
-- « GRANT CONNECT ON DATABASE omika_diagnostics TO ... »
|
||||
-- → base cible renommée « omika_diagnostics_preprod ».
|
||||
-- Rôles, privilèges, ownership et idempotence STRICTEMENT identiques.
|
||||
-- (L'original code en dur le nom de la base centrale Production
|
||||
-- « omika_diagnostics » ; cette variante ne change QUE ce nom.)
|
||||
--
|
||||
-- Teste réellement, sur une base locale jetable, l'exécutabilité des
|
||||
-- rôles/grants 003 : création de rôles cluster-wide, ownership, REVOKE
|
||||
-- public, GRANT worker/web. Aucun mot de passe (posé manuellement via
|
||||
-- \password, jamais dans ce fichier).
|
||||
-- ============================================================
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'omika_diagnostics_owner') THEN
|
||||
CREATE ROLE omika_diagnostics_owner;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'omika_diagnostics_worker') THEN
|
||||
CREATE ROLE omika_diagnostics_worker;
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'omika_diagnostics_web') THEN
|
||||
CREATE ROLE omika_diagnostics_web;
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
ALTER ROLE omika_diagnostics_owner NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS NOLOGIN;
|
||||
ALTER ROLE omika_diagnostics_worker NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS LOGIN;
|
||||
ALTER ROLE omika_diagnostics_web NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS LOGIN;
|
||||
|
||||
ALTER DATABASE omika_diagnostics_preprod OWNER TO omika_diagnostics_owner;
|
||||
ALTER SCHEMA public OWNER TO omika_diagnostics_owner;
|
||||
|
||||
ALTER TABLE diagnostic_event OWNER TO omika_diagnostics_owner;
|
||||
ALTER TABLE telemetry_metric_hourly OWNER TO omika_diagnostics_owner;
|
||||
|
||||
ALTER SEQUENCE diagnostic_event_id_seq OWNER TO omika_diagnostics_owner;
|
||||
ALTER SEQUENCE telemetry_metric_hourly_id_seq OWNER TO omika_diagnostics_owner;
|
||||
|
||||
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
|
||||
|
||||
GRANT CONNECT ON DATABASE omika_diagnostics_preprod TO omika_diagnostics_worker, omika_diagnostics_web;
|
||||
|
||||
GRANT USAGE ON SCHEMA public TO omika_diagnostics_worker, omika_diagnostics_web;
|
||||
|
||||
GRANT INSERT ON TABLE diagnostic_event TO omika_diagnostics_worker;
|
||||
GRANT USAGE ON SEQUENCE diagnostic_event_id_seq TO omika_diagnostics_worker;
|
||||
|
||||
GRANT SELECT ON TABLE diagnostic_event TO omika_diagnostics_web;
|
||||
|
||||
-- ============================================================
|
||||
-- END 003 (variante preprod). Les mots de passe des rôles LOGIN
|
||||
-- (worker/web) sont posés manuellement via \password si nécessaire.
|
||||
-- ============================================================
|
||||
@@ -0,0 +1,39 @@
|
||||
-- ============================================================
|
||||
-- Omika Diagnostic Center -- 004_diagnostics_retention.sql (Phase 5C.2C)
|
||||
-- PostgreSQL : moindre privilège pour la rétention diagnostic.
|
||||
-- ============================================================
|
||||
-- TO RUN ON SRV-OMIKA as PostgreSQL superuser (e.g. psql -U postgres).
|
||||
-- DO NOT run from the development PC.
|
||||
--
|
||||
-- Guarantees :
|
||||
-- - PostgreSQL 18 compatible.
|
||||
-- - Idempotent : re-running is a no-op (GRANT is idempotent).
|
||||
-- - Non destructive : NO DROP, NO DELETE/TRUNCATE, NO role/object removal.
|
||||
-- - NO password, NO secret.
|
||||
--
|
||||
-- Context (Phase 5C.2C) : the retention loop (DiagnosticRetentionWorker) purges
|
||||
-- diagnostic_event by BOUNDED batches :
|
||||
--
|
||||
-- DELETE FROM diagnostic_event
|
||||
-- WHERE id IN (
|
||||
-- SELECT id FROM diagnostic_event
|
||||
-- WHERE timestamp_utc < @cutoff
|
||||
-- ORDER BY timestamp_utc ASC, id ASC
|
||||
-- LIMIT @batchSize
|
||||
-- )
|
||||
--
|
||||
-- This SQL requires :
|
||||
-- * DELETE on diagnostic_event (the target of the DELETE) ;
|
||||
-- * SELECT on diagnostic_event (the sub-query reads the table).
|
||||
--
|
||||
-- The worker role (omika_diagnostics_worker) currently holds only INSERT +
|
||||
-- USAGE sequence (see 003). Least privilege : grant ONLY SELECT + DELETE.
|
||||
-- NO UPDATE, NO TRUNCATE, NO DDL, NO privilege on telemetry_metric_hourly.
|
||||
-- ============================================================
|
||||
|
||||
GRANT SELECT, DELETE ON TABLE diagnostic_event TO omika_diagnostics_worker;
|
||||
|
||||
-- ============================================================
|
||||
-- END 004. Apply order on server : 001 -> 002 -> 003 -> 004.
|
||||
-- No server action is performed by the development PC.
|
||||
-- ============================================================
|
||||
@@ -0,0 +1,45 @@
|
||||
-- ============================================================
|
||||
-- Omika Diagnostic Center -- 005_diagnostics_client_context.sql
|
||||
-- Tâche 13-A : contexte client / serveur / environnement (additif).
|
||||
-- ============================================================
|
||||
-- IDEMPOTENT : ADD COLUMN IF NOT EXISTS + CREATE INDEX IF NOT EXISTS.
|
||||
-- AUCUN DROP, AUCUNE suppression, AUCUNE migration destructive.
|
||||
-- PostgreSQL 18 compatible (syntaxe disponible depuis 9.6).
|
||||
--
|
||||
-- Ajoute au diagnostic_event trois dimensions de contexte de déploiement,
|
||||
-- NULLABLES : les événements antérieurs restent parfaitement valides
|
||||
-- (client_code = NULL, server_instance = NULL, environment = NULL),
|
||||
-- AUCUN backfill, AUCUNE valeur inventée (« UNKNOWN »/« N/A » interdits).
|
||||
--
|
||||
-- client_code : CodeClient de référence = ClientBase.CL_Code
|
||||
-- (identifiant NON secret du client Omika, jamais
|
||||
-- Client.CL_CodeClient, CL_CleAPIInstall, clé licence,
|
||||
-- ActorKey ni nom société).
|
||||
-- server_instance : identifiant d'instance serveur (configuré, ex.
|
||||
-- « API-SRV-01 ») — T13-D.
|
||||
-- environment : environnement ASP.NET (Development / Production / …).
|
||||
--
|
||||
-- CHOIX DE TYPE (cohérence schéma) : le schéma existant utilise TEXT sans
|
||||
-- VARCHAR ni CHECK de longueur (priority, level, component, app_version…).
|
||||
-- On reste cohérent : colonnes TEXT, PAS de CHECK de longueur arbitraire.
|
||||
-- La validation de longueur (<= 100 / <= 100 / <= 20) sera portée par le
|
||||
-- code au moment de la résolution (T13-D), comme pour les autres champs.
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE diagnostic_event
|
||||
ADD COLUMN IF NOT EXISTS client_code TEXT,
|
||||
ADD COLUMN IF NOT EXISTS server_instance TEXT,
|
||||
ADD COLUMN IF NOT EXISTS environment TEXT;
|
||||
|
||||
-- Recherche support « tous les diagnostics d'un client » : index composé
|
||||
-- (client_code, timestamp_utc DESC), aligné sur le style de
|
||||
-- ix_diagnostic_event_actor_key_timestamp (002).
|
||||
--
|
||||
-- PAS d'index dédié sur server_instance : aucun usage immédiat ne justifie
|
||||
-- le coût d'écriture/maintenance d'un second index composé (T13 — « composite
|
||||
-- éventuel »). À réévaluer en T13-F si un filtre serveur devient un hot path.
|
||||
--
|
||||
-- PAS d'index sur environment : très faible cardinalité (2-3 valeurs), un
|
||||
-- index serait peu sélectif et sans bénéfice réel.
|
||||
CREATE INDEX IF NOT EXISTS ix_diagnostic_event_client_code_timestamp
|
||||
ON diagnostic_event (client_code, timestamp_utc DESC);
|
||||
@@ -0,0 +1,69 @@
|
||||
-- ============================================================
|
||||
-- Omika Diagnostic Center -- 006_diagnostics_device_license.sql
|
||||
-- Tâche 17-B : dimensions appareil + licence (additif).
|
||||
-- ============================================================
|
||||
-- IDEMPOTENT : ADD COLUMN IF NOT EXISTS + CREATE INDEX IF NOT EXISTS.
|
||||
-- AUCUN DROP, AUCUNE suppression, AUCUNE migration destructive.
|
||||
-- PostgreSQL 18 compatible (syntaxe disponible depuis 9.6).
|
||||
--
|
||||
-- Ajoute au diagnostic_event les dimensions appareil/licence, TOUTES NULLABLES :
|
||||
-- les événements antérieurs restent parfaitement valides (toutes nouvelles colonnes
|
||||
-- à NULL), AUCUN backfill, AUCUNE valeur inventée (« UNKNOWN »/« N/A » interdits).
|
||||
--
|
||||
-- license_number : numéro de licence fonctionnel = Licence.LI_Numero,
|
||||
-- dérivé SERVEUR du token authentifié
|
||||
-- (MobileTokenIdentity.NumeroLicence). JAMAIS la PK HFSQL
|
||||
-- LI_PKID, JAMAIS un secret, JAMAIS fourni par le mobile.
|
||||
-- device_key : pseudonyme d'appareil HMAC-SHA256 calculé SERVEUR =
|
||||
-- HMAC("v1|tenant:<CodeClient>|device:<DeviceId>").
|
||||
-- JAMAIS le DeviceId brut.
|
||||
-- device_key_version : version de l'algorithme DeviceKey (1 aujourd'hui).
|
||||
-- device_manufacturer : fabricant (mobile, déclaratif).
|
||||
-- device_model : modèle d'appareil (mobile, déclaratif).
|
||||
-- operating_system : nom de l'OS (mobile, déclaratif).
|
||||
-- operating_system_version : version de l'OS (mobile, déclaratif).
|
||||
-- app_build : numéro de build applicatif (mobile, déclaratif).
|
||||
-- screen_width : largeur d'écran physique (px, >= 0).
|
||||
-- screen_height : hauteur d'écran physique (px, >= 0).
|
||||
-- logical_width : largeur d'écran logique (unités indépendantes densité).
|
||||
-- logical_height : hauteur d'écran logique.
|
||||
-- display_scale : facteur d'échelle (ex. 1, 1.25, 1.5, 2, 3).
|
||||
--
|
||||
-- CHOIX DE TYPE (cohérence schéma) : le schéma existant utilise TEXT sans VARCHAR
|
||||
-- pour les chaînes (priority, level, component, app_version…) ; on reste cohérent.
|
||||
-- Les dimensions écran physiques/logiques sont des entiers non négatifs (INTEGER) ;
|
||||
-- display_scale est un réel (DOUBLE PRECISION, valeurs décimales 1.25/1.5/2.75/3).
|
||||
-- La validation (longueur, bornes, non-négativité) est portée par le code au moment
|
||||
-- de la résolution (T17-C), comme pour les autres champs.
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE diagnostic_event
|
||||
ADD COLUMN IF NOT EXISTS license_number TEXT,
|
||||
ADD COLUMN IF NOT EXISTS device_key TEXT,
|
||||
ADD COLUMN IF NOT EXISTS device_key_version INTEGER,
|
||||
ADD COLUMN IF NOT EXISTS device_manufacturer TEXT,
|
||||
ADD COLUMN IF NOT EXISTS device_model TEXT,
|
||||
ADD COLUMN IF NOT EXISTS operating_system TEXT,
|
||||
ADD COLUMN IF NOT EXISTS operating_system_version TEXT,
|
||||
ADD COLUMN IF NOT EXISTS app_build TEXT,
|
||||
ADD COLUMN IF NOT EXISTS screen_width INTEGER,
|
||||
ADD COLUMN IF NOT EXISTS screen_height INTEGER,
|
||||
ADD COLUMN IF NOT EXISTS logical_width INTEGER,
|
||||
ADD COLUMN IF NOT EXISTS logical_height INTEGER,
|
||||
ADD COLUMN IF NOT EXISTS display_scale DOUBLE PRECISION;
|
||||
|
||||
-- Index composés immédiatement justifiés (recherche support « tous les diagnostics
|
||||
-- d'un appareil / d'une licence »), alignés sur le style de
|
||||
-- ix_diagnostic_event_actor_key_timestamp (002) et
|
||||
-- ix_diagnostic_event_client_code_timestamp (005).
|
||||
CREATE INDEX IF NOT EXISTS ix_diagnostic_event_device_key_timestamp
|
||||
ON diagnostic_event (device_key, timestamp_utc DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_diagnostic_event_license_number_timestamp
|
||||
ON diagnostic_event (license_number, timestamp_utc DESC);
|
||||
|
||||
-- PAS d'index dédié sur device_manufacturer / device_model / operating_system /
|
||||
-- operating_system_version / app_build / screen_* : dimensions de filtrage/agrégation
|
||||
-- futures (T17-E) mais sans usage immédiat justifiant le coût d'écriture/maintenance.
|
||||
-- PAS d'index sur display_scale (valeur continue, jamais filtrée par égalité).
|
||||
-- À réévaluer en T17-E lorsque les statistiques de parc seront définies.
|
||||
@@ -0,0 +1,47 @@
|
||||
-- ============================================================
|
||||
-- Omika Diagnostic Center -- 007_diagnostics_usage.sql
|
||||
-- Tâche 18 : télémétrie d'utilisation des écrans / fonctionnalités (additif).
|
||||
-- ============================================================
|
||||
-- IDEMPOTENT : ADD COLUMN IF NOT EXISTS + CREATE INDEX IF NOT EXISTS.
|
||||
-- AUCUN DROP, AUCUNE suppression, AUCUNE migration destructive.
|
||||
-- PostgreSQL 18 compatible (syntaxe disponible depuis 9.6).
|
||||
--
|
||||
-- Ajoute au diagnostic_event deux dimensions d'USAGE fonctionnel, NULLABLES :
|
||||
-- les événements antérieurs restent parfaitement valides (screen_code = NULL,
|
||||
-- feature_code = NULL), AUCUN backfill, AUCUNE valeur inventée (« UNKNOWN »/« N/A »
|
||||
-- interdits).
|
||||
--
|
||||
-- screen_code : code fonctionnel STABLE d'un écran (ex. AGENDA, SITE_DETAIL).
|
||||
-- Produit DEPUIS le catalogue applicatif (jamais un nom de
|
||||
-- Page/ViewModel, jamais un identifiant métier, jamais une saisie
|
||||
-- utilisateur). Null pour les événements background sans écran.
|
||||
-- feature_code: code fonctionnel STABLE d'une action métier significative
|
||||
-- (ex. EMAIL_SEND, REPORT_GENERATE). Attaché à l'événement précis
|
||||
-- de l'action, jamais à un « contexte courant » global. Null si non
|
||||
-- applicable.
|
||||
--
|
||||
-- CHOIX DE TYPE (cohérence schéma) : TEXT sans VARCHAR, comme les autres chaînes
|
||||
-- (priority, level, component, license_number…). La validation (longueur, valeurs
|
||||
-- bornées) est portée par le code au moment de la résolution, comme les autres
|
||||
-- champs. Les deux colonnes sont NON SENSIBLES : jamais de DeviceId/UserId/
|
||||
-- CollaborateurId/email/téléphone/token/id métier dynamique (§9/§10/§72).
|
||||
-- ============================================================
|
||||
|
||||
ALTER TABLE diagnostic_event
|
||||
ADD COLUMN IF NOT EXISTS screen_code TEXT,
|
||||
ADD COLUMN IF NOT EXISTS feature_code TEXT;
|
||||
|
||||
-- Index de recherche « tous les ScreenView d'un écran dans le temps » (T18, §49) :
|
||||
-- les agrégations d'usage filtrent par (event_code, screen_code) puis par période,
|
||||
-- mais l'index composé (screen_code, timestamp_utc DESC) couvre le filtre écran +
|
||||
-- le tri temporel des diagnostics filtrés (§60), aligné sur le style de
|
||||
-- ix_diagnostic_event_device_key_timestamp (006).
|
||||
CREATE INDEX IF NOT EXISTS ix_diagnostic_event_screen_code_timestamp
|
||||
ON diagnostic_event (screen_code, timestamp_utc DESC);
|
||||
|
||||
-- Index (feature_code, timestamp_utc DESC) : filtre fonctionnalité + tri temporel.
|
||||
-- FeatureCode est une dimension de catalogue à cardinalité TRÈS faible (< 10), donc
|
||||
-- un index est peu sélectif ; il est néanmoins ajouté par symétrie avec screen_code
|
||||
-- car le filtre « Tous les diagnostics d'une fonctionnalité » (§60) est prévu.
|
||||
CREATE INDEX IF NOT EXISTS ix_diagnostic_event_feature_code_timestamp
|
||||
ON diagnostic_event (feature_code, timestamp_utc DESC);
|
||||
@@ -0,0 +1,38 @@
|
||||
-- ============================================================
|
||||
-- Omika Diagnostic Center -- 008_diagnostics_clients.sql
|
||||
-- Tâche 19 : registre des clients autorisés (serveurs clients) de l'API centrale.
|
||||
-- ============================================================
|
||||
-- IDEMPOTENT : CREATE TABLE IF NOT EXISTS + CREATE UNIQUE INDEX IF NOT EXISTS.
|
||||
-- AUCUN DROP, AUCUNE suppression, AUCUNE migration destructive.
|
||||
-- PostgreSQL 18 compatible.
|
||||
--
|
||||
-- Une ligne = un SERVEUR CLIENT autorisé à transmettre ses diagnostics vers
|
||||
-- Omika.Diagnostics.Api (CodeClient métier, ex. BATIK.01).
|
||||
--
|
||||
-- client_code : CodeClient métier autoritaire (unique, normalisé UPPER côté code).
|
||||
-- enabled : FALSE => le client ne peut plus envoyer (réponse 403).
|
||||
-- api_key_hash : hash SHA-256 (hex 64) de la clé d'accès. JAMAIS la clé en clair.
|
||||
-- key_prefix : préfixe d'affichage de la clé (ex. "omkdiag_"), NON secret,
|
||||
-- permet d'identifier la clé dans l'interface sans l'exposer.
|
||||
-- created_at_utc : création du client.
|
||||
-- key_created_at_utc : génération de la clé COURANTE (utile après régénération).
|
||||
-- last_seen_at_utc : dernière réception d'un lot accepté ; NULL si jamais.
|
||||
--
|
||||
-- CHOIX DE TYPE : aligné sur diagnostic_event (TEXT, TIMESTAMPTZ). Aucun secret
|
||||
-- réversible stocké ; la clé n'existe que sous forme de hash SHA-256 (§29/§30).
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS diagnostics_clients (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
client_code TEXT NOT NULL,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
api_key_hash TEXT NOT NULL,
|
||||
key_prefix TEXT NOT NULL,
|
||||
created_at_utc TIMESTAMPTZ NOT NULL,
|
||||
key_created_at_utc TIMESTAMPTZ NOT NULL,
|
||||
last_seen_at_utc TIMESTAMPTZ
|
||||
);
|
||||
|
||||
-- Unicité par CodeClient (un seul client par code, §26).
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_diagnostics_clients_client_code
|
||||
ON diagnostics_clients (client_code);
|
||||
@@ -0,0 +1,103 @@
|
||||
-- ============================================================
|
||||
-- Omika Diagnostic Center -- 009_diagnostics_clients_grants.sql
|
||||
-- T22 : moindre privilège pour la table diagnostics_clients (008).
|
||||
-- ============================================================
|
||||
-- TO RUN ON SRV-OMIKA as PostgreSQL superuser (e.g. psql -U postgres -d omika_diagnostics -f 009_...),
|
||||
-- AFTER 008 (table diagnostics_clients) and AFTER 003/004 (rôles owner/worker/web).
|
||||
--
|
||||
-- Guarantees :
|
||||
-- - PostgreSQL 18 compatible.
|
||||
-- - Idempotent : re-running is a no-op.
|
||||
-- - Non destructive : NO DROP, NO DELETE/TRUNCATE, no data removal.
|
||||
-- - NO password, NO secret (LOGIN role password is set manually via \password).
|
||||
--
|
||||
-- Motivations (T22) :
|
||||
-- 008 crée diagnostics_clients APRÈS 003/004. La table reste donc possédée
|
||||
-- par le rôle qui exécute 008 (postgres) et ne reçoit AUCUN GRANT applicatif :
|
||||
-- l'API centrale (authentification ClientCode+ApiKey + last_seen_at_utc) et
|
||||
-- l'administration Web (/Clients) échoueraient avec leurs comptes de service.
|
||||
--
|
||||
-- Rôle AJOUTÉ : omika_diagnostics_api (LOGIN). Le modèle 003 ne prévoyait que
|
||||
-- owner/worker/web ; Omika.Diagnostics.Api (service distinct du Worker et du
|
||||
-- Web) n'y figurait pas. Elle n'accède QU'À diagnostics_clients.
|
||||
--
|
||||
-- Ownership : même propriétaire logique que les autres tables Diagnostics
|
||||
-- (omika_diagnostics_owner), comme 003. Les index suivent automatiquement la
|
||||
-- table ; la séquence BIGSERIAL est transférée explicitement.
|
||||
--
|
||||
-- Note portabilité : GRANT CONNECT utilise current_database() — la migration
|
||||
-- s'applique donc à la base courante (psql -d <base>), sans coder en dur le
|
||||
-- nom (contrairement à 003 qui, pour ALTER DATABASE, devait le fixer).
|
||||
-- ============================================================
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 1. Créer le rôle API s'il est absent (aucun mot de passe ici).
|
||||
-- ------------------------------------------------------------
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'omika_diagnostics_api') THEN
|
||||
CREATE ROLE omika_diagnostics_api;
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
ALTER ROLE omika_diagnostics_api NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS LOGIN;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 2. Ownership : omika_diagnostics_owner (comme 003).
|
||||
-- ------------------------------------------------------------
|
||||
ALTER TABLE diagnostics_clients OWNER TO omika_diagnostics_owner;
|
||||
ALTER SEQUENCE diagnostics_clients_id_seq OWNER TO omika_diagnostics_owner;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 3. Défense PUBLIC (ceinture + bretelles, idempotent) : aucune
|
||||
-- table Diagnostics n'est accessible à PUBLIC.
|
||||
-- ------------------------------------------------------------
|
||||
REVOKE ALL ON TABLE diagnostics_clients FROM PUBLIC;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 4. Connexion explicite (intention documentée, comme 003) + schéma.
|
||||
-- ------------------------------------------------------------
|
||||
DO $$
|
||||
BEGIN
|
||||
EXECUTE format('GRANT CONNECT ON DATABASE %I TO omika_diagnostics_api', current_database());
|
||||
END
|
||||
$$;
|
||||
|
||||
GRANT USAGE ON SCHEMA public TO omika_diagnostics_api;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 5. API centrale (Omika.Diagnostics.Api) : SELECT (auth : client,
|
||||
-- enabled, api_key_hash…) + UPDATE(last_seen_at_utc) uniquement.
|
||||
-- AUCUN INSERT, AUCUN DELETE, AUCUNE modification de enabled /
|
||||
-- api_key_hash / key_prefix (moindre privilège, anti-falsification
|
||||
-- de l'autorisation en cas de compromission de l'API).
|
||||
-- ------------------------------------------------------------
|
||||
GRANT SELECT ON TABLE diagnostics_clients TO omika_diagnostics_api;
|
||||
GRANT UPDATE (last_seen_at_utc) ON TABLE diagnostics_clients TO omika_diagnostics_api;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 6. Web (Omika.Diagnostics.Web, admin /Clients) : liste + création +
|
||||
-- activation/désactivation + régénération de clé. AUCUN DELETE
|
||||
-- (aucune opération de suppression n'existe dans le code T19).
|
||||
-- USAGE séquence : INSERT ... RETURNING id (id BIGSERIAL).
|
||||
-- ------------------------------------------------------------
|
||||
GRANT SELECT ON TABLE diagnostics_clients TO omika_diagnostics_web;
|
||||
GRANT INSERT ON TABLE diagnostics_clients TO omika_diagnostics_web;
|
||||
GRANT UPDATE (enabled, api_key_hash, key_prefix, key_created_at_utc)
|
||||
ON TABLE diagnostics_clients TO omika_diagnostics_web;
|
||||
GRANT USAGE ON SEQUENCE diagnostics_clients_id_seq TO omika_diagnostics_web;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 7. Worker (Omika.Diagnostics.Worker) : AUCUN droit sur
|
||||
-- diagnostics_clients (il n'y accède jamais : import JSONL ->
|
||||
-- diagnostic_event + rétention diagnostic_event uniquement).
|
||||
-- Rien à accorder. Aucun REVOKE nécessaire : le rôle n'a reçu
|
||||
-- aucun privilège sur cette table.
|
||||
-- ------------------------------------------------------------
|
||||
|
||||
-- ============================================================
|
||||
-- END 009. Ordre serveur : 001 -> 002 -> 003 -> 004 -> 005 -> 006
|
||||
-- -> 007 -> 008 -> 009. Mots de passe posés manuellement (\password),
|
||||
-- jamais dans un script/log.
|
||||
-- ============================================================
|
||||
@@ -0,0 +1,99 @@
|
||||
-- ============================================================
|
||||
-- Omika Diagnostic Center -- 010_diagnostics_web_users.sql
|
||||
-- T23 : gestion des utilisateurs de Omika.Diagnostics.Web.
|
||||
-- ============================================================
|
||||
-- TO RUN ON SRV-OMIKA as PostgreSQL superuser (e.g. psql -U postgres -d <base> -f 010_...),
|
||||
-- AFTER 003 (rôles owner/worker/web), 008 (diagnostics_clients) and 009 (rôle api + grants clients).
|
||||
--
|
||||
-- Guarantees :
|
||||
-- - PostgreSQL 18 compatible.
|
||||
-- - Idempotent : re-running is a no-op (CREATE ... IF NOT EXISTS + GRANT idempotent).
|
||||
-- - Non destructive : NO DROP, NO DELETE/TRUNCATE, no data removal.
|
||||
-- - NO password, NO secret (no column stores a clear-text password).
|
||||
--
|
||||
-- Une ligne = une PERSONNE autorisée à ouvrir Omika.Diagnostics.Web
|
||||
-- (username + password_hash + rôle). À NE PAS confondre avec
|
||||
-- diagnostics_clients (serveurs clients, ClientCode + ApiKey).
|
||||
--
|
||||
-- username : identifiant unique (unicité insensible à la casse).
|
||||
-- password_hash : hash PBKDF2-SHA256 (DiagnosticPasswordHasher). JAMAIS en clair.
|
||||
-- role : 'Support' ou 'Admin' (validé par le code, pas de CHECK arbitraire).
|
||||
-- enabled : FALSE => l'utilisateur ne peut plus se connecter.
|
||||
-- created_at_utc : création du compte.
|
||||
-- updated_at_utc : dernière mutation du compte.
|
||||
-- password_changed_at_utc : dernier changement de mot de passe.
|
||||
-- last_login_at_utc : dernier login réussi ; NULL si jamais.
|
||||
--
|
||||
-- CHOIX DE TYPE : aligné sur diagnostic_event / diagnostics_clients (TEXT, TIMESTAMPTZ,
|
||||
-- BOOLEAN). Aucun VARCHAR, aucun CHECK de longueur (validation portée par le code,
|
||||
-- comme 005/007/008). Aucune colonne password/plain_password/clear_password.
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS diagnostics_users (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
username TEXT NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
role TEXT NOT NULL,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at_utc TIMESTAMPTZ NOT NULL,
|
||||
updated_at_utc TIMESTAMPTZ NOT NULL,
|
||||
password_changed_at_utc TIMESTAMPTZ NOT NULL,
|
||||
last_login_at_utc TIMESTAMPTZ
|
||||
);
|
||||
|
||||
-- Unicité insensible à la casse (§T23/11) : un index sur LOWER(username) garantit
|
||||
-- qu'un même identifiant ne peut exister deux fois quelle que soit la casse.
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_diagnostics_users_username
|
||||
ON diagnostics_users (LOWER(username));
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- Ownership : omika_diagnostics_owner (comme 003/009).
|
||||
-- ------------------------------------------------------------
|
||||
ALTER TABLE diagnostics_users OWNER TO omika_diagnostics_owner;
|
||||
ALTER SEQUENCE diagnostics_users_id_seq OWNER TO omika_diagnostics_owner;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- Défense PUBLIC (ceinture + bretelles, idempotent).
|
||||
-- ------------------------------------------------------------
|
||||
REVOKE ALL ON TABLE diagnostics_users FROM PUBLIC;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- Connexion explicite + schéma pour omika_diagnostics_web
|
||||
-- (déjà accordés par 003 ; ré-affirmé idempotent, intention documentée).
|
||||
-- ------------------------------------------------------------
|
||||
DO $$
|
||||
BEGIN
|
||||
EXECUTE format('GRANT CONNECT ON DATABASE %I TO omika_diagnostics_web', current_database());
|
||||
END
|
||||
$$;
|
||||
|
||||
GRANT USAGE ON SCHEMA public TO omika_diagnostics_web;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- Web (Omika.Diagnostics.Web, admin /Users) : SELECT + INSERT +
|
||||
-- UPDATE colonne-ciblé (moindre privilège) + USAGE séquence.
|
||||
-- - INSERT : création utilisateur (id BIGSERIAL).
|
||||
-- - UPDATE (password_hash, role, enabled, updated_at_utc,
|
||||
-- password_changed_at_utc, last_login_at_utc) : reset mot de passe,
|
||||
-- changement rôle, activation/désactivation, last login.
|
||||
-- - username n'est JAMAIS mis à jour (aucun renommage en V1).
|
||||
-- - AUCUN DELETE (aucune suppression physique en V1).
|
||||
-- ------------------------------------------------------------
|
||||
GRANT SELECT ON TABLE diagnostics_users TO omika_diagnostics_web;
|
||||
GRANT INSERT ON TABLE diagnostics_users TO omika_diagnostics_web;
|
||||
GRANT UPDATE (password_hash, role, enabled, updated_at_utc, password_changed_at_utc, last_login_at_utc)
|
||||
ON TABLE diagnostics_users TO omika_diagnostics_web;
|
||||
GRANT USAGE ON SEQUENCE diagnostics_users_id_seq TO omika_diagnostics_web;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- API (omika_diagnostics_api) : AUCUN droit sur diagnostics_users.
|
||||
-- Worker (omika_diagnostics_worker) : AUCUN droit sur diagnostics_users.
|
||||
-- Aucun GRANT accordé ; les rôles n'ont aucun privilège par défaut.
|
||||
-- (Aucun REVOKE nécessaire : aucun privilège n'a été accordé.)
|
||||
-- ------------------------------------------------------------
|
||||
|
||||
-- ============================================================
|
||||
-- END 010. Ordre serveur : 001 -> 002 -> 003 -> 004 -> 005 -> 006
|
||||
-- -> 007 -> 008 -> 009 -> 010. Mots de passe posés manuellement
|
||||
-- (\password), jamais dans un script/log.
|
||||
-- ============================================================
|
||||
@@ -0,0 +1,140 @@
|
||||
-- ============================================================
|
||||
-- Omika Diagnostic Center -- 011_diagnostics_reference.sql
|
||||
-- T24-A : référentiel administrable ScreenCode / FeatureCode.
|
||||
-- ============================================================
|
||||
-- TO RUN ON SRV-OMIKA as PostgreSQL superuser (e.g. psql -U postgres -d <base> -f 011_...),
|
||||
-- AFTER 010 (diagnostics_users). Order: 001 -> ... -> 010 -> 011.
|
||||
--
|
||||
-- Guarantees :
|
||||
-- - PostgreSQL 18 compatible.
|
||||
-- - Idempotent : re-running is a no-op (CREATE ... IF NOT EXISTS + GRANT idempotent
|
||||
-- + seed ON CONFLICT DO NOTHING).
|
||||
-- - Non destructive : NO DROP, NO DELETE/TRUNCATE, no data removal.
|
||||
-- - NO password, NO secret.
|
||||
--
|
||||
-- PURPOSE :
|
||||
-- diagnostic_event reste la VÉRITÉ sur ce qui a réellement été observé. Ce
|
||||
-- référentiel est un ENRICHISSEMENT administrable (libellé / description /
|
||||
-- ordre / état) depuis Diagnostics.Web. Il ne détermine JAMAIS quels événements
|
||||
-- sont acceptés : un nouveau code observé reste visible même non référencé.
|
||||
--
|
||||
-- reference_type : 'SCREEN_CODE' ou 'FEATURE_CODE' (V1). Extensible plus tard.
|
||||
-- code : code technique, normalisé TRIM + UPPER (unique par type).
|
||||
-- label : libellé métier français.
|
||||
-- description : documentation métier (nullable).
|
||||
-- enabled : FALSE => archivé (aucun DELETE physique en V1).
|
||||
-- sort_order : ordre d'affichage.
|
||||
-- created_at_utc / updated_at_utc : TIMESTAMPTZ (comme 005/007/008/010).
|
||||
--
|
||||
-- Ownership : omika_diagnostics_owner (comme 003/008/009/010).
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS diagnostics_reference (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
reference_type TEXT NOT NULL,
|
||||
code TEXT NOT NULL,
|
||||
label TEXT NOT NULL,
|
||||
description TEXT,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
created_at_utc TIMESTAMPTZ NOT NULL,
|
||||
updated_at_utc TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
|
||||
-- Unicité d'un code POUR SON TYPE, insensible à la casse (T24-A §20) : l'index
|
||||
-- porte sur UPPER(code), donc 'agenda' / 'AGENDA' / 'Agenda' sont le même code.
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_diagnostics_reference_type_code
|
||||
ON diagnostics_reference (reference_type, UPPER(code));
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- Ownership : omika_diagnostics_owner.
|
||||
-- ------------------------------------------------------------
|
||||
ALTER TABLE diagnostics_reference OWNER TO omika_diagnostics_owner;
|
||||
ALTER SEQUENCE diagnostics_reference_id_seq OWNER TO omika_diagnostics_owner;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- Défense PUBLIC (ceinture + bretelles, idempotent).
|
||||
-- ------------------------------------------------------------
|
||||
REVOKE ALL ON TABLE diagnostics_reference FROM PUBLIC;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- Connexion explicite + schéma pour omika_diagnostics_web
|
||||
-- (déjà accordés par 003 ; ré-affirmé idempotent).
|
||||
-- ------------------------------------------------------------
|
||||
DO $$
|
||||
BEGIN
|
||||
EXECUTE format('GRANT CONNECT ON DATABASE %I TO omika_diagnostics_web', current_database());
|
||||
END
|
||||
$$;
|
||||
|
||||
GRANT USAGE ON SCHEMA public TO omika_diagnostics_web;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- Web (Omika.Diagnostics.Web, admin /Referentiel/Admin) : SELECT + INSERT +
|
||||
-- UPDATE colonne-ciblé (moindre privilège) + USAGE séquence.
|
||||
-- - INSERT : création d'une entrée (id BIGSERIAL).
|
||||
-- - UPDATE (label, description, enabled, sort_order, updated_at_utc) :
|
||||
-- modification + archivage (enabled=false). Le CODE est immuable.
|
||||
-- - AUCUN DELETE (aucune suppression physique en V1, §24).
|
||||
-- ------------------------------------------------------------
|
||||
GRANT SELECT ON TABLE diagnostics_reference TO omika_diagnostics_web;
|
||||
GRANT INSERT ON TABLE diagnostics_reference TO omika_diagnostics_web;
|
||||
GRANT UPDATE (label, description, enabled, sort_order, updated_at_utc)
|
||||
ON TABLE diagnostics_reference TO omika_diagnostics_web;
|
||||
GRANT USAGE ON SEQUENCE diagnostics_reference_id_seq TO omika_diagnostics_web;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- API (omika_diagnostics_api) : AUCUN droit sur diagnostics_reference.
|
||||
-- Worker (omika_diagnostics_worker) : AUCUN droit sur diagnostics_reference.
|
||||
-- Aucun GRANT accordé ; les rôles n'ont aucun privilège par défaut.
|
||||
-- (Testé négativement : SELECT référentiel REFUSÉ pour api et worker.)
|
||||
-- ------------------------------------------------------------
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- INITIALISATION IDEMPOTENTE (T24-A §36) : les 29 ScreenCode et 3 FeatureCode
|
||||
-- du catalogue applicatif. ON CONFLICT DO NOTHING => un libellé modifié par un
|
||||
-- Admin n'est JAMAIS écrasé au redémarrage. Les libellés correspondent EXACTEMENT
|
||||
-- à DiagnosticScreenCodeCatalog / DiagnosticFeatureCodeCatalog.
|
||||
-- ------------------------------------------------------------
|
||||
|
||||
INSERT INTO diagnostics_reference
|
||||
(reference_type, code, label, description, enabled, sort_order, created_at_utc, updated_at_utc)
|
||||
VALUES
|
||||
('SCREEN_CODE', 'AGENDA', 'Agenda', NULL, TRUE, 10, now(), now()),
|
||||
('SCREEN_CODE', 'INTERVENTION_SEARCH', 'Recherche d''interventions', NULL, TRUE, 20, now(), now()),
|
||||
('SCREEN_CODE', 'DOSSIER_LIST', 'Dossiers', NULL, TRUE, 30, now(), now()),
|
||||
('SCREEN_CODE', 'TEAM', 'Équipe', NULL, TRUE, 40, now(), now()),
|
||||
('SCREEN_CODE', 'TEAM_DETAIL', 'Détail d''équipe', NULL, TRUE, 50, now(), now()),
|
||||
('SCREEN_CODE', 'CLIENT_LIST', 'Clients', NULL, TRUE, 60, now(), now()),
|
||||
('SCREEN_CODE', 'SITE_LIST', 'Sites', NULL, TRUE, 70, now(), now()),
|
||||
('SCREEN_CODE', 'SETTINGS', 'Paramètres', NULL, TRUE, 80, now(), now()),
|
||||
('SCREEN_CODE', 'RENDEZVOUS_DETAIL', 'Détail de rendez-vous', NULL, TRUE, 90, now(), now()),
|
||||
('SCREEN_CODE', 'AGENDA_TEAM', 'Agenda d''équipe', NULL, TRUE, 100, now(), now()),
|
||||
('SCREEN_CODE', 'INTERVENTION_DETAIL', 'Détail d''intervention', NULL, TRUE, 110, now(), now()),
|
||||
('SCREEN_CODE', 'INTERVENTION_DOCUMENTS', 'Documents d''intervention', NULL, TRUE, 120, now(), now()),
|
||||
('SCREEN_CODE', 'INTERVENTION_NOTES', 'Notes d''intervention', NULL, TRUE, 130, now(), now()),
|
||||
('SCREEN_CODE', 'INTERVENTION_NOTES_ADMIN', 'Notes administratives', NULL, TRUE, 140, now(), now()),
|
||||
('SCREEN_CODE', 'INTERVENTION_EMAILS', 'Courriels d''intervention', NULL, TRUE, 150, now(), now()),
|
||||
('SCREEN_CODE', 'EMAIL', 'Nouveau courriel', NULL, TRUE, 160, now(), now()),
|
||||
('SCREEN_CODE', 'RAPPORT', 'Rapport', NULL, TRUE, 170, now(), now()),
|
||||
('SCREEN_CODE', 'INTERVENTION_RESOURCES', 'Contenus d''intervention', NULL, TRUE, 180, now(), now()),
|
||||
('SCREEN_CODE', 'PHOTO_VIEWER', 'Visionneuse photo', NULL, TRUE, 190, now(), now()),
|
||||
('SCREEN_CODE', 'IMAGE_EDITOR', 'Éditeur d''image', NULL, TRUE, 200, now(), now()),
|
||||
('SCREEN_CODE', 'INTERVENTION_CONTACTS', 'Contacts d''intervention', NULL, TRUE, 210, now(), now()),
|
||||
('SCREEN_CODE', 'CONTACT_DETAIL', 'Détail de contact', NULL, TRUE, 220, now(), now()),
|
||||
('SCREEN_CODE', 'DOSSIER_DETAIL', 'Détail de dossier', NULL, TRUE, 230, now(), now()),
|
||||
('SCREEN_CODE', 'CLIENT_DETAIL', 'Détail de client', NULL, TRUE, 240, now(), now()),
|
||||
('SCREEN_CODE', 'SITE_DETAIL', 'Détail de site', NULL, TRUE, 250, now(), now()),
|
||||
('SCREEN_CODE', 'COLLABORATEUR_DETAIL', 'Détail de collaborateur', NULL, TRUE, 260, now(), now()),
|
||||
('SCREEN_CODE', 'INTERVENTION_EDIT', 'Formulaire intervention', NULL, TRUE, 270, now(), now()),
|
||||
('SCREEN_CODE', 'SUIVI_TEMPS_CREATE', 'Saisie de temps', NULL, TRUE, 280, now(), now()),
|
||||
('SCREEN_CODE', 'COMPTE_RENDU_CREATE', 'Compte rendu', NULL, TRUE, 290, now(), now()),
|
||||
('FEATURE_CODE', 'INTERVENTION_SEARCH_RUN', 'Recherche d''intervention', NULL, TRUE, 10, now(), now()),
|
||||
('FEATURE_CODE', 'REPORT_GENERATE', 'Génération de rapport', NULL, TRUE, 20, now(), now()),
|
||||
('FEATURE_CODE', 'EMAIL_SEND', 'Envoi de courriel', NULL, TRUE, 30, now(), now())
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- END 011. Ordre serveur : 001 -> 002 -> 003 -> 004 -> 005 -> 006
|
||||
-- -> 007 -> 008 -> 009 -> 010 -> 011. Aucun DELETE, aucune donnée écrasée.
|
||||
-- ============================================================
|
||||
@@ -0,0 +1,90 @@
|
||||
-- ============================================================
|
||||
-- Omika Diagnostic Center -- 012_diagnostics_tasks.sql
|
||||
-- T25 : tâches de traitement des bugs récurrents.
|
||||
-- ============================================================
|
||||
-- TO RUN ON SRV-OMIKA as PostgreSQL superuser (e.g. psql -U postgres -d <base> -f 012_...),
|
||||
-- AFTER 003 (rôles owner/worker/web) and 010 (diagnostics_users).
|
||||
--
|
||||
-- Guarantees :
|
||||
-- - PostgreSQL 18 compatible.
|
||||
-- - Idempotent : re-running is a no-op (CREATE ... IF NOT EXISTS + GRANT idempotent).
|
||||
-- - Non destructive : NO DROP, NO DELETE/TRUNCATE, no data removal.
|
||||
-- - NO password, NO secret.
|
||||
--
|
||||
-- Une ligne = une TÂCHE de traitement liée à un « type de bug », c'est-à-dire une
|
||||
-- signature d'erreur (code événement + code erreur + exception + composant normalisés).
|
||||
-- La signature est stockée sous sa forme URL-safe (DiagnosticErrorGroup.UrlIdentifier),
|
||||
-- identique à celle utilisée par la page /Erreurs/{id}. Une seule tâche par signature
|
||||
-- (unicité) : toutes les occurrences du même type de bug sont couvertes par la tâche.
|
||||
--
|
||||
-- group_url_identifier : identifiant URL-safe du groupe d'erreurs lié (unique, immuable).
|
||||
-- title : intitulé de la tâche.
|
||||
-- description : description libre du bug (nullable).
|
||||
-- cause : cause de l'erreur / analyse root cause (nullable).
|
||||
-- status : 'Todo' | 'InProgress' | 'Resolved' | 'Rejected' (liste fermée,
|
||||
-- validée par le code, pas de CHECK arbitraire).
|
||||
-- created_by_username : username de l'auteur (immuable, jamais renommé en V1).
|
||||
-- assignee_username : username du responsable (nullable, optionnel).
|
||||
-- created_at_utc : création.
|
||||
-- updated_at_utc : dernière mutation.
|
||||
-- resolved_at_utc : date de résolution/rejet ; NULL si statut non terminal.
|
||||
--
|
||||
-- CHOIX DE TYPE : aligné sur diagnostics_clients / diagnostics_users (TEXT, TIMESTAMPTZ,
|
||||
-- BOOLEAN). Aucun VARCHAR, aucun CHECK de longueur (validation portée par le code).
|
||||
-- Aucune colonne secret. Aucune colonne « password ».
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS diagnostics_tasks (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
group_url_identifier TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
cause TEXT,
|
||||
status TEXT NOT NULL,
|
||||
created_by_username TEXT NOT NULL,
|
||||
assignee_username TEXT,
|
||||
created_at_utc TIMESTAMPTZ NOT NULL,
|
||||
updated_at_utc TIMESTAMPTZ NOT NULL,
|
||||
resolved_at_utc TIMESTAMPTZ
|
||||
);
|
||||
|
||||
-- Une seule tâche par signature d'erreur (T25 §4).
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_diagnostics_tasks_group_url_identifier
|
||||
ON diagnostics_tasks (group_url_identifier);
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- Ownership : omika_diagnostics_owner (comme 003/008/010).
|
||||
-- ------------------------------------------------------------
|
||||
ALTER TABLE diagnostics_tasks OWNER TO omika_diagnostics_owner;
|
||||
ALTER SEQUENCE diagnostics_tasks_id_seq OWNER TO omika_diagnostics_owner;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- Défense PUBLIC (ceinture + bretelles, idempotent).
|
||||
-- ------------------------------------------------------------
|
||||
REVOKE ALL ON TABLE diagnostics_tasks FROM PUBLIC;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- Web (Omika.Diagnostics.Web) : SELECT + INSERT + UPDATE
|
||||
-- colonne-ciblé (moindre privilège) + USAGE séquence.
|
||||
-- - INSERT : création d'une tâche (id BIGSERIAL).
|
||||
-- - UPDATE (title, description, cause, status, assignee_username,
|
||||
-- updated_at_utc, resolved_at_utc) : édition de la tâche.
|
||||
-- - group_url_identifier et created_by_username ne sont JAMAIS mis à jour.
|
||||
-- - AUCUN DELETE (aucune suppression physique en V1).
|
||||
-- ------------------------------------------------------------
|
||||
GRANT SELECT ON TABLE diagnostics_tasks TO omika_diagnostics_web;
|
||||
GRANT INSERT ON TABLE diagnostics_tasks TO omika_diagnostics_web;
|
||||
GRANT UPDATE (title, description, cause, status, assignee_username, updated_at_utc, resolved_at_utc)
|
||||
ON TABLE diagnostics_tasks TO omika_diagnostics_web;
|
||||
GRANT USAGE ON SEQUENCE diagnostics_tasks_id_seq TO omika_diagnostics_web;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- API (omika_diagnostics_api) : AUCUN droit sur diagnostics_tasks.
|
||||
-- Worker (omika_diagnostics_worker) : AUCUN droit sur diagnostics_tasks.
|
||||
-- Aucun GRANT accordé ; les rôles n'ont aucun privilège par défaut.
|
||||
-- (Aucun REVOKE nécessaire : aucun privilège n'a été accordé.)
|
||||
-- ------------------------------------------------------------
|
||||
|
||||
-- ============================================================
|
||||
-- END 012. Ordre serveur : 001 -> 002 -> ... -> 011 -> 012.
|
||||
-- ============================================================
|
||||
@@ -0,0 +1,24 @@
|
||||
-- ============================================================
|
||||
-- Omika Diagnostic Center -- 013_diagnostics_tasks_delete.sql
|
||||
-- T25 : autorise la suppression d'une tâche de traitement (V2).
|
||||
-- ============================================================
|
||||
-- TO RUN ON SRV-OMIKA as PostgreSQL superuser (e.g. psql -U postgres -d <base> -f 013_...),
|
||||
-- AFTER 012 (table diagnostics_tasks + rôles Web).
|
||||
--
|
||||
-- Guarantees :
|
||||
-- - PostgreSQL 18 compatible.
|
||||
-- - Idempotent : re-running is a no-op (GRANT is idempotent).
|
||||
-- - Non destructive : NO DROP, NO DELETE/TRUNCATE, no data removal.
|
||||
-- - NO password, NO secret.
|
||||
--
|
||||
-- Jusqu'en V1, aucune suppression physique n'était prévue : 012 n'accordait AUCUN
|
||||
-- DELETE. La V2 ajoute un bouton « Supprimer » sur /Taches : on accorde DELETE au
|
||||
-- seul rôle Web (moindre privilège). L'API (omika_diagnostics_api) et le Worker
|
||||
-- (omika_diagnostics_worker) n'ont TOUJOURS aucun droit de suppression.
|
||||
-- ============================================================
|
||||
|
||||
GRANT DELETE ON TABLE diagnostics_tasks TO omika_diagnostics_web;
|
||||
|
||||
-- ============================================================
|
||||
-- END 013. Ordre serveur : 001 -> 002 -> ... -> 012 -> 013.
|
||||
-- ============================================================
|
||||
@@ -0,0 +1,40 @@
|
||||
-- ============================================================
|
||||
-- Omika Diagnostic Center -- 014_diagnostics_telemetry_retention.sql
|
||||
-- Rétention télémétrie : privilège de purge de telemetry_metric_hourly.
|
||||
-- ============================================================
|
||||
-- TO RUN ON SRV-OMIKA as PostgreSQL superuser (e.g. psql -U postgres -d <base> -f 014_...),
|
||||
-- AFTER 003 (rôles) et 001 (table telemetry_metric_hourly).
|
||||
--
|
||||
-- Guarantees :
|
||||
-- - PostgreSQL 18 compatible.
|
||||
-- - Idempotent : re-running is a no-op (GRANT is idempotent).
|
||||
-- - Non destructive : NO DROP, NO DELETE/TRUNCATE, no data removal.
|
||||
-- - NO password, NO secret.
|
||||
--
|
||||
-- Context : la boucle DiagnosticTelemetryRetentionWorker purge les agrégats horaires
|
||||
-- plus anciens que Diagnostics__TelemetryRetentionDays (défaut 180 j = 6 mois), par
|
||||
-- LOTS BORNÉS :
|
||||
--
|
||||
-- DELETE FROM telemetry_metric_hourly
|
||||
-- WHERE id IN (
|
||||
-- SELECT id FROM telemetry_metric_hourly
|
||||
-- WHERE bucket_utc < @cutoff
|
||||
-- ORDER BY bucket_utc ASC, id ASC
|
||||
-- LIMIT @batchSize
|
||||
-- )
|
||||
--
|
||||
-- Cette requête requiert :
|
||||
-- * DELETE sur telemetry_metric_hourly (cible du DELETE) ;
|
||||
-- * SELECT sur telemetry_metric_hourly (la sous-requête lit la table).
|
||||
--
|
||||
-- Jusqu'ici le rôle worker (omika_diagnostics_worker) n'avait AUCUN droit sur cette
|
||||
-- table (least privilege, cf. 003). On accorde le MINIMUM pour la rétention :
|
||||
-- SELECT + DELETE uniquement. NO UPDATE, NO TRUNCATE, NO DDL, NO INSERT (l'upsert de
|
||||
-- télémétrie n'a toujours pas de caller, la table reste non alimentée côté worker).
|
||||
-- ============================================================
|
||||
|
||||
GRANT SELECT, DELETE ON TABLE telemetry_metric_hourly TO omika_diagnostics_worker;
|
||||
|
||||
-- ============================================================
|
||||
-- END 014. Ordre serveur : 001 -> 002 -> 003 -> 004 -> ... -> 013 -> 014.
|
||||
-- ============================================================
|
||||
@@ -0,0 +1,51 @@
|
||||
-- ============================================================
|
||||
-- Omika Diagnostic Center -- 015_diagnostics_retention_untreated.sql
|
||||
-- Rétention : ne pas purger les erreurs non traitées (privilège SELECT colonne-ciblé).
|
||||
-- ============================================================
|
||||
-- TO RUN ON SRV-OMIKA as PostgreSQL superuser (e.g. psql -U postgres -d <base> -f 015_...),
|
||||
-- AFTER 004 (SELECT/DELETE sur diagnostic_event) et 012 (table diagnostics_tasks).
|
||||
--
|
||||
-- Guarantees :
|
||||
-- - PostgreSQL 18 compatible.
|
||||
-- - Idempotent : re-running is a no-op (GRANT is idempotent).
|
||||
-- - Non destructive : NO DROP, NO DELETE/TRUNCATE, no data removal.
|
||||
-- - NO password, NO secret.
|
||||
--
|
||||
-- Règle de conservation : la purge DiagnosticRetentionWorker supprime un événement
|
||||
-- ancien selon DEUX cas :
|
||||
-- 1) événement NON-erreur (trace/debug/info/warning, ou level NULL) → toujours purgé ;
|
||||
-- 2) événement d'ERREUR (error/critical) → purgé SEULEMENT si son groupe possède une
|
||||
-- tâche (diagnostics_tasks) en statut TERMINAL (Resolved/Rejected). Une erreur non
|
||||
-- traitée (aucune tâche, ou Todo/InProgress) est conservée même au-delà du cutoff.
|
||||
--
|
||||
-- Le DELETE joint donc diagnostics_tasks sur l'identifiant URL-safe du groupe (recalculé
|
||||
-- en SQL, sans colonne fingerprint) :
|
||||
--
|
||||
-- DELETE FROM diagnostic_event
|
||||
-- WHERE id IN (
|
||||
-- SELECT id FROM diagnostic_event
|
||||
-- WHERE timestamp_utc < @cutoff
|
||||
-- AND (
|
||||
-- level IS NULL OR LOWER(level) NOT IN ('error', 'critical') OR
|
||||
-- (LOWER(level) IN ('error', 'critical') AND <group_url_identifier recalculé> IN (
|
||||
-- SELECT group_url_identifier FROM diagnostics_tasks
|
||||
-- WHERE LOWER(status) IN ('resolved','rejected')
|
||||
-- ))
|
||||
-- )
|
||||
-- ORDER BY timestamp_utc ASC, id ASC
|
||||
-- LIMIT @batchSize
|
||||
-- )
|
||||
--
|
||||
-- Jusqu'ici le rôle worker (omika_diagnostics_worker) n'avait AUCUN droit sur
|
||||
-- diagnostics_tasks (012). On accorde le MINIMUM pour le rapprochement : SELECT
|
||||
-- UNIQUEMENT sur group_url_identifier (la clé) et status (le statut terminal).
|
||||
-- NO SELECT sur title/description/cause (notes de support, potentiellement sensibles),
|
||||
-- NO SELECT sur assignee/created_by (usernames), NO UPDATE/INSERT/DELETE.
|
||||
-- ============================================================
|
||||
|
||||
GRANT SELECT (group_url_identifier, status)
|
||||
ON TABLE diagnostics_tasks TO omika_diagnostics_worker;
|
||||
|
||||
-- ============================================================
|
||||
-- END 015. Ordre serveur : 001 -> 002 -> ... -> 013 -> 014 -> 015.
|
||||
-- ============================================================
|
||||
@@ -0,0 +1,31 @@
|
||||
-- ============================================================
|
||||
-- Omika Diagnostic Center -- 016_diagnostics_event_delete.sql
|
||||
-- Erreurs : autorise la suppression d'un groupe d'erreurs complet (V2).
|
||||
-- ============================================================
|
||||
-- TO RUN ON SRV-OMIKA as PostgreSQL superuser (e.g. psql -U postgres -d <base> -f 016_...),
|
||||
-- AFTER 003 (rôles owner/worker/web).
|
||||
--
|
||||
-- Guarantees :
|
||||
-- - PostgreSQL 18 compatible.
|
||||
-- - Idempotent : re-running is a no-op (GRANT is idempotent).
|
||||
-- - Non destructive : NO DROP, NO DELETE/TRUNCATE, no data removal.
|
||||
-- - NO password, NO secret.
|
||||
--
|
||||
-- Jusqu'ici le rôle Web (omika_diagnostics_web) n'avait que SELECT sur
|
||||
-- diagnostic_event (migration 003) : il lisait les événements mais ne pouvait jamais en
|
||||
-- supprimer. La V2 ajoute, sur la page /Erreurs, un bouton « Supprimer le groupe » qui
|
||||
-- supprime TOUTES les occurrences d'un groupe d'erreurs (signature normalisée :
|
||||
-- event_code + error_code/exception_type/component). Le DELETE est exécuté par le Web,
|
||||
-- donc on accorde le privilège DELETE au seul rôle Web (moindre privilège).
|
||||
--
|
||||
-- Le Worker (omika_diagnostics_worker) conserve déjà SELECT + DELETE (migration 004,
|
||||
-- rétention par lots bornés) ; son périmètre est inchangé. L'API n'a AUCUN droit de
|
||||
-- suppression. Le DELETE du Web est borné par la signature du groupe (jamais un DELETE
|
||||
-- massif non filtré) et restreint aux niveaux d'erreur réels par le code applicatif.
|
||||
-- ============================================================
|
||||
|
||||
GRANT DELETE ON TABLE diagnostic_event TO omika_diagnostics_web;
|
||||
|
||||
-- ============================================================
|
||||
-- END 016. Ordre serveur : 001 -> 002 -> ... -> 015 -> 016.
|
||||
-- ============================================================
|
||||
Reference in New Issue
Block a user