-- ============================================================================ -- PTA V7.8 — Sécurisation : les règles d'accès descendent en base -- ============================================================================ -- À exécuter UNE SEULE FOIS sur une base PTA V7.7 existante : -- SOURCE database/migration_v7_8_securite_acces.sql; -- -- Objectif : plus aucune règle de périmètre ni de droit n'est reconstruite en -- PHP. Le contrôleur pose une question à la base, la base répond oui ou non. -- Toute évolution des droits se fait désormais en SQL, sans redéploiement. -- ============================================================================ -- ---------------------------------------------------------------------------- -- 1. MATRICE DES DROITS : rôle -> pages et rôle -> permissions -- ---------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS acces_role_page ( role ENUM('AGENT', 'RESPONSABLE_STRUCTURE', 'SERVICE_ENFANCE') NOT NULL, page VARCHAR(50) NOT NULL, ordre_affichage SMALLINT UNSIGNED NOT NULL DEFAULT 100, PRIMARY KEY (role, page) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; CREATE TABLE IF NOT EXISTS acces_role_permission ( role ENUM('AGENT', 'RESPONSABLE_STRUCTURE', 'SERVICE_ENFANCE') NOT NULL, permission VARCHAR(50) NOT NULL, autorise BOOLEAN NOT NULL DEFAULT FALSE, PRIMARY KEY (role, permission) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Pages autorisées — reprise à l'identique des droits V7.7. INSERT INTO acces_role_page (role, page, ordre_affichage) VALUES ('AGENT', 'agents', 10), ('AGENT', 'structures', 20), ('RESPONSABLE_STRUCTURE', 'planning', 10), ('RESPONSABLE_STRUCTURE', 'structures', 20), ('RESPONSABLE_STRUCTURE', 'agents', 30), ('RESPONSABLE_STRUCTURE', 'coverage', 40), ('SERVICE_ENFANCE', 'planning', 10), ('SERVICE_ENFANCE', 'pending', 20), ('SERVICE_ENFANCE', 'coverage', 30), ('SERVICE_ENFANCE', 'structures', 40), ('SERVICE_ENFANCE', 'agents', 50), ('SERVICE_ENFANCE', 'annual', 60), ('SERVICE_ENFANCE', 'pta', 70), ('SERVICE_ENFANCE', 'teams', 80), ('SERVICE_ENFANCE', 'vacations', 90), ('SERVICE_ENFANCE', 'administration', 100) ON DUPLICATE KEY UPDATE ordre_affichage = VALUES(ordre_affichage); -- Permissions fonctionnelles — reprise à l'identique des droits V7.7. INSERT INTO acces_role_permission (role, permission, autorise) VALUES ('AGENT', 'read_only', TRUE), ('AGENT', 'can_edit_draft', FALSE), ('AGENT', 'can_edit_cross_structure', FALSE), ('AGENT', 'can_validate', FALSE), ('AGENT', 'can_reopen', FALSE), ('AGENT', 'can_use_templates', FALSE), ('AGENT', 'can_copy_full_week', FALSE), ('AGENT', 'can_view_pending', FALSE), ('AGENT', 'can_manage_coverage', FALSE), ('AGENT', 'can_manage_agents', FALSE), ('AGENT', 'can_manage_structures', FALSE), ('AGENT', 'can_manage_vacations', FALSE), ('AGENT', 'can_manage_teams', FALSE), ('AGENT', 'can_manage_pta', FALSE), ('AGENT', 'can_forecast_next_year', FALSE), ('RESPONSABLE_STRUCTURE', 'read_only', FALSE), ('RESPONSABLE_STRUCTURE', 'can_edit_draft', TRUE), ('RESPONSABLE_STRUCTURE', 'can_edit_cross_structure', FALSE), ('RESPONSABLE_STRUCTURE', 'can_validate', FALSE), ('RESPONSABLE_STRUCTURE', 'can_reopen', TRUE), ('RESPONSABLE_STRUCTURE', 'can_use_templates', FALSE), ('RESPONSABLE_STRUCTURE', 'can_copy_full_week', FALSE), ('RESPONSABLE_STRUCTURE', 'can_view_pending', FALSE), ('RESPONSABLE_STRUCTURE', 'can_manage_coverage', FALSE), ('RESPONSABLE_STRUCTURE', 'can_manage_agents', FALSE), ('RESPONSABLE_STRUCTURE', 'can_manage_structures', FALSE), ('RESPONSABLE_STRUCTURE', 'can_manage_vacations', FALSE), ('RESPONSABLE_STRUCTURE', 'can_manage_teams', FALSE), ('RESPONSABLE_STRUCTURE', 'can_manage_pta', FALSE), ('RESPONSABLE_STRUCTURE', 'can_forecast_next_year', FALSE), ('SERVICE_ENFANCE', 'read_only', FALSE), ('SERVICE_ENFANCE', 'can_edit_draft', TRUE), ('SERVICE_ENFANCE', 'can_edit_cross_structure', TRUE), ('SERVICE_ENFANCE', 'can_validate', TRUE), ('SERVICE_ENFANCE', 'can_reopen', TRUE), ('SERVICE_ENFANCE', 'can_use_templates', TRUE), ('SERVICE_ENFANCE', 'can_copy_full_week', TRUE), ('SERVICE_ENFANCE', 'can_view_pending', TRUE), ('SERVICE_ENFANCE', 'can_manage_coverage', TRUE), ('SERVICE_ENFANCE', 'can_manage_agents', TRUE), ('SERVICE_ENFANCE', 'can_manage_structures', TRUE), ('SERVICE_ENFANCE', 'can_manage_vacations', TRUE), ('SERVICE_ENFANCE', 'can_manage_teams', TRUE), ('SERVICE_ENFANCE', 'can_manage_pta', TRUE), ('SERVICE_ENFANCE', 'can_forecast_next_year', TRUE) ON DUPLICATE KEY UPDATE autorise = VALUES(autorise); -- ---------------------------------------------------------------------------- -- 2. VUES DE DROITS -- ---------------------------------------------------------------------------- CREATE OR REPLACE VIEW v_acces_role_page AS SELECT role, page, ordre_affichage FROM acces_role_page; CREATE OR REPLACE VIEW v_acces_role_permission AS SELECT role, permission, autorise FROM acces_role_permission; -- ---------------------------------------------------------------------------- -- 3. VUES D'IDENTITÉ -- ---------------------------------------------------------------------------- -- Identité complète d'un agent actif : sert à valider la sélection de rôle et -- à construire le bandeau de profil. Un agent inactif n'apparaît jamais ici, -- il ne peut donc pas être choisi comme identité. CREATE OR REPLACE VIEW v_acces_identite_agent AS SELECT a.id_agent, a.matricule, a.nom, a.prenom, a.id_structure, s.nom AS structure_nom, s.code AS structure_code FROM agent a LEFT JOIN structure s ON s.id_structure = a.id_structure AND s.actif = TRUE WHERE a.actif = TRUE; -- Lieux d'affectation sélectionnables par un responsable de structure. CREATE OR REPLACE VIEW v_acces_identite_structure AS SELECT s.id_structure, s.code, s.nom, s.type_affectation FROM structure s WHERE s.actif = TRUE; -- ---------------------------------------------------------------------------- -- 4. VUES DE PÉRIMÈTRE -- ---------------------------------------------------------------------------- -- Quels agents un lieu d'affectation a-t-il le droit de consulter ? -- Deux origines : le rattachement principal, ou une planification effective -- sur ce lieu. C'est la règle exacte que le PHP reconstruisait à la main. CREATE OR REPLACE VIEW v_acces_agent_par_structure AS SELECT DISTINCT a.id_agent, a.id_structure AS id_structure_autorisee, 'RATTACHEMENT' AS origine FROM agent a WHERE a.actif = TRUE AND a.id_structure IS NOT NULL UNION SELECT DISTINCT a.id_agent, p.id_structure AS id_structure_autorisee, 'PLANIFICATION' AS origine FROM agent a INNER JOIN planning p ON p.id_agent = a.id_agent WHERE a.actif = TRUE; -- Périmètre d'un créneau : à quel lieu et à quel planning appartient-il ? -- Utilisé avant toute modification ou suppression d'affectation. CREATE OR REPLACE VIEW v_acces_perimetre_creneau AS SELECT c.id_creneau, c.id_planning, p.id_structure, p.id_agent, p.statut AS statut_planning, s.nom AS structure_nom FROM creneau_horaire c INNER JOIN planning p ON p.id_planning = c.id_planning INNER JOIN structure s ON s.id_structure = p.id_structure; -- Périmètre d'un planning : utilisé avant validation ou réouverture. CREATE OR REPLACE VIEW v_acces_perimetre_planning AS SELECT p.id_planning, p.id_structure, p.id_agent, p.statut AS statut_planning, s.nom AS structure_nom, a.nom AS agent_nom, a.prenom AS agent_prenom FROM planning p INNER JOIN structure s ON s.id_structure = p.id_structure INNER JOIN agent a ON a.id_agent = p.id_agent; -- ============================================================================ -- Vérification recommandée après import : -- SELECT role, COUNT(*) FROM acces_role_page GROUP BY role; -- SELECT * FROM v_acces_agent_par_structure LIMIT 10; -- ============================================================================