Les utilisateurs Classeo étaient limités à un seul rôle, alors que dans la réalité scolaire un directeur peut aussi être enseignant, ou un parent peut avoir un rôle vie scolaire. Cette limitation obligeait à créer des comptes distincts par fonction. Le modèle User supporte désormais plusieurs rôles simultanés avec basculement via le header. L'admin peut attribuer/retirer des rôles depuis l'interface de gestion, avec des garde-fous : pas d'auto- destitution, pas d'escalade de privilèges (seul SUPER_ADMIN peut attribuer SUPER_ADMIN), vérification du statut actif pour le switch de rôle, et TTL explicite sur le cache de rôle actif.
99 lines
3.5 KiB
PHP
99 lines
3.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Unit\Administration\Infrastructure\Security;
|
|
|
|
use App\Administration\Domain\Model\User\Email;
|
|
use App\Administration\Domain\Model\User\Role;
|
|
use App\Administration\Domain\Model\User\StatutCompte;
|
|
use App\Administration\Domain\Model\User\User;
|
|
use App\Administration\Domain\Model\User\UserId;
|
|
use App\Administration\Infrastructure\Security\SecurityUserFactory;
|
|
use App\Shared\Infrastructure\Tenant\TenantId;
|
|
use DateTimeImmutable;
|
|
use PHPUnit\Framework\Attributes\DataProvider;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class SecurityUserTest extends TestCase
|
|
{
|
|
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440002';
|
|
|
|
private SecurityUserFactory $factory;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->factory = new SecurityUserFactory();
|
|
}
|
|
|
|
#[Test]
|
|
public function factoryCreatesSecurityUserWithCorrectData(): void
|
|
{
|
|
$domainUser = $this->createActivatedUser(Role::PARENT);
|
|
|
|
$securityUser = $this->factory->fromDomainUser($domainUser);
|
|
|
|
self::assertSame((string) $domainUser->email, $securityUser->getUserIdentifier());
|
|
self::assertSame((string) $domainUser->id, $securityUser->userId());
|
|
self::assertSame((string) $domainUser->email, $securityUser->email());
|
|
self::assertSame($domainUser->hashedPassword, $securityUser->getPassword());
|
|
self::assertSame((string) $domainUser->tenantId, $securityUser->tenantId());
|
|
}
|
|
|
|
#[Test]
|
|
#[DataProvider('roleProvider')]
|
|
public function factoryMapsRolesToSymfonyRoles(Role $domainRole, string $expectedSymfonyRole): void
|
|
{
|
|
$domainUser = $this->createActivatedUser($domainRole);
|
|
|
|
$securityUser = $this->factory->fromDomainUser($domainUser);
|
|
|
|
self::assertContains($expectedSymfonyRole, $securityUser->getRoles());
|
|
}
|
|
|
|
/**
|
|
* @return iterable<string, array{Role, string}>
|
|
*/
|
|
public static function roleProvider(): iterable
|
|
{
|
|
yield 'Super Admin' => [Role::SUPER_ADMIN, 'ROLE_SUPER_ADMIN'];
|
|
yield 'Admin' => [Role::ADMIN, 'ROLE_ADMIN'];
|
|
yield 'Prof' => [Role::PROF, 'ROLE_PROF'];
|
|
yield 'Vie scolaire' => [Role::VIE_SCOLAIRE, 'ROLE_VIE_SCOLAIRE'];
|
|
yield 'Secrétariat' => [Role::SECRETARIAT, 'ROLE_SECRETARIAT'];
|
|
yield 'Parent' => [Role::PARENT, 'ROLE_PARENT'];
|
|
yield 'Elève' => [Role::ELEVE, 'ROLE_ELEVE'];
|
|
}
|
|
|
|
#[Test]
|
|
public function eraseCredentialsDoesNothing(): void
|
|
{
|
|
$domainUser = $this->createActivatedUser(Role::PARENT);
|
|
$securityUser = $this->factory->fromDomainUser($domainUser);
|
|
$passwordBefore = $securityUser->getPassword();
|
|
|
|
$securityUser->eraseCredentials();
|
|
|
|
// Les credentials sont immutables, donc rien ne change
|
|
self::assertSame($passwordBefore, $securityUser->getPassword());
|
|
}
|
|
|
|
private function createActivatedUser(Role $role): User
|
|
{
|
|
return User::reconstitute(
|
|
id: UserId::generate(),
|
|
email: new Email('user@example.com'),
|
|
roles: [$role],
|
|
tenantId: TenantId::fromString(self::TENANT_ID),
|
|
schoolName: 'École Test',
|
|
statut: StatutCompte::ACTIF,
|
|
dateNaissance: null,
|
|
createdAt: new DateTimeImmutable('2026-01-15 10:00:00'),
|
|
hashedPassword: '$argon2id$v=19$m=65536,t=4,p=1$salt$hash',
|
|
activatedAt: new DateTimeImmutable('2026-01-15 12:00:00'),
|
|
consentementParental: null,
|
|
);
|
|
}
|
|
}
|