feat: Liaison parents-enfants avec gestion des tuteurs
Les parents doivent pouvoir suivre la scolarité de leurs enfants (notes, emploi du temps, devoirs). Cela nécessite un lien formalisé entre le compte parent et le compte élève, géré par les administrateurs. Le lien est établi soit manuellement via l'interface d'administration, soit automatiquement lors de l'activation du compte parent lorsque l'invitation inclut un élève cible. Ce lien conditionne l'accès aux données scolaires de l'enfant (autorisations vérifiées par un voter dédié).
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Administration\Infrastructure\Security;
|
||||
|
||||
use App\Administration\Domain\Model\User\Role;
|
||||
use App\Administration\Domain\Model\User\UserId;
|
||||
use App\Administration\Domain\Repository\StudentGuardianRepository;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use App\Shared\Infrastructure\Tenant\TenantContext;
|
||||
|
||||
use function in_array;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
use function is_string;
|
||||
|
||||
use Override;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Vote;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
|
||||
/**
|
||||
* Voter pour l'accès aux données d'un élève.
|
||||
*
|
||||
* Un parent ne peut voir que les données de ses enfants liés.
|
||||
* Le personnel de l'établissement a accès à tous les élèves.
|
||||
*
|
||||
* @extends Voter<string, string>
|
||||
*/
|
||||
final class StudentGuardianVoter extends Voter
|
||||
{
|
||||
public const string VIEW_STUDENT = 'STUDENT_GUARDIAN_VIEW_STUDENT';
|
||||
public const string MANAGE = 'STUDENT_GUARDIAN_MANAGE';
|
||||
|
||||
public function __construct(
|
||||
private readonly StudentGuardianRepository $repository,
|
||||
private readonly TenantContext $tenantContext,
|
||||
) {
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function supports(string $attribute, mixed $subject): bool
|
||||
{
|
||||
if ($attribute === self::VIEW_STUDENT && is_string($subject)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $attribute === self::MANAGE && $subject === null;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool
|
||||
{
|
||||
$user = $token->getUser();
|
||||
|
||||
if (!$user instanceof SecurityUser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$roles = $user->getRoles();
|
||||
|
||||
if ($attribute === self::MANAGE) {
|
||||
return $this->isStaff($roles);
|
||||
}
|
||||
|
||||
if ($this->isStaff($roles)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->isParent($roles)) {
|
||||
return $this->parentIsLinkedToStudent($user->userId(), $subject);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $roles
|
||||
*/
|
||||
private function isStaff(array $roles): bool
|
||||
{
|
||||
return $this->hasAnyRole($roles, [
|
||||
Role::SUPER_ADMIN->value,
|
||||
Role::ADMIN->value,
|
||||
Role::SECRETARIAT->value,
|
||||
Role::PROF->value,
|
||||
Role::VIE_SCOLAIRE->value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $roles
|
||||
*/
|
||||
private function isParent(array $roles): bool
|
||||
{
|
||||
return in_array(Role::PARENT->value, $roles, true);
|
||||
}
|
||||
|
||||
private function parentIsLinkedToStudent(string $guardianId, string $studentId): bool
|
||||
{
|
||||
if (!$this->tenantContext->hasTenant()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$tenantId = $this->tenantContext->getCurrentTenantId();
|
||||
|
||||
$link = $this->repository->findByStudentAndGuardian(
|
||||
UserId::fromString($studentId),
|
||||
UserId::fromString($guardianId),
|
||||
TenantId::fromString((string) $tenantId),
|
||||
);
|
||||
|
||||
return $link !== null;
|
||||
} catch (InvalidArgumentException) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $userRoles
|
||||
* @param string[] $allowedRoles
|
||||
*/
|
||||
private function hasAnyRole(array $userRoles, array $allowedRoles): bool
|
||||
{
|
||||
foreach ($userRoles as $role) {
|
||||
if (in_array($role, $allowedRoles, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user