src/Security/MissionShareDemandVoter.php line 69

Open in your IDE?
  1. <?php
  2. //------------------------------------------------------------------------------
  3. // src/Security/MissionShareDemandVoter.php
  4. //------------------------------------------------------------------------------
  5. namespace App\Security;
  6. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  7. use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
  8. use Symfony\Component\Security\Core\Authorization\Voter\Voter;
  9. use Doctrine\Persistence\ManagerRegistry;
  10. use App\Entity\Access;
  11. use App\Entity\Config\Config;
  12. use App\Entity\Config\Module;
  13. use App\Entity\HR\AccessFunction;
  14. use App\Entity\Mission\Mission;
  15. use App\Entity\Mission\MissionShareDemand;
  16. use App\Entity\Platform\SocietyGroup\SocietyGroupFavorite;
  17. use App\Entity\Security\Acl;
  18. use App\Entity\Security\AclPermission;
  19. use App\Services\LogTools;
  20. use App\Services\Config\ModuleTools;
  21. class MissionShareDemandVoter extends Voter
  22. {
  23.     //--------------------------------------------------------------------------------
  24.     // is_granted constants
  25.     // Limit Commissions to JCAF SocietyGroup for now
  26.     const USE_MISSION_SHARE_COMMISSIONS "use_mission_share_commissions";
  27.     // The ones below apply to the demands belonging to the current society group
  28.     const ANNUL_SHARE "annul_share_mission_demand";
  29.     // The ones below apply to the demands shared with the current society group
  30.     const LISTING "list_shared_mission_demands";
  31.     const VIEW "view_shared_mission_demand";
  32.     const ACCEPT "accept_shared_mission_demand";
  33.     const REFUSE "refuse_shared_mission_demand";
  34.     const ACCEPT_OR_REFUSE "accept_or_refuse_shared_mission_demand";
  35.     const REFUSE_ACCEPTED "refuse_accepted_shared_mission_demand";
  36.     const IS_GRANTED_CONSTANTS = array(
  37.         self::USE_MISSION_SHARE_COMMISSIONS,
  38.         self::ANNUL_SHARE,
  39.         self::LISTING,
  40.         self::VIEW,
  41.         self::ACCEPT,
  42.         self::REFUSE,
  43.         self::ACCEPT_OR_REFUSE,
  44.         self::REFUSE_ACCEPTED,
  45.     );
  46.     //--------------------------------------------------------------------------------
  47.     // acl constants
  48.     // The ones below apply to the demands shared with the current society group
  49.     const ACL_PERM_LIST 'mission_shared_list';
  50.     const ACL_PERM_ACCEPT_REFUSE 'mission_shared_accept_refuse';
  51.     //--------------------------------------------------------------------------------
  52.     public function __construct(AccessDecisionManagerInterface $accessDecisionManagerManagerRegistry $doctrineModuleTools $moduleToolsLogTools $logTools)
  53.     {
  54.         $this->accessDecisionManager $accessDecisionManager;
  55.         $this->em $doctrine->getManager();
  56.         $this->moduleTools $moduleTools;
  57.         $this->logTools $logTools;
  58.         $this->aclRepository $this->em->getRepository(Acl::class);
  59.         $this->aclPermissionRepository $this->em->getRepository(AclPermission::class);
  60.     }
  61.     // Plan.io Task #4453 [See AccessVoter for details]
  62.     public function supportsAttribute(string $attribute): bool
  63.     {
  64.         return in_array($attributeself::IS_GRANTED_CONSTANTStrue);
  65.     }
  66.     
  67.     protected function supports(string $attribute$subject): bool
  68.     {
  69.         // if the attribute isn't one we support, return false
  70.         if (!in_array($attributeself::IS_GRANTED_CONSTANTS))
  71.         {
  72.             return false;
  73.         }
  74.         // only vote on MissionShareDemand objects inside this voter
  75.         if ($subject !== null && !$subject instanceof MissionShareDemand)
  76.         {
  77.             return false;
  78.         }
  79.         return true;
  80.     }
  81.     protected function voteOnAttribute(string $attribute$subjectTokenInterface $token): bool
  82.     {
  83.         $user $token->getUser();
  84.         if (!$user instanceof Access)
  85.         {
  86.             // the user must be logged in; if not, deny access
  87.             return false;
  88.         }
  89.         // The user must have a function; if not deny access
  90.         $function $user->getFunction();
  91.         if ($function === null)        return false;
  92.         // Plan.io Task #3710 : Get current group
  93.         $currentGroup $user->getSocietyGroup();
  94.         if ($currentGroup === null)
  95.             return false;
  96.         $this->currentGroup $currentGroup;
  97.         // This one also needs clients or clients light
  98.         if (
  99.             $this->moduleTools->isInactiveByCode($currentGroupModule::MODULE_CLIENT) &&
  100.             $this->moduleTools->isInactiveByCode($currentGroupModule::MODULE_CLIENT_LIGHT)
  101.             )
  102.         {
  103.             return false;
  104.         }
  105.         // you know $subject is a MissionShareDemand object, thanks to supports
  106.         /** @var MissionShareDemand $missionShareDemand */
  107.         $missionShareDemand $subject;
  108.         switch ($attribute)
  109.         {
  110.             case self::USE_MISSION_SHARE_COMMISSIONS:
  111.                 return $this->canUseMissionShareCommissions();
  112.             case self::ANNUL_SHARE:
  113.                 return $this->canAnnulShare($missionShareDemand$user$function$token);
  114.             case self::LISTING:
  115.                 return $this->canList($user$function);
  116.             case self::VIEW:
  117.                 return $this->canView($missionShareDemand$user$function);
  118.             case self::ACCEPT:
  119.                 return $this->canAcceptOrRefuse($missionShareDemand$user$function);
  120.             case self::REFUSE:
  121.                 return $this->canAcceptOrRefuse($missionShareDemand$user$function);
  122.             case self::REFUSE_ACCEPTED:
  123.             {
  124.                 return $this->canRefuseAccepted($missionShareDemand$user$function);
  125.             }
  126.             case self::ACCEPT_OR_REFUSE:
  127.                 return $this->canAcceptOrRefuse($missionShareDemand$user$function);
  128.         }
  129.         throw new \LogicException('This code should not be reached!');
  130.     }
  131.     private function canUseMissionShareCommissions()
  132.     {
  133.         if ($this->currentGroup->isJcaf())
  134.         {
  135.             return true;
  136.         }
  137.         return false;
  138.     }
  139.     private function canAnnulShare(MissionShareDemand $missionShareDemandAccess $userAccessFunction $function$token)
  140.     {
  141.         // This requires the sharing module to be activated
  142.         if ($this->moduleTools->isInactiveByCode($this->currentGroupModule::MODULE_MISSION_PLUS))
  143.         {
  144.             return false;
  145.         }
  146.         // Check current group affectation
  147.         // A society group can only annul the demands it has created
  148.         $missionShareDemandGroup $missionShareDemand->getEmitter();
  149.         if ($missionShareDemandGroup === null)
  150.             return false;
  151.         if (!$this->currentGroup->equals($missionShareDemandGroup))
  152.             return false;
  153.         // Cannot annul a demand that is accepted / expired / refused / annulled
  154.         // Always check expiration first
  155.         if ($missionShareDemand->isExpired())
  156.         {
  157.             return false;
  158.         }
  159.         if ($missionShareDemand->isAccepted())
  160.         {
  161.             return false;
  162.         }
  163.         if ($missionShareDemand->isRefused())
  164.         {
  165.             return false;
  166.         }
  167.         if ($missionShareDemand->isAnnulled())
  168.         {
  169.             return false;
  170.         }
  171.         // All looks good
  172.         // Apply standard sharing conditions
  173.         return $this->accessDecisionManager->decide($token, ['share_mission'], $missionShareDemand->getMission());
  174.     }
  175.     private function canList(Access $userAccessFunction $function)
  176.     {
  177.         // Module activated ?
  178.         // At least one, not necessarly the plus one
  179.         // MODULE_MISSION_PLUS is required only if we want to share missions,
  180.         // not accept / refuse missions shared with us
  181.         if (
  182.             $this->moduleTools->isInactiveByCode($this->currentGroupModule::MODULE_MISSION) &&
  183.             $this->moduleTools->isInactiveByCode($this->currentGroupModule::MODULE_MISSION_LIGHT) &&
  184.             $this->moduleTools->isInactiveByCode($this->currentGroupModule::MODULE_MISSION_PLUS)
  185.             )
  186.         {
  187.             return false;
  188.         }
  189.         // Task plan.io #4096
  190.         // Check has favorite
  191.         $societyGroupFavorites $this->em->getRepository(SocietyGroupFavorite::class)
  192.             ->findBy(array('favorite' => $user->getSocietyGroup(),));
  193.         if (sizeof($societyGroupFavorites) < 1)
  194.         {
  195.             return false;
  196.         }
  197.         // Get Acl_Permission
  198.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_LIST);
  199.         if ($aclPerm === null)        return false;
  200.         // Get Acl
  201.         $acl $this->aclRepository->findOneBy(array(
  202.             'function'        =>    $function,
  203.             'permission'    =>    $aclPerm
  204.         ));
  205.         if ($acl === null)        return false;
  206.         // Since only one acl type can exist
  207.         // we can return the result of the acl_permission
  208.         return $acl->getValue();
  209.     }
  210.     // Can View a MissionShareDemand that has been shared with my society group
  211.     private function canView(MissionShareDemand $missionShareDemandAccess $userAccessFunction $function)
  212.     {
  213.         // Module activated ?
  214.         // At least one, not necessarly the plus one
  215.         // MODULE_MISSION_PLUS is required only if we want to share missions,
  216.         // not accept / refuse missions shared with us
  217.         if (
  218.             $this->moduleTools->isInactiveByCode($this->currentGroupModule::MODULE_MISSION) &&
  219.             $this->moduleTools->isInactiveByCode($this->currentGroupModule::MODULE_MISSION_LIGHT) &&
  220.             $this->moduleTools->isInactiveByCode($this->currentGroupModule::MODULE_MISSION_PLUS)
  221.             )
  222.         {
  223.             return false;
  224.         }
  225.         $mission $missionShareDemand->getMission();
  226.         if ($mission === null)
  227.         {
  228.             return false;
  229.         }
  230.         // CurrentGroup should be the receiver
  231.         $receiver $missionShareDemand->getReceiver();
  232.         if ($receiver === null)
  233.         {
  234.             return false;
  235.         }
  236.         if (!$receiver->equals($this->currentGroup))
  237.         {
  238.             return false;
  239.         }
  240.         // The demand should be either accepted or in the default status (not accepted)
  241.         // Refused / Annulled / Expired => Die hard
  242.         if ($missionShareDemand->isExpired())
  243.         {
  244.             return false;
  245.         }
  246.         if ($missionShareDemand->isRefused())
  247.         {
  248.             return false;
  249.         }
  250.         if ($missionShareDemand->isAnnulled())
  251.         {
  252.             return false;
  253.         }
  254.         // If we are here, it means the status is ok (Default or Accepted),
  255.         // so simply check if the user has the required permissions to list shared demands
  256.         // Get Acl_Permission
  257.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_LIST);
  258.         if ($aclPerm === null)        return false;
  259.         // Get Acl
  260.         $acl $this->aclRepository->findOneBy(array(
  261.             'function'        =>    $function,
  262.             'permission'    =>    $aclPerm
  263.         ));
  264.         if ($acl === null)        return false;
  265.         // Since only one acl type can exist
  266.         // we can return the result of the acl_permission
  267.         return $acl->getValue();
  268.     }
  269.     private function canAcceptOrRefuse(MissionShareDemand $missionShareDemandAccess $userAccessFunction $function)
  270.     {
  271.         // Module activated ?
  272.         // At least one, not necessarly the plus one
  273.         // MODULE_MISSION_PLUS is required only if we want to share missions,
  274.         // not accept / refuse missions shared with us
  275.         if (
  276.             $this->moduleTools->isInactiveByCode($this->currentGroupModule::MODULE_MISSION) &&
  277.             $this->moduleTools->isInactiveByCode($this->currentGroupModule::MODULE_MISSION_LIGHT) &&
  278.             $this->moduleTools->isInactiveByCode($this->currentGroupModule::MODULE_MISSION_PLUS)
  279.             )
  280.         {
  281.             return false;
  282.         }
  283.         $mission $missionShareDemand->getMission();
  284.         if ($mission === null)
  285.         {
  286.             return false;
  287.         }
  288.         // CurrentGroup should be the receiver
  289.         $receiver $missionShareDemand->getReceiver();
  290.         if ($receiver === null)
  291.         {
  292.             return false;
  293.         }
  294.         if (!$receiver->equals($this->currentGroup))
  295.         {
  296.             return false;
  297.         }
  298.         // The demand should be in the default status
  299.         // Accepted / Refused / Annulled / Expired => Die hard
  300.         if ($missionShareDemand->isExpired())
  301.         {
  302.             return false;
  303.         }
  304.         if ($missionShareDemand->isAccepted())
  305.         {
  306.             return false;
  307.         }
  308.         if ($missionShareDemand->isRefused())
  309.         {
  310.             return false;
  311.         }
  312.         if ($missionShareDemand->isAnnulled())
  313.         {
  314.             return false;
  315.         }
  316.         // If we are here, it means the status is ok (Default),
  317.         // so simply check if the user has the required permissions to list shared demands
  318.         // Get Acl_Permission
  319.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_ACCEPT_REFUSE);
  320.         if ($aclPerm === null)        return false;
  321.         // Get Acl
  322.         $acl $this->aclRepository->findOneBy(array(
  323.             'function'        =>    $function,
  324.             'permission'    =>    $aclPerm
  325.         ));
  326.         if ($acl === null)        return false;
  327.         // Since only one acl type can exist
  328.         // we can return the result of the acl_permission
  329.         return $acl->getValue();
  330.     }
  331.     private function canRefuseAccepted(MissionShareDemand $missionShareDemandAccess $userAccessFunction $function)
  332.     {
  333.         // Module activated ?
  334.         // At least one, not necessarly the plus one
  335.         // MODULE_MISSION_PLUS is required only if we want to share missions,
  336.         // not accept / refuse missions shared with us
  337.         if (
  338.             $this->moduleTools->isInactiveByCode($this->currentGroupModule::MODULE_MISSION) &&
  339.             $this->moduleTools->isInactiveByCode($this->currentGroupModule::MODULE_MISSION_LIGHT) &&
  340.             $this->moduleTools->isInactiveByCode($this->currentGroupModule::MODULE_MISSION_PLUS)
  341.             )
  342.         {
  343.             return false;
  344.         }
  345.         $mission $missionShareDemand->getMission();
  346.         if ($mission === null)
  347.         {
  348.             return false;
  349.         }
  350.         // Plan.io Task #3304
  351.         // Deny refusing a mission for which the owner was paid
  352.         if ($mission->ownerWasPaid())
  353.         {
  354.             return false;
  355.         }
  356.         // CurrentGroup should be the receiver
  357.         $receiver $missionShareDemand->getReceiver();
  358.         if ($receiver === null)
  359.         {
  360.             return false;
  361.         }
  362.         if (!$receiver->equals($this->currentGroup))
  363.         {
  364.             return false;
  365.         }
  366.         // If we are here, it means the status is ok (Accepted),
  367.         // so simply check if the user has the required permissions to list shared demands
  368.         // Get Acl_Permission
  369.         $aclPerm $this->aclPermissionRepository->findOneByName(self::ACL_PERM_ACCEPT_REFUSE);
  370.         if ($aclPerm === null)        return false;
  371.         // Get Acl
  372.         $acl $this->aclRepository->findOneBy(array(
  373.             'function'        =>    $function,
  374.             'permission'    =>    $aclPerm
  375.         ));
  376.         if ($acl === null)        return false;
  377.         // Since only one acl type can exist
  378.         // we can return the result of the acl_permission
  379.         // The demand should be Accepted status
  380.         if ($missionShareDemand->isAccepted())
  381.         {
  382.             return $acl->getValue();
  383.         }
  384.         // We are doing the status check here because we want to avoid the following case :
  385.         // Demand is accepted, but is has also expired
  386.         // If we check Expired before checking Acl,
  387.         // an expired but accepted demand will return false
  388.         // when it should return true
  389.         // Wtf we are doing here ? :D
  390.         return false;
  391.     }
  392. }