diff --git a/src/services/service.ts b/src/services/service.ts index 2c7219e..a79a35d 100755 --- a/src/services/service.ts +++ b/src/services/service.ts @@ -1709,118 +1709,160 @@ export default class Services { } } + // La fonction principale est maintenant beaucoup plus simple à lire. + // Elle sert d'orchestrateur. async decryptAttribute(processId: string, state: ProcessState, attribute: string): Promise { - // Le groupe principal est "collapsed" pour ne pas polluer la console par défaut console.groupCollapsed(`[Services:decryptAttribute] 🔑 Déchiffrement de '${attribute}' (Process: ${processId})`); try { let hash = state.pcd_commitment[attribute]; - if (!hash) { - console.warn(`⚠️ L'attribut n'existe pas (pas de hash).`); - return null; // Le 'finally' s'exécutera - } let key = state.keys[attribute]; const pairingProcessId = this.getPairingProcessId(); - // If key is missing, request an update and then retry + // 1. Garde : A-t-on au moins le hash ? + if (!hash) { + console.warn(`⚠️ L'attribut n'existe pas (pas de hash).`); + return null; + } + + // 2. Si la clé est manquante, on la récupère if (!key) { - // On crée un sous-groupe pour la logique de récupération de la clé - console.group(`🔐 Gestion de la clé manquante pour '${attribute}'`); - - console.warn(`Vérification de l'accès et demande aux pairs...`); - const roles = state.roles; - 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; - } - } - } - } - - if (!hasAccess) { + // 2a. Vérifier l'accès (logique extraite) + if (!this._checkAccess(state, attribute, pairingProcessId)) { 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) + // 2b. Tenter de récupérer la clé (logique extraite) + const result = await this._fetchMissingKey(processId, state, attribute); + hash = result.hash; // Mettre à jour le hash (il a pu être rafraîchi) + key = result.key; // Mettre à jour la clé + } + // 3. Si on a tout (soit depuis le début, soit après récupération) 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 + + if (!blob) { + console.error(`💥 Échec: Blob non trouvé en BDD pour le hash ${hash}`); + return null; + } + + try { 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}`); + const clear = this.sdkClient.decrypt_data(keyUIntArray, cipher); + if (!clear) { + throw new Error('decrypt_data returned null'); } - } else { - console.error(`💥 Échec: Blob non trouvé en BDD pour le hash ${hash}`); + + const decoded = this.sdkClient.decode_value(clear); + console.log(`✅ Attribut '${attribute}' déchiffré avec succès.`); + return decoded; + } catch (e) { + console.error(`💥 Échec du déchiffrement (decrypt_data): ${e}`); + return null; } - } else { - console.error(`💥 Échec: Clé ou hash manquant après ${maxRetries} tentatives pour '${attribute}'.`); } - return null; // Le 'finally' s'exécutera + // 4. Échec final si la clé ou le hash manque toujours + console.error(`💥 Échec: Clé ou hash manquant après tentatives pour '${attribute}'.`); + return null; } 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é. + // Garantit que le groupe principal est TOUJOURS fermé + console.groupEnd(); + } + } + + /** + * NOUVELLE MÉTHODE PRIVÉE + * Vérifie si l'utilisateur courant a le droit d'accéder à cet attribut + * en se basant sur les rôles du state. + */ + private _checkAccess(state: ProcessState, attribute: string, pairingProcessId: string): boolean { + const roles = state.roles; + + // Utilise .some() pour une lecture plus claire et un arrêt anticipé + // "Existe-t-il AU MOINS UN rôle..." + return Object.values(roles).some((role) => { + // "...tel que l'utilisateur est membre..." + const isMember = role.members.includes(pairingProcessId); + if (!isMember) { + return false; // Passe au rôle suivant + } + + // "...ET ce rôle a AU MOINS UNE règle de validation qui inclut cet attribut" + return Object.values(role.validation_rules).some((rule) => rule.fields.includes(attribute)); + }); + } + + /** + * NOUVELLE MÉTHODE PRIVÉE + * Gère la logique complexe de demande aux pairs et la boucle de retentative. + */ + private async _fetchMissingKey(processId: string, state: ProcessState, attribute: string): Promise<{ hash: string | null; key: string | null }> { + console.group(`🔐 Gestion de la clé manquante pour '${attribute}'`); + + try { + const process = await this.getProcess(processId); + if (!process) { + console.error(`💥 Impossible de trouver le processus ${processId} pour ensureConnections.`); + return { hash: null, key: null }; + } + + await this.ensureConnections(process); + console.log(`🗣️ Demande de données aux pairs...`); + await this.requestDataFromPeers(processId, [state.state_id], [state.roles]); + + // Boucle de retentative + const maxRetries = 5; + const retryDelay = 500; + let retries = 0; + let hash: string | null = null; + let key: string | null = null; + + console.groupCollapsed(`⏳ Boucle d'attente de la clé (max ${maxRetries} tentatives)`); + try { + while (retries < maxRetries) { + console.log(`(Tentative ${retries + 1}/${maxRetries})...`); + await new Promise((resolve) => setTimeout(resolve, retryDelay)); + + // On rafraîchit les données depuis la source + 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]; + + if (hash && key) { + console.log('✅ Clé et hash reçus !'); + break; // Sortir de la boucle while + } + } + retries++; + } + } finally { + // Garantit la fermeture du groupe de la boucle + console.groupEnd(); + } + + if (!hash || !key) { + console.warn(`⌛ Clé ou hash toujours manquant après ${maxRetries} tentatives.`); + } + + return { hash, key }; + } catch (e) { + console.error(`💥 Erreur durant _fetchMissingKey: ${e}`); + return { hash: null, key: null }; + } finally { + // Garantit la fermeture du groupe de gestion de clé console.groupEnd(); } }