<?php
namespace MedBrief\MSR\Security\Voter;
use Doctrine\ORM\EntityManagerInterface;
use MedBrief\MSR\Entity\Account;
use MedBrief\MSR\Entity\Firm;
use MedBrief\MSR\Entity\User;
use MedBrief\MSR\Repository\UserInternalRepository;
use Override;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\User\UserInterface;
class AccountVoter extends Voter
{
public const CREATE = 'CREATE';
public const READ = 'READ';
public const UPDATE = 'UPDATE';
public const DELETE = 'DELETE';
public const ADMINISTRATION = 'ADMINISTRATION';
public const USER_ADMINISTRATION = 'USER_ADMINISTRATION';
public const SORTING_SETTINGS_ADMINISTRATION = 'SORTING_SETTINGS_ADMINISTRATION';
public const MATTER_REQUEST_DEFAULTS_ADMINISTRATION = 'MATTER_REQUEST_DEFAULTS_ADMINISTRATION';
public const INDEX_HEADER_ADMINISTRATION = 'INDEX_HEADER_ADMINISTRATION';
public const TECHNICAL_ADMINISTRATION = 'TECHNICAL_ADMINISTRATION';
public const UPDATE_PHYSICAL_ADDRESS = 'UPDATE_PHYSICAL_ADDRESS';
public const ADD_OFFICE = 'ADD_OFFICE';
public const ADD_MATTER_REQUEST = 'ADD_MATTER_REQUEST';
public const ADD_LICENCE_RENEWAL_TERM = 'ADD_LICENCE_RENEWAL_TERM';
public const ACCESS_GRANTED = true;
public const ACCESS_DENIED = false;
public function __construct(
private readonly AuthorizationCheckerInterface $authorizationChecker,
private readonly UserInternalRepository $userInternalRepository,
private readonly EntityManagerInterface $entityManager
) {
}
#[Override]
protected function supports($attribute, $subject)
{
return in_array($attribute, [
self::CREATE,
self::READ,
self::UPDATE,
self::DELETE,
self::ADMINISTRATION,
self::USER_ADMINISTRATION,
self::SORTING_SETTINGS_ADMINISTRATION,
self::MATTER_REQUEST_DEFAULTS_ADMINISTRATION,
self::INDEX_HEADER_ADMINISTRATION,
self::UPDATE_PHYSICAL_ADDRESS,
self::ADD_OFFICE,
self::TECHNICAL_ADMINISTRATION,
self::ADD_MATTER_REQUEST,
self::ADD_LICENCE_RENEWAL_TERM,
])
&& ($subject instanceof Account || is_subclass_of($subject, Account::class));
}
#[Override]
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
/** @var User $user */
$user = $token->getUser();
// if the user is anonymous, do not grant access
if (!$user instanceof UserInterface) {
return self::ACCESS_DENIED;
}
if ($user instanceof Firm && $user->getClientAreas()->contains($subject)) {
return self::ACCESS_GRANTED;
}
// Super Admin and Admin users can add/edit terms
if ($attribute === self::ADD_LICENCE_RENEWAL_TERM) {
return $this->canCreate();
}
// Super Admin users can do everything
if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
return self::ACCESS_GRANTED;
}
// Only Super Admins may create, delete Accounts or have full administration rights
if ($attribute === self::MATTER_REQUEST_DEFAULTS_ADMINISTRATION) {
return self::ACCESS_DENIED;
}
// Attributes that only internal users (MedBrief staff) can perform
$internalOnlyAttributes = [
self::CREATE,
self::DELETE,
self::ADMINISTRATION,
self::SORTING_SETTINGS_ADMINISTRATION,
self::UPDATE,
];
// Attributes that both internal users and client administrators can perform
$internalAndClientAdminAttributes = [
self::UPDATE_PHYSICAL_ADDRESS,
self::ADD_OFFICE,
];
// Internal users (MedBrief staff) can update any account
if ($user instanceof User
&& $user->isUserInternal($this->userInternalRepository, $this->entityManager)
&& (in_array($attribute, array_merge($internalOnlyAttributes, $internalAndClientAdminAttributes)))) {
return self::ACCESS_GRANTED;
}
// For client users, deny by default and only allow if user has specific account permissions
// This will be evaluated by the role checks below (ADMINISTRATOR or SUPERADMINISTRATOR for this account)
// If this user is a Client Super Administrator for the Account then they can
// do everything else
if ($this->authorizationChecker->isGranted('ROLE_ACCOUNT_' . $subject->getId() . '_SUPERADMINISTRATOR')
&& !in_array($attribute, $internalOnlyAttributes)
) {
return self::ACCESS_GRANTED;
}
// Otherwise if this user is a Client Administrator for the Account then they can
// do everything else except for User Administration and Technical Admin
if ($this->authorizationChecker->isGranted('ROLE_ACCOUNT_' . $subject->getId() . '_ADMINISTRATOR')
&& !in_array($attribute, array_merge([
self::USER_ADMINISTRATION,
self::TECHNICAL_ADMINISTRATION,
], $internalOnlyAttributes))
) {
return self::ACCESS_GRANTED;
}
// A Client technical administrator may perform technical administration as well as other duties
if ($this->authorizationChecker->isGranted('ROLE_ACCOUNT_' . $subject->getId() . '_TECHNICAL_ADMIN')
&& in_array($attribute, array_merge([
self::READ,
self::TECHNICAL_ADMINISTRATION,
self::ADD_MATTER_REQUEST,
], $internalAndClientAdminAttributes))) {
return self::ACCESS_GRANTED;
}
return self::ACCESS_DENIED;
}
private function canCreate(): bool
{
return $this->authorizationChecker->isGranted('ROLE_ADMIN');
}
}