src/Security/PunchOutVoter.php line 60

Open in your IDE?
  1. <?php
  2. //------------------------------------------------------------------------------
  3. // src/Security/PunchOutVoter.php
  4. //------------------------------------------------------------------------------
  5. namespace App\Security;
  6. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  7. use Symfony\Component\Security\Core\Authorization\Voter\Voter;
  8. use Doctrine\Persistence\ManagerRegistry;
  9. use App\Entity\Access;
  10. use App\Entity\Config\Config;
  11. use App\Entity\Config\Module;
  12. use App\Entity\HR\AccessFunction;
  13. use App\Entity\Platform\Command\Command;
  14. use App\Entity\Security\Acl;
  15. use App\Entity\Security\AclPermission;
  16. use App\Services\Config\ModuleTools;
  17. class PunchOutVoter extends Voter
  18. {
  19.     //--------------------------------------------------------------------------------
  20.     // is_granted constants
  21.     const IS_ACTIVE "punchout_is_active";
  22.     const LIST_OWN "list_own_punchout_commands";
  23.     const APPROVE "approve_punchout_command";
  24.     const LIST_WAITING_VALIDATION "list_waiting_validation_punchout_commands";
  25.     const LIST_WAITING_VALIDATION_SOCIETY "list_waiting_validation_punchout_commands_society";
  26.     const LIST_WAITING_VALIDATION_ANY "list_waiting_validation_punchout_commands_any";
  27.     const DELETE "delete_punchout_command";
  28.     const IS_GRANTED_CONSTANTS = array(
  29.         self::IS_ACTIVE,
  30.         self::LIST_OWN,
  31.         self::APPROVE,
  32.         self::LIST_WAITING_VALIDATION,
  33.         self::LIST_WAITING_VALIDATION_SOCIETY,
  34.         self::LIST_WAITING_VALIDATION_ANY,
  35.         self::DELETE,
  36.     );
  37.     //--------------------------------------------------------------------------------
  38.     // acl constants
  39.     const ACL_PERM_DELETE_PUNCHOUT "punchout_command_delete";
  40.     const ACL_PERM_DELETE_PUNCHOUT_SOCIETY "punchout_command_delete_society";
  41.     const ACL_PERM_DELETE_PUNCHOUT_MANAGER "punchout_command_delete_manager";
  42.     const ACL_PERM_APPROVE_PUNCHOUT "punchout_command_approve";
  43.     const ACL_PERM_APPROVE_PUNCHOUT_SOCIETY "punchout_command_approve_society";
  44.     //--------------------------------------------------------------------------------
  45.     public function __construct(ManagerRegistry $doctrineModuleTools $moduleTools)
  46.     {
  47.         $this->em $doctrine->getManager();
  48.         $this->moduleTools $moduleTools;
  49.         $this->aclRepository $this->em->getRepository(Acl::class);
  50.         $this->aclPermissionRepository $this->em->getRepository(AclPermission::class);
  51.     }
  52.     // Plan.io Task #4453 [See AccessVoter for details]
  53.     public function supportsAttribute(string $attribute): bool
  54.     {
  55.         return in_array($attributeself::IS_GRANTED_CONSTANTStrue);
  56.     }
  57.     
  58.     protected function supports(string $attribute$subject null): bool
  59.     {
  60.         // if the attribute isn't one we support, return false
  61.         if (!in_array($attributeself::IS_GRANTED_CONSTANTS))
  62.         {
  63.             return false;
  64.         }
  65.         // only vote on Command objects inside this voter
  66.         if ($subject !== null && !$subject instanceof Command)
  67.         {
  68.             return false;
  69.         }
  70.         return true;
  71.     }
  72.     protected function voteOnAttribute(string $attribute$subjectTokenInterface $token): bool
  73.     {
  74.         $user $token->getUser();
  75.         if (!$user instanceof Access)
  76.         {
  77.             // the user must be logged in; if not, deny access
  78.             return false;
  79.         }
  80.         // The user must have a function; if not deny access
  81.         $function $user->getFunction();
  82.         if ($function === null)        return false;
  83.         // Plan.io Task #3710 : Get current group
  84.         $currentGroup $user->getSocietyGroup();
  85.         if ($currentGroup === null)
  86.             return false;
  87.         // Module activated ?
  88.         if ($attribute != self::LIST_OWN)
  89.         {
  90.             // Make an Exception for list own
  91.             if ($this->moduleTools->isInactiveByCode($currentGroupModule::MODULE_PUNCHOUT))
  92.             {
  93.                 return false;
  94.             }
  95.         }
  96.         // you know $subject is a Command object, thanks to supports
  97.         /** @var Command $command */
  98.         $command $subject;
  99.         // This Voter is for PunchOut Commands only
  100.         if ($command !== null)
  101.         {
  102.             if ($command->isNotPunchOut())
  103.             {
  104.                 return false;
  105.             }
  106.         }
  107.         // Check current group affectation
  108.         if ($subject !== null)
  109.         {
  110.             $subjectSociety $subject->getSociety();
  111.             if ($subjectSociety === null)
  112.                 return false;
  113.             $subjectGroup $subjectSociety->getGroup();
  114.             if ($subjectGroup === null)
  115.                 return false;
  116.             if (!$currentGroup->equals($subjectGroup))
  117.                 return false;
  118.         }
  119.         switch ($attribute)
  120.         {
  121.             // Check is done before, in the voteOnAttribute
  122.             case self::IS_ACTIVE:
  123.                 return true;
  124.             case self::LIST_OWN:
  125.                 return $this->canListOwn($user$function);
  126.             case self::LIST_WAITING_VALIDATION:
  127.                 return $this->canListWaitingValidation($user$function);
  128.             case self::LIST_WAITING_VALIDATION_SOCIETY:
  129.                 return $this->canListWaitingValidationSociety($user$function);
  130.             case self::LIST_WAITING_VALIDATION_ANY:
  131.                 return $this->canListWaitingValidationAny($user$function);
  132.             case self::APPROVE:
  133.                 return $this->canApprove($command$user$function);
  134.             case self::DELETE:
  135.                 return $this->canDelete($command$user$function);
  136.         }
  137.         throw new \LogicException('This code should not be reached!');
  138.     }
  139.     // $access is the user trying to load the resource
  140.     // $command is the resource being loaded
  141.     // Check if the Society of the resource
  142.     // belongs to the societies of the $access
  143.     private function checkSociety(Command $commandAccess $access)
  144.     {
  145.         // Get all the societies of the access
  146.         $societies $access->getSocieties();
  147.         // Get the Society of the Command
  148.         $commandSociety $command->getSociety();
  149.         if ($commandSociety === null)
  150.             return false;
  151.         $found false;
  152.         foreach ($societies as $society)
  153.         {
  154.             if ($society->getId() == $commandSociety->getId())
  155.             {
  156.                 $found true;
  157.                 break;
  158.             }
  159.         }
  160.         return $found;
  161.     }
  162.     // Check if the $access is the manager / author of the $command
  163.     private function checkManager(Command $commandAccess $access)
  164.     {
  165.         // Get manager
  166.         $manager $command->getManager();
  167.         $author $command->getAuthor();
  168.         $accessCommand $command->getAccess();
  169.         if ($manager === null && $author === null)
  170.             return false;
  171.         if ($manager !== null)
  172.             if ($manager->getId() === $access->getId())
  173.                 return true;
  174.         if ($author !== null)
  175.             if ($author->getId() === $access->getId())
  176.                 return true;
  177.         if ($accessCommand !== null)
  178.             if ($accessCommand->getId() === $access->getId())
  179.                 return true;
  180.         return false;
  181.     }
  182.     private function canListOwn(Access $userAccessFunction $function)
  183.     {
  184.         // $this->denyAccessUnlessGranted('punchout_is_active');
  185.         // Actually no ... display the list in the following cases
  186.         //        - module is active
  187.         //        - module is inactive, but the user has at least one PunchOutCommand
  188.         $userSocietyGroup $user->getSocietyGroup();
  189.         $nbPunchOutCommands intval($this->em->getRepository(Command::class)->countPunchOutForAuthor($userSocietyGroup$user));
  190.         if ($nbPunchOutCommands 0)
  191.         {
  192.             // If the user has at least one PunchOutCommand
  193.             // the list should be available
  194.             return true;
  195.         }
  196.         // If we are here it means that the user has no PunchOutCommands
  197.         // Decide access based on module activation
  198.         if ($this->moduleTools->isActiveByCode($userSocietyGroupModule::MODULE_PUNCHOUT))
  199.         {
  200.             return true;
  201.         }
  202.         // All hope is lost
  203.         return false;
  204.     }
  205.     private function canDelete(Command $commandAccess $userAccessFunction $function)
  206.     {
  207.         // Deny on all punchOut Commands that have already beed sent to the Supplier
  208.         if ($command->hasBeenSentToPunchOutSupplier())
  209.         {
  210.             return false;
  211.         }
  212.         // Allow Authors to delete their own commands
  213.         $author $command->getAuthor();
  214.         if ($user->equals($author))
  215.         {
  216.             return true;
  217.         }
  218.         // Three Acl_Permission may exist
  219.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_DELETE_PUNCHOUT);
  220.         $aclPermSociety $this->aclPermissionRepository->findOneByName(self::ACL_PERM_DELETE_PUNCHOUT_SOCIETY);
  221.         $aclPermManager $this->aclPermissionRepository->findOneByName(self::ACL_PERM_DELETE_PUNCHOUT_MANAGER);
  222.         // If all are null, exit
  223.         if ($aclPerm === null && $aclPermSociety === null && $aclPermManager === null)
  224.             return false;
  225.         // Get First one
  226.         if ($aclPerm !== null)
  227.         {
  228.             $acl $this->aclRepository->findOneBy(array(
  229.                 'function'        =>    $function,
  230.                 'permission'    =>    $aclPerm
  231.             ));
  232.             if ($acl !== null)
  233.             {
  234.                 if ($acl->getValue())
  235.                 {
  236.                     // A single positive answer is enough
  237.                     return true;
  238.                 }
  239.             }
  240.         }
  241.         // If we are here it means that nothing good has been found
  242.         // Load second permission
  243.         if ($aclPermSociety !== null)
  244.         {
  245.             $acl $this->aclRepository->findOneBy(array(
  246.                 'function'        =>    $function,
  247.                 'permission'    =>    $aclPermSociety
  248.             ));
  249.             if ($acl !== null)
  250.             {
  251.                 if ($acl->getValue())
  252.                 {
  253.                     // A single positive answer is enough
  254.                     // In this case the good answer will be provided by the checkSociety
  255.                     return $this->checkSociety($command$user);
  256.                 }
  257.             }
  258.         }
  259.         // If we are here it means that nothing good has been found
  260.         // Load third permission
  261.         if ($aclPermManager !== null)
  262.         {
  263.             $acl $this->aclRepository->findOneBy(array(
  264.                 'function'        =>    $function,
  265.                 'permission'    =>    $aclPermManager
  266.             ));
  267.             if ($acl !== null)
  268.             {
  269.                 if ($acl->getValue())
  270.                 {
  271.                     // A single positive answer is enough
  272.                     // In this case the good answer will be provided by the checkOwn
  273.                     return $this->checkManager($command$user);
  274.                 }
  275.             }
  276.         }
  277.         // If we are here, all hope is lost
  278.         return false;
  279.     }
  280.     // Plan.io task #4261
  281.     public function canApprove(Command $commandAccess $userAccessFunction $function)
  282.     {
  283.         // Authorize approve only if command is waiting validation
  284.         if (!$command->isPunchOutValidation())
  285.         {
  286.             return false;
  287.         }
  288.         // Two Acl_Permission may exist
  289.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_APPROVE_PUNCHOUT);
  290.         $aclPermSociety $this->aclPermissionRepository->findOneByName(self::ACL_PERM_APPROVE_PUNCHOUT_SOCIETY);
  291.         // If all are null, exit
  292.         if ($aclPerm === null && $aclPermSociety === null)
  293.             return false;
  294.         // Get First one
  295.         if ($aclPerm !== null)
  296.         {
  297.             $acl $this->aclRepository->findOneBy(array(
  298.                 'function'        =>    $function,
  299.                 'permission'    =>    $aclPerm
  300.             ));
  301.             if ($acl !== null)
  302.             {
  303.                 if ($acl->getValue())
  304.                 {
  305.                     // A single positive answer is enough
  306.                     return true;
  307.                 }
  308.             }
  309.         }
  310.         // If we are here it means that nothing good has been found
  311.         // Load second permission
  312.         if ($aclPermSociety !== null)
  313.         {
  314.             $acl $this->aclRepository->findOneBy(array(
  315.                 'function'        =>    $function,
  316.                 'permission'    =>    $aclPermSociety
  317.             ));
  318.             if ($acl !== null)
  319.             {
  320.                 if ($acl->getValue())
  321.                 {
  322.                     // A single positive answer is enough
  323.                     // In this case the good answer will be provided by the checkSociety
  324.                     return $this->checkSociety($command$user);
  325.                 }
  326.             }
  327.         }
  328.         // If we are here, all hope is lost
  329.         return false;
  330.     }
  331.     // Plan.io task #4261
  332.     public function canListWaitingValidationAny(Access $userAccessFunction $function)
  333.     {
  334.         $canListWaitingValidation $this->canListWaitingValidation($user$function);
  335.         if ($canListWaitingValidation)
  336.         {
  337.             // Access granted
  338.             return true;
  339.         }
  340.         $canListWaitingValidationSociety $this->canListWaitingValidationSociety($user$function);
  341.         if ($canListWaitingValidationSociety)
  342.         {
  343.             // Access granted
  344.             return true;
  345.         }
  346.         // If we are here, all hope is lost
  347.         return false;
  348.     }
  349.     // Plan.io task #4261
  350.     public function canListWaitingValidation(Access $userAccessFunction $function)
  351.     {
  352.         // Get Acl_Permission
  353.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_APPROVE_PUNCHOUT);
  354.         if ($aclPerm === null)        return false;
  355.         // Get Acl
  356.         $acl $this->aclRepository->findOneBy(array(
  357.             'function'        =>    $function,
  358.             'permission'    =>    $aclPerm
  359.         ));
  360.         if ($acl === null)        return false;
  361.         // Since only one acl type can exist
  362.         // we can return the result of the acl_permission
  363.         // Further filtering is done in the Controller
  364.         return $acl->getValue();
  365.     }
  366.     // Plan.io task #4261
  367.     public function canListWaitingValidationSociety(Access $userAccessFunction $function)
  368.     {
  369.         // Get Acl_Permission
  370.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_APPROVE_PUNCHOUT_SOCIETY);
  371.         if ($aclPerm === null)        return false;
  372.         // Get Acl
  373.         $acl $this->aclRepository->findOneBy(array(
  374.             'function'        =>    $function,
  375.             'permission'    =>    $aclPerm
  376.         ));
  377.         if ($acl === null)        return false;
  378.         // Since only one acl type can exist
  379.         // we can return the result of the acl_permission
  380.         // Further filtering is done in the Controller
  381.         return $acl->getValue();
  382.     }
  383. }