src/Security/Voter/CourseSecurityVoter.php line 17

Open in your IDE?
  1. <?php
  2. namespace App\Security\Voter;
  3. use App\Entity\Course;
  4. use App\Repository\OAuth\ClientRepository;
  5. use App\User\Entity\Client;
  6. use App\User\Entity\User;
  7. use League\Bundle\OAuth2ServerBundle\Security\User\NullUser;
  8. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  9. use Symfony\Component\Security\Core\Authorization\Voter\Voter;
  10. /**
  11.  * Security voter to control access to Course entities based on client ownership.
  12.  * Note: This is separate from CourseVoter which handles Provider access.
  13.  */
  14. class CourseSecurityVoter extends Voter
  15. {
  16.     public const VIEW 'view';
  17.     public const EDIT 'edit';
  18.     public const DELETE 'delete';
  19.     private ClientRepository $clientRepository;
  20.     public function __construct(ClientRepository $clientRepository)
  21.     {
  22.         $this->clientRepository $clientRepository;
  23.     }
  24.     protected function supports(string $attribute$subject): bool
  25.     {
  26.         return in_array($attribute, [self::VIEWself::EDITself::DELETE], true)
  27.             && $subject instanceof Course;
  28.     }
  29.     protected function voteOnAttribute(string $attribute$subjectTokenInterface $token): bool
  30.     {
  31.         $user $token->getUser();
  32.         // Determine the client based on user type
  33.         $userClient null;
  34.         
  35.         if ($user instanceof User) {
  36.             $userClient $user->getClient();
  37.             
  38.             // ROLE_SUPER_USER can access everything
  39.             if (in_array('ROLE_SUPER_USER'$user->getRoles(), true)) {
  40.                 return true;
  41.             }
  42.         } elseif ($user instanceof NullUser) {
  43.             // OAuth2 client credentials flow - get client from token
  44.             $oauthClientId $token->getAttribute('oauth_client_id');
  45.             if ($oauthClientId) {
  46.                 $oauthClient $this->clientRepository->find($oauthClientId);
  47.                 if ($oauthClient) {
  48.                     $userClient $oauthClient->getApplicationClient();
  49.                 }
  50.             }
  51.         } else {
  52.             // Unknown user type
  53.             return false;
  54.         }
  55.         /** @var Course $course */
  56.         $course $subject;
  57.         // Check if we have a valid user client
  58.         if (!$userClient) {
  59.             return false;
  60.         }
  61.         // Get the client of the course (direct FK)
  62.         $courseClient $course->getClient();
  63.         if (!$courseClient) {
  64.             return false;
  65.         }
  66.         // Check if both belong to the same client
  67.         if ($userClient->getId() !== $courseClient->getId()) {
  68.             return false;
  69.         }
  70.         // For OAuth2 NullUser (API access), if client matches, allow VIEW access
  71.         if ($user instanceof NullUser && $attribute === self::VIEW) {
  72.             return true;
  73.         }
  74.         // For regular User, check roles
  75.         if ($user instanceof User) {
  76.             // ROLE_MANAGER and ROLE_ADMIN can access all courses of their client
  77.             if (in_array('ROLE_MANAGER'$user->getRoles(), true) || in_array('ROLE_ADMIN'$user->getRoles(), true)) {
  78.                 return true;
  79.             }
  80.             
  81.             // ROLE_SPEAKER can access courses they are assigned to as speaker
  82.             // Speakers are assigned to CourseOccurrences, not directly to Course
  83.             if (in_array('ROLE_SPEAKER'$user->getRoles(), true)) {
  84.                 // Check if speaker is assigned to any occurrence of this course
  85.                 foreach ($course->getOccurrences() as $occurrence) {
  86.                     foreach ($occurrence->getSpeakers() as $speaker) {
  87.                         if ($speaker->getUser() && $speaker->getUser()->getId() === $user->getId()) {
  88.                             return true;
  89.                         }
  90.                     }
  91.                 }
  92.             }
  93.         }
  94.         
  95.         return false;
  96.     }
  97. }