**Motivations :** - Corriger les erreurs TypeScript qui bloquaient la compilation - Corriger les erreurs ESLint pour améliorer la qualité du code - Utiliser les bons formats pour secureLogger.error **Modifications :** - Corriger les appels secureLogger.error pour passer error comme 2e paramètre - Ajouter les imports manquants (Database dans security-mode.service.ts) - Préfixer les variables non utilisées avec _ pour respecter ESLint - Corriger les comparaisons BigInt (utiliser BigInt(0) au lieu de 0n) - Ajouter @ts-expect-error pour PasswordCredential API expérimentale - Corriger le paramètre services non utilisé dans router.ts **Pages affectées :** - src/services/service.ts (comparaisons BigInt, imports) - src/services/security-mode.service.ts (import Database) - src/services/secure-credentials.service.ts (secureLogger.error, PasswordCredential) - src/services/credentials/encryption.service.ts (secureLogger.error, salt type) - src/router.ts (paramètre _services) - src/components/device-management/device-management.ts (variable _data) - src/components/secure-credentials/secure-credentials.ts (variable _error) - src/components/security-mode-selector/security-mode-selector.ts (paramètre _mode) - src/components/login-modal/login-modal.js (window globals)
357 lines
10 KiB
TypeScript
357 lines
10 KiB
TypeScript
/**
|
|
* SecurityModeService - Gestion des modes de sécurisation
|
|
* Gère le stockage et la récupération du mode de sécurisation choisi par l'utilisateur
|
|
* NOTE: Le mode de sécurité est stocké uniquement avec la clé PBKDF2, pas dans un store séparé
|
|
*/
|
|
|
|
import { secureLogger } from './secure-logger';
|
|
import Database from './database.service';
|
|
|
|
export type SecurityMode = 'proton-pass' | 'os' | 'otp' | '2fa' | 'password' | 'none';
|
|
|
|
export interface SecurityModeConfig {
|
|
mode: SecurityMode;
|
|
name: string;
|
|
description: string;
|
|
securityLevel: 'high' | 'medium' | 'low' | 'critical';
|
|
requiresConfirmation: boolean;
|
|
warnings: string[];
|
|
implementation: {
|
|
useWebAuthn: boolean;
|
|
useEncryption: boolean;
|
|
usePlatformAuth: boolean;
|
|
storageType: 'encrypted' | 'plain' | 'hybrid';
|
|
requiresOTP?: boolean;
|
|
requiresPassword?: boolean;
|
|
};
|
|
}
|
|
|
|
export class SecurityModeService {
|
|
private static instance: SecurityModeService;
|
|
private currentMode: SecurityMode | null = null;
|
|
|
|
private constructor() {
|
|
// N'instancier pas Database ici, il sera récupéré via getInstance() quand nécessaire
|
|
}
|
|
|
|
public static getInstance(): SecurityModeService {
|
|
if (!SecurityModeService.instance) {
|
|
SecurityModeService.instance = new SecurityModeService();
|
|
}
|
|
return SecurityModeService.instance;
|
|
}
|
|
|
|
/**
|
|
* Récupère le mode de sécurisation actuel
|
|
* NOTE: Le mode est stocké uniquement en mémoire, dérivé de la clé PBKDF2 stockée
|
|
*/
|
|
public async getCurrentMode(): Promise<SecurityMode | null> {
|
|
if (this.currentMode) {
|
|
return this.currentMode;
|
|
}
|
|
|
|
// Le mode de sécurité n'est pas stocké en base, il est dérivé de la clé PBKDF2
|
|
// Ici on retourne null si aucun mode n'est en mémoire
|
|
// Le mode sera défini lors de la génération de la clé PBKDF2
|
|
secureLogger.info('No security mode in memory (will be set when PBKDF2 key is generated)', {
|
|
component: 'SecurityModeService',
|
|
operation: 'getCurrentMode'
|
|
});
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Définit le mode de sécurisation
|
|
* NOTE: Le mode de sécurité est stocké uniquement avec la clé PBKDF2, pas dans un store séparé
|
|
*/
|
|
public async setSecurityMode(mode: SecurityMode): Promise<void> {
|
|
try {
|
|
const modeConfig = this.getSecurityModeConfig(mode);
|
|
|
|
// Stocker uniquement en mémoire
|
|
this.currentMode = mode;
|
|
|
|
secureLogger.info('Security mode set successfully', {
|
|
component: 'SecurityModeService',
|
|
operation: 'setSecurityMode',
|
|
mode,
|
|
securityLevel: modeConfig.securityLevel
|
|
});
|
|
|
|
// Émettre un événement pour notifier les autres services
|
|
window.dispatchEvent(new CustomEvent('securityModeChanged', {
|
|
detail: { mode, config: modeConfig }
|
|
}));
|
|
|
|
} catch (error) {
|
|
secureLogger.error('Failed to set security mode', error as Error, {
|
|
component: 'SecurityModeService',
|
|
operation: 'setSecurityMode',
|
|
mode
|
|
});
|
|
// NE PAS FAIRE DE FALLBACK - échouer complètement
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Récupère la configuration d'un mode de sécurisation
|
|
*/
|
|
public getSecurityModeConfig(mode: SecurityMode): SecurityModeConfig {
|
|
const configs: Record<SecurityMode, SecurityModeConfig> = {
|
|
'proton-pass': {
|
|
mode: 'proton-pass',
|
|
name: 'Proton Pass',
|
|
description: 'Utilise Proton Pass pour l\'authentification biométrique et la gestion des clés',
|
|
securityLevel: 'high',
|
|
requiresConfirmation: false,
|
|
warnings: [],
|
|
implementation: {
|
|
useWebAuthn: true,
|
|
useEncryption: true,
|
|
usePlatformAuth: true,
|
|
storageType: 'encrypted'
|
|
}
|
|
},
|
|
'os': {
|
|
mode: 'os',
|
|
name: 'Authentificateur OS',
|
|
description: 'Utilise l\'authentificateur intégré de votre système d\'exploitation',
|
|
securityLevel: 'high',
|
|
requiresConfirmation: false,
|
|
warnings: [],
|
|
implementation: {
|
|
useWebAuthn: true,
|
|
useEncryption: true,
|
|
usePlatformAuth: true,
|
|
storageType: 'encrypted'
|
|
}
|
|
},
|
|
'otp': {
|
|
mode: 'otp',
|
|
name: 'OTP (Proton Pass Compatible)',
|
|
description: 'Utilise un code OTP généré par une application compatible (Proton Pass, Google Authenticator, etc.)',
|
|
securityLevel: 'high',
|
|
requiresConfirmation: false,
|
|
warnings: [],
|
|
implementation: {
|
|
useWebAuthn: false,
|
|
useEncryption: true,
|
|
usePlatformAuth: false,
|
|
storageType: 'encrypted',
|
|
requiresOTP: true
|
|
}
|
|
},
|
|
'2fa': {
|
|
mode: '2fa',
|
|
name: 'Application 2FA',
|
|
description: 'Stockage en clair avec authentification par application 2FA',
|
|
securityLevel: 'low',
|
|
requiresConfirmation: true,
|
|
warnings: [
|
|
'⚠️ Clés stockées en clair',
|
|
'⚠️ Risque de compromission',
|
|
'⚠️ Non recommandé pour des données sensibles'
|
|
],
|
|
implementation: {
|
|
useWebAuthn: false,
|
|
useEncryption: false,
|
|
usePlatformAuth: false,
|
|
storageType: 'plain'
|
|
}
|
|
},
|
|
'password': {
|
|
mode: 'password',
|
|
name: 'Mot de Passe (Non Sauvegardé)',
|
|
description: 'Vos clés sont chiffrées avec un mot de passe que vous devez saisir à chaque utilisation',
|
|
securityLevel: 'low',
|
|
requiresConfirmation: true,
|
|
warnings: [
|
|
'⚠️ Le mot de passe n\'est PAS sauvegardé',
|
|
'⚠️ NON récupérable en cas d\'oubli',
|
|
'⚠️ À saisir à chaque utilisation'
|
|
],
|
|
implementation: {
|
|
useWebAuthn: false,
|
|
useEncryption: true,
|
|
usePlatformAuth: false,
|
|
storageType: 'encrypted',
|
|
requiresPassword: true
|
|
}
|
|
},
|
|
'none': {
|
|
mode: 'none',
|
|
name: 'Aucune Sécurité',
|
|
description: 'Chiffrement avec une clé déterminée en dur (non sécurisé)',
|
|
securityLevel: 'critical',
|
|
requiresConfirmation: true,
|
|
warnings: [
|
|
'🚨 Clé de chiffrement en dur',
|
|
'🚨 Accès non protégé',
|
|
'🚨 RISQUE ÉLEVÉ'
|
|
],
|
|
implementation: {
|
|
useWebAuthn: false,
|
|
useEncryption: true,
|
|
usePlatformAuth: false,
|
|
storageType: 'encrypted',
|
|
requiresPassword: false
|
|
}
|
|
}
|
|
};
|
|
|
|
return configs[mode];
|
|
}
|
|
|
|
/**
|
|
* Vérifie si un mode nécessite une confirmation
|
|
*/
|
|
public requiresConfirmation(mode: SecurityMode): boolean {
|
|
const config = this.getSecurityModeConfig(mode);
|
|
return config.requiresConfirmation;
|
|
}
|
|
|
|
/**
|
|
* Récupère les avertissements pour un mode
|
|
*/
|
|
public getWarnings(mode: SecurityMode): string[] {
|
|
const config = this.getSecurityModeConfig(mode);
|
|
return config.warnings;
|
|
}
|
|
|
|
/**
|
|
* Vérifie si le mode actuel est sécurisé
|
|
*/
|
|
public async isCurrentModeSecure(): Promise<boolean> {
|
|
const currentMode = await this.getCurrentMode();
|
|
if (!currentMode) {return false;}
|
|
|
|
const config = this.getSecurityModeConfig(currentMode);
|
|
return config.securityLevel === 'high' || config.securityLevel === 'medium';
|
|
}
|
|
|
|
/**
|
|
* Récupère tous les modes disponibles
|
|
*/
|
|
public getAvailableModes(): SecurityMode[] {
|
|
return ['proton-pass', 'os', 'otp', '2fa', 'password', 'none'];
|
|
}
|
|
|
|
/**
|
|
* Récupère les modes recommandés (sécurisés)
|
|
*/
|
|
public getRecommendedModes(): SecurityMode[] {
|
|
return ['proton-pass', 'os', 'otp'];
|
|
}
|
|
|
|
/**
|
|
* Récupère les modes non recommandés (non sécurisés)
|
|
*/
|
|
public getNonRecommendedModes(): SecurityMode[] {
|
|
return ['2fa', 'none'];
|
|
}
|
|
|
|
/**
|
|
* Vérifie si un mode utilise WebAuthn
|
|
*/
|
|
public usesWebAuthn(mode: SecurityMode): boolean {
|
|
const config = this.getSecurityModeConfig(mode);
|
|
return config.implementation.useWebAuthn;
|
|
}
|
|
|
|
/**
|
|
* Vérifie si un mode utilise le chiffrement
|
|
*/
|
|
public usesEncryption(mode: SecurityMode): boolean {
|
|
const config = this.getSecurityModeConfig(mode);
|
|
return config.implementation.useEncryption;
|
|
}
|
|
|
|
/**
|
|
* Vérifie si un mode utilise l'authentificateur de plateforme
|
|
*/
|
|
public usesPlatformAuth(mode: SecurityMode): boolean {
|
|
const config = this.getSecurityModeConfig(mode);
|
|
return config.implementation.usePlatformAuth;
|
|
}
|
|
|
|
/**
|
|
* Récupère le type de stockage pour un mode
|
|
*/
|
|
public getStorageType(mode: SecurityMode): 'encrypted' | 'plain' | 'hybrid' {
|
|
const config = this.getSecurityModeConfig(mode);
|
|
return config.implementation.storageType;
|
|
}
|
|
|
|
/**
|
|
* Réinitialise le mode de sécurisation
|
|
*/
|
|
public async resetSecurityMode(): Promise<void> {
|
|
try {
|
|
const database = await Database.getInstance();
|
|
await database.deleteObject('security_settings', 'current_mode');
|
|
this.currentMode = null;
|
|
|
|
secureLogger.info('Security mode reset', {
|
|
component: 'SecurityModeService',
|
|
operation: 'resetSecurityMode'
|
|
});
|
|
} catch (error) {
|
|
secureLogger.error('Failed to reset security mode', error as Error, {
|
|
component: 'SecurityModeService',
|
|
operation: 'resetSecurityMode'
|
|
});
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Récupère l'historique des modes de sécurisation
|
|
*/
|
|
public async getSecurityModeHistory(): Promise<any[]> {
|
|
try {
|
|
const database = await Database.getInstance();
|
|
const history = await database.getObject('security_settings', 'mode_history') || [];
|
|
return history;
|
|
} catch (error) {
|
|
secureLogger.error('Failed to retrieve security mode history', error as Error, {
|
|
component: 'SecurityModeService',
|
|
operation: 'getSecurityModeHistory'
|
|
});
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ajoute une entrée à l'historique des modes
|
|
*/
|
|
private async addToHistory(mode: SecurityMode): Promise<void> {
|
|
try {
|
|
const history = await this.getSecurityModeHistory();
|
|
history.unshift({
|
|
mode,
|
|
timestamp: Date.now(),
|
|
config: this.getSecurityModeConfig(mode)
|
|
});
|
|
|
|
// Garder seulement les 10 dernières entrées
|
|
if (history.length > 10) {
|
|
history.splice(10);
|
|
}
|
|
|
|
const database = await Database.getInstance();
|
|
await database.addObject({
|
|
storeName: 'security_settings',
|
|
key: 'mode_history',
|
|
object: history
|
|
});
|
|
} catch (error) {
|
|
secureLogger.error('Failed to add to security mode history', error as Error, {
|
|
component: 'SecurityModeService',
|
|
operation: 'addToHistory',
|
|
mode
|
|
});
|
|
}
|
|
}
|
|
}
|