/** * SecureCredentialsComponent - Composant pour la gestion des credentials sécurisés * Interface utilisateur pour la gestion des clés de spend et de scan avec PBKDF2 */ import { SecureCredentialsService } from '../../services/secure-credentials.service'; import { secureLogger } from '../../services/secure-logger'; import { eventBus } from '../../services/event-bus'; export class SecureCredentialsComponent { private container: HTMLElement | null = null; private isInitialized = false; constructor() { this.init(); } /** * Initialise le composant */ private async init(): Promise { try { this.container = document.getElementById('secure-credentials-container'); if (!this.container) { throw new Error('Secure credentials container not found'); } await this.loadHTML(); await this.loadCSS(); this.attachEventListeners(); await this.updateUI(); this.isInitialized = true; secureLogger.info('SecureCredentialsComponent initialized', { component: 'SecureCredentialsComponent', operation: 'init' }); } catch (error) { secureLogger.error('Failed to initialize SecureCredentialsComponent', error as Error, { component: 'SecureCredentialsComponent', operation: 'init' }); } } /** * Charge le HTML du composant */ private async loadHTML(): Promise { try { const response = await fetch('/src/components/secure-credentials/secure-credentials.html'); const html = await response.text(); this.container!.innerHTML = html; } catch (error) { secureLogger.error('Failed to load secure credentials HTML', error as Error, { component: 'SecureCredentialsComponent', operation: 'loadHTML' }); } } /** * Charge le CSS du composant */ private async loadCSS(): Promise { try { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = '/src/components/secure-credentials/secure-credentials.css'; document.head.appendChild(link); } catch (error) { secureLogger.error('Failed to load secure credentials CSS', error as Error, { component: 'SecureCredentialsComponent', operation: 'loadCSS' }); } } /** * Attache les écouteurs d'événements */ private attachEventListeners(): void { // Formulaire de création de credentials const createForm = document.getElementById('create-credentials-form') as HTMLFormElement; if (createForm) { createForm.addEventListener('submit', this.handleCreateCredentials.bind(this)); } // Formulaire d'accès aux credentials const accessForm = document.getElementById('access-credentials-form') as HTMLFormElement; if (accessForm) { accessForm.addEventListener('submit', this.handleAccessCredentials.bind(this)); } // Validation du mot de passe en temps réel const passwordInput = document.getElementById('password') as HTMLInputElement; if (passwordInput) { passwordInput.addEventListener('input', this.handlePasswordInput.bind(this)); } // Boutons d'action const refreshBtn = document.getElementById('refresh-credentials-btn'); if (refreshBtn) { refreshBtn.addEventListener('click', this.handleRefreshCredentials.bind(this)); } const deleteBtn = document.getElementById('delete-credentials-btn'); if (deleteBtn) { deleteBtn.addEventListener('click', this.handleDeleteCredentials.bind(this)); } // Écouter les événements du service eventBus.on('credentials:created', this.handleCredentialsCreated.bind(this)); eventBus.on('credentials:deleted', this.handleCredentialsDeleted.bind(this)); } /** * Gère la création de credentials */ private async handleCreateCredentials(event: Event): Promise { event.preventDefault(); const form = event.target as HTMLFormElement; const formData = new FormData(form); const password = formData.get('password') as string; const confirmPassword = formData.get('confirm-password') as string; if (password !== confirmPassword) { this.showMessage('Les mots de passe ne correspondent pas', 'error'); return; } try { this.showMessage('Création des credentials en cours...', 'info'); // Générer les credentials const secureCredentialsService = SecureCredentialsService.getInstance(); const credentials = await secureCredentialsService.generateSecureCredentials(password); // Stocker les credentials await secureCredentialsService.storeCredentials(credentials, password); this.showMessage('Credentials créés et stockés avec succès !', 'success'); await this.updateUI(); // Émettre l'événement eventBus.emit('credentials:created', { credentials }); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Erreur inconnue'; this.showMessage(`Erreur lors de la création des credentials: ${errorMessage}`, 'error'); secureLogger.error('Failed to create credentials', error as Error, { component: 'SecureCredentialsComponent', operation: 'handleCreateCredentials' }); } } /** * Gère l'accès aux credentials */ private async handleAccessCredentials(event: Event): Promise { event.preventDefault(); const form = event.target as HTMLFormElement; const formData = new FormData(form); const password = formData.get('access-password') as string; try { this.showMessage('Récupération des credentials...', 'info'); const secureCredentialsService = SecureCredentialsService.getInstance(); const credentials = await secureCredentialsService.retrieveCredentials(password); if (credentials) { this.showMessage('Credentials récupérés avec succès !', 'success'); await this.updateCredentialsInfo(credentials); await this.updateUI(); } else { this.showMessage('Aucun credential trouvé ou mot de passe incorrect', 'error'); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Erreur inconnue'; this.showMessage(`Erreur lors de la récupération des credentials: ${errorMessage}`, 'error'); secureLogger.error('Failed to access credentials', error as Error, { component: 'SecureCredentialsComponent', operation: 'handleAccessCredentials' }); } } /** * Gère la validation du mot de passe en temps réel */ private handlePasswordInput(event: Event): void { const input = event.target as HTMLInputElement; const password = input.value; const strengthDiv = document.getElementById('password-strength'); if (strengthDiv) { strengthDiv.className = 'password-strength'; if (password.length === 0) { strengthDiv.textContent = ''; return; } // Simple password strength check const score = password.length >= 12 ? (password.length >= 16 ? 5 : 4) : (password.length >= 8 ? 3 : 2); if (score < 3) { strengthDiv.className += ' weak'; strengthDiv.textContent = 'Mot de passe faible'; } else if (score < 5) { strengthDiv.className += ' medium'; strengthDiv.textContent = 'Mot de passe moyen'; } else { strengthDiv.className += ' strong'; strengthDiv.textContent = 'Mot de passe fort'; } } } /** * Gère l'actualisation des credentials */ private async handleRefreshCredentials(): Promise { try { await this.updateUI(); this.showMessage('Credentials actualisés', 'success'); } catch (_error) { this.showMessage('Erreur lors de l\'actualisation', 'error'); } } /** * Gère la suppression des credentials */ private async handleDeleteCredentials(): Promise { if (!confirm('Êtes-vous sûr de vouloir supprimer tous les credentials ? Cette action est irréversible.')) { return; } try { // TODO: Implement credentials deletion console.log('Credentials deletion requested but not implemented'); this.showMessage('Suppression des credentials non implémentée', 'warning'); await this.updateUI(); // Émettre l'événement eventBus.emit('credentials:deleted'); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Erreur inconnue'; this.showMessage(`Erreur lors de la suppression: ${errorMessage}`, 'error'); secureLogger.error('Failed to delete credentials', error as Error, { component: 'SecureCredentialsComponent', operation: 'handleDeleteCredentials' }); } } /** * Met à jour l'interface utilisateur */ private async updateUI(): Promise { try { const secureCredentialsService = SecureCredentialsService.getInstance(); const hasCredentials = await secureCredentialsService.hasCredentials(); const createSection = document.getElementById('create-credentials-section'); const accessSection = document.getElementById('access-credentials-section'); const manageSection = document.getElementById('manage-credentials-section'); if (hasCredentials) { createSection!.style.display = 'none'; accessSection!.style.display = 'block'; manageSection!.style.display = 'block'; } else { createSection!.style.display = 'block'; accessSection!.style.display = 'none'; manageSection!.style.display = 'none'; } // Mettre à jour le statut const statusElement = document.getElementById('credentials-status'); if (statusElement) { statusElement.textContent = hasCredentials ? 'Disponibles' : 'Non disponibles'; statusElement.className = hasCredentials ? 'value success' : 'value error'; } } catch (error) { secureLogger.error('Failed to update UI', error as Error, { component: 'SecureCredentialsComponent', operation: 'updateUI' }); } } /** * Met à jour les informations des credentials */ private async updateCredentialsInfo(credentials: any): Promise { const spendKeyStatus = document.getElementById('spend-key-status'); const scanKeyStatus = document.getElementById('scan-key-status'); const timestampElement = document.getElementById('credentials-timestamp'); if (spendKeyStatus) { spendKeyStatus.textContent = credentials.spendKey ? 'Disponible' : 'Non disponible'; spendKeyStatus.className = credentials.spendKey ? 'value success' : 'value error'; } if (scanKeyStatus) { scanKeyStatus.textContent = credentials.scanKey ? 'Disponible' : 'Non disponible'; scanKeyStatus.className = credentials.scanKey ? 'value success' : 'value error'; } if (timestampElement && credentials.timestamp) { const date = new Date(credentials.timestamp); timestampElement.textContent = date.toLocaleString(); } } /** * Affiche un message à l'utilisateur */ private showMessage(message: string, type: 'success' | 'error' | 'warning' | 'info'): void { const messagesContainer = document.getElementById('credentials-messages'); if (!messagesContainer) {return;} const messageDiv = document.createElement('div'); messageDiv.className = `message ${type}`; messageDiv.textContent = message; messagesContainer.appendChild(messageDiv); // Supprimer le message après 5 secondes setTimeout(() => { if (messageDiv.parentNode) { messageDiv.parentNode.removeChild(messageDiv); } }, 5000); } /** * Gère l'événement de création de credentials */ private handleCredentialsCreated(data: any): void { // Use data variable console.log('Credentials created:', data); this.showMessage('Credentials créés avec succès !', 'success'); this.updateUI(); } /** * Gère l'événement de suppression de credentials */ private handleCredentialsDeleted(): void { this.showMessage('Credentials supprimés', 'info'); this.updateUI(); } /** * Détruit le composant */ destroy(): void { if (this.isInitialized) { // Nettoyer les écouteurs d'événements eventBus.off('credentials:created', this.handleCredentialsCreated.bind(this)); eventBus.off('credentials:deleted', this.handleCredentialsDeleted.bind(this)); this.isInitialized = false; secureLogger.info('SecureCredentialsComponent destroyed', { component: 'SecureCredentialsComponent', operation: 'destroy' }); } } } // Export du composant export default SecureCredentialsComponent;