<?php
//------------------------------------------------------------------------------
// src/Security/SmsVoter.php
//------------------------------------------------------------------------------
namespace App\Security;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Doctrine\Persistence\ManagerRegistry;
use App\Entity\Access;
use App\Entity\Client\Client;
use App\Entity\Config\Config;
use App\Entity\Communication\Sms\SmsPhoneNumber;
use App\Entity\Config\Module;
use App\Entity\HR\AccessFunction;
use App\Entity\HR\HumanResource;
use App\Entity\Security\Acl;
use App\Entity\Security\AclPermission;
use App\Entity\SocietyGroup;
use App\Services\Communication\SmsTools;
use App\Services\Config\ModuleTools;
class SmsVoter extends Voter
{
//--------------------------------------------------------------------------------
// is_granted constants
const IS_ACTIVE = "sms_is_active";
const NUMBER_ASSIGNED = "sms_number_is_assigned";
const CAN_SEND = "can_send_sms";
const IS_GRANTED_CONSTANTS = array(
self::IS_ACTIVE,
self::NUMBER_ASSIGNED,
self::CAN_SEND,
);
public function __construct(ManagerRegistry $doctrine, ModuleTools $moduleTools, SmsTools $smsTools)
{
$this->em = $doctrine->getManager();
$this->moduleTools = $moduleTools;
$this->smsTools = $smsTools;
$this->aclRepository = $this->em->getRepository(Acl::class);
$this->aclPermissionRepository = $this->em->getRepository(AclPermission::class);
}
// Plan.io Task #4453 [See AccessVoter for details]
public function supportsAttribute(string $attribute): bool
{
return in_array($attribute, self::IS_GRANTED_CONSTANTS, true);
}
protected function supports(string $attribute, $subject = null): bool
{
// if the attribute isn't one we support, return false
if (!in_array($attribute, self::IS_GRANTED_CONSTANTS))
{
return false;
}
// only vote on Client objects inside this voter
if ($subject !== null && !$subject instanceof Client && !$subject instanceof HumanResource)
{
return false;
}
return true;
}
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
$user = $token->getUser();
if (!$user instanceof Access)
{
// the user must be logged in; if not, deny access
return false;
}
// The user must have a function; if not deny access
$function = $user->getFunction();
if ($function === null) return false;
// Plan.io Task #3710 : Get current group
$currentGroup = $user->getSocietyGroup();
if ($currentGroup === null)
return false;
$this->currentGroup = $currentGroup;
// Module activated ?
if ($this->moduleTools->isInactiveByCode($currentGroup, Module::MODULE_SMS))
{
return false;
}
// Check current group affectation
if ($subject !== null)
{
$subjectSociety = $subject->getSociety();
if ($subjectSociety === null)
{
return false;
}
$subjectGroup = $subjectSociety->getGroup();
if ($subjectGroup === null)
{
return false;
}
if (!$currentGroup->equals($subjectGroup))
{
// Classic case
if ($subject instanceof Client === false)
{
return false;
}
// Task plan.io #4313 - Allow send sms for "shared" Client
if ($subject->hasMissionOwnedBySocietyGroup($this->currentGroup) === false)
{
return false;
}
}
}
switch ($attribute)
{
// Check is done before, in the voteOnAttribute
case self::IS_ACTIVE:
return true;
case self::NUMBER_ASSIGNED:
return $this->numberAssigned($currentGroup, $user, $function);
case self::CAN_SEND:
return $this->canSend($subject, $user, $function);
}
throw new \LogicException('This code should not be reached!');
}
private function numberAssigned(SocietyGroup $societyGroup, Access $user, AccessFunction $function)
{
// Plan.io Task #3980
// This handles both cases : SmsMode and TwilioSms
$smsPhoneNumber = $this->smsTools->getPhoneNumberForSocietyGroup($societyGroup);
if ($smsPhoneNumber === null)
{
return false;
}
return true;
}
// Updated by Plan.io Task #4515
private function canSend($recipient, Access $user, AccessFunction $function)
{
// Check if recipient has a valid phone
if ($recipient instanceof Client)
{
$individual = $recipient->getIndividual();
$validPhoneObjectList = $this->smsTools->getIndividualPhonesForSms($individual);
if (count($validPhoneObjectList) < 1)
{
return false;
}
}
else if ($recipient instanceof HumanResource)
{
$phoneFormatted = $this->smsTools->phoneFormat($recipient->getPhone());
if ($phoneFormatted === null)
{
return false;
}
}
else
{
// Default case
return false;
}
// Does the society group have a phone number ?
// Plan.io Task #3980
// This handles both cases : SmsMode and TwilioSms
// Plan.io Task #4313 : <!> Don't use the client SocietyGroup
$smsPhoneNumber = $this->smsTools->getPhoneNumberForSocietyGroup($this->currentGroup);
if ($smsPhoneNumber === null)
{
return false;
}
// All looks good
return true;
}
}