<?php
declare(strict_types=1);
namespace App\Security\Voter;
use App\Entity\Security\Administrator;
use App\Entity\Security\Customer;
use App\Entity\Security\Developer;
use App\Entity\Security\Manager;
use App\Entity\Security\RoleInterface;
use App\Entity\Security\ShopManager;
use App\Entity\Security\Support;
use App\Entity\Security\User;
use App\Repository\Shop\CustomerShopRepository;
use App\Service\Security\CustomerService;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
class UserVoter extends Voter
{
public const SHOW = 'USER_SHOW';
public function __construct(
private AuthorizationCheckerInterface $authorizationChecker,
private CustomerService $customerService,
private CustomerShopRepository $customerShopRepository,
) {
}
protected function supports(string $attribute, mixed $subject): bool
{
return self::SHOW === $attribute && $subject instanceof User;
}
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$loggedUser = $token->getUser();
if (!$loggedUser instanceof User) {
return false;
}
return $loggedUser->getId() === $subject->getId() || $this->canView($loggedUser, $subject);
}
private function canView(User $loggedUser, User $targetUser): bool
{
$targetRole = match (true) {
$targetUser instanceof Administrator => RoleInterface::ROLE_ADMIN,
$targetUser instanceof Developer => RoleInterface::ROLE_DEVELOPER,
$targetUser instanceof Support => RoleInterface::ROLE_SUPPORT,
$targetUser instanceof Manager => RoleInterface::ROLE_MANAGER,
$targetUser instanceof ShopManager => RoleInterface::ROLE_SHOP_MANAGER,
$targetUser instanceof Customer => RoleInterface::ROLE_CLIENT,
default => null,
};
if (!$this->authorizationChecker->isGranted($targetRole)) {
return false;
}
return $this->checkConstraints($loggedUser, $targetUser);
}
private function checkConstraints(User $loggedUser, User $targetUser): bool
{
if ($loggedUser::class === $targetUser::class && !$loggedUser instanceof Administrator) {
return false;
}
return match (true) {
$loggedUser instanceof Administrator,
$loggedUser instanceof Developer,
$loggedUser instanceof Support => true,
$loggedUser instanceof Manager => match (true) {
$targetUser instanceof Customer => $this->customerService->customerHasFranchise($targetUser, $loggedUser->getFranchise()),
$targetUser instanceof ShopManager => $targetUser->getShop()->getFranchise() === $loggedUser->getFranchise(),
$targetUser instanceof Manager => false,
default => false,
},
$loggedUser instanceof ShopManager => match (true) {
$targetUser instanceof Customer => $this->customerShopRepository->exist($loggedUser->getShop()->getId(), $targetUser->getId()),
$targetUser instanceof ShopManager => false,
default => false,
},
default => false,
};
}
}