src/Security/EInvoicingVoter.php line 64

Open in your IDE?
  1. <?php
  2. //------------------------------------------------------------------------------
  3. // src/Security/EInvoicingVoter.php
  4. // Plan.io Task #4634 — E-invoicing module ACL
  5. //------------------------------------------------------------------------------
  6. namespace App\Security;
  7. use Doctrine\Persistence\ManagerRegistry;
  8. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  9. use Symfony\Component\Security\Core\Authorization\Voter\Voter;
  10. use App\Entity\Access;
  11. use App\Entity\HR\AccessFunction;
  12. use App\Entity\APIRest\AccessAPI;
  13. use App\Entity\Config\Module;
  14. use App\Entity\EInvoicing\EInvoicingInboundInvoice;
  15. use App\Entity\Security\Acl;
  16. use App\Entity\Security\AclPermission;
  17. use App\Services\Config\ModuleTools;
  18. class EInvoicingVoter extends Voter
  19. {
  20.     const IS_ACTIVE   = 'einvoicing_is_active';
  21.     // Plan.io Task #4634 — inbound invoices received from a PDP
  22.     
  23.     const VIEW_INBOUND  = 'view_inbound_einvoice';
  24.     const EDIT_INBOUND  = 'edit_inbound_einvoice';
  25.     const LISTING_INBOUND = "list_inbound_einvoices";
  26.     const LISTING_INBOUND_SOCIETY = "list_inbound_einvoices_society";    
  27.     const LISTING_INBOUND_ANY = "list_inbound_einvoices_any";
  28.     const IS_GRANTED_CONSTANTS = [
  29.         self::IS_ACTIVE,
  30.         self::VIEW_INBOUND,
  31.         self::EDIT_INBOUND,
  32.         
  33.         self::LISTING_INBOUND,
  34.         self::LISTING_INBOUND_SOCIETY,        
  35.         self::LISTING_INBOUND_ANY,
  36.     ];
  37.     //--------------------------------------------------------------------------------
  38.     // acl constants
  39.     const ACL_PERM_LISTING = "einvoicing_list";
  40.     const ACL_PERM_LISTING_SOCIETY = "einvoicing_list_society";    
  41.     const ACL_PERM_LISTING_ANY = "einvoicing_list_any";
  42.     const ACL_PERM_VIEW = "einvoicing_view";
  43.     const ACL_PERM_VIEW_SOCIETY = "einvoicing_view_society";    
  44.     const ACL_PERM_EDIT = "einvoicing_edit";
  45.     const ACL_PERM_EDIT_SOCIETY = "einvoicing_edit_society";    
  46.     public function __construct(ManagerRegistry $doctrine, ModuleTools $moduleTools)
  47.     {
  48.         $this->em = $doctrine->getManager();
  49.         $this->moduleTools = $moduleTools;
  50.         $this->aclRepository = $this->em->getRepository(Acl::class);
  51.         $this->aclPermissionRepository = $this->em->getRepository(AclPermission::class);
  52.     }
  53.     public function supportsAttribute(string $attribute): bool
  54.     {
  55.         return in_array($attribute, self::IS_GRANTED_CONSTANTS, true);
  56.     }
  57.     protected function supports(string $attribute, $subject): bool
  58.     {
  59.         // if the attribute isn't one we support, return false
  60.         if (!in_array($attribute, self::IS_GRANTED_CONSTANTS))
  61.         {
  62.             return false;
  63.         }
  64.         // only vote on Invoice objects inside this voter
  65.         if ($subject !== null && !($subject instanceof EInvoicingInboundInvoice))
  66.         {
  67.             return false;
  68.         }
  69.         return true;
  70.     }
  71.     protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
  72.     {
  73.         $user = $token->getUser();
  74.         // Plan.io Task #3707
  75.         if ($user instanceof AccessAPI) 
  76.         {
  77.             if ($user->getAccess() === null) 
  78.             {
  79.                 return false;
  80.             }
  81.             $user = $user->getAccess();
  82.         }
  83.         // Plan.io Task #3707
  84.         // At this point $user is an object of Access type
  85.         // even if the $token->getUser() is AccessAPI
  86.         if (!$user instanceof Access) 
  87.         {
  88.             // the user must be logged in; if not, deny access
  89.             return false;
  90.         }
  91.         // The user must have a function; if not deny access
  92.         $function = $user->getFunction();
  93.         if ($function === null) 
  94.         {
  95.             return false;
  96.         }
  97.         $currentGroup = $user->getSocietyGroup();
  98.         if ($currentGroup === null) 
  99.         {
  100.             return false;
  101.         }
  102.         // Module activated ?
  103.         if ($this->moduleTools->isInactiveByCode($currentGroup, Module::MODULE_EINVOICING)) 
  104.         {
  105.             return false;
  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.             case self::IS_ACTIVE:
  122.                 return true;
  123.             case self::LISTING_INBOUND:
  124.                 return $this->canList($user, $function);
  125.             case self::LISTING_INBOUND_SOCIETY:
  126.                 return $this->canListSociety($user, $function);
  127.             case self::LISTING_INBOUND_ANY:
  128.                 return $this->canListAny($user, $function);
  129.             case self::VIEW_INBOUND:
  130.                 return $this->canView($subject, $user, $function);
  131.             case self::EDIT_INBOUND:
  132.                 return $this->canEdit($subject, $user, $function);
  133.         }
  134.         throw new \LogicException('This code should not be reached!');
  135.     }
  136.     // Check if the Society of the resource
  137.     // belongs to the societies of the $access
  138.     private function checkSociety(EInvoicingInboundInvoice $eInvoice, Access $access)
  139.     {
  140.         // Get all the societies of the access
  141.         $societies = $access->getSocieties();
  142.         // Get the Society of the Cost
  143.         $eInvoiceSociety = $eInvoice->getSociety();
  144.         if ($eInvoiceSociety === null)
  145.             return false;
  146.         $found = false;
  147.         foreach ($societies as $society)
  148.         {
  149.             if ($society->getId() == $eInvoiceSociety->getId())
  150.             {
  151.                 $found = true;
  152.                 break;
  153.             }
  154.         }
  155.         return $found;
  156.     }
  157.     private function canView(EInvoicingInboundInvoice $eInvoice, Access $user, AccessFunction $function)
  158.     {
  159.         // Get Acl_Permissions
  160.         $aclPerm = $this->aclPermissionRepository->findOneByName(self::ACL_PERM_VIEW);
  161.         $aclPermSociety = $this->aclPermissionRepository->findOneByName(self::ACL_PERM_VIEW_SOCIETY);        
  162.         // If all are null, exit
  163.         if ($aclPerm === null && $aclPermSociety === null)
  164.             return false;
  165.         // Get First one
  166.         if ($aclPerm !== null)
  167.         {
  168.             $acl = $this->aclRepository->findOneBy(array(
  169.                 'function'        =>    $function,
  170.                 'permission'    =>    $aclPerm
  171.             ));
  172.             if ($acl !== null)
  173.             {
  174.                 if ($acl->getValue())
  175.                 {
  176.                     // A single positive answer is enough
  177.                     return true;
  178.                 }
  179.             }
  180.         }
  181.         // If we are here it means that nothing good has been found
  182.         // Load second permission
  183.         if ($aclPermSociety !== null)
  184.         {
  185.             $acl = $this->aclRepository->findOneBy(array(
  186.                 'function'        =>    $function,
  187.                 'permission'    =>    $aclPermSociety
  188.             ));
  189.             if ($acl !== null)
  190.             {
  191.                 if ($acl->getValue())
  192.                 {
  193.                     // A single positive answer is enough
  194.                     // In this case the good answer will be provided by the checkSociety
  195.                     return $this->checkSociety($eInvoice, $user);
  196.                 }
  197.             }
  198.         }
  199.         // If we are here, all hope is lost
  200.         return false;
  201.     }
  202.     private function canEdit(EInvoicingInboundInvoice $eInvoice, Access $user, AccessFunction $function)
  203.     {
  204.         // Get Acl_Permissions
  205.         $aclPerm = $this->aclPermissionRepository->findOneByName(self::ACL_PERM_EDIT);
  206.         $aclPermSociety = $this->aclPermissionRepository->findOneByName(self::ACL_PERM_EDIT_SOCIETY);        
  207.         // If all are null, exit
  208.         if ($aclPerm === null && $aclPermSociety === null)
  209.             return false;
  210.         // Get First one
  211.         if ($aclPerm !== null)
  212.         {
  213.             $acl = $this->aclRepository->findOneBy(array(
  214.                 'function'        =>    $function,
  215.                 'permission'    =>    $aclPerm
  216.             ));
  217.             if ($acl !== null)
  218.             {
  219.                 if ($acl->getValue())
  220.                 {
  221.                     // A single positive answer is enough
  222.                     return true;
  223.                 }
  224.             }
  225.         }
  226.         // If we are here it means that nothing good has been found
  227.         // Load second permission
  228.         if ($aclPermSociety !== null)
  229.         {
  230.             $acl = $this->aclRepository->findOneBy(array(
  231.                 'function'        =>    $function,
  232.                 'permission'    =>    $aclPermSociety
  233.             ));
  234.             if ($acl !== null)
  235.             {
  236.                 if ($acl->getValue())
  237.                 {
  238.                     // A single positive answer is enough
  239.                     // In this case the good answer will be provided by the checkSociety
  240.                     return $this->checkSociety($eInvoice, $user);
  241.                 }
  242.             }
  243.         }
  244.         // If we are here, all hope is lost
  245.         return false;
  246.     }
  247.     private function canList(Access $user, AccessFunction $function)
  248.     {
  249.         // Get Acl_Permission
  250.         $aclPerm = $this->aclPermissionRepository->findOneByName(self::ACL_PERM_LISTING);
  251.         if ($aclPerm === null)        return false;
  252.         // Get Acl
  253.         $acl = $this->aclRepository->findOneBy(array(
  254.             'function'        =>    $function,
  255.             'permission'    =>    $aclPerm
  256.         ));
  257.         if ($acl === null)        return false;
  258.         // Since only one acl type can exist
  259.         // we can return the result of the acl_permission
  260.         return $acl->getValue();
  261.     }
  262.     private function canListSociety(Access $user, AccessFunction $function)
  263.     {
  264.         // Get Acl_Permission
  265.         $aclPerm = $this->aclPermissionRepository->findOneByName(self::ACL_PERM_LISTING_SOCIETY);
  266.         if ($aclPerm === null)        return false;
  267.         // Get Acl
  268.         $acl = $this->aclRepository->findOneBy(array(
  269.             'function'        =>    $function,
  270.             'permission'    =>    $aclPerm
  271.         ));
  272.         if ($acl === null)        return false;
  273.         // Since only one acl type can exist
  274.         // we can return the result of the acl_permission
  275.         // Further filtering is done in the Controller
  276.         return $acl->getValue();
  277.     }
  278.     private function canListAny(Access $user, AccessFunction $function)
  279.     {
  280.         // Two Acl_Permission may exist
  281.         $aclPerm = $this->aclPermissionRepository->findOneByName(self::ACL_PERM_LISTING);
  282.         $aclPermSociety = $this->aclPermissionRepository->findOneByName(self::ACL_PERM_LISTING_SOCIETY);        
  283.         // If all are null, exit
  284.         if ($aclPerm === null && $aclPermSociety === null)
  285.             return false;
  286.         // Get First one
  287.         if ($aclPerm !== null)
  288.         {
  289.             $acl = $this->aclRepository->findOneBy(array(
  290.                 'function'        =>    $function,
  291.                 'permission'    =>    $aclPerm
  292.             ));
  293.             if ($acl !== null)
  294.             {
  295.                 if ($acl->getValue())
  296.                 {
  297.                     // A single positive answer is enough
  298.                     return true;
  299.                 }
  300.             }
  301.         }
  302.         // If we are here it means that nothing good has been found
  303.         // Load second permission
  304.         if ($aclPermSociety !== null)
  305.         {
  306.             $acl = $this->aclRepository->findOneBy(array(
  307.                 'function'        =>    $function,
  308.                 'permission'    =>    $aclPermSociety
  309.             ));
  310.             if ($acl !== null)
  311.             {
  312.                 if ($acl->getValue())
  313.                 {
  314.                     // A single positive answer is enough
  315.                     return true;
  316.                 }
  317.             }
  318.         }
  319.         // If we are here, all hope is lost
  320.         return false;
  321.     }
  322. }