Authentification stable - WIP

This commit is contained in:
DahoudG
2025-09-19 12:35:46 +00:00
parent 63fe107f98
commit 098894bdc1
383 changed files with 13072 additions and 93334 deletions

View File

@@ -1,143 +0,0 @@
import 'package:equatable/equatable.dart';
import 'user_info.dart';
/// États d'authentification possibles
enum AuthStatus {
unknown, // État initial
checking, // Vérification en cours
authenticated,// Utilisateur connecté
unauthenticated, // Utilisateur non connecté
expired, // Session expirée
error, // Erreur d'authentification
}
/// État d'authentification de l'application
class AuthState extends Equatable {
final AuthStatus status;
final UserInfo? user;
final String? accessToken;
final String? refreshToken;
final DateTime? expiresAt;
final String? errorMessage;
final bool isLoading;
const AuthState({
this.status = AuthStatus.unknown,
this.user,
this.accessToken,
this.refreshToken,
this.expiresAt,
this.errorMessage,
this.isLoading = false,
});
/// État initial inconnu
const AuthState.unknown() : this(status: AuthStatus.unknown);
/// État de vérification
const AuthState.checking() : this(
status: AuthStatus.checking,
isLoading: true,
);
/// État authentifié
const AuthState.authenticated({
required UserInfo user,
required String accessToken,
required String refreshToken,
required DateTime expiresAt,
}) : this(
status: AuthStatus.authenticated,
user: user,
accessToken: accessToken,
refreshToken: refreshToken,
expiresAt: expiresAt,
isLoading: false,
);
/// État non authentifié
const AuthState.unauthenticated({String? errorMessage}) : this(
status: AuthStatus.unauthenticated,
errorMessage: errorMessage,
isLoading: false,
);
/// État de session expirée
const AuthState.expired() : this(
status: AuthStatus.expired,
isLoading: false,
);
/// État d'erreur
const AuthState.error(String errorMessage) : this(
status: AuthStatus.error,
errorMessage: errorMessage,
isLoading: false,
);
/// Vérifie si l'utilisateur est connecté
bool get isAuthenticated => status == AuthStatus.authenticated;
/// Vérifie si l'authentification est en cours de vérification
bool get isChecking => status == AuthStatus.checking;
/// Vérifie si la session est valide
bool get isSessionValid {
if (!isAuthenticated || expiresAt == null) return false;
return DateTime.now().isBefore(expiresAt!);
}
/// Vérifie si la session expire bientôt
bool get isExpiringSoon {
if (!isAuthenticated || expiresAt == null) return false;
final threshold = DateTime.now().add(const Duration(minutes: 5));
return expiresAt!.isBefore(threshold);
}
/// Crée une copie avec des modifications
AuthState copyWith({
AuthStatus? status,
UserInfo? user,
String? accessToken,
String? refreshToken,
DateTime? expiresAt,
String? errorMessage,
bool? isLoading,
}) {
return AuthState(
status: status ?? this.status,
user: user ?? this.user,
accessToken: accessToken ?? this.accessToken,
refreshToken: refreshToken ?? this.refreshToken,
expiresAt: expiresAt ?? this.expiresAt,
errorMessage: errorMessage ?? this.errorMessage,
isLoading: isLoading ?? this.isLoading,
);
}
/// Crée une copie en effaçant les données sensibles
AuthState clearSensitiveData() {
return AuthState(
status: status,
user: user,
errorMessage: errorMessage,
isLoading: isLoading,
);
}
@override
List<Object?> get props => [
status,
user,
accessToken,
refreshToken,
expiresAt,
errorMessage,
isLoading,
];
@override
String toString() {
return 'AuthState(status: $status, user: ${user?.email}, isLoading: $isLoading, error: $errorMessage)';
}
}

View File

@@ -1,50 +0,0 @@
import 'package:equatable/equatable.dart';
/// Modèle de requête de connexion
class LoginRequest extends Equatable {
final String email;
final String password;
final bool rememberMe;
const LoginRequest({
required this.email,
required this.password,
this.rememberMe = false,
});
Map<String, dynamic> toJson() {
return {
'email': email,
'password': password,
'rememberMe': rememberMe,
};
}
factory LoginRequest.fromJson(Map<String, dynamic> json) {
return LoginRequest(
email: json['email'] ?? '',
password: json['password'] ?? '',
rememberMe: json['rememberMe'] ?? false,
);
}
LoginRequest copyWith({
String? email,
String? password,
bool? rememberMe,
}) {
return LoginRequest(
email: email ?? this.email,
password: password ?? this.password,
rememberMe: rememberMe ?? this.rememberMe,
);
}
@override
List<Object?> get props => [email, password, rememberMe];
@override
String toString() {
return 'LoginRequest(email: $email, rememberMe: $rememberMe)';
}
}

View File

