src/Security/Voter/ContentCriteriaVoter.php line 13

Open in your IDE?
  1. <?php
  2. namespace App\Security\Voter;
  3. use App\Entity\BlogPost;
  4. use App\Entity\User;
  5. use App\Service\BlogPostPreviewService;
  6. use Psr\Log\LoggerInterface;
  7. use Ramsey\Collection\Collection;
  8. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  9. use Symfony\Component\Security\Core\Authorization\Voter\Voter;
  10. class ContentCriteriaVoter extends Voter
  11. {
  12.     public function __construct(
  13.         private LoggerInterface $logger,
  14.         private BlogPostPreviewService $blogPostPreviewService,
  15.     ) {
  16.     }
  17.     protected function supports(string $attribute$subject): bool
  18.     {
  19.         return $attribute === 'VIEW_CONTENT' && is_object($subject);
  20.     }
  21.     protected function voteOnAttribute(string $attribute$subjectTokenInterface $token): bool
  22.     {
  23.         $user $token->getUser();
  24.         if (!$user instanceof User) {
  25.             return false;
  26.         }
  27.         if ($subject instanceof BlogPost && $this->blogPostPreviewService->isPreviewRequested()) {
  28.             if ($this->blogPostPreviewService->isValidPreviewRequestFor($subject)) {
  29.                 $this->logger->debug('BO preview bypass granted for content.', [
  30.                     'blog_post_id' => $subject->getId(),
  31.                     'user_id' => $user->getId(),
  32.                 ]);
  33.                 return true;
  34.             }
  35.             return false;
  36.         }
  37.         if (method_exists($subject'getRoles')) {
  38.             $contentRoles $subject->getRoles();
  39.             $contentRoleCodes = [];
  40.             foreach ($contentRoles as $role) {
  41.                 if (method_exists($role'getCode')) {
  42.                     $contentRoleCodes[] = $role->getCode();
  43.                 }
  44.             }
  45.             if (!empty($contentRoleCodes) && empty(array_intersect($contentRoleCodes$user->getRoles()))) {
  46.                 $this->logger->info('Access denied on roles', [
  47.                     'required' => $contentRoleCodes,
  48.                     'user' => $user->getRoles(),
  49.                 ]);
  50.                 return false;
  51.             }
  52.         }
  53.         $userCriteria $this->getUserCriteria($user);
  54.         $contentCriteria $this->getContentCriteria($subject);
  55.         $hasAllCriteria = [
  56.             'criteria1Items' => method_exists($subject'isHasAllCriteria1') ? $subject->isHasAllCriteria1() : false,
  57.             'criteria2Items' => method_exists($subject'isHasAllCriteria2') ? $subject->isHasAllCriteria2() : false,
  58.             'criteria3Items' => method_exists($subject'isHasAllCriteria3') ? $subject->isHasAllCriteria3() : false,
  59.             'criteria4Items' => method_exists($subject'isHasAllCriteria4') ? $subject->isHasAllCriteria4() : false,
  60.             'criteria5Items' => method_exists($subject'isHasAllCriteria5') ? $subject->isHasAllCriteria5() : false,
  61.         ];
  62.         foreach ($contentCriteria as $key => $required) {
  63.             if (isset($hasAllCriteria[$key]) && $hasAllCriteria[$key]) {
  64.                 continue;
  65.             }
  66.             if (empty($required)) {
  67.                 continue;
  68.             }
  69.             $userValue $userCriteria[$key] ?? [];
  70.             if (is_iterable($required)) {
  71.                 $requiredIds = [];
  72.                 foreach ($required as $item) {
  73.                     if (method_exists($item'getId')) {
  74.                         $requiredIds[] = $item->getId();
  75.                     } else {
  76.                         $requiredIds[] = (string) $item;
  77.                     }
  78.                 }
  79.                 if (empty($requiredIds)) {
  80.                     continue;
  81.                 }
  82.                 $userIds = [];
  83.                 foreach ($userValue as $item) {
  84.                     if (method_exists($item'getId')) {
  85.                         $userIds[] = $item->getId();
  86.                     } else {
  87.                         $userIds[] = (string) $item;
  88.                     }
  89.                 }
  90.                 if (empty(array_intersect($requiredIds$userIds))) {
  91.                     $this->logger->info("Access denied on criterion '$key'", [
  92.                         'required' => $requiredIds,
  93.                         'user' => $userIds,
  94.                     ]);
  95.                     return false;
  96.                 }
  97.             } elseif ($userValue != $required) {
  98.                 $this->logger->info("Access denied on simple criterion '$key'", [
  99.                     'required' => $required,
  100.                     'user' => $userValue,
  101.                 ]);
  102.                 return false;
  103.             }
  104.         }
  105.         return true;
  106.     }
  107.     private function getUserCriteria(User $user): array
  108.     {
  109.         return [
  110.             'instances' => method_exists($user'getInstances') ? $user->getInstances() : [],
  111.             'criteria1Items' => method_exists($user'getCriteria1Items') ? $user->getCriteria1Items() : [],
  112.             'criteria2Items' => method_exists($user'getCriteria2Items') ? $user->getCriteria2Items() : [],
  113.             'criteria3Items' => method_exists($user'getCriteria3Items') ? $user->getCriteria3Items() : [],
  114.             'criteria4Items' => method_exists($user'getCriteria4Items') ? $user->getCriteria4Items() : [],
  115.             'criteria5Items' => method_exists($user'getCriteria5Items') ? $user->getCriteria5Items() : [],
  116.         ];
  117.     }
  118.     private function getContentCriteria($content): array
  119.     {
  120.         return [
  121.             'instances' => method_exists($content'getInstances') ? $content->getInstances() : [],
  122.             'criteria1Items' => method_exists($content'getCriteria1Items') ? $content->getCriteria1Items() : [],
  123.             'criteria2Items' => method_exists($content'getCriteria2Items') ? $content->getCriteria2Items() : [],
  124.             'criteria3Items' => method_exists($content'getCriteria3Items') ? $content->getCriteria3Items() : [],
  125.             'criteria4Items' => method_exists($content'getCriteria4Items') ? $content->getCriteria4Items() : [],
  126.             'criteria5Items' => method_exists($content'getCriteria5Items') ? $content->getCriteria5Items() : [],
  127.         ];
  128.     }
  129.     private function extractValues($collection): array
  130.     {
  131.         $values = [];
  132.         if ($collection instanceof Collection || is_iterable($collection)) {
  133.             foreach ($collection as $item) {
  134.                 if (method_exists($item'getId')) {
  135.                     $values[] = $item->getId();
  136.                 } else {
  137.                     $values[] = (string) $item;
  138.                 }
  139.             }
  140.         }
  141.         return $values;
  142.     }
  143. }