Files
Classeo/backend/tests/Unit/Administration/Infrastructure/Security/SecurityUserTest.php
Mathias STRASSER e930c505df feat: Attribution de rôles multiples par utilisateur
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.
2026-02-10 11:46:55 +01:00

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,
);
}
}