V1.22.23 Passage à Claude Code et création du BackOffice

This commit is contained in:
2026-09-11 13:38:34 +00:00
parent a9af2a5213
commit 05df1c5a28
28 changed files with 2660 additions and 670 deletions
+83
View File
@@ -0,0 +1,83 @@
<?php
/**
* ASPIC — espace_utilisateur.php
*
* Résolution de l'espace disque privé de chaque utilisateur (11 septembre
* 2026, feuille de route "BackOffice" point 2). Reprend, en l'affinant, une
* proposition initiale de Jean-Marc (hacher le nom de l'instance/de la base
* en SHA256 tronqué) — écartée en cours de discussion : SHA256 est un hash
* RAPIDE, donc trivialement cassable par dictionnaire dès que l'univers des
* valeurs possibles est petit et connu (les noms d'instances/bases ASPIC ne
* sont ni nombreux ni secrets). La bonne pièce existe déjà dans ce projet :
* `LIEN_DOC_CLE_SECRETE` (voir prospects_core.php, fabriquerJetonDoc()) —
* même principe repris ici avec sa propre clé (`ESPACE_CLE_SECRETE`) :
*
* - Dossier "tenant" (une base de travail, ex. MS-DEV) : HMAC-SHA256(clé
* secrète, host+base), tronqué. Déterministe (pas besoin de le stocker),
* mais infaisable à deviner sans la clé — contrairement à un simple
* SHA256, la présence d'une clé secrète rend une attaque par dictionnaire
* inopérante même en connaissant tous les noms de bases possibles.
* - Dossier "utilisateur" (une ligne MIR_Users) : jeton ALÉATOIRE
* (random_bytes), jamais dérivé d'un nom ou d'un numéro — rien à deviner,
* rien à casser. Généré une seule fois, à la volée, au premier besoin
* réel (même principe de création différée que creerSiteSiBesoin() dans
* mission_agent.php) puis conservé dans MIR_Users.MIR_Espace.
*
* Rappel de architecture (voir Réflexions stratégiques, section 21) : cette
* opacité de nom n'est qu'une défense en profondeur. La vraie protection est
* que RACINE_ESPACES vit hors de toute racine servie par Apache (volume
* Docker dédié, ajouté le 11 septembre 2026) — aucune URL ne peut jamais
* l'atteindre, quel que soit le nom des dossiers.
*/
const RACINE_ESPACES = '/var/www/espaces_utilisateurs';
function cheminTenant(string $host, string $baseNom): string
{
$cle = getenv('ESPACE_CLE_SECRETE');
if (!$cle) {
error_log('ASPIC espace_utilisateur : ESPACE_CLE_SECRETE non configurée');
$cle = 'repli-non-securise'; // ne devrait jamais arriver en pratique — voir docker-compose.yml
}
return substr(hash_hmac('sha256', "$host|$baseNom", $cle), 0, 16);
}
/**
* Jeton opaque de l'utilisateur (ligne MIR_Users) — lit MIR_Espace, le
* génère et le persiste au besoin (jamais recalculé une fois posé, pour ne
* jamais faire "bouger" un dossier déjà créé sur disque).
*
* Clé = MIR_Email, jamais MIR_Code : MIR_Code n'est PAS unique dans
* MIR_Users (ex. "JMP" partagé par les lignes "JMP Dev"/"JMP Tests", deux
* comptes distincts du même humain sur deux bases différentes) — seul
* MIR_Email identifie une ligne sans ambiguïté (même colonne que la
* vérification de mot de passe, voir aspic.php).
*/
function jetonUtilisateur(PDO $pdoRouter, string $mirEmail): string
{
$stmt = $pdoRouter->prepare('SELECT MIR_Espace FROM MIR_Users WHERE MIR_Email = ? LIMIT 1');
$stmt->execute([$mirEmail]);
$ligne = $stmt->fetch();
if ($ligne && $ligne['MIR_Espace']) {
return $ligne['MIR_Espace'];
}
$jeton = bin2hex(random_bytes(8)); // 16 caractères hexadécimaux, aléatoires, jamais dérivés d'un nom
$pdoRouter->prepare('UPDATE MIR_Users SET MIR_Espace = ? WHERE MIR_Email = ?')
->execute([$jeton, $mirEmail]);
return $jeton;
}
/**
* Résout (et crée si besoin) le dossier physique complet d'un utilisateur.
* Retourne le chemin absolu sur le disque du conteneur.
*/
function resoudreEspaceUtilisateur(PDO $pdoRouter, string $mirEmail, string $host, string $baseNom): string
{
$dossierRelatif = cheminTenant($host, $baseNom) . '/' . jetonUtilisateur($pdoRouter, $mirEmail);
$chemin = RACINE_ESPACES . '/' . $dossierRelatif;
if (!is_dir($chemin)) {
@mkdir($chemin, 0777, true);
@chmod($chemin, 0777);
}
return $chemin;
}