**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)
389 lines
12 KiB
TypeScript
389 lines
12 KiB
TypeScript
/**
|
|
* 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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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;
|