feat: Implémentation authentification NIST SP 800-63B v3.0.8

- Ajout du service PasswordSecurityService conforme NIST SP 800-63B
- Vérification des mots de passe contre la base Have I Been Pwned
- Validation : minimum 8 caractères, maximum 64 caractères
- Pas d'exigences de composition obligatoires (conforme NIST)
- Intégration dans LoginController et UserController
- Génération de mots de passe sécurisés non compromis

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-15 15:31:23 +02:00
parent 206c76c7db
commit 5e255ebf5e
49 changed files with 152716 additions and 149802 deletions

View File

@@ -258,88 +258,7 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
// Afficher le contenu seulement si les données sont chargées ou en cours de mise à jour
if (isDataLoaded || isLoading) ...[
// Cartes de synthèse
isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 2,
child: _buildSummaryCard(
context,
'Passages totaux',
totalPassages.toString(),
Icons.map_outlined,
AppTheme.primaryColor,
),
),
const SizedBox(width: AppTheme.spacingM),
Expanded(
flex: 2,
child: _buildSummaryCard(
context,
'Montant collecté',
'${totalAmounts.toStringAsFixed(2)}',
Icons.euro_outlined,
AppTheme.buttonSuccessColor,
),
),
const SizedBox(width: AppTheme.spacingM),
Expanded(
flex: 3,
child: SectorDistributionCard(
key: ValueKey('sector_distribution_${isFirstLoad ? 'initial' : 'refreshed'}_$isLoading'),
height: 200,
),
),
],
)
: Column(
children: [
_buildSummaryCard(
context,
'Passages totaux',
totalPassages.toString(),
Icons.map_outlined,
AppTheme.primaryColor,
),
const SizedBox(height: AppTheme.spacingM),
_buildSummaryCard(
context,
'Montant collecté',
'${totalAmounts.toStringAsFixed(2)}',
Icons.euro_outlined,
AppTheme.buttonSuccessColor,
),
const SizedBox(height: AppTheme.spacingM),
SectorDistributionCard(
key: ValueKey('sector_distribution_${isFirstLoad ? 'initial' : 'refreshed'}_$isLoading'),
height: 200,
),
],
),
const SizedBox(height: AppTheme.spacingL),
// Graphique d'activité
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium),
boxShadow: AppTheme.cardShadow,
),
child: ActivityChart(
key: ValueKey('activity_chart_${isFirstLoad ? 'initial' : 'refreshed'}_$isLoading'),
height: 350,
showAllPassages: true, // Tous les passages, pas seulement ceux de l'utilisateur courant
title: 'Passages réalisés par jour (15 derniers jours)',
daysToShow: 15,
),
),
const SizedBox(height: AppTheme.spacingL),
// Graphiques de répartition
// LIGNE 1 : Graphiques de répartition (type de passage et mode de paiement)
isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -363,6 +282,33 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
const SizedBox(height: AppTheme.spacingL),
// LIGNE 2 : Carte de répartition par secteur (pleine largeur)
SectorDistributionCard(
key: ValueKey('sector_distribution_${isFirstLoad ? 'initial' : 'refreshed'}_$isLoading'),
title: 'Répartition sur les 31 secteurs',
height: 500, // Hauteur maximale pour afficher tous les secteurs
),
const SizedBox(height: AppTheme.spacingL),
// LIGNE 3 : Graphique d'activité
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium),
boxShadow: AppTheme.cardShadow,
),
child: ActivityChart(
key: ValueKey('activity_chart_${isFirstLoad ? 'initial' : 'refreshed'}_$isLoading'),
height: 350,
showAllPassages: true, // Tous les passages, pas seulement ceux de l'utilisateur courant
title: 'Passages réalisés par jour (15 derniers jours)',
daysToShow: 15,
),
),
const SizedBox(height: AppTheme.spacingL),
// Actions rapides - uniquement visible sur le web
if (kIsWeb) ...[
Container(
@@ -415,61 +361,6 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
]);
}
Widget _buildSummaryCard(
BuildContext context,
String label,
String value,
IconData icon,
Color color,
) {
return Container(
padding: const EdgeInsets.all(AppTheme.spacingM),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium),
boxShadow: AppTheme.cardShadow,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(AppTheme.borderRadiusSmall),
),
child: Icon(
icon,
color: color,
size: 24,
),
),
const SizedBox(width: AppTheme.spacingM),
Text(
label,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
],
),
const SizedBox(height: AppTheme.spacingM),
Text(
value,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24,
color: color,
),
),
],
),
);
}
// Construit la carte de répartition par type de passage avec liste
Widget _buildPassageTypeCard(BuildContext context) {
return PassageSummaryCard(