<?php
declare(strict_types=1);
namespace App\Security\Voter\Procedure;
use App\Model\User\Entity\User\Role\Permission;
use App\Model\User\Entity\User\Role\RoleConstants;
use App\ReadModel\Procedure\InvitedMember\InvitedMemberFetcher;
use App\ReadModel\Procedure\Lot\DetailView;
use App\ReadModel\Profile\ProfileFetcher;
use App\Security\UserIdentity;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Security;
class ProcedureVoter extends Voter
{
public const CONTRACTS_SHOW_TO_LOT = 'contracts_show_to_lot';
public const PROCEDURE_SHOW = 'procedure_show';
private Security $security;
private $user;
/** @var \App\ReadModel\Profile\DetailView */
private \App\ReadModel\Profile\DetailView $profile;
private InvitedMemberFetcher $invitedMemberFetcher;
private ProfileFetcher $profileFetcher;
public function __construct(Security $security, InvitedMemberFetcher $invitedMemberFetcher, ProfileFetcher $profileFetcher)
{
$this->security = $security;
$this->invitedMemberFetcher = $invitedMemberFetcher;
$this->profileFetcher = $profileFetcher;
}
protected function supports(string $attribute, $subject): bool
{
return in_array($attribute, [
self::CONTRACTS_SHOW_TO_LOT,
self::PROCEDURE_SHOW
], true);
}
/**
* @param string $attribute
* @param DetailView $subject
* @param TokenInterface $token
* @return bool
*/
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
// Moderators have full access
if ($this->security->isGranted('ROLE_MODERATOR')) {
return true;
}
$user = $token->getUser();
// Only authenticated UserIdentity can proceed
if (!$user instanceof UserIdentity) {
return false;
}
$this->profile = $this->profileFetcher->find($user->getProfileId());
$role = (new RoleConstants($user->getRole()));
if ($role->isOrganizerUser()) {
if ($this->checkPermissionEmployee($user, $attribute) === false) {
return $this->handleException();
}
}
//is not owner object
if ($this->isOwnerObject($subject, $user) === false) {
if ($subject->getStatus()->isNew() or $subject->getStatus()->isArchive()) {
return $this->handleException();
}
}
if ($this->checkClosedPurchase($subject) === false) {
return $this->handleException();
}
return true;
}
private function checkClosedPurchase(DetailView $lot): bool
{
//закрытая закупка не виден не приглашенным
if ($lot->closed_purchase && $lot->is_hide_closed_purchase) {
$inn = $this->profile->getInn();
//заказчик
if ($lot->organizer_profile_id === $this->profile->id) {
return true;
}
if (!$this->invitedMemberFetcher->isInvitedByInnOrProfile($inn, $lot->procedure_id, $this->profile->id)) {
return false;
}
}
return true;
}
/**
* Проверка разрешений сотрудника
* @param UserIdentity $user
* @param string $attribute
* @return bool
*/
private function checkPermissionEmployee(UserIdentity $user, string $attribute): bool
{
switch ($attribute) {
case self::CONTRACTS_SHOW_TO_LOT:
return $user->isPermission(Permission::CONTRACTS_SHOW_TO_LOT);
break;
case self::PROCEDURE_SHOW:
return true;
break;
}
return false;
}
private function isOwnerObject(DetailView $subject, UserIdentity $user): bool
{
if ($subject->organizer_profile_id === $user->getProfileId()) {
return true;
}
return false;
}
private function handleException()
{
throw new AccessDeniedException('Доступ запрещен. У вас недостаточно прав для совершения этого действия.');
}
}