@@ -1,96 +0,0 @@
import 'package:equatable/equatable.dart';
import 'user_info.dart';
/// Modèle de réponse de connexion
class LoginResponse extends Equatable {
final String accessToken;
final String refreshToken;
final String tokenType;
final DateTime expiresAt;
final DateTime refreshExpiresAt;
final UserInfo user;
const LoginResponse({
required this.accessToken,
required this.refreshToken,
required this.tokenType,
required this.expiresAt,
required this.refreshExpiresAt,
required this.user,
});
/// Vérifie si le token d'accès est expiré
bool get isAccessTokenExpired {
return DateTime.now().isAfter(expiresAt);
}
/// Vérifie si le refresh token est expiré
bool get isRefreshTokenExpired {
return DateTime.now().isAfter(refreshExpiresAt);
}
/// Vérifie si le token expire dans les prochaines minutes
bool isExpiringSoon({int minutes = 5}) {
final threshold = DateTime.now().add(Duration(minutes: minutes));
return expiresAt.isBefore(threshold);
}
factory LoginResponse.fromJson(Map<String, dynamic> json) {
return LoginResponse(
accessToken: json['accessToken'] ?? '',
refreshToken: json['refreshToken'] ?? '',
tokenType: json['tokenType'] ?? 'Bearer',
expiresAt: json['expiresAt'] != null
? DateTime.parse(json['expiresAt'])
: DateTime.now().add(const Duration(minutes: 15)),
refreshExpiresAt: json['refreshExpiresAt'] != null
? DateTime.parse(json['refreshExpiresAt'])
: DateTime.now().add(const Duration(days: 7)),
user: UserInfo.fromJson(json['user'] ?? {}),
);
}
Map<String, dynamic> toJson() {
return {
'accessToken': accessToken,
'refreshToken': refreshToken,
'tokenType': tokenType,
'expiresAt': expiresAt.toIso8601String(),
'refreshExpiresAt': refreshExpiresAt.toIso8601String(),
'user': user.toJson(),
};
}
LoginResponse copyWith({
String? accessToken,
String? refreshToken,
String? tokenType,
DateTime? expiresAt,
DateTime? refreshExpiresAt,
UserInfo? user,
}) {
return LoginResponse(
accessToken: accessToken ?? this.accessToken,
refreshToken: refreshToken ?? this.refreshToken,
tokenType: tokenType ?? this.tokenType,
expiresAt: expiresAt ?? this.expiresAt,
refreshExpiresAt: refreshExpiresAt ?? this.refreshExpiresAt,
user: user ?? this.user,
);
}
@override
List<Object?> get props => [
accessToken,
refreshToken,
tokenType,
expiresAt,
refreshExpiresAt,
user,
];
@override
String toString() {
return 'LoginResponse(tokenType: $tokenType, user: ${user.email}, expiresAt: $expiresAt)';
}
}

View File

@@ -1,5 +0,0 @@
// Export all auth models
export 'auth_state.dart';
export 'login_request.dart';
export 'login_response.dart';
export 'user_info.dart';

View File

