src/Security/DevisSimulationVoter.php line 58

Open in your IDE?
  1. <?php
  2. //------------------------------------------------------------------------------
  3. // src/Security/DevisSimulationVoter.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\Mission\Mission;
  14. use App\Entity\Planning\Task;
  15. use App\Entity\Platform\Devis\Devis;
  16. use App\Entity\Security\Acl;
  17. use App\Entity\Security\AclPermission;
  18. use App\Services\Config\ModuleTools;
  19. class DevisSimulationVoter extends Voter
  20. {
  21.     // For now manager = author (both)
  22.     //--------------------------------------------------------------------------------
  23.     // is_granted constants
  24.     const IS_ACTIVE "devis_simulation_is_active";
  25.     const ADD_SIMULATION "add_devis_simulation";
  26.     const CONVERT_SIMULATION "convert_devis_simulation";
  27.     const DELETE_SIMULATION "delete_devis_simulation";
  28.     // Listing / Viewing is based on adding for now
  29.     // Since simulations are not society based,
  30.     // the users adding them should also be able to view them
  31.     // Maybe later make a distinction on whether all users should see all simulations
  32.     // or some users should only see their own simulations
  33.     const LIST_SIMULATION "list_devis_simulation";
  34.     const VIEW_SIMULATION "view_devis_simulation";
  35.     const IS_GRANTED_CONSTANTS = array(
  36.         self::IS_ACTIVE,
  37.         self::ADD_SIMULATION,
  38.         self::CONVERT_SIMULATION,
  39.         self::DELETE_SIMULATION,
  40.         self::LIST_SIMULATION,
  41.         self::VIEW_SIMULATION,
  42.     );
  43.     //--------------------------------------------------------------------------------
  44.     // acl constants
  45.     const ACL_PERM_ADD_SIMULATION "devis_simulation_add";
  46.     const ACL_PERM_CONVERT_SIMULATION "devis_simulation_convert";
  47.     const ACL_PERM_DELETE_SIMULATION "devis_simulation_delete";
  48.     //--------------------------------------------------------------------------------
  49.     public function __construct(ManagerRegistry $doctrineModuleTools $moduleTools)
  50.     {
  51.         $this->em $doctrine->getManager();
  52.         $this->moduleTools $moduleTools;
  53.         $this->aclRepository $this->em->getRepository(Acl::class);
  54.         $this->aclPermissionRepository $this->em->getRepository(AclPermission::class);
  55.     }
  56.     // Plan.io Task #4453 [See AccessVoter for details]
  57.     public function supportsAttribute(string $attribute): bool
  58.     {
  59.         return in_array($attributeself::IS_GRANTED_CONSTANTStrue);
  60.     }
  61.     protected function supports(string $attribute$subject): bool
  62.     {
  63.         // if the attribute isn't one we support, return false
  64.         if (!in_array($attributeself::IS_GRANTED_CONSTANTS))
  65.         {
  66.             return false;
  67.         }
  68.         // Only vote on Devis and Mission objects inside this voter
  69.         if ($subject !== null && !($subject instanceof Devis || $subject instanceof Mission))
  70.         {
  71.             return false;
  72.         }
  73.         return true;
  74.     }
  75.     protected function voteOnAttribute(string $attribute$subjectTokenInterface $token): bool
  76.     {
  77.         $user $token->getUser();
  78.         if (!$user instanceof Access)
  79.         {
  80.             // the user must be logged in; if not, deny access
  81.             return false;
  82.         }
  83.         // The user must have a function; if not deny access
  84.         $function $user->getFunction();
  85.         if ($function === null)        return false;
  86.         // Plan.io Task #3710 : Get current group
  87.         $currentGroup $user->getSocietyGroup();
  88.         if ($currentGroup === null)
  89.             return false;
  90.         $this->currentGroup $currentGroup;
  91.         // Module activated ?
  92.         if ($this->moduleTools->isInactiveByCode($currentGroupModule::MODULE_DEVIS_SIMULATION))
  93.         {
  94.             return false;
  95.         }
  96.         $devis null;
  97.         $mission null;
  98.         if ($subject instanceof Devis)
  99.         {
  100.             /** @var Devis $devis */
  101.             $devis $subject;
  102.             // Check current group affectation
  103.             if ($subject !== null)
  104.             {
  105.                 $subjectSociety $subject->getSociety();
  106.                 if ($subjectSociety === null)
  107.                     return false;
  108.                 $subjectGroup $subjectSociety->getGroup();
  109.                 if ($subjectGroup === null)
  110.                     return false;
  111.                 if (!$currentGroup->equals($subjectGroup))
  112.                     return false;
  113.             }
  114.         }
  115.         else
  116.         {
  117.             if ($subject instanceof Mission)
  118.             {
  119.                 /** @var Mission $mission */
  120.                 $mission $subject;
  121.             }
  122.         }
  123.         switch ($attribute)
  124.         {
  125.             case self::IS_ACTIVE:
  126.                 return true;
  127.             // This is different from the Devis, because we can create a simulation outside a mission
  128.             // So the mission parameter is optional
  129.             case self::ADD_SIMULATION:
  130.                 return $this->canAddSimulation($user$function$mission);
  131.             case self::CONVERT_SIMULATION:
  132.                 return $this->canConvertSimulation($user$function);
  133.             case self::DELETE_SIMULATION:
  134.                 return $this->canDeleteSimulation($devis$user$function);
  135.             // Listing / Viewing is based on adding for now
  136.             // Since simulations are not society based,
  137.             // the users adding them should also be able to view them
  138.             // Maybe later make a distinction on whether all users should see all simulations
  139.             // or some users should only see their own simulations
  140.             case self::LIST_SIMULATION:
  141.                 return $this->canAddSimulation($user$function);
  142.             case self::VIEW_SIMULATION:
  143.                 return $this->canAddSimulation($user$function);
  144.         }
  145.         throw new \LogicException('This code should not be reached!');
  146.     }
  147.     // $access is the user trying to load the resource
  148.     // $devis is the resource being loaded
  149.     // Check if the Society of the resource
  150.     // belongs to the societies of the $access
  151.     private function checkSociety(Devis $devisAccess $access)
  152.     {
  153.         // Get all the societies of the access
  154.         $societies $access->getSocieties();
  155.         // Get the Society of the Devis
  156.         $devisSociety $devis->getSociety();
  157.         if ($devisSociety === null)
  158.             return false;
  159.         $found false;
  160.         foreach ($societies as $society)
  161.         {
  162.             if ($society->getId() == $devisSociety->getId())
  163.             {
  164.                 $found true;
  165.                 break;
  166.             }
  167.         }
  168.         return $found;
  169.     }
  170.     // Check if the $access is the manager / author of the $devis
  171.     private function checkManager(Devis $devisAccess $access)
  172.     {
  173.         // Get manager
  174.         $manager $devis->getManager();
  175.         $author $devis->getAuthor();
  176.         if ($manager === null && $author === null)
  177.             return false;
  178.         if ($manager !== null)
  179.             if ($manager->getId() === $access->getId())
  180.                 return true;
  181.         if ($author !== null)
  182.             if ($author->getId() === $access->getId())
  183.                 return true;
  184.         return false;
  185.     }
  186.     // Check if the $access is the manager of the $devis
  187.     private function checkClientManager(Devis $devisAccess $access)
  188.     {
  189.         // Get Client
  190.         $client $devis->getReceiver();
  191.         if ($client === null)
  192.             return false;
  193.         if ($client->getIndividual() !== null)
  194.         {
  195.             // Only Individuals have managers
  196.             // Get manager
  197.             $manager $devis->getReceiver()->getIndividual()->getManager();
  198.             if ($manager === null)
  199.                 return false;
  200.             if ($manager->getId() === $access->getId())
  201.                 return true;
  202.         }
  203.         return false;
  204.     }
  205.     // Check if there is at least one task
  206.     // having this access as resource and this devis
  207.     private function checkTask(Devis $devisAccess $access)
  208.     {
  209.         $taskRep $this->em->getRepository(Task::class);
  210.         $resources $access->getPlanningResources();
  211.         foreach ($resources as $r)
  212.         {
  213.             $tasks $taskRep->findForDevisAndResource($devis$r);
  214.             if (count($tasks) > 0)
  215.                 return true;
  216.         }
  217.         return false;
  218.     }
  219.     private function canAddSimulation(Access $userAccessFunction $function$mission null)
  220.     {
  221.         // If the mission is shared and the author is the current group, deny adding devis
  222.         // When a mission is shared, the author cannot edit it
  223.         if ($mission !== null)
  224.         {
  225.             // Deny actions on archivedRefused objects
  226.             if ($mission->isArchivedRefused())
  227.             {
  228.                 return false;
  229.             }
  230.             if ($mission->isShared())
  231.             {
  232.                 if ($mission->isSharedBySocietyGroup($this->currentGroup))
  233.                 {
  234.                     return false;
  235.                 }
  236.             }
  237.         }
  238.         // Get Acl_Permission
  239.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_ADD_SIMULATION);
  240.         if ($aclPerm === null)        return false;
  241.         // Get Acl
  242.         $acl $this->aclRepository->findOneBy(array(
  243.             'function'        =>    $function,
  244.             'permission'    =>    $aclPerm
  245.         ));
  246.         if ($acl === null)        return false;
  247.         // Since only one acl type can exist
  248.         // we can return the result of the acl_permission
  249.         return $acl->getValue();
  250.     }
  251.     private function canConvertSimulation(Access $userAccessFunction $function)
  252.     {
  253.         // Converting simulations to devis, requires the devis module to be activated
  254.         // Module activated ?
  255.         if ($this->moduleTools->isInactiveByCode($this->currentGroupModule::MODULE_DEVIS))
  256.         {
  257.             return false;
  258.         }
  259.         // Get Acl_Permission
  260.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_CONVERT_SIMULATION);
  261.         if ($aclPerm === null)        return false;
  262.         // Get Acl
  263.         $acl $this->aclRepository->findOneBy(array(
  264.             'function'        =>    $function,
  265.             'permission'    =>    $aclPerm
  266.         ));
  267.         if ($acl === null)        return false;
  268.         // Since only one acl type can exist
  269.         // we can return the result of the acl_permission
  270.         return $acl->getValue();
  271.     }
  272.     private function canDeleteSimulation(Devis $devis nullAccess $userAccessFunction $function)
  273.     {
  274.         // Only allow deleteing simulations without a client [Why ?!]
  275.         if ($devis->isNotSimulation())
  276.             return false;
  277.         if ($devis->getReceiver() !== null)
  278.             return false;
  279.         // Get Acl_Permissions
  280.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_DELETE_SIMULATION);
  281.         // If all are null, exit
  282.         if ($aclPerm === null)
  283.             return false;
  284.         // Get First one
  285.         if ($aclPerm !== null)
  286.         {
  287.             $acl $this->aclRepository->findOneBy(array(
  288.                 'function'        =>    $function,
  289.                 'permission'    =>    $aclPerm
  290.             ));
  291.             if ($acl !== null)
  292.             {
  293.                 if ($acl->getValue())
  294.                 {
  295.                     // A single positive answer is enough
  296.                     return true;
  297.                 }
  298.             }
  299.         }
  300.         // If we are here, all hope is lost
  301.         return false;
  302.     }
  303. }