Compare commits

..

No commits in common. "bd0e3b9114e1261f2beed4f793f8b7d51b409f3f" and "8cc016c2a5b518654050930091634611ccf99cda" have entirely different histories.

2 changed files with 114 additions and 110 deletions

View File

@ -529,7 +529,8 @@ export async function registerAllListeners() {
console.log('[Router:API] 🚀 Démarrage de la création de processus standard...'); console.log('[Router:API] 🚀 Démarrage de la création de processus standard...');
const { privateData, publicData } = splitPrivateData(processData, privateFields); const { privateData, publicData } = splitPrivateData(processData, privateFields);
console.log('[Router:API] 1/2: Création du processus...'); // --- 1. CRÉATION DU PROCESSUS ---
console.log('[Router:API] 1/4: Création du processus...');
const createProcessReturn = await services.createProcess(privateData, publicData, roles); const createProcessReturn = await services.createProcess(privateData, publicData, roles);
if (!createProcessReturn.updated_process) { if (!createProcessReturn.updated_process) {
throw new Error('Empty updated_process in createProcessReturn'); throw new Error('Empty updated_process in createProcessReturn');
@ -538,10 +539,20 @@ export async function registerAllListeners() {
const processId = createProcessReturn.updated_process.process_id; const processId = createProcessReturn.updated_process.process_id;
const process = createProcessReturn.updated_process.current_process; const process = createProcessReturn.updated_process.current_process;
const stateId = process.states[0].state_id; const stateId = process.states[0].state_id;
console.log(`[Router:API] 2/2: Processus ${processId} créé. Traitement...`); console.log(`[Router:API] 2/4: Processus ${processId} créé. Traitement...`);
await services.handleApiReturn(createProcessReturn); await services.handleApiReturn(createProcessReturn);
console.log(`[Router:API] 🎉 Processus ${processId} créé.`); // --- 2. MISE À JOUR PRD ---
console.log('[Router:API] 3/4: Création de la mise à jour PRD...');
const createPrdUpdateReturn = await services.createPrdUpdate(processId, stateId);
await services.handleApiReturn(createPrdUpdateReturn);
// --- 3. APPROBATION DU CHANGEMENT ---
console.log('[Router:API] 4/4: Approbation du changement...');
const approveChangeReturn = await services.approveChange(processId, stateId);
await services.handleApiReturn(approveChangeReturn);
console.log(`[Router:API] 🎉 Processus ${processId} créé et auto-approuvé.`);
const res = { const res = {
processId, processId,
@ -768,7 +779,12 @@ export async function registerAllListeners() {
window.addEventListener('message', handleMessage); window.addEventListener('message', handleMessage);
async function handleMessage(event: MessageEvent) { async function handleMessage(event: MessageEvent) {
// Le 'switch' est dans un 'try...catch'
// Grâce au 'await' sur chaque 'handle', si l'un d'eux échoue (throw),
// l'erreur sera interceptée ici et renvoyée proprement.
try { try {
// AMÉLIORATION CRITIQUE: Ajout de 'await' à chaque appel
// pour que le try/catch puisse intercepter les erreurs.
switch (event.data.type) { switch (event.data.type) {
case MessageType.REQUEST_LINK: case MessageType.REQUEST_LINK:
await handleRequestLink(event); await handleRequestLink(event);
@ -797,6 +813,10 @@ export async function registerAllListeners() {
case MessageType.CREATE_PROCESS: case MessageType.CREATE_PROCESS:
await handleCreateProcess(event); await handleCreateProcess(event);
break; break;
case MessageType.CREATE_CONVERSATION:
// @ts-ignore - 'handleCreateConversationProcess' n'est pas défini dans le fichier
await handleCreateConversationProcess(event);
break;
case MessageType.NOTIFY_UPDATE: case MessageType.NOTIFY_UPDATE:
await handleNotifyUpdate(event); await handleNotifyUpdate(event);
break; break;
@ -819,14 +839,16 @@ export async function registerAllListeners() {
await handleValidateMerkleProof(event); await handleValidateMerkleProof(event);
break; break;
default: default:
console.warn('[Router:API] ⚠️ Message non géré reçu:', event.data); console.warn(`[Router:API] ⚠️ Message non géré reçu: ${event.data.type}`);
} }
} catch (error) { } catch (error) {
// C'est le "filet de sécurité" global
const errorMsg = `[Router:API] 💥 Erreur de haut niveau: ${error.message || error}`; const errorMsg = `[Router:API] 💥 Erreur de haut niveau: ${error.message || error}`;
errorResponse(errorMsg, event.origin, event.data.messageId); errorResponse(errorMsg, event.origin, event.data.messageId);
} }
} }
// Notifie l'application parente que l'Iframe (le "serveur") est prête
window.parent.postMessage( window.parent.postMessage(
{ {
type: MessageType.LISTENING, type: MessageType.LISTENING,

View File

@ -809,6 +809,7 @@ export default class Services {
console.error(`[Services:parseCipher] 💥 Échec critique du déchiffrement: ${e}`); console.error(`[Services:parseCipher] 💥 Échec critique du déchiffrement: ${e}`);
console.warn(`[Services:parseCipher] Contrainte d'anonymat: L'expéditeur est inconnu.`); console.warn(`[Services:parseCipher] Contrainte d'anonymat: L'expéditeur est inconnu.`);
// --- 🚨 NOUVELLE LOGIQUE 🚨 ---
// On ne supprime rien. On lève juste un drapeau pour // On ne supprime rien. On lève juste un drapeau pour
// forcer 'ensureConnections' à se méfier de la BDD. // forcer 'ensureConnections' à se méfier de la BDD.
console.warn(`[Services:parseCipher] 🚩 ACTION: Levée du drapeau 'secretsAreCompromised'.`); console.warn(`[Services:parseCipher] 🚩 ACTION: Levée du drapeau 'secretsAreCompromised'.`);
@ -816,6 +817,7 @@ export default class Services {
} }
} }
// --- AMÉLIORATION: Refactorisé en sous-fonctions ---
async parseNewTx(newTxMsg: string) { async parseNewTx(newTxMsg: string) {
console.log('[Services:parseNewTx] 📄 Nouveau message NewTx reçu.'); console.log('[Services:parseNewTx] 📄 Nouveau message NewTx reçu.');
const parsedMsg: NewTxMessage = JSON.parse(newTxMsg); const parsedMsg: NewTxMessage = JSON.parse(newTxMsg);
@ -1712,119 +1714,99 @@ export default class Services {
} }
async decryptAttribute(processId: string, state: ProcessState, attribute: string): Promise<any | null> { async decryptAttribute(processId: string, state: ProcessState, attribute: string): Promise<any | null> {
// Le groupe principal est "collapsed" pour ne pas polluer la console par défaut console.log(`[Services:decryptAttribute] 🔑 Tentative de déchiffrement de l'attribut '${attribute}' pour le processus ${processId}`);
console.groupCollapsed(`[Services:decryptAttribute] 🔑 Déchiffrement de '${attribute}' (Process: ${processId})`); let hash = state.pcd_commitment[attribute];
if (!hash) {
console.warn(`[Services:decryptAttribute] ⚠️ L'attribut '${attribute}' n'existe pas (pas de hash).`);
return null;
}
let key = state.keys[attribute];
const pairingProcessId = this.getPairingProcessId();
try { // If key is missing, request an update and then retry
let hash = state.pcd_commitment[attribute]; if (!key) {
if (!hash) { console.warn(`[Services:decryptAttribute] ⚠️ Clé manquante pour '${attribute}'. Vérification de l'accès et demande aux pairs...`);
console.warn(`⚠️ L'attribut n'existe pas (pas de hash).`); const roles = state.roles;
return null; // Le 'finally' s'exécutera let hasAccess = false;
// If we're not supposed to have access to this attribute, ignore
for (const role of Object.values(roles)) {
for (const rule of Object.values(role.validation_rules)) {
if (rule.fields.includes(attribute)) {
if (role.members.includes(pairingProcessId)) {
// We have access to this attribute
hasAccess = true;
break;
}
}
}
} }
let key = state.keys[attribute];
const pairingProcessId = this.getPairingProcessId();
// If key is missing, request an update and then retry if (!hasAccess) {
if (!key) { console.log(`[Services:decryptAttribute] ⛔ Accès non autorisé à '${attribute}'. Abandon.`);
// On crée un sous-groupe pour la logique de récupération de la clé return null;
console.group(`🔐 Gestion de la clé manquante pour '${attribute}'`); }
console.warn(`Vérification de l'accès et demande aux pairs...`); const process = await this.getProcess(processId);
const roles = state.roles; if (!process) {
let hasAccess = false; console.error(`[Services:decryptAttribute] 💥 Impossible de trouver le processus ${processId} pour ensureConnections.`);
// If we're not supposed to have access to this attribute, ignore return null;
for (const role of Object.values(roles)) { }
for (const rule of Object.values(role.validation_rules)) {
if (rule.fields.includes(attribute)) { await this.ensureConnections(process);
if (role.members.includes(pairingProcessId)) { // We should have the key, so we're going to ask other members for it
// We have access to this attribute console.log(`[Services:decryptAttribute] 🗣️ Demande de données aux pairs pour '${attribute}'...`);
hasAccess = true; await this.requestDataFromPeers(processId, [state.state_id], [state.roles]);
break;
} const maxRetries = 5;
} const retryDelay = 500; // delay in milliseconds
let retries = 0;
while ((!hash || !key) && retries < maxRetries) {
console.log(`[Services:decryptAttribute] ⏳ Attente de la clé... (Tentative ${retries + 1}/${maxRetries})`);
await new Promise((resolve) => setTimeout(resolve, retryDelay));
// Re-read hash and key after waiting
// AMÉLIORATION: On doit relire l'état complet depuis la BDD/cache, car 'state' est un instantané
const updatedProcess = await this.getProcess(processId);
const updatedState = this.getStateFromId(updatedProcess, state.state_id);
if (updatedState) {
hash = updatedState.pcd_commitment[attribute];
key = updatedState.keys[attribute];
}
retries++;
}
}
if (hash && key) {
console.log(`[Services:decryptAttribute] Clé et hash trouvés pour '${attribute}'. Tentative de déchiffrement...`);
const blob = await this.getBlobFromDb(hash);
if (blob) {
// Decrypt the data
const buf = await blob.arrayBuffer();
const cipher = new Uint8Array(buf);
const keyUIntArray = this.hexToUInt8Array(key);
try {
const clear = this.sdkClient.decrypt_data(keyUIntArray, cipher);
if (clear) {
// deserialize the result to get the actual data
const decoded = this.sdkClient.decode_value(clear);
console.log(`[Services:decryptAttribute] ✅ Attribut '${attribute}' déchiffré avec succès.`);
return decoded;
} else {
throw new Error('decrypt_data returned null');
} }
} } catch (e) {
console.error(`[Services:decryptAttribute] 💥 Échec du déchiffrement (decrypt_data): ${e}`);
if (!hasAccess) {
console.log(`⛔ Accès non autorisé. Abandon.`);
console.groupEnd(); // Ferme le sous-groupe "Gestion de la clé manquante"
return null; // Le 'finally' principal s'exécutera
}
const process = await this.getProcess(processId);
if (!process) {
console.error(`💥 Impossible de trouver le processus ${processId} pour ensureConnections.`);
console.groupEnd(); // Ferme le sous-groupe "Gestion de la clé manquante"
return null;
}
await this.ensureConnections(process);
// We should have the key, so we're going to ask other members for it
console.log(`🗣️ Demande de données aux pairs...`);
await this.requestDataFromPeers(processId, [state.state_id], [state.roles]);
const maxRetries = 5;
const retryDelay = 500; // delay in milliseconds
let retries = 0;
// On crée un sous-groupe replié pour la boucle de réessai (potentiellement verbeuse)
console.groupCollapsed(`⏳ Boucle d'attente de la clé (max ${maxRetries} tentatives)`);
while ((!hash || !key) && retries < maxRetries) {
console.log(`(Tentative ${retries + 1}/${maxRetries})...`);
await new Promise((resolve) => setTimeout(resolve, retryDelay));
// Re-read hash and key after waiting
const updatedProcess = await this.getProcess(processId);
const updatedState = this.getStateFromId(updatedProcess, state.state_id);
if (updatedState) {
hash = updatedState.pcd_commitment[attribute];
key = updatedState.keys[attribute];
}
retries++;
}
console.groupEnd(); // Ferme le sous-groupe "Boucle d'attente"
console.groupEnd(); // Ferme le sous-groupe "Gestion de la clé manquante"
} // Fin de if (!key)
if (hash && key) {
console.log(` Clé et hash trouvés. Tentative de déchiffrement...`);
const blob = await this.getBlobFromDb(hash);
if (blob) {
// Decrypt the data
const buf = await blob.arrayBuffer();
const cipher = new Uint8Array(buf);
const keyUIntArray = this.hexToUInt8Array(key);
try {
const clear = this.sdkClient.decrypt_data(keyUIntArray, cipher);
if (clear) {
// deserialize the result to get the actual data
const decoded = this.sdkClient.decode_value(clear);
console.log(`✅ Attribut '${attribute}' déchiffré avec succès.`);
return decoded; // Le 'finally' s'exécutera
} else {
throw new Error('decrypt_data returned null');
}
} catch (e) {
console.error(`💥 Échec du déchiffrement (decrypt_data): ${e}`);
}
} else {
console.error(`💥 Échec: Blob non trouvé en BDD pour le hash ${hash}`);
} }
} else { } else {
console.error(`💥 Échec: Clé ou hash manquant après ${maxRetries} tentatives pour '${attribute}'.`); console.error(`[Services:decryptAttribute] 💥 Échec: Blob non trouvé en BDD pour le hash ${hash}`);
} }
} else {
return null; // Le 'finally' s'exécutera console.error(`[Services:decryptAttribute] 💥 Échec: Clé ou hash manquant après ${maxRetries} tentatives pour '${attribute}'.`);
} catch (error) {
// Intercepte les erreurs inattendues non gérées
console.error(`💥 Erreur inattendue dans decryptAttribute:`, error);
return null;
} finally {
// Ce bloc est TOUJOURS exécuté, assurant que le groupe est fermé.
console.groupEnd();
} }
return null;
} }
getNotifications(): any[] | null { getNotifications(): any[] | null {