<?php
namespace App\Security\Voters;
use App\Domain\Exception\Restrict\RestrictException;
use App\Services\Accounts\ChildAccountService;
use App\Services\Clients\ClientSystemState\ClientSystemStateService;
use App\Entity\Client;
use App\Entity\CompanyPerson;
use App\Entity\OA2User;
use App\Repository\ClientAccountRepository;
use App\Repository\ClientRepository;
use App\Repository\CompanyPersonRepository;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
class ClientVoter extends Voter
{
public const MODE_READ = 'read';
public const MODE_UPDATE = 'update';
public const MODE_DELETE = 'delete';
public const CAN_OPEN_ACCOUNTS = 'can_open_accounts';
public const CAN_GRANT_PERMISSIONS = 'can_grant_permissions';
public const CAN_ADD_COMPANY_PERSONS = 'can_add_company_persons';
public const CAN_CHANGE_COMPANY_ACCOUNT_LIMITS = 'can_change_company_account_limits';
public const CAN_MAKE_CLUSTERS = 'can_make_clusters';
public const IN_COMPANY = 'in_company';
public const CAN_VIEW_SHAREHOLDERS = 'can_view_shareholders';
public const CAN_ADD_SHAREHOLDERS = 'can_add_shareholders';
public const CAN_UPDATE_COMPANY = 'can_update_company';
public const CAN_CREATE_COMPANY = 'can_create_company';
/**
* @var ClientRepository
*/
private $clientRepository;
/**
* @var ClientAccountRepository
*/
private $clientAccountRepository;
/**
* @var CompanyPersonRepository
*/
private $companyPersonRepository;
/**
* @var ClientSystemStateService
*/
private $systemStateService;
private $childAccountService;
public function __construct(
ClientRepository $clientRepository,
CompanyPersonRepository $companyPersonRepository,
ClientAccountRepository $clientAccountRepository,
ChildAccountService $childAccountService,
ClientSystemStateService $systemStateService
) {
$this->clientRepository = $clientRepository;
$this->companyPersonRepository = $companyPersonRepository;
$this->clientAccountRepository = $clientAccountRepository;
$this->childAccountService = $childAccountService;
$this->systemStateService = $systemStateService;
}
protected function supports($attribute, $subject)
{
if (!in_array($attribute, $this->getSupportedPermissions())) {
return false;
}
// only vote on Post objects inside this voter
return !(!$subject instanceof Client)
;
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
if (!in_array($attribute, $this->getSupportedPermissions())) {
return false;
}
/** @var Client $ownerClient */
$ownerClient = $subject;
/** @var Client $currentClient */
$currentClient = $this->getCurrentClient($token);
if (!$currentClient) {
return false;
}
// Its a workaround for shareholders functionality
// Specially for that endpoints we need check company relation without checking company write permissions
if ($attribute == self::CAN_VIEW_SHAREHOLDERS) {
return $this->inCompany($currentClient, $ownerClient);
}
if ($attribute == self::CAN_ADD_SHAREHOLDERS) {
if (!$this->allowUseSystem($currentClient)) {
return false;
}
return $this->inCompany($currentClient, $ownerClient);
}
if ($attribute === self::CAN_UPDATE_COMPANY) {
return $this->canGrantPermissions($currentClient, $ownerClient);
}
if ($attribute !== self::MODE_READ) {
if (!$this->allowUseSystem($currentClient) || !$this->allowUseSystem($ownerClient)) {
return false;
}
}
if ($ownerClient->getId() === $currentClient->getId()) {
return true;
}
if ($this->isChild($ownerClient, $currentClient)) {
return true;
}
if ($attribute === self::IN_COMPANY) {
return $this->inCompany($currentClient, $ownerClient);
}
if ($attribute === self::CAN_OPEN_ACCOUNTS) {
return $this->canOpenAccounts($currentClient, $ownerClient);
}
if ($attribute === self::CAN_ADD_COMPANY_PERSONS) {
return $this->canAddCompanyPersons($currentClient, $ownerClient);
}
if ($attribute === self::CAN_GRANT_PERMISSIONS) {
return $this->canGrantPermissions($currentClient, $ownerClient);
}
if ($attribute === self::CAN_CHANGE_COMPANY_ACCOUNT_LIMITS) {
return $this->canChangeCompanyAccountLimits($currentClient, $ownerClient);
}
if ($attribute === self::CAN_MAKE_CLUSTERS) {
return $this->canMakeClusters($currentClient, $ownerClient);
}
// TODO: check owner rules!
$relationAccount = $this->clientAccountRepository->findOneByOwnerClient($currentClient, $ownerClient);
if ($relationAccount) {
return true;
}
$relationCompany = $this->companyPersonRepository->findOneByOwnerClient($currentClient, $ownerClient);
return (bool) ($relationCompany)
;
}
private function isChild(Client $ownerClient, Client $currentClient): bool
{
if (!$ownerClient->isPerson()) {
return false;
}
$parent = $this->childAccountService->getValidParentClient($ownerClient->getPerson());
return (bool) ($parent && $parent->getId() === $currentClient->getId())
;
}
private function canGrantPermissions(Client $currentClient, Client $ownerClient): bool
{
/** @var CompanyPerson $relationCompany */
$relationCompany = $this->companyPersonRepository->findOneByOwnerClient($currentClient, $ownerClient);
return $relationCompany ? $relationCompany->getCanGrantPermissions() : false;
}
private function inCompany(Client $currentClient, Client $companyClient)
{
/** @var CompanyPerson $relationCompany */
$relationCompany = $this->companyPersonRepository->findOneByOwnerClient($currentClient, $companyClient);
return (bool) $relationCompany;
}
private function canAddCompanyPersons(Client $currentClient, Client $ownerClient): bool
{
/** @var CompanyPerson $relationCompany */
$relationCompany = $this->companyPersonRepository->findOneByOwnerClient($currentClient, $ownerClient);
return $relationCompany ? $relationCompany->getCanAddCompanyPersons() : false;
}
private function canOpenAccounts(Client $currentClient, Client $ownerClient): bool
{
/** @var CompanyPerson $relationCompany */
$relationCompany = $this->companyPersonRepository->findOneByOwnerClient($currentClient, $ownerClient);
return $relationCompany ? $relationCompany->getCanOpenAccounts() : false;
}
private function canChangeCompanyAccountLimits(Client $currentClient, Client $ownerClient): bool
{
/** @var CompanyPerson $relationCompany */
$relationCompany = $this->companyPersonRepository->findOneByOwnerClient($currentClient, $ownerClient);
return $relationCompany ? $relationCompany->getCanChangeCompanyAccountLimits() : false;
}
private function canMakeClusters(Client $currentClient, Client $ownerClient): bool
{
/** @var CompanyPerson $relationCompany */
$relationCompany = $this->companyPersonRepository->findOneByOwnerClient($currentClient, $ownerClient);
return $relationCompany ? $relationCompany->getCanMakeClusters() : false;
}
private function allowUseSystem(Client $client)
{
try {
$this->systemStateService->allowUseSystem($client);
return true;
} catch (RestrictException $exception) {
return false;
}
}
private function getSupportedPermissions(): array
{
return [
self::MODE_READ,
self::MODE_UPDATE,
self::MODE_DELETE,
self::CAN_OPEN_ACCOUNTS,
self::CAN_GRANT_PERMISSIONS,
self::CAN_ADD_COMPANY_PERSONS,
self::CAN_CHANGE_COMPANY_ACCOUNT_LIMITS,
self::IN_COMPANY,
self::CAN_MAKE_CLUSTERS,
self::CAN_VIEW_SHAREHOLDERS,
self::CAN_ADD_SHAREHOLDERS,
self::CAN_UPDATE_COMPANY,
self::CAN_CREATE_COMPANY,
];
}
private function getCurrentClient(TokenInterface $token): ?Client
{
/** @var OA2User $oa2User */
$oa2User = $token->getUser();
if (!$oa2User instanceof OA2User || $oa2User->isBankUser()) {
return null; // Client user only
}
return $this->clientRepository->findOneByOa2User($oa2User);
}
}