<?php
/**
* This file is part of the educat package.
*
* (c) Solvee
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace App\ContentFile\Application\Security;
use App\Common\MentorApplicants\MentorApplicantsProviderInterface;
use App\Common\Model\ContentFile\ContentFileInterface;
use App\Common\Model\Core\AccountInterface;
use App\Common\Model\Core\UserInterface;
use App\Common\Model\Mentor\MentorInterface;
use App\Common\Security\AccountRole;
use App\Common\Security\VoterTrait;
use App\ContentFile\Application\DTO\CanCopyContentFileVoterInput;
use LogicException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Security;
/**
* Class ContentFileVoter
*
* @author Jarosław Dylewski <jaroslaw.dylewski@interia.pl>
*/
class ContentFileVoter extends Voter
{
use VoterTrait;
public const CAN_ACCESS = 'access';
public const CAN_COPY_CONTENT_FILE = 'copy_content_file';
public const CAN_MODIFY_CONTENT_FILE_ACCESS = 'modify_access';
/** @var MentorApplicantsProviderInterface */
private MentorApplicantsProviderInterface $mentorApplicantsProvider;
/**
* @param Security $security
* @param MentorApplicantsProviderInterface $mentorApplicantsProvider
*/
public function __construct(
Security $security,
MentorApplicantsProviderInterface $mentorApplicantsProvider
) {
$this->security = $security;
$this->mentorApplicantsProvider = $mentorApplicantsProvider;
}
/**
* @param string $attribute
* @param mixed $subject
*
* @return bool
*/
protected function supports($attribute, $subject)
{
if (!in_array(
$attribute,
[
self::CAN_COPY_CONTENT_FILE,
self::CAN_MODIFY_CONTENT_FILE_ACCESS,
self::CAN_ACCESS,
]
)) {
return false;
}
return $attribute && ($subject instanceof ContentFileInterface || $subject instanceof CanCopyContentFileVoterInput);
}
/**
* @inheritDoc
*/
protected function voteOnAttribute(mixed $attribute, mixed $subject, TokenInterface $token)
{
$user = $token->getUser();
if (!$user instanceof UserInterface) {
return false;
}
switch ($attribute) {
case self::CAN_COPY_CONTENT_FILE:
return $this->canCopyContentFile($subject, $user);
case self::CAN_MODIFY_CONTENT_FILE_ACCESS:
return $this->canModifyAccess($subject, $user);
case self::CAN_ACCESS:
return $this->canAccess($subject, $user);
default:
throw new LogicException();
}
}
/**
* @param CanCopyContentFileVoterInput $input
* @param UserInterface $user
*
* @return bool
*/
private function canCopyContentFile(CanCopyContentFileVoterInput $input, UserInterface $user): bool
{
if ($this->currentUserIsAdmin()) {
return true;
}
$account = $user->getAccount();
$contentFile = $input->contentFile;
if (!$contentFile->hasAccessToContentFile($account)) {
return false;
}
if ($account->isMentor()) {
return $this->mentorCanCopyFile($input, $account);
}
if ($account->isApplicant()) {
return $this->applicantCanCopyFile($input, $account);
}
return false;
}
/**
* @param ContentFileInterface $contentFile
* @param UserInterface $user
*
* @return bool
*/
private function canModifyAccess(ContentFileInterface $contentFile, UserInterface $user): bool
{
if ($this->currentUserIsAdmin()) {
return true;
}
$account = $user->getAccount();
if (!$account->isApplicant()) {
return false;
}
return $contentFile->hasAccessToContentFile($user->getAccount());
}
/**
* @param ContentFileInterface $contentFile
* @param UserInterface $user
*
* @return bool
*/
private function canAccess(ContentFileInterface $contentFile, UserInterface $user): bool
{
if ($this->currentUserIsAdmin()) {
return true;
}
return $contentFile->hasAccessToContentFile($user->getAccount());
}
/**
* @return bool
*/
private function currentUserIsAdmin(): bool
{
return $this->security->isGranted(AccountRole::ADMIN);
}
/**
* @param CanCopyContentFileVoterInput $input
* @param AccountInterface $account
*
* @return bool
*/
private function mentorCanCopyFile(CanCopyContentFileVoterInput $input, AccountInterface $account): bool
{
$forAccount = $input->forAccount;
if (!$forAccount->isApplicant()) {
return false;
}
/** @var MentorInterface $mentor */
$mentor = $account->getMentor();
return $this->mentorApplicantsProvider->doesApplicantBelongToMentor($forAccount->getApplicant(), $mentor);
}
/**
* @param CanCopyContentFileVoterInput $input
* @param AccountInterface $account
*
* @return bool
*/
private function applicantCanCopyFile(CanCopyContentFileVoterInput $input, AccountInterface $account): bool
{
return $account === $input->forAccount;
}
}