feat: Permettre la génération et l'envoi de codes d'invitation aux parents

Les administrateurs ont besoin d'un moyen simple pour inviter les parents
à rejoindre la plateforme. Cette fonctionnalité permet de générer des codes
d'invitation uniques (8 caractères alphanumériques) avec une validité de
48h, de les envoyer par email, et de les activer via une page publique
dédiée qui crée automatiquement le compte parent.

L'interface d'administration offre l'envoi unitaire et en masse, le renvoi,
le filtrage par statut, ainsi que la visualisation de l'état de chaque
invitation (en attente, activée, expirée).
This commit is contained in:
2026-02-28 00:08:56 +01:00
parent de5880e25e
commit be1b0b60a6
68 changed files with 8787 additions and 1 deletions

View File

@@ -52,6 +52,9 @@ framework:
App\Administration\Domain\Event\MotDePasseChange: async
# CompteBloqueTemporairement: sync (SendLockoutAlertHandler = immediate security alert)
# ConnexionReussie, ConnexionEchouee: sync (audit-only, no email)
# Parent invitation events → async (email sending)
App\Administration\Domain\Event\InvitationParentEnvoyee: async
App\Administration\Domain\Event\InvitationParentActivee: async
# Import élèves/enseignants → async (batch processing, peut être long)
App\Administration\Application\Command\ImportStudents\ImportStudentsCommand: async
App\Administration\Application\Command\ImportTeachers\ImportTeachersCommand: async

View File

@@ -31,3 +31,10 @@ framework:
limit: 10
interval: '1 hour'
cache_pool: cache.rate_limiter
# Limite les tentatives d'activation par IP (protection contre DoS via bcrypt)
parent_activation_by_ip:
policy: sliding_window
limit: 10
interval: '15 minutes'
cache_pool: cache.rate_limiter

View File

@@ -54,7 +54,7 @@ security:
jwt: ~
provider: super_admin_provider
api_public:
pattern: ^/api/(activation-tokens|activate|token/(refresh|logout)|password/(forgot|reset)|docs)(/|$)
pattern: ^/api/(activation-tokens|activate|token/(refresh|logout)|password/(forgot|reset)|parent-invitations/activate|docs)(/|$)
stateless: true
security: false
api:
@@ -78,6 +78,7 @@ security:
- { path: ^/api/token/logout, roles: PUBLIC_ACCESS }
- { path: ^/api/password/forgot, roles: PUBLIC_ACCESS }
- { path: ^/api/password/reset, roles: PUBLIC_ACCESS }
- { path: ^/api/parent-invitations/activate, roles: PUBLIC_ACCESS }
- { path: ^/api/import, roles: ROLE_ADMIN }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }

View File

@@ -225,6 +225,10 @@ services:
App\Administration\Domain\Repository\SavedTeacherColumnMappingRepository:
alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineSavedTeacherColumnMappingRepository
# Parent Invitation Repository (Story 3.3 - Invitation parents)
App\Administration\Domain\Repository\ParentInvitationRepository:
alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineParentInvitationRepository
# Student Guardian Repository (Story 2.7 - Liaison parents-enfants)
App\Administration\Infrastructure\Persistence\Cache\CacheStudentGuardianRepository:
arguments:
@@ -251,6 +255,11 @@ services:
$passwordResetByEmailLimiter: '@limiter.password_reset_by_email'
$passwordResetByIpLimiter: '@limiter.password_reset_by_ip'
# Parent Activation Processor with rate limiter
App\Administration\Infrastructure\Api\Processor\ActivateParentInvitationProcessor:
arguments:
$parentActivationByIpLimiter: '@limiter.parent_activation_by_ip'
# Login handlers
App\Administration\Infrastructure\Security\LoginSuccessHandler:
tags: