Refactoring - Version OK
This commit is contained in:
@@ -0,0 +1,375 @@
|
||||
/// 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<String, _CachedPermission> _permissionCache = {};
|
||||
|
||||
/// Cache des permissions effectives par utilisateur
|
||||
static final Map<String, _CachedUserPermissions> _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<PermissionAuditEvent> _auditController =
|
||||
StreamController<PermissionAuditEvent>.broadcast();
|
||||
|
||||
/// Stream des événements d'audit
|
||||
static Stream<PermissionAuditEvent> 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<bool> 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<Map<String, bool>> hasPermissions(
|
||||
User user,
|
||||
List<String> permissions, {
|
||||
String? organizationId,
|
||||
bool auditLog = true,
|
||||
}) async {
|
||||
final results = <String, bool>{};
|
||||
|
||||
// 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<List<String>> 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<bool> 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 = <String>[];
|
||||
|
||||
// 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 = <String>[];
|
||||
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<String, dynamic> 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<bool> _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<bool> _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<String> 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<String> 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<String, dynamic> toJson() {
|
||||
return {
|
||||
'userId': userId,
|
||||
'userEmail': userEmail,
|
||||
'permission': permission,
|
||||
'granted': granted,
|
||||
'reason': reason,
|
||||
'organizationId': organizationId,
|
||||
'timestamp': timestamp.toIso8601String(),
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user