<?php
declare(strict_types=1);
namespace App\Security\Core;
use App\Entity\Core\ActivitySessionInterface;
use App\Entity\User\User;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
/**
* Controls read and edit access to activity sessions (QuizSession, DictateSession, ReadingTestSession).
*
* A teacher may view or edit a session if:
* - they created the session (createdBy), OR
* - they are listed as a teacher in the session's classroom (classroom_teacher).
*
* This allows supervisors to create folders/sessions and assign them to classrooms
* without blocking the classroom's teachers from viewing results or updating sessions.
*/
class SessionAccessVoter extends Voter
{
public const string SESSION_VIEW = 'SESSION_VIEW';
public const string SESSION_EDIT = 'SESSION_EDIT';
private const array SUPPORTED_ATTRIBUTES = [self::SESSION_VIEW, self::SESSION_EDIT];
/**
*
* @param string $attribute
* @param mixed $subject
*
* @return bool
*/
protected function supports(string $attribute, mixed $subject): bool
{
return in_array($attribute, self::SUPPORTED_ATTRIBUTES, true)
&& $subject instanceof ActivitySessionInterface;
}
/**
* Perform a single access check operation on a given attribute, subject and token.
* It is safe to assume that $attribute and $subject already passed the "supports()" method check.
*
* @param string $attribute
* @param mixed $subject
* @param TokenInterface $token
* @return bool
*/
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();
if (!$user instanceof User) {
return false;
}
/** @var ActivitySessionInterface $subject */
// The session creator always has access.
if ($subject->getCreatedBy()?->getId() === $user->getId()) {
return true;
}
// Any teacher associated with the session's classroom also has access.
// Classroom::hasTeacher() checks the classroom_teacher join table by user ID.
$classroom = $subject->getClassroom();
return $classroom !== null && $classroom->hasTeacher($user);
}
}