<?php
declare(strict_types=1);
namespace App\Bundles\TemplateBundle\Security\Voter;
use App\Bundles\OrganizationBundle\Entity\Organization;
use App\Bundles\OrganizationBundle\Exception\UserOrganizationNotFoundException;
use App\Bundles\OrganizationBundle\Service\Organization\OrganizationProvider;
use App\Bundles\OrganizationBundle\Service\UserOrganization\UserOrganizationProvider;
use App\Bundles\TemplateBundle\Entity\Template;
use App\Bundles\UserBundle\Entity\User;
use App\Bundles\UserBundle\Enum\SystemPermissionEnum;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Security;
class TemplateViewVoter extends Voter
{
public function __construct(
private readonly Security $security,
private readonly UserOrganizationProvider $userOrganizationProvider,
private readonly OrganizationProvider $organizationProvider,
) {
}
protected function supports(string $attribute, $subject): bool
{
return $attribute === SystemPermissionEnum::SINGLE_TEMPLATE_VIEW->value;
}
/**
* @param Template $subject
* @throws UserOrganizationNotFoundException
*/
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
$user = $this->security->getUser();
if (!$user instanceof User) {
return false;
}
if ($subject->getUser() === $user && $subject->isPersonal()) {
return true;
}
$organizationFromSession = $this->resolveOrganization();
if (
$subject->isSharedForOrganization() &&
$this->checkAccessForOrganization($subject, $organizationFromSession)
) {
return true;
}
if (
$subject->isSharedForChildOrganization() &&
$this->checkAccessForChildOrganization($subject, $organizationFromSession)
) {
return true;
}
return false;
}
/**
* @throws UserOrganizationNotFoundException
*/
private function resolveOrganization(): Organization
{
$userOrganization = $this->userOrganizationProvider->provideFromSession();
return $userOrganization->getOrganization();
}
private function checkAccessForOrganization(Template $subject, Organization $organizationFromSession): bool
{
$organization = $subject->getOrganization();
return $organization->getId() === $organizationFromSession->getId();
}
private function checkAccessForChildOrganization(Template $subject, Organization $organizationFromSession): bool
{
$parentOrganization = $subject->getOrganization();
return $this->checkAccessForOrganization($subject, $organizationFromSession) ||
in_array($organizationFromSession->getId(), $this->provideOrganizationsIds($parentOrganization));
}
private function provideOrganizationsIds(Organization $parentOrganization): array
{
$organizations = $this->organizationProvider->provideByParentOrganization($parentOrganization);
return array_map(function (Organization $organization) {
return $organization->getId();
}, $organizations);
}
}