src/Security/IkeaVoter.php line 111

Open in your IDE?
  1. <?php
  2. //------------------------------------------------------------------------------
  3. // src/Security/IkeaVoter.php
  4. //------------------------------------------------------------------------------
  5. namespace App\Security;
  6. use Doctrine\Persistence\ManagerRegistry;
  7. use Symfony\Component\Security\Core\Authorization\Voter\Voter;
  8. use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
  9. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  10. use App\Entity\Access;
  11. use App\Entity\APIRest\AccessAPI;
  12. use App\Entity\Config\Module;
  13. use App\Entity\HR\AccessFunction;
  14. use App\Entity\Ikea\ServiceOrder;
  15. use App\Entity\Platform\Devis\Devis;
  16. use App\Entity\Platform\Invoice\Invoice;
  17. use App\Entity\Security\Acl;
  18. use App\Entity\Security\AclPermission;
  19. use App\Entity\Webapp\Document;
  20. use App\Services\Config\ModuleTools;
  21. use App\Services\Config\OptionConfigTools;
  22. use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
  23. class IkeaVoter extends Voter
  24. {
  25.     const IS_ACTIVE "ikea_service_order_is_active";
  26.     const IS_ACTIVE_IKEA_BP "ikea_bp_is_active";
  27.     const USE_IKEA_BP "use_ikea_bp";
  28.     const LISTING "list_ikea_service_orders";
  29.     const LISTING_SOCIETY "list_ikea_service_orders_society";
  30.     const LISTING_ANY "list_ikea_service_orders_any";
  31.     const VIEW "view_ikea_service_order";
  32.     const EDIT "edit_ikea_service_order";
  33.     const EDIT_SOCIETY "edit_ikea_service_order_society";
  34.     const CONVERT_TO_MISSION "convert_ikea_service_order_to_mission";
  35.     const LINK_TO_MISSION "link_ikea_service_order_to_mission";
  36.     const CHANGE_MISSION "change_ikea_service_mission";
  37.     const VIEW_ERRORS "view_ikea_service_order_errors";
  38.     // Plan.io Task #3846
  39.     const UPDATE_STATUS_FORCED "update_status_forced";
  40.     const IS_GRANTED_CONSTANTS = array(
  41.         self::IS_ACTIVE,
  42.         self::IS_ACTIVE_IKEA_BP,
  43.         self::USE_IKEA_BP,
  44.         self::LISTING,
  45.         self::LISTING_SOCIETY,
  46.         self::LISTING_ANY,
  47.         self::VIEW,
  48.         self::EDIT,
  49.         self::EDIT_SOCIETY,
  50.         self::CONVERT_TO_MISSION,
  51.         self::LINK_TO_MISSION,
  52.         self::CHANGE_MISSION,
  53.         self::VIEW_ERRORS,
  54.         self::UPDATE_STATUS_FORCED,        // Plan.io Task #3846
  55.     );
  56.     // Plan.io Task #3846
  57.     const UPDATE_STATUS_FORCED_ACCESSES = array(
  58.         'johann''alexandre''oanalivia''dylan'
  59.     );
  60.     //--------------------------------------------------------------------------------
  61.     // acl constants
  62.     const ACL_PERM_ADD 'ikea_service_order_add';
  63.     const ACL_PERM_LIST 'ikea_service_order_list';
  64.     const ACL_PERM_LIST_SOCIETY 'ikea_service_order_list_society';
  65.     const ACL_PERM_VIEW 'ikea_service_order_view';
  66.     const ACL_PERM_VIEW_SOCIETY 'ikea_service_order_view_society';
  67.     const ACL_PERM_EDIT 'ikea_service_order_edit';
  68.     const ACL_PERM_EDIT_SOCIETY 'ikea_service_order_edit_society';
  69.     const ACL_PERM_SOCIETY_EDIT 'ikea_service_order_society_edit';
  70.     const ACL_PERM_SOCIETY_EDIT_SOCIETY 'ikea_service_order_society_edit_society';
  71.     const ACL_PERM_VIEW_ERRORS 'ikea_service_order_view_errors';
  72.     // Plan.io Task #4040
  73.     const ACL_PERM_UPDATE_STATUS_FORCED 'ikea_service_order_edit_status_forced';
  74.     
  75.     // Plan.io Task #4448
  76.     const ACL_PERM_USE_IKEA_BP 'use_ikea_bp';
  77.     //--------------------------------------------------------------------------------
  78.     public function __construct(AccessDecisionManagerInterface $accessDecisionManagerManagerRegistry $doctrine,
  79.         ModuleTools $moduleToolsOptionConfigTools $optionConfigTools)
  80.     {
  81.         $this->accessDecisionManager $accessDecisionManager;
  82.         $this->em $doctrine->getManager();
  83.         $this->moduleTools $moduleTools;
  84.         $this->optionConfigTools $optionConfigTools;
  85.         $this->aclRepository $this->em->getRepository(Acl::class);
  86.         $this->aclPermissionRepository $this->em->getRepository(AclPermission::class);
  87.     }
  88.     // Plan.io Task #4453 [See AccessVoter for details]
  89.     public function supportsAttribute(string $attribute): bool
  90.     {
  91.         return in_array($attributeself::IS_GRANTED_CONSTANTStrue);
  92.     }
  93.     protected function supports(string $attribute$subject): bool
  94.     {
  95.         // if the attribute isn't one we support, return false
  96.         if (!in_array($attributeself::IS_GRANTED_CONSTANTS))
  97.         {
  98.             return false;
  99.         }
  100.         // only vote on ServiceOrder objects inside this voter
  101.         if ($subject !== null && !$subject instanceof ServiceOrder)
  102.         {
  103.             return false;
  104.         }
  105.         return true;
  106.     }
  107.     protected function voteOnAttribute(string $attribute$subjectTokenInterface $token): bool
  108.     {
  109.         $user $token->getUser();
  110.         $originalUserIsAccess true;
  111.         // Plan.io Task #3707
  112.         if ($user instanceof AccessAPI)
  113.         {
  114.             if ($user->getAccess() === null)
  115.             {
  116.                 return false;
  117.             }
  118.             $user $user->getAccess();
  119.             $originalUserIsAccess false;
  120.         }
  121.         // Plan.io Task #3707
  122.         // At this point $user is an object of Access type
  123.         // even if the $token->getUser() is AccessAPI
  124.         if (!$user instanceof Access)
  125.         {
  126.             // the user must be logged in; if not, deny access
  127.             return false;
  128.         }
  129.         // The user must have a function; if not deny access
  130.         $function $user->getFunction();
  131.         if ($function === null)        return false;
  132.         // Plan.io Task #3710 : Get current group
  133.         $currentGroup $user->getSocietyGroup();
  134.         if ($currentGroup === null)
  135.             return false;
  136.         $this->currentGroup $currentGroup;
  137.         // Module activated ?
  138.         if ($this->moduleTools->isInactiveByCode($currentGroupModule::MODULE_IKEA_SERVICE_ORDER))
  139.         {
  140.             return false;
  141.         }
  142.         // you know $subject is a ServiceOrder object, thanks to supports
  143.         /** @var ServiceOrder $serviceOrder */
  144.         $serviceOrder $subject;
  145.         // Check current group affectation
  146.         if ($subject !== null)
  147.         {
  148.             $subjectSociety $subject->getSociety();
  149.             if ($subjectSociety === null)
  150.                 return false;
  151.             $subjectGroup $subjectSociety->getSocietyGroup();
  152.             if ($subjectGroup === null)
  153.                 return false;
  154.             if (!$currentGroup->equals($subjectGroup))
  155.                 return false;
  156.         }
  157.         $this->currentGroup $currentGroup;
  158.         switch ($attribute)
  159.         {
  160.             case self::IS_ACTIVE:
  161.                 return true;
  162.             case self::IS_ACTIVE_IKEA_BP:
  163.             {                
  164.                 // TODO_4448 : Remove this before test / prod
  165.                 // if (!$this->accessDecisionManager->decide($token, ['open_sesame']))
  166.                 //     return false;
  167.                 
  168.                 return $this->optionConfigTools->isActive_IkeaBP($currentGroup);
  169.             }
  170.             case self::USE_IKEA_BP:
  171.             {                
  172.                 // TODO_4448 : Remove this before test / prod
  173.                 // if (!$this->accessDecisionManager->decide($token, ['open_sesame']))
  174.                 //     return false;
  175.                 // This also checks OptionConfig :: isActive_IkeaBP
  176.                 // So it can be used to check both if option is active and user can use option
  177.                 return $this->canUseIkeaBp($user$function);
  178.             }                
  179.             case self::VIEW:
  180.                 return $this->canView($serviceOrder$user$function);
  181.             case self::EDIT:
  182.                 return $this->canEdit($serviceOrder$user$function);
  183.             case self::EDIT_SOCIETY:
  184.                 return $this->canEditSociety($serviceOrder$user$function);
  185.             case self::LISTING:
  186.                 return $this->canList($user$function);
  187.             case self::LISTING_SOCIETY:
  188.                 return $this->canListSociety($user$function);
  189.             case self::LISTING_ANY:
  190.                 return $this->canListAny($user$function);
  191.             case self::CONVERT_TO_MISSION:
  192.                 return $this->canConvertToMission($serviceOrder$user$function$token);
  193.             case self::LINK_TO_MISSION:
  194.                 return $this->canLinkToMission($serviceOrder$user$function$token);
  195.             case self::CHANGE_MISSION:
  196.                 return $this->canChangeMission($serviceOrder$user$function$token);
  197.             case self::VIEW_ERRORS:
  198.                 return $this->canViewErrors($user$function);
  199.             // Plan.io Task #3846, #4040
  200.             case self::UPDATE_STATUS_FORCED:
  201.                 return $this->canUpdateStatusForced($user$function$token);
  202.         }
  203.         throw new \LogicException('This code should not be reached!');
  204.     }
  205.     // Check if the Society of the Demand
  206.     // belongs to the societies of the $access
  207.     private function checkSociety(ServiceOrder $serviceOrderAccess $access)
  208.     {
  209.         // Get all the societies of the access
  210.         $societies $access->getSocieties();
  211.         // Get the Society of the Demand
  212.         $serviceOrderSociety $serviceOrder->getSociety();
  213.         if ($serviceOrderSociety === null)
  214.             return false;
  215.         $found false;
  216.         foreach ($societies as $society)
  217.         {
  218.             if ($society->getId() == $serviceOrderSociety->getId())
  219.             {
  220.                 $found true;
  221.                 break;
  222.             }
  223.         }
  224.         return $found;
  225.     }
  226.     private function canView(ServiceOrder $serviceOrderAccess $userAccessFunction $function)
  227.     {
  228.         // Get Acl_Perdemands
  229.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_VIEW);
  230.         $aclPermSociety $this->aclPermissionRepository->findOneByName(self::ACL_PERM_VIEW_SOCIETY);
  231.         // If all are null, exit
  232.         if ($aclPerm === null && $aclPermSociety === null)
  233.             return false;
  234.         // Get First one
  235.         if ($aclPerm !== null)
  236.         {
  237.             $acl $this->aclRepository->findOneBy(array(
  238.                 'function'        =>    $function,
  239.                 'permission'    =>    $aclPerm
  240.             ));
  241.             if ($acl !== null)
  242.             {
  243.                 if ($acl->getValue())
  244.                 {
  245.                     // A single positive answer is enough
  246.                     return true;
  247.                 }
  248.             }
  249.         }
  250.         // If we are here it means that nothing good has been found
  251.         // Load second permission
  252.         if ($aclPermSociety !== null)
  253.         {
  254.             $acl $this->aclRepository->findOneBy(array(
  255.                 'function'        =>    $function,
  256.                 'permission'    =>    $aclPermSociety
  257.             ));
  258.             if ($acl !== null)
  259.             {
  260.                 if ($acl->getValue())
  261.                 {
  262.                     return $this->checkSociety($serviceOrder$user);
  263.                 }
  264.             }
  265.         }
  266.         // If we are here, all hope is lost
  267.         return false;
  268.     }
  269.     private function canEdit(ServiceOrder $serviceOrderAccess $userAccessFunction $function)
  270.     {
  271.         // Get Acl_Permissions
  272.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_EDIT);
  273.         $aclPermSociety $this->aclPermissionRepository->findOneByName(self::ACL_PERM_EDIT_SOCIETY);
  274.         // If all are null, exit
  275.         if ($aclPerm === null && $aclPermSociety === null)
  276.             return false;
  277.         // Get First one
  278.         if ($aclPerm !== null)
  279.         {
  280.             $acl $this->aclRepository->findOneBy(array(
  281.                 'function'        =>    $function,
  282.                 'permission'    =>    $aclPerm
  283.             ));
  284.             if ($acl !== null)
  285.             {
  286.                 if ($acl->getValue())
  287.                 {
  288.                     // A single positive answer is enough
  289.                     return true;
  290.                 }
  291.             }
  292.         }
  293.         // If we are here it means that nothing good has been found
  294.         // Load second permission
  295.         if ($aclPermSociety !== null)
  296.         {
  297.             $acl $this->aclRepository->findOneBy(array(
  298.                 'function'        =>    $function,
  299.                 'permission'    =>    $aclPermSociety
  300.             ));
  301.             if ($acl !== null)
  302.             {
  303.                 if ($acl->getValue())
  304.                 {
  305.                     return $this->checkSociety($serviceOrder$user);
  306.                 }
  307.             }
  308.         }
  309.         // If we are here, all hope is lost
  310.         return false;
  311.     }
  312.     private function canEditSociety(ServiceOrder $serviceOrderAccess $userAccessFunction $function)
  313.     {
  314.         // Get Acl_Perdemands
  315.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_SOCIETY_EDIT);
  316.         $aclPermSociety $this->aclPermissionRepository->findOneByName(self::ACL_PERM_SOCIETY_EDIT_SOCIETY);
  317.         // If all are null, exit
  318.         if ($aclPerm === null && $aclPermSociety === null)
  319.             return false;
  320.         // Get First one
  321.         if ($aclPerm !== null)
  322.         {
  323.             $acl $this->aclRepository->findOneBy(array(
  324.                 'function'        =>    $function,
  325.                 'permission'    =>    $aclPerm
  326.             ));
  327.             if ($acl !== null)
  328.             {
  329.                 if ($acl->getValue())
  330.                 {
  331.                     // A single positive answer is enough
  332.                     return true;
  333.                 }
  334.             }
  335.         }
  336.         // If we are here it means that nothing good has been found
  337.         // Load second permission
  338.         if ($aclPermSociety !== null)
  339.         {
  340.             $acl $this->aclRepository->findOneBy(array(
  341.                 'function'        =>    $function,
  342.                 'permission'    =>    $aclPermSociety
  343.             ));
  344.             if ($acl !== null)
  345.             {
  346.                 if ($acl->getValue())
  347.                 {
  348.                     return $this->checkSociety($serviceOrder$user);
  349.                 }
  350.             }
  351.         }
  352.         // If we are here, all hope is lost
  353.         return false;
  354.     }
  355.     private function canList(Access $userAccessFunction $function)
  356.     {
  357.         // If canView, then canList ;)
  358.         // Get Acl_Perdemand
  359.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_LIST);
  360.         if ($aclPerm === null)        return false;
  361.         // Get Acl
  362.         $acl $this->aclRepository->findOneBy(array(
  363.             'function'        =>    $function,
  364.             'permission'    =>    $aclPerm
  365.         ));
  366.         if ($acl === null)        return false;
  367.         // Since only one acl type can exist
  368.         // we can return the result of the acl_permission
  369.         return $acl->getValue();
  370.     }
  371.     private function canListSociety(Access $userAccessFunction $function)
  372.     {
  373.         // If canView, then canList ;)
  374.         // Get Acl_Perdemand
  375.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_LIST_SOCIETY);
  376.         if ($aclPerm === null)        return false;
  377.         // Get Acl
  378.         $acl $this->aclRepository->findOneBy(array(
  379.             'function'        =>    $function,
  380.             'permission'    =>    $aclPerm
  381.         ));
  382.         if ($acl === null)        return false;
  383.         // Since only one acl type can exist
  384.         // we can return the result of the acl_permission
  385.         return $acl->getValue();
  386.     }
  387.     private function canListAny(Access $userAccessFunction $function)
  388.     {
  389.         // Three Acl_Perdemand may exist
  390.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_LIST);
  391.         $aclPermSociety $this->aclPermissionRepository->findOneByName(self::ACL_PERM_LIST_SOCIETY);
  392.         // If all are null, exit
  393.         if ($aclPerm === null && $aclPermSociety === null)
  394.             return false;
  395.         // Get First one
  396.         if ($aclPerm !== null)
  397.         {
  398.             $acl $this->aclRepository->findOneBy(array(
  399.                 'function'        =>    $function,
  400.                 'permission'    =>    $aclPerm
  401.             ));
  402.             if ($acl !== null)
  403.             {
  404.                 if ($acl->getValue())
  405.                 {
  406.                     // A single positive answer is enough
  407.                     return true;
  408.                 }
  409.             }
  410.         }
  411.         // If we are here it means that nothing good has been found
  412.         // Load second permission
  413.         if ($aclPermSociety !== null)
  414.         {
  415.             $acl $this->aclRepository->findOneBy(array(
  416.                 'function'        =>    $function,
  417.                 'permission'    =>    $aclPermSociety
  418.             ));
  419.             if ($acl !== null)
  420.             {
  421.                 if ($acl->getValue())
  422.                 {
  423.                     // A single positive answer is enough
  424.                     return true;
  425.                 }
  426.             }
  427.         }
  428.         // If we are here, all hope is lost
  429.         return false;
  430.     }
  431.     private function canConvertToMission(ServiceOrder $serviceOrderAccess $userAccessFunction $function$token)
  432.     {
  433.         if ($serviceOrder->getClient() === null)
  434.         {
  435.             // This should not happen
  436.             return false;
  437.         }
  438.         if ($serviceOrder->getMission() !== null)
  439.         {
  440.             // Already attached to a mission
  441.             return false;
  442.         }
  443.         // if ($serviceOrder->getResultingMission() !== null)
  444.         // {
  445.         //     // Already converted to a mission
  446.         //     return false;
  447.         // }
  448.         // Request permission to add missions
  449.         $canAddMissions $this->accessDecisionManager->decide($token, ['add_mission']);
  450.         // Request permission to edit this demand
  451.         $canEditServiceOrder $this->canEdit($serviceOrder$user$function);
  452.         if ($canAddMissions && $canEditServiceOrder)
  453.         {
  454.             return true;
  455.         }
  456.         return false;
  457.     }
  458.     private function canLinkToMission(ServiceOrder $serviceOrderAccess $userAccessFunction $function$token)
  459.     {
  460.         if ($serviceOrder->getClient() === null)
  461.         {
  462.             // This should not happen
  463.             return false;
  464.         }
  465.         if ($serviceOrder->getMission() !== null)
  466.         {
  467.             // Already attached to a mission
  468.             return false;
  469.         }
  470.         // if ($serviceOrder->getResultingMission() !== null)
  471.         // {
  472.         //     // Already converted to a mission
  473.         //     return false;
  474.         // }
  475.         // Request permission to edit this demand
  476.         $canEditServiceOrder $this->canEdit($serviceOrder$user$function);
  477.         if ($canEditServiceOrder)
  478.         {
  479.             return true;
  480.         }
  481.         return false;
  482.     }
  483.     private function canChangeMission(ServiceOrder $serviceOrderAccess $userAccessFunction $function$token)
  484.     {
  485.         if ($serviceOrder->getClient() === null)
  486.         {
  487.             // This should not happen
  488.             return false;
  489.         }
  490.         if ($serviceOrder->getMission() === null)
  491.         {
  492.             // No changing if no mission at all
  493.             return false;
  494.         }
  495.         // TODO.3621
  496.         // Get only the distinct orderNumbers from the ServiceOrders attached to the mission
  497.         // // Get all service orders linked to the same mission
  498.         // // or with the potential to be linked to the same mission
  499.         // $twinServiceOrders = $this->em->getRepository(ServiceOrder::class)
  500.         //     ->getTwinsForMission($serviceOrder, $serviceOrder->getMission());
  501.         // // Add the current one
  502.         // $twinServiceOrders[] = $serviceOrder;
  503.         // foreach ($twinServiceOrders as $so)
  504.         // {
  505.         //     $object = $this->em->getRepository(Devis::class)->findOneByIkeaServiceOrder($so);
  506.         //     if ($object !== null)
  507.         //     {
  508.         //         return false;
  509.         //     }
  510.         //     $object = $this->em->getRepository(Invoice::class)->findOneByIkeaServiceOrder($so);
  511.         //     if ($object !== null)
  512.         //     {
  513.         //         return false;
  514.         //     }
  515.         //     $object = $this->em->getRepository(Document::class)->findOneByIkeaServiceOrder($so);
  516.         //     if ($object !== null)
  517.         //     {
  518.         //         return false;
  519.         //     }
  520.         // }
  521.         // Request permission to edit this demand
  522.         $canEditServiceOrder $this->canEdit($serviceOrder$user$function);
  523.         if ($canEditServiceOrder)
  524.         {
  525.             return true;
  526.         }
  527.         return false;
  528.     }
  529.     private function canViewErrors(Access $userAccessFunction $function)
  530.     {
  531.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_VIEW_ERRORS);
  532.         if ($aclPerm === null)        return false;
  533.         // Get Acl
  534.         $acl $this->aclRepository->findOneBy(array(
  535.             'function'        =>    $function,
  536.             'permission'    =>    $aclPerm
  537.         ));
  538.         if ($acl === null)        return false;
  539.         // Since only one acl type can exist
  540.         // we can return the result of the acl_permission
  541.         return $acl->getValue();
  542.     }
  543.     // Plan.io Task #3846
  544.     private function canUpdateStatusForced(Access $userAccessFunction $functionTokenInterface $token)
  545.     {
  546.         // Keep original code also
  547.         // I want the real user here, not the impersonated one
  548.         $originalUser null;
  549.         if ($token instanceof SwitchUserToken)
  550.         {
  551.             $originalUser $token->getOriginalToken()->getUser();
  552.         }
  553.         else
  554.         {
  555.             $originalUser $user;
  556.         }
  557.         if (in_array($originalUser->getUserIdentifier(), self::UPDATE_STATUS_FORCED_ACCESSES))
  558.         {
  559.             return true;
  560.         }
  561.         // Plan.io Task #4040 : Add Acl based code
  562.         // Get Acl_Permissions
  563.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_UPDATE_STATUS_FORCED);
  564.         // If all are null, exit
  565.         if ($aclPerm === null)
  566.             return false;
  567.         // Get First one
  568.         if ($aclPerm !== null)
  569.         {
  570.             $acl $this->aclRepository->findOneBy(array(
  571.                 'function'        =>    $function,
  572.                 'permission'    =>    $aclPerm
  573.             ));
  574.             if ($acl !== null)
  575.             {
  576.                 if ($acl->getValue())
  577.                 {
  578.                     // A single positive answer is enough
  579.                     return true;
  580.                 }
  581.             }
  582.         }
  583.         // If we are here, all hope is lost
  584.         return false;
  585.     }
  586.     private function canUseIkeaBp(Access $userAccessFunction $function)
  587.     {
  588.         // Check Option Active first
  589.         if (!$this->optionConfigTools->isActive_IkeaBP($this->currentGroup))
  590.         {
  591.             return false;
  592.         }
  593.         $aclPerm $this->aclPermissionRepository->findOneByName(self::USE_IKEA_BP);
  594.         if ($aclPerm === null)        return false;
  595.         // Get Acl
  596.         $acl $this->aclRepository->findOneBy(array(
  597.             'function'        =>    $function,
  598.             'permission'    =>    $aclPerm
  599.         ));
  600.         if ($acl === null)        return false;
  601.         // Since only one acl type can exist
  602.         // we can return the result of the acl_permission
  603.         return $acl->getValue();
  604.     }
  605. }