@@ -0,0 +1,212 @@
/// Système de permissions granulaires ultra-sophistiqué
/// Plus de 50 permissions atomiques avec héritage intelligent
library permission_matrix;
/// Matrice de permissions atomiques pour contrôle granulaire
///
/// Chaque permission suit la convention : `domain.action.scope`
/// Exemples : `members.edit.own`, `finances.view.all`, `system.admin.global`
class PermissionMatrix {
// === PERMISSIONS SYSTÈME ===
static const String SYSTEM_ADMIN = 'system.admin.global';
static const String SYSTEM_CONFIG = 'system.config.global';
static const String SYSTEM_MONITORING = 'system.monitoring.view';
static const String SYSTEM_BACKUP = 'system.backup.manage';
static const String SYSTEM_SECURITY = 'system.security.manage';
static const String SYSTEM_AUDIT = 'system.audit.view';
static const String SYSTEM_LOGS = 'system.logs.view';
static const String SYSTEM_MAINTENANCE = 'system.maintenance.execute';
// === PERMISSIONS ORGANISATION ===
static const String ORG_CREATE = 'organization.create.global';
static const String ORG_DELETE = 'organization.delete.own';
static const String ORG_CONFIG = 'organization.config.own';
static const String ORG_BRANDING = 'organization.branding.manage';
static const String ORG_SETTINGS = 'organization.settings.manage';
static const String ORG_PERMISSIONS = 'organization.permissions.manage';
static const String ORG_WORKFLOWS = 'organization.workflows.manage';
static const String ORG_INTEGRATIONS = 'organization.integrations.manage';
// === PERMISSIONS DASHBOARD ===
static const String DASHBOARD_VIEW = 'dashboard.view.own';
static const String DASHBOARD_ADMIN = 'dashboard.admin.view';
static const String DASHBOARD_ANALYTICS = 'dashboard.analytics.view';
static const String DASHBOARD_REPORTS = 'dashboard.reports.generate';
static const String DASHBOARD_EXPORT = 'dashboard.export.data';
static const String DASHBOARD_CUSTOMIZE = 'dashboard.customize.layout';
// === PERMISSIONS MEMBRES ===
static const String MEMBERS_VIEW_ALL = 'members.view.all';
static const String MEMBERS_VIEW_OWN = 'members.view.own';
static const String MEMBERS_CREATE = 'members.create.organization';
static const String MEMBERS_EDIT_ALL = 'members.edit.all';
static const String MEMBERS_EDIT_OWN = 'members.edit.own';
static const String MEMBERS_EDIT_BASIC = 'members.edit.basic';
static const String MEMBERS_DELETE = 'members.delete.organization';
static const String MEMBERS_DELETE_ALL = 'members.delete.all';
static const String MEMBERS_APPROVE = 'members.approve.requests';
static const String MEMBERS_SUSPEND = 'members.suspend.organization';
static const String MEMBERS_EXPORT = 'members.export.data';
static const String MEMBERS_IMPORT = 'members.import.data';
static const String MEMBERS_COMMUNICATE = 'members.communicate.all';
// === PERMISSIONS FINANCES ===
static const String FINANCES_VIEW_ALL = 'finances.view.all';
static const String FINANCES_VIEW_OWN = 'finances.view.own';
static const String FINANCES_EDIT_ALL = 'finances.edit.all';
static const String FINANCES_MANAGE = 'finances.manage.organization';
static const String FINANCES_APPROVE = 'finances.approve.transactions';
static const String FINANCES_REPORTS = 'finances.reports.generate';
static const String FINANCES_BUDGET = 'finances.budget.manage';
static const String FINANCES_AUDIT = 'finances.audit.access';
// === PERMISSIONS ÉVÉNEMENTS ===
static const String EVENTS_VIEW_ALL = 'events.view.all';
static const String EVENTS_VIEW_PUBLIC = 'events.view.public';
static const String EVENTS_CREATE = 'events.create.organization';
static const String EVENTS_EDIT_ALL = 'events.edit.all';
static const String EVENTS_EDIT_OWN = 'events.edit.own';
static const String EVENTS_DELETE = 'events.delete.organization';
static const String EVENTS_PARTICIPATE = 'events.participate.public';
static const String EVENTS_MODERATE = 'events.moderate.organization';
static const String EVENTS_ANALYTICS = 'events.analytics.view';
// === PERMISSIONS SOLIDARITÉ ===
static const String SOLIDARITY_VIEW_ALL = 'solidarity.view.all';
static const String SOLIDARITY_VIEW_OWN = 'solidarity.view.own';
static const String SOLIDARITY_VIEW_PUBLIC = 'solidarity.view.public';
static const String SOLIDARITY_CREATE = 'solidarity.create.request';
static const String SOLIDARITY_EDIT_ALL = 'solidarity.edit.all';
static const String SOLIDARITY_APPROVE = 'solidarity.approve.requests';
static const String SOLIDARITY_PARTICIPATE = 'solidarity.participate.actions';
static const String SOLIDARITY_MANAGE = 'solidarity.manage.organization';
static const String SOLIDARITY_FUND = 'solidarity.fund.manage';
// === PERMISSIONS COMMUNICATION ===
static const String COMM_SEND_ALL = 'communication.send.all';
static const String COMM_SEND_MEMBERS = 'communication.send.members';
static const String COMM_MODERATE = 'communication.moderate.organization';
static const String COMM_BROADCAST = 'communication.broadcast.organization';
static const String COMM_TEMPLATES = 'communication.templates.manage';
// === PERMISSIONS RAPPORTS ===
static const String REPORTS_VIEW_ALL = 'reports.view.all';
static const String REPORTS_GENERATE = 'reports.generate.organization';
static const String REPORTS_EXPORT = 'reports.export.data';
static const String REPORTS_SCHEDULE = 'reports.schedule.automated';
// === PERMISSIONS MODÉRATION ===
static const String MODERATION_CONTENT = 'moderation.content.manage';
static const String MODERATION_USERS = 'moderation.users.manage';
static const String MODERATION_REPORTS = 'moderation.reports.handle';
/// Toutes les permissions disponibles dans le système
static const List<String> ALL_PERMISSIONS = [
// Système
SYSTEM_ADMIN, SYSTEM_CONFIG, SYSTEM_MONITORING, SYSTEM_BACKUP,
SYSTEM_SECURITY, SYSTEM_AUDIT, SYSTEM_LOGS, SYSTEM_MAINTENANCE,
// Organisation
ORG_CREATE, ORG_DELETE, ORG_CONFIG, ORG_BRANDING, ORG_SETTINGS,
ORG_PERMISSIONS, ORG_WORKFLOWS, ORG_INTEGRATIONS,
// Dashboard
DASHBOARD_VIEW, DASHBOARD_ADMIN, DASHBOARD_ANALYTICS, DASHBOARD_REPORTS,
DASHBOARD_EXPORT, DASHBOARD_CUSTOMIZE,
// Membres
MEMBERS_VIEW_ALL, MEMBERS_VIEW_OWN, MEMBERS_CREATE, MEMBERS_EDIT_ALL,
MEMBERS_EDIT_OWN, MEMBERS_DELETE, MEMBERS_APPROVE, MEMBERS_SUSPEND,
MEMBERS_EXPORT, MEMBERS_IMPORT, MEMBERS_COMMUNICATE,
// Finances
FINANCES_VIEW_ALL, FINANCES_VIEW_OWN, FINANCES_MANAGE, FINANCES_APPROVE,
FINANCES_REPORTS, FINANCES_BUDGET, FINANCES_AUDIT,
// Événements
EVENTS_VIEW_ALL, EVENTS_VIEW_PUBLIC, EVENTS_CREATE, EVENTS_EDIT_ALL,
EVENTS_EDIT_OWN, EVENTS_DELETE, EVENTS_MODERATE, EVENTS_ANALYTICS,
// Solidarité
SOLIDARITY_VIEW_ALL, SOLIDARITY_VIEW_OWN, SOLIDARITY_CREATE,
SOLIDARITY_APPROVE, SOLIDARITY_MANAGE, SOLIDARITY_FUND,
// Communication
COMM_SEND_ALL, COMM_SEND_MEMBERS, COMM_MODERATE, COMM_BROADCAST,
COMM_TEMPLATES,
// Rapports
REPORTS_VIEW_ALL, REPORTS_GENERATE, REPORTS_EXPORT, REPORTS_SCHEDULE,
// Modération
MODERATION_CONTENT, MODERATION_USERS, MODERATION_REPORTS,
];
/// Permissions publiques (accessibles sans authentification)
static const List<String> PUBLIC_PERMISSIONS = [
EVENTS_VIEW_PUBLIC,
];
/// Vérifie si une permission est publique
static bool isPublicPermission(String permission) {
return PUBLIC_PERMISSIONS.contains(permission);
}
/// Obtient le domaine d'une permission (partie avant le premier point)
static String getDomain(String permission) {
return permission.split('.').first;
}
/// Obtient l'action d'une permission (partie du milieu)
static String getAction(String permission) {
final parts = permission.split('.');
return parts.length > 1 ? parts[1] : '';
}
/// Obtient la portée d'une permission (partie après le dernier point)
static String getScope(String permission) {
return permission.split('.').last;
}
/// Vérifie si une permission implique une autre (héritage)
static bool implies(String higherPermission, String lowerPermission) {
// Exemple : 'members.edit.all' implique 'members.view.all'
final higherParts = higherPermission.split('.');
final lowerParts = lowerPermission.split('.');
if (higherParts.length != 3 || lowerParts.length != 3) return false;
// Même domaine requis
if (higherParts[0] != lowerParts[0]) return false;
// Vérification des implications d'actions
return _actionImplies(higherParts[1], lowerParts[1]) &&
_scopeImplies(higherParts[2], lowerParts[2]);
}
/// Vérifie si une action implique une autre
static bool _actionImplies(String higherAction, String lowerAction) {
const actionHierarchy = {
'admin': ['manage', 'edit', 'create', 'delete', 'view'],
'manage': ['edit', 'create', 'delete', 'view'],
'edit': ['view'],
'create': ['view'],
'delete': ['view'],
};
return actionHierarchy[higherAction]?.contains(lowerAction) ??
higherAction == lowerAction;
}
/// Vérifie si une portée implique une autre
static bool _scopeImplies(String higherScope, String lowerScope) {
const scopeHierarchy = {
'global': ['all', 'organization', 'own'],
'all': ['organization', 'own'],
'organization': ['own'],
};
return scopeHierarchy[higherScope]?.contains(lowerScope) ??
higherScope == lowerScope;
}
}

