/// Moteur de permissions ultra-performant avec cache intelligent /// Vérifications contextuelles et audit trail intégré library permission_engine; import 'dart:async'; import 'package:flutter/foundation.dart'; import '../models/user.dart'; import '../models/user_role.dart'; import '../models/permission_matrix.dart'; /// Moteur de permissions haute performance avec cache multi-niveaux /// /// Fonctionnalités : /// - Cache mémoire ultra-rapide avec TTL /// - Vérifications contextuelles avancées /// - Audit trail automatique /// - Support des permissions héritées /// - Invalidation intelligente du cache class PermissionEngine { static final PermissionEngine _instance = PermissionEngine._internal(); factory PermissionEngine() => _instance; PermissionEngine._internal(); /// Cache mémoire des permissions avec TTL static final Map _permissionCache = {}; /// Cache des permissions effectives par utilisateur static final Map _userPermissionsCache = {}; /// Durée de vie du cache (5 minutes par défaut) static const Duration _defaultCacheTTL = Duration(minutes: 5); /// Durée de vie du cache pour les super admins (plus long) static const Duration _superAdminCacheTTL = Duration(minutes: 15); /// Compteur de hits/miss du cache pour monitoring static int _cacheHits = 0; static int _cacheMisses = 0; /// Stream pour les événements d'audit static final StreamController _auditController = StreamController.broadcast(); /// Stream des événements d'audit static Stream get auditStream => _auditController.stream; /// Vérifie si un utilisateur a une permission spécifique /// /// [user] - Utilisateur à vérifier /// [permission] - Permission à vérifier /// [organizationId] - Contexte organisationnel optionnel /// [auditLog] - Activer l'audit trail (défaut: true) static Future hasPermission( User user, String permission, { String? organizationId, bool auditLog = true, }) async { final cacheKey = _generateCacheKey(user.id, permission, organizationId); // Vérification du cache final cachedResult = _getCachedPermission(cacheKey); if (cachedResult != null) { _cacheHits++; if (auditLog && !cachedResult.result) { _logAuditEvent(user, permission, false, 'CACHED_DENIED', organizationId); } return cachedResult.result; } _cacheMisses++; // Calcul de la permission final result = await _computePermission(user, permission, organizationId); // Mise en cache _cachePermission(cacheKey, result, user.primaryRole); // Audit trail if (auditLog) { _logAuditEvent( user, permission, result, result ? 'GRANTED' : 'DENIED', organizationId, ); } return result; } /// Vérifie plusieurs permissions en une seule fois static Future> hasPermissions( User user, List permissions, { String? organizationId, bool auditLog = true, }) async { final results = {}; // Traitement en parallèle pour les performances final futures = permissions.map((permission) => hasPermission(user, permission, organizationId: organizationId, auditLog: auditLog) .then((result) => MapEntry(permission, result)) ); final entries = await Future.wait(futures); for (final entry in entries) { results[entry.key] = entry.value; } return results; } /// Obtient toutes les permissions effectives d'un utilisateur static Future> getEffectivePermissions( User user, { String? organizationId, }) async { final cacheKey = '${user.id}_effective_${organizationId ?? 'global'}'; // Vérification du cache utilisateur final cachedUserPermissions = _getCachedUserPermissions(cacheKey); if (cachedUserPermissions != null) { _cacheHits++; return cachedUserPermissions.permissions; } _cacheMisses++; // Calcul des permissions effectives final permissions = user.getEffectivePermissions(organizationId: organizationId); // Mise en cache _cacheUserPermissions(cacheKey, permissions, user.primaryRole); return permissions; } /// Vérifie si un utilisateur peut effectuer une action sur un domaine static Future canPerformAction( User user, String domain, String action, { String scope = 'own', String? organizationId, }) async { final permission = '$domain.$action.$scope'; return hasPermission(user, permission, organizationId: organizationId); } /// Invalide le cache pour un utilisateur spécifique static void invalidateUserCache(String userId) { final keysToRemove = []; // Invalider le cache des permissions for (final key in _permissionCache.keys) { if (key.startsWith('${userId}_')) { keysToRemove.add(key); } } for (final key in keysToRemove) { _permissionCache.remove(key); } // Invalider le cache des permissions utilisateur final userKeysToRemove = []; for (final key in _userPermissionsCache.keys) { if (key.startsWith('${userId}_')) { userKeysToRemove.add(key); } } for (final key in userKeysToRemove) { _userPermissionsCache.remove(key); } debugPrint('Cache invalidé pour l\'utilisateur: $userId'); } /// Invalide tout le cache static void invalidateAllCache() { _permissionCache.clear(); _userPermissionsCache.clear(); debugPrint('Cache complet invalidé'); } /// Obtient les statistiques du cache static Map getCacheStats() { final totalRequests = _cacheHits + _cacheMisses; final hitRate = totalRequests > 0 ? (_cacheHits / totalRequests * 100) : 0.0; return { 'cacheHits': _cacheHits, 'cacheMisses': _cacheMisses, 'hitRate': hitRate.toStringAsFixed(2), 'permissionCacheSize': _permissionCache.length, 'userPermissionsCacheSize': _userPermissionsCache.length, }; } /// Nettoie le cache expiré static void cleanExpiredCache() { final now = DateTime.now(); // Nettoyer le cache des permissions _permissionCache.removeWhere((key, cached) => cached.expiresAt.isBefore(now)); // Nettoyer le cache des permissions utilisateur _userPermissionsCache.removeWhere((key, cached) => cached.expiresAt.isBefore(now)); debugPrint('Cache expiré nettoyé'); } // === MÉTHODES PRIVÉES === /// Calcule une permission sans cache static Future _computePermission( User user, String permission, String? organizationId, ) async { // Vérification des permissions publiques if (PermissionMatrix.isPublicPermission(permission)) { return true; } // Vérification utilisateur actif if (!user.isActive) return false; // Vérification directe de l'utilisateur if (user.hasPermission(permission, organizationId: organizationId)) { return true; } // Vérifications contextuelles avancées return _checkContextualPermissions(user, permission, organizationId); } /// Vérifications contextuelles avancées static Future _checkContextualPermissions( User user, String permission, String? organizationId, ) async { // Logique contextuelle future (intégration avec le serveur) // Pour l'instant, retourne false return false; } /// Génère une clé de cache unique static String _generateCacheKey(String userId, String permission, String? organizationId) { return '${userId}_${permission}_${organizationId ?? 'global'}'; } /// Obtient une permission depuis le cache static _CachedPermission? _getCachedPermission(String key) { final cached = _permissionCache[key]; if (cached != null && cached.expiresAt.isAfter(DateTime.now())) { return cached; } if (cached != null) { _permissionCache.remove(key); } return null; } /// Met en cache une permission static void _cachePermission(String key, bool result, UserRole userRole) { final ttl = userRole == UserRole.superAdmin ? _superAdminCacheTTL : _defaultCacheTTL; _permissionCache[key] = _CachedPermission( result: result, expiresAt: DateTime.now().add(ttl), ); } /// Obtient les permissions utilisateur depuis le cache static _CachedUserPermissions? _getCachedUserPermissions(String key) { final cached = _userPermissionsCache[key]; if (cached != null && cached.expiresAt.isAfter(DateTime.now())) { return cached; } if (cached != null) { _userPermissionsCache.remove(key); } return null; } /// Met en cache les permissions utilisateur static void _cacheUserPermissions(String key, List permissions, UserRole userRole) { final ttl = userRole == UserRole.superAdmin ? _superAdminCacheTTL : _defaultCacheTTL; _userPermissionsCache[key] = _CachedUserPermissions( permissions: permissions, expiresAt: DateTime.now().add(ttl), ); } /// Enregistre un événement d'audit static void _logAuditEvent( User user, String permission, bool granted, String reason, String? organizationId, ) { final event = PermissionAuditEvent( userId: user.id, userEmail: user.email, permission: permission, granted: granted, reason: reason, organizationId: organizationId, timestamp: DateTime.now(), ); _auditController.add(event); } } /// Classe pour les permissions mises en cache class _CachedPermission { final bool result; final DateTime expiresAt; _CachedPermission({required this.result, required this.expiresAt}); } /// Classe pour les permissions utilisateur mises en cache class _CachedUserPermissions { final List permissions; final DateTime expiresAt; _CachedUserPermissions({required this.permissions, required this.expiresAt}); } /// Événement d'audit des permissions class PermissionAuditEvent { final String userId; final String userEmail; final String permission; final bool granted; final String reason; final String? organizationId; final DateTime timestamp; PermissionAuditEvent({ required this.userId, required this.userEmail, required this.permission, required this.granted, required this.reason, this.organizationId, required this.timestamp, }); Map toJson() { return { 'userId': userId, 'userEmail': userEmail, 'permission': permission, 'granted': granted, 'reason': reason, 'organizationId': organizationId, 'timestamp': timestamp.toIso8601String(), }; } }