View File

@@ -0,0 +1,360 @@
/// Modèles de données utilisateur avec contexte et permissions
/// Support des relations multi-organisations et permissions contextuelles
library user_models;
import 'package:equatable/equatable.dart';
import 'user_role.dart';
import 'permission_matrix.dart';
/// Modèle utilisateur principal avec contexte multi-organisations
///
/// Supporte les utilisateurs ayant des rôles différents dans plusieurs organisations
/// avec des permissions contextuelles et des préférences personnalisées
class User extends Equatable {
/// Identifiant unique de l'utilisateur
final String id;
/// Informations personnelles
final String email;
final String firstName;
final String lastName;
final String? avatar;
final String? phone;
/// Rôle principal de l'utilisateur (le plus élevé)
final UserRole primaryRole;
/// Contextes organisationnels (rôles dans différentes organisations)
final List<UserOrganizationContext> organizationContexts;
/// Permissions supplémentaires accordées spécifiquement
final List<String> additionalPermissions;
/// Permissions révoquées spécifiquement
final List<String> revokedPermissions;
/// Préférences utilisateur
final UserPreferences preferences;
/// Métadonnées
final DateTime createdAt;
final DateTime lastLoginAt;
final bool isActive;
final bool isVerified;
/// Constructeur du modèle utilisateur
const User({
required this.id,
required this.email,
required this.firstName,
required this.lastName,
required this.primaryRole,
this.avatar,
this.phone,
this.organizationContexts = const [],
this.additionalPermissions = const [],
this.revokedPermissions = const [],
this.preferences = const UserPreferences(),
required this.createdAt,
required this.lastLoginAt,
this.isActive = true,
this.isVerified = false,
});
/// Nom complet de l'utilisateur
String get fullName => '$firstName $lastName';
/// Initiales de l'utilisateur
String get initials => '${firstName[0]}${lastName[0]}'.toUpperCase();
/// Vérifie si l'utilisateur a une permission dans le contexte actuel
bool hasPermission(String permission, {String? organizationId}) {
// Vérification des permissions révoquées
if (revokedPermissions.contains(permission)) return false;
// Vérification des permissions additionnelles
if (additionalPermissions.contains(permission)) return true;
// Vérification du rôle principal
if (primaryRole.hasPermission(permission)) return true;
// Vérification dans le contexte organisationnel spécifique
if (organizationId != null) {
final context = getOrganizationContext(organizationId);
if (context?.role.hasPermission(permission) == true) return true;
}
// Vérification dans tous les contextes organisationnels
return organizationContexts.any((context) =>
context.role.hasPermission(permission));
}
/// Obtient le contexte organisationnel pour une organisation
UserOrganizationContext? getOrganizationContext(String organizationId) {
try {
return organizationContexts.firstWhere(
(context) => context.organizationId == organizationId,
);
} catch (e) {
return null;
}
}
/// Obtient le rôle dans une organisation spécifique
UserRole getRoleInOrganization(String organizationId) {
final context = getOrganizationContext(organizationId);
return context?.role ?? primaryRole;
}
/// Vérifie si l'utilisateur est membre d'une organisation
bool isMemberOfOrganization(String organizationId) {
return organizationContexts.any(
(context) => context.organizationId == organizationId,
);
}
/// Obtient toutes les permissions effectives de l'utilisateur
List<String> getEffectivePermissions({String? organizationId}) {
final permissions = <String>{};
// Permissions du rôle principal
permissions.addAll(primaryRole.getEffectivePermissions());
// Permissions des contextes organisationnels
if (organizationId != null) {
final context = getOrganizationContext(organizationId);
if (context != null) {
permissions.addAll(context.role.getEffectivePermissions());
}
} else {
for (final context in organizationContexts) {
permissions.addAll(context.role.getEffectivePermissions());
}
}
// Permissions additionnelles
permissions.addAll(additionalPermissions);
// Retirer les permissions révoquées
permissions.removeAll(revokedPermissions);
return permissions.toList()..sort();
}
/// Crée une copie de l'utilisateur avec des modifications
User copyWith({
String? email,
String? firstName,
String? lastName,
String? avatar,
String? phone,
UserRole? primaryRole,
List<UserOrganizationContext>? organizationContexts,
List<String>? additionalPermissions,
List<String>? revokedPermissions,
UserPreferences? preferences,
DateTime? lastLoginAt,
bool? isActive,
bool? isVerified,
}) {
return User(
id: id,
email: email ?? this.email,
firstName: firstName ?? this.firstName,
lastName: lastName ?? this.lastName,
avatar: avatar ?? this.avatar,
phone: phone ?? this.phone,
primaryRole: primaryRole ?? this.primaryRole,
organizationContexts: organizationContexts ?? this.organizationContexts,
additionalPermissions: additionalPermissions ?? this.additionalPermissions,
revokedPermissions: revokedPermissions ?? this.revokedPermissions,
preferences: preferences ?? this.preferences,
createdAt: createdAt,
lastLoginAt: lastLoginAt ?? this.lastLoginAt,
isActive: isActive ?? this.isActive,
isVerified: isVerified ?? this.isVerified,
);
}
/// Conversion vers Map pour sérialisation
Map<String, dynamic> toJson() {
return {
'id': id,
'email': email,
'firstName': firstName,
'lastName': lastName,
'avatar': avatar,
'phone': phone,
'primaryRole': primaryRole.name,
'organizationContexts': organizationContexts.map((c) => c.toJson()).toList(),
'additionalPermissions': additionalPermissions,
'revokedPermissions': revokedPermissions,
'preferences': preferences.toJson(),
'createdAt': createdAt.toIso8601String(),
'lastLoginAt': lastLoginAt.toIso8601String(),
'isActive': isActive,
'isVerified': isVerified,
};
}
/// Création depuis Map pour désérialisation
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'],
email: json['email'],
firstName: json['firstName'],
lastName: json['lastName'],
avatar: json['avatar'],
phone: json['phone'],
primaryRole: UserRole.fromString(json['primaryRole']) ?? UserRole.visitor,
organizationContexts: (json['organizationContexts'] as List?)
?.map((c) => UserOrganizationContext.fromJson(c))
.toList() ?? [],
additionalPermissions: List<String>.from(json['additionalPermissions'] ?? []),
revokedPermissions: List<String>.from(json['revokedPermissions'] ?? []),
preferences: UserPreferences.fromJson(json['preferences'] ?? {}),
createdAt: DateTime.parse(json['createdAt']),
lastLoginAt: DateTime.parse(json['lastLoginAt']),
isActive: json['isActive'] ?? true,
isVerified: json['isVerified'] ?? false,
);
}
@override
List<Object?> get props => [
id, email, firstName, lastName, avatar, phone, primaryRole,
organizationContexts, additionalPermissions, revokedPermissions,
preferences, createdAt, lastLoginAt, isActive, isVerified,
];
}
/// Contexte organisationnel d'un utilisateur
///
/// Définit le rôle et les permissions spécifiques dans une organisation
class UserOrganizationContext extends Equatable {
/// Identifiant de l'organisation
final String organizationId;
/// Nom de l'organisation
final String organizationName;
/// Rôle de l'utilisateur dans cette organisation
final UserRole role;
/// Permissions spécifiques dans cette organisation
final List<String> specificPermissions;
/// Date d'adhésion à l'organisation
final DateTime joinedAt;
/// Statut dans l'organisation
final bool isActive;
/// Constructeur du contexte organisationnel
const UserOrganizationContext({
required this.organizationId,
required this.organizationName,
required this.role,
this.specificPermissions = const [],
required this.joinedAt,
this.isActive = true,
});
/// Conversion vers Map
Map<String, dynamic> toJson() {
return {
'organizationId': organizationId,
'organizationName': organizationName,
'role': role.name,
'specificPermissions': specificPermissions,
'joinedAt': joinedAt.toIso8601String(),
'isActive': isActive,
};
}
/// Création depuis Map
factory UserOrganizationContext.fromJson(Map<String, dynamic> json) {
return UserOrganizationContext(
organizationId: json['organizationId'],
organizationName: json['organizationName'],
role: UserRole.fromString(json['role']) ?? UserRole.visitor,
specificPermissions: List<String>.from(json['specificPermissions'] ?? []),
joinedAt: DateTime.parse(json['joinedAt']),
isActive: json['isActive'] ?? true,
);
}
@override
List<Object?> get props => [
organizationId, organizationName, role, specificPermissions, joinedAt, isActive,
];
}
/// Préférences utilisateur personnalisables
class UserPreferences extends Equatable {
/// Langue préférée
final String language;
/// Thème préféré
final String theme;
/// Notifications activées
final bool notificationsEnabled;
/// Notifications par email
final bool emailNotifications;
/// Notifications push
final bool pushNotifications;
/// Layout du dashboard préféré
final String dashboardLayout;
/// Timezone
final String timezone;
/// Constructeur des préférences
const UserPreferences({
this.language = 'fr',
this.theme = 'system',
this.notificationsEnabled = true,
this.emailNotifications = true,
this.pushNotifications = true,
this.dashboardLayout = 'default',
this.timezone = 'Europe/Paris',
});
/// Conversion vers Map
Map<String, dynamic> toJson() {
return {
'language': language,
'theme': theme,
'notificationsEnabled': notificationsEnabled,
'emailNotifications': emailNotifications,
'pushNotifications': pushNotifications,
'dashboardLayout': dashboardLayout,
'timezone': timezone,
};
}
/// Création depuis Map
factory UserPreferences.fromJson(Map<String, dynamic> json) {
return UserPreferences(
language: json['language'] ?? 'fr',
theme: json['theme'] ?? 'system',
notificationsEnabled: json['notificationsEnabled'] ?? true,
emailNotifications: json['emailNotifications'] ?? true,
pushNotifications: json['pushNotifications'] ?? true,
dashboardLayout: json['dashboardLayout'] ?? 'default',
timezone: json['timezone'] ?? 'Europe/Paris',
);
}
@override
List<Object?> get props => [
language, theme, notificationsEnabled, emailNotifications,
pushNotifications, dashboardLayout, timezone,
];
}

View File

@@ -1,97 +0,0 @@
import 'package:equatable/equatable.dart';
/// Modèle des informations utilisateur
class UserInfo extends Equatable {
final String id;
final String email;
final String firstName;
final String lastName;
final String role;
final List<String>? roles;
final String? profilePicture;
final bool isActive;
const UserInfo({
required this.id,
required this.email,
required this.firstName,
required this.lastName,
required this.role,
this.roles,
this.profilePicture,
required this.isActive,
});
String get fullName => '$firstName $lastName';
String get initials {
final f = firstName.isNotEmpty ? firstName[0] : '';
final l = lastName.isNotEmpty ? lastName[0] : '';
return '$f$l'.toUpperCase();
}
factory UserInfo.fromJson(Map<String, dynamic> json) {
return UserInfo(
id: json['id'] ?? '',
email: json['email'] ?? '',
firstName: json['firstName'] ?? '',
lastName: json['lastName'] ?? '',
role: json['role'] ?? 'membre',
roles: json['roles'] != null ? List<String>.from(json['roles']) : null,
profilePicture: json['profilePicture'],
isActive: json['isActive'] ?? true,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'email': email,
'firstName': firstName,
'lastName': lastName,
'role': role,
'roles': roles,
'profilePicture': profilePicture,
'isActive': isActive,
};
}
UserInfo copyWith({
String? id,
String? email,
String? firstName,
String? lastName,
String? role,
List<String>? roles,
String? profilePicture,
bool? isActive,
}) {
return UserInfo(
id: id ?? this.id,
email: email ?? this.email,
firstName: firstName ?? this.firstName,
lastName: lastName ?? this.lastName,
role: role ?? this.role,
roles: roles ?? this.roles,
profilePicture: profilePicture ?? this.profilePicture,
isActive: isActive ?? this.isActive,
);
}
@override
List<Object?> get props => [
id,
email,
firstName,
lastName,
role,
roles,
profilePicture,
isActive,
];
@override
String toString() {
return 'UserInfo(id: $id, email: $email, fullName: $fullName, role: $role, isActive: $isActive)';
}
}

View File

@@ -0,0 +1,319 @@
/// Système de rôles utilisateurs avec hiérarchie intelligente
/// 6 niveaux de rôles avec permissions héritées et contextuelles
library user_role;
import 'permission_matrix.dart';
/// Énumération des rôles utilisateurs avec hiérarchie et permissions
///
/// Chaque rôle a un niveau numérique pour faciliter les comparaisons
/// et une liste de permissions spécifiques avec héritage intelligent
enum UserRole {
/// Super Administrateur - Niveau système (100)
/// Accès complet à toutes les fonctionnalités multi-organisations
superAdmin(
level: 100,
displayName: 'Super Administrateur',
description: 'Accès complet système et multi-organisations',
color: 0xFF6C5CE7, // Violet sophistiqué
permissions: _superAdminPermissions,
),
/// Administrateur d'Organisation - Niveau organisation (80)
/// Gestion complète de son organisation uniquement
orgAdmin(
level: 80,
displayName: 'Administrateur',
description: 'Gestion complète de l\'organisation',
color: 0xFF0984E3, // Bleu corporate
permissions: _orgAdminPermissions,
),
/// Modérateur/Gestionnaire - Niveau intermédiaire (60)
/// Gestion partielle selon permissions accordées
moderator(
level: 60,
displayName: 'Modérateur',
description: 'Gestion partielle et modération',
color: 0xFFE17055, // Orange focus
permissions: _moderatorPermissions,
),
/// Membre Actif - Niveau utilisateur (40)
/// Accès aux fonctionnalités membres avec participation active
activeMember(
level: 40,
displayName: 'Membre Actif',
description: 'Participation active aux activités',
color: 0xFF00B894, // Vert communauté
permissions: _activeMemberPermissions,
),
/// Membre Simple - Niveau basique (20)
/// Accès limité aux informations personnelles
simpleMember(
level: 20,
displayName: 'Membre',
description: 'Accès aux informations de base',
color: 0xFF00CEC9, // Teal simple
permissions: _simpleMemberPermissions,
),
/// Visiteur/Invité - Niveau public (0)
/// Accès aux informations publiques uniquement
visitor(
level: 0,
displayName: 'Visiteur',
description: 'Accès aux informations publiques',
color: 0xFF6C5CE7, // Indigo accueillant
permissions: _visitorPermissions,
);
/// Constructeur du rôle avec toutes ses propriétés
const UserRole({
required this.level,
required this.displayName,
required this.description,
required this.color,
required this.permissions,
});
/// Niveau numérique du rôle (0-100)
final int level;
/// Nom d'affichage du rôle
final String displayName;
/// Description détaillée du rôle
final String description;
/// Couleur thématique du rôle (format 0xFFRRGGBB)
final int color;
/// Liste des permissions spécifiques au rôle
final List<String> permissions;
/// Vérifie si ce rôle a un niveau supérieur ou égal à un autre
bool hasLevelOrAbove(UserRole other) => level >= other.level;
/// Vérifie si ce rôle a un niveau strictement supérieur à un autre
bool hasLevelAbove(UserRole other) => level > other.level;
/// Vérifie si ce rôle possède une permission spécifique
bool hasPermission(String permission) {
// Vérification directe
if (permissions.contains(permission)) return true;
// Vérification par héritage (permissions impliquées)
return permissions.any((p) => PermissionMatrix.implies(p, permission));
}
/// Obtient toutes les permissions effectives (directes + héritées)
List<String> getEffectivePermissions() {
final effective = <String>{};
// Ajouter les permissions directes
effective.addAll(permissions);
// Ajouter les permissions impliquées
for (final permission in permissions) {
for (final allPermission in PermissionMatrix.ALL_PERMISSIONS) {
if (PermissionMatrix.implies(permission, allPermission)) {
effective.add(allPermission);
}
}
}
return effective.toList()..sort();
}
/// Vérifie si ce rôle peut effectuer une action sur un domaine
bool canPerformAction(String domain, String action, {String scope = 'own'}) {
final permission = '$domain.$action.$scope';
return hasPermission(permission);
}
/// Obtient le rôle à partir de son nom
static UserRole? fromString(String roleName) {
return UserRole.values.firstWhere(
(role) => role.name == roleName,
orElse: () => UserRole.visitor,
);
}
/// Obtient tous les rôles avec un niveau inférieur ou égal
List<UserRole> getSubordinateRoles() {
return UserRole.values.where((role) => role.level < level).toList();
}
/// Obtient tous les rôles avec un niveau supérieur ou égal
List<UserRole> getSuperiorRoles() {
return UserRole.values.where((role) => role.level >= level).toList();
}
}
// === DÉFINITIONS DES PERMISSIONS PAR RÔLE ===
/// Permissions du Super Administrateur (accès complet)
const List<String> _superAdminPermissions = [
// Toutes les permissions système
PermissionMatrix.SYSTEM_ADMIN,
PermissionMatrix.SYSTEM_CONFIG,
PermissionMatrix.SYSTEM_MONITORING,
PermissionMatrix.SYSTEM_BACKUP,
PermissionMatrix.SYSTEM_SECURITY,
PermissionMatrix.SYSTEM_AUDIT,
PermissionMatrix.SYSTEM_LOGS,
PermissionMatrix.SYSTEM_MAINTENANCE,
// Gestion globale des organisations
PermissionMatrix.ORG_CREATE,
PermissionMatrix.ORG_DELETE,
PermissionMatrix.ORG_CONFIG,
// Accès complet aux dashboards
PermissionMatrix.DASHBOARD_ADMIN,
PermissionMatrix.DASHBOARD_ANALYTICS,
PermissionMatrix.DASHBOARD_REPORTS,
PermissionMatrix.DASHBOARD_EXPORT,
// Gestion complète des membres
PermissionMatrix.MEMBERS_VIEW_ALL,
PermissionMatrix.MEMBERS_EDIT_ALL,
PermissionMatrix.MEMBERS_DELETE,
PermissionMatrix.MEMBERS_EXPORT,
PermissionMatrix.MEMBERS_IMPORT,
// Accès complet aux finances
PermissionMatrix.FINANCES_VIEW_ALL,
PermissionMatrix.FINANCES_MANAGE,
PermissionMatrix.FINANCES_AUDIT,
// Tous les rapports
PermissionMatrix.REPORTS_VIEW_ALL,
PermissionMatrix.REPORTS_GENERATE,
PermissionMatrix.REPORTS_EXPORT,
PermissionMatrix.REPORTS_SCHEDULE,
];
/// Permissions de l'Administrateur d'Organisation
const List<String> _orgAdminPermissions = [
// Configuration organisation
PermissionMatrix.ORG_CONFIG,
PermissionMatrix.ORG_BRANDING,
PermissionMatrix.ORG_SETTINGS,
PermissionMatrix.ORG_PERMISSIONS,
PermissionMatrix.ORG_WORKFLOWS,
// Dashboard organisation
PermissionMatrix.DASHBOARD_VIEW,
PermissionMatrix.DASHBOARD_ANALYTICS,
PermissionMatrix.DASHBOARD_REPORTS,
PermissionMatrix.DASHBOARD_CUSTOMIZE,
// Gestion des membres
PermissionMatrix.MEMBERS_VIEW_ALL,
PermissionMatrix.MEMBERS_CREATE,
PermissionMatrix.MEMBERS_EDIT_ALL,
PermissionMatrix.MEMBERS_APPROVE,
PermissionMatrix.MEMBERS_SUSPEND,
PermissionMatrix.MEMBERS_COMMUNICATE,
// Gestion financière
PermissionMatrix.FINANCES_VIEW_ALL,
PermissionMatrix.FINANCES_MANAGE,
PermissionMatrix.FINANCES_REPORTS,
PermissionMatrix.FINANCES_BUDGET,
// Gestion des événements
PermissionMatrix.EVENTS_VIEW_ALL,
PermissionMatrix.EVENTS_CREATE,
PermissionMatrix.EVENTS_EDIT_ALL,
PermissionMatrix.EVENTS_DELETE,
PermissionMatrix.EVENTS_ANALYTICS,
// Gestion de la solidarité
PermissionMatrix.SOLIDARITY_VIEW_ALL,
PermissionMatrix.SOLIDARITY_APPROVE,
PermissionMatrix.SOLIDARITY_MANAGE,
PermissionMatrix.SOLIDARITY_FUND,
// Communication
PermissionMatrix.COMM_SEND_ALL,
PermissionMatrix.COMM_BROADCAST,
PermissionMatrix.COMM_TEMPLATES,
// Rapports organisation
PermissionMatrix.REPORTS_GENERATE,
PermissionMatrix.REPORTS_EXPORT,
];
/// Permissions du Modérateur
const List<String> _moderatorPermissions = [
// Dashboard limité
PermissionMatrix.DASHBOARD_VIEW,
// Modération des membres
PermissionMatrix.MEMBERS_VIEW_ALL,
PermissionMatrix.MEMBERS_APPROVE,
PermissionMatrix.MODERATION_USERS,
// Modération du contenu
PermissionMatrix.MODERATION_CONTENT,
PermissionMatrix.MODERATION_REPORTS,
// Événements limités
PermissionMatrix.EVENTS_VIEW_ALL,
PermissionMatrix.EVENTS_MODERATE,
// Communication modérée
PermissionMatrix.COMM_MODERATE,
PermissionMatrix.COMM_SEND_MEMBERS,
];
/// Permissions du Membre Actif
const List<String> _activeMemberPermissions = [
// Dashboard personnel
PermissionMatrix.DASHBOARD_VIEW,
// Profil personnel
PermissionMatrix.MEMBERS_VIEW_OWN,
PermissionMatrix.MEMBERS_EDIT_OWN,
// Finances personnelles
PermissionMatrix.FINANCES_VIEW_OWN,
// Événements
PermissionMatrix.EVENTS_VIEW_ALL,
PermissionMatrix.EVENTS_CREATE,
PermissionMatrix.EVENTS_EDIT_OWN,
// Solidarité
PermissionMatrix.SOLIDARITY_VIEW_ALL,
PermissionMatrix.SOLIDARITY_CREATE,
];
/// Permissions du Membre Simple
const List<String> _simpleMemberPermissions = [
// Dashboard basique
PermissionMatrix.DASHBOARD_VIEW,
// Profil personnel uniquement
PermissionMatrix.MEMBERS_VIEW_OWN,
PermissionMatrix.MEMBERS_EDIT_OWN,
// Finances personnelles
PermissionMatrix.FINANCES_VIEW_OWN,
// Événements publics
PermissionMatrix.EVENTS_VIEW_PUBLIC,
// Solidarité consultation
PermissionMatrix.SOLIDARITY_VIEW_OWN,
];
/// Permissions du Visiteur
const List<String> _visitorPermissions = [
// Événements publics uniquement
PermissionMatrix.EVENTS_VIEW_PUBLIC,
];