mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-21 01:25:55 +02:00
Use the new VoterHelper in voters
This commit is contained in:
parent
fc6643bd6f
commit
6be55d1837
16 changed files with 146 additions and 162 deletions
|
@ -22,6 +22,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Security\Voter;
|
namespace App\Security\Voter;
|
||||||
|
|
||||||
|
use App\Services\UserSystem\VoterHelper;
|
||||||
use Symfony\Bundle\SecurityBundle\Security;
|
use Symfony\Bundle\SecurityBundle\Security;
|
||||||
use App\Entity\Attachments\AttachmentContainingDBElement;
|
use App\Entity\Attachments\AttachmentContainingDBElement;
|
||||||
use App\Entity\Attachments\Attachment;
|
use App\Entity\Attachments\Attachment;
|
||||||
|
@ -37,29 +38,21 @@ use App\Entity\Attachments\ProjectAttachment;
|
||||||
use App\Entity\Attachments\StorelocationAttachment;
|
use App\Entity\Attachments\StorelocationAttachment;
|
||||||
use App\Entity\Attachments\SupplierAttachment;
|
use App\Entity\Attachments\SupplierAttachment;
|
||||||
use App\Entity\Attachments\UserAttachment;
|
use App\Entity\Attachments\UserAttachment;
|
||||||
use App\Entity\UserSystem\User;
|
|
||||||
use App\Services\UserSystem\PermissionManager;
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
|
|
||||||
use function in_array;
|
use function in_array;
|
||||||
|
|
||||||
class AttachmentVoter extends ExtendedVoter
|
final class AttachmentVoter extends Voter
|
||||||
{
|
{
|
||||||
public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager, protected Security $security)
|
public function __construct(private readonly Security $security, private readonly VoterHelper $helper)
|
||||||
{
|
{
|
||||||
parent::__construct($resolver, $entityManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||||
* Similar to voteOnAttribute, but checking for the anonymous user is already done.
|
|
||||||
* The current user (or the anonymous user) is passed by $user.
|
|
||||||
*
|
|
||||||
* @param string $attribute
|
|
||||||
*/
|
|
||||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
|
||||||
{
|
{
|
||||||
//return $this->resolver->inherit($user, 'attachments', $attribute) ?? false;
|
|
||||||
|
|
||||||
//This voter only works for attachments
|
//This voter only works for attachments
|
||||||
if (!is_a($subject, Attachment::class, true)) {
|
if (!is_a($subject, Attachment::class, true)) {
|
||||||
|
@ -67,7 +60,7 @@ class AttachmentVoter extends ExtendedVoter
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($attribute === 'show_private') {
|
if ($attribute === 'show_private') {
|
||||||
return $this->resolver->inherit($user, 'attachments', 'show_private') ?? false;
|
return $this->helper->isGranted($token, 'attachments', 'show_private');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -113,7 +106,7 @@ class AttachmentVoter extends ExtendedVoter
|
||||||
throw new RuntimeException('Encountered unknown Parameter type: ' . $subject);
|
throw new RuntimeException('Encountered unknown Parameter type: ' . $subject);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->resolver->inherit($user, $param, $this->mapOperation($attribute)) ?? false;
|
return $this->helper->isGranted($token, $param, $this->mapOperation($attribute));
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published
|
|
||||||
* by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Security\Voter;
|
|
||||||
|
|
||||||
use App\Entity\UserSystem\User;
|
|
||||||
use App\Repository\UserRepository;
|
|
||||||
use App\Services\UserSystem\PermissionManager;
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
|
||||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The purpose of this class is, to use the anonymous user from DB in the case, that nobody is logged in.
|
|
||||||
*/
|
|
||||||
abstract class ExtendedVoter extends Voter
|
|
||||||
{
|
|
||||||
public function __construct(protected PermissionManager $resolver, protected EntityManagerInterface $entityManager)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
final protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
|
|
||||||
{
|
|
||||||
$user = $token->getUser();
|
|
||||||
|
|
||||||
//An allowed user is not allowed to do anything...
|
|
||||||
if ($user instanceof User && $user->isDisabled()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the user is anonymous (meaning $user is null), we use the anonymous user.
|
|
||||||
if (!$user instanceof User) {
|
|
||||||
/** @var UserRepository $repo */
|
|
||||||
$repo = $this->entityManager->getRepository(User::class);
|
|
||||||
$user = $repo->getAnonymousUser();
|
|
||||||
if (!$user instanceof User) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->voteOnUser($attribute, $subject, $user);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Similar to voteOnAttribute, but checking for the anonymous user is already done.
|
|
||||||
* The current user (or the anonymous user) is passed by $user.
|
|
||||||
*/
|
|
||||||
abstract protected function voteOnUser(string $attribute, $subject, User $user): bool;
|
|
||||||
}
|
|
|
@ -24,18 +24,26 @@ namespace App\Security\Voter;
|
||||||
|
|
||||||
use App\Entity\UserSystem\Group;
|
use App\Entity\UserSystem\Group;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
|
use App\Services\UserSystem\VoterHelper;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
|
|
||||||
class GroupVoter extends ExtendedVoter
|
final class GroupVoter extends Voter
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public function __construct(private readonly VoterHelper $helper)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Similar to voteOnAttribute, but checking for the anonymous user is already done.
|
* Similar to voteOnAttribute, but checking for the anonymous user is already done.
|
||||||
* The current user (or the anonymous user) is passed by $user.
|
* The current user (or the anonymous user) is passed by $user.
|
||||||
*
|
*
|
||||||
* @param string $attribute
|
* @param string $attribute
|
||||||
*/
|
*/
|
||||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||||
{
|
{
|
||||||
return $this->resolver->inherit($user, 'groups', $attribute) ?? false;
|
return $this->helper->isGranted($token, 'groups', $attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,7 +57,7 @@ class GroupVoter extends ExtendedVoter
|
||||||
protected function supports(string $attribute, $subject): bool
|
protected function supports(string $attribute, $subject): bool
|
||||||
{
|
{
|
||||||
if (is_a($subject, Group::class, true)) {
|
if (is_a($subject, Group::class, true)) {
|
||||||
return $this->resolver->isValidOperation('groups', $attribute);
|
return $this->helper->isValidOperation('groups', $attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -24,18 +24,27 @@ declare(strict_types=1);
|
||||||
namespace App\Security\Voter;
|
namespace App\Security\Voter;
|
||||||
|
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
|
use App\Services\UserSystem\PermissionManager;
|
||||||
|
use App\Services\UserSystem\VoterHelper;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This voter implements a virtual role, which can be used if the user has any permission set to allowed.
|
* This voter implements a virtual role, which can be used if the user has any permission set to allowed.
|
||||||
* We use this to restrict access to the homepage.
|
* We use this to restrict access to the homepage.
|
||||||
*/
|
*/
|
||||||
class HasAccessPermissionsVoter extends ExtendedVoter
|
final class HasAccessPermissionsVoter extends Voter
|
||||||
{
|
{
|
||||||
public const ROLE = "HAS_ACCESS_PERMISSIONS";
|
public const ROLE = "HAS_ACCESS_PERMISSIONS";
|
||||||
|
|
||||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
public function __construct(private readonly PermissionManager $permissionManager, private readonly VoterHelper $helper)
|
||||||
{
|
{
|
||||||
return $this->resolver->hasAnyPermissionSetToAllowInherited($user);
|
}
|
||||||
|
|
||||||
|
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||||
|
{
|
||||||
|
$user = $this->helper->resolveUser($token);
|
||||||
|
return $this->permissionManager->hasAnyPermissionSetToAllowInherited($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function supports(string $attribute, mixed $subject): bool
|
protected function supports(string $attribute, mixed $subject): bool
|
||||||
|
|
|
@ -25,36 +25,26 @@ namespace App\Security\Voter;
|
||||||
|
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
use App\Services\UserSystem\PermissionManager;
|
use App\Services\UserSystem\PermissionManager;
|
||||||
|
use App\Services\UserSystem\VoterHelper;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
|
||||||
class ImpersonateUserVoter extends Voter
|
final class ImpersonateUserVoter extends Voter
|
||||||
{
|
{
|
||||||
|
|
||||||
public function __construct(private PermissionManager $permissionManager)
|
public function __construct(private readonly VoterHelper $helper)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function supports(string $attribute, mixed $subject): bool
|
protected function supports(string $attribute, mixed $subject): bool
|
||||||
{
|
{
|
||||||
return $attribute == 'CAN_SWITCH_USER'
|
return $attribute === 'CAN_SWITCH_USER'
|
||||||
&& $subject instanceof UserInterface;
|
&& $subject instanceof UserInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
|
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
|
||||||
{
|
{
|
||||||
$user = $token->getUser();
|
return $this->helper->isGranted($token, 'users', 'impersonate');
|
||||||
|
|
||||||
if (!$user instanceof User || !$subject instanceof UserInterface) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//An disabled user is not allowed to do anything...
|
|
||||||
if ($user->isDisabled()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->permissionManager->inherit($user, 'users', 'impersonate') ?? false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -43,8 +43,11 @@ namespace App\Security\Voter;
|
||||||
|
|
||||||
use App\Entity\LabelSystem\LabelProfile;
|
use App\Entity\LabelSystem\LabelProfile;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
|
use App\Services\UserSystem\VoterHelper;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
|
|
||||||
class LabelProfileVoter extends ExtendedVoter
|
final class LabelProfileVoter extends Voter
|
||||||
{
|
{
|
||||||
protected const MAPPING = [
|
protected const MAPPING = [
|
||||||
'read' => 'read_profiles',
|
'read' => 'read_profiles',
|
||||||
|
@ -55,9 +58,12 @@ class LabelProfileVoter extends ExtendedVoter
|
||||||
'revert_element' => 'revert_element',
|
'revert_element' => 'revert_element',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
public function __construct(private readonly VoterHelper $helper)
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||||
{
|
{
|
||||||
return $this->resolver->inherit($user, 'labels', self::MAPPING[$attribute]) ?? false;
|
return $this->helper->isGranted($token, 'labels', self::MAPPING[$attribute]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function supports($attribute, $subject): bool
|
protected function supports($attribute, $subject): bool
|
||||||
|
@ -67,7 +73,7 @@ class LabelProfileVoter extends ExtendedVoter
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->resolver->isValidOperation('labels', self::MAPPING[$attribute]);
|
return $this->helper->isValidOperation('labels', self::MAPPING[$attribute]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -22,41 +22,45 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Security\Voter;
|
namespace App\Security\Voter;
|
||||||
|
|
||||||
|
use App\Services\UserSystem\VoterHelper;
|
||||||
use Symfony\Bundle\SecurityBundle\Security;
|
use Symfony\Bundle\SecurityBundle\Security;
|
||||||
use App\Entity\LogSystem\AbstractLogEntry;
|
use App\Entity\LogSystem\AbstractLogEntry;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
use App\Services\UserSystem\PermissionManager;
|
use App\Services\UserSystem\PermissionManager;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
|
|
||||||
class LogEntryVoter extends ExtendedVoter
|
final class LogEntryVoter extends Voter
|
||||||
{
|
{
|
||||||
final public const ALLOWED_OPS = ['read', 'show_details', 'delete'];
|
final public const ALLOWED_OPS = ['read', 'show_details', 'delete'];
|
||||||
|
|
||||||
public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager, private readonly Security $security)
|
public function __construct(private readonly Security $security, private readonly VoterHelper $helper)
|
||||||
{
|
{
|
||||||
parent::__construct($resolver, $entityManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||||
{
|
{
|
||||||
|
$user = $this->helper->resolveUser($token);
|
||||||
|
|
||||||
if (!$subject instanceof AbstractLogEntry) {
|
if (!$subject instanceof AbstractLogEntry) {
|
||||||
throw new \InvalidArgumentException('The subject must be an instance of '.AbstractLogEntry::class);
|
throw new \InvalidArgumentException('The subject must be an instance of '.AbstractLogEntry::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('delete' === $attribute) {
|
if ('delete' === $attribute) {
|
||||||
return $this->resolver->inherit($user, 'system', 'delete_logs') ?? false;
|
return $this->helper->isGranted($token, 'system', 'delete_logs');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('read' === $attribute) {
|
if ('read' === $attribute) {
|
||||||
//Allow read of the users own log entries
|
//Allow read of the users own log entries
|
||||||
if (
|
if (
|
||||||
$subject->getUser() === $user
|
$subject->getUser() === $user
|
||||||
&& $this->resolver->inherit($user, 'self', 'show_logs')
|
&& $this->helper->isGranted($token, 'self', 'show_logs')
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->resolver->inherit($user, 'system', 'show_logs') ?? false;
|
return $this->helper->isGranted($token, 'system', 'show_logs');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('show_details' === $attribute) {
|
if ('show_details' === $attribute) {
|
||||||
|
@ -67,7 +71,7 @@ class LogEntryVoter extends ExtendedVoter
|
||||||
}
|
}
|
||||||
|
|
||||||
//In other cases, this behaves like the read permission
|
//In other cases, this behaves like the read permission
|
||||||
return $this->voteOnUser('read', $subject, $user);
|
return $this->voteOnAttribute('read', $subject, $token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -41,23 +41,25 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Security\Voter;
|
namespace App\Security\Voter;
|
||||||
|
|
||||||
|
use App\Services\UserSystem\VoterHelper;
|
||||||
use Symfony\Bundle\SecurityBundle\Security;
|
use Symfony\Bundle\SecurityBundle\Security;
|
||||||
use App\Entity\Parts\Part;
|
use App\Entity\Parts\Part;
|
||||||
use App\Entity\PriceInformations\Orderdetail;
|
use App\Entity\PriceInformations\Orderdetail;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
use App\Services\UserSystem\PermissionManager;
|
use App\Services\UserSystem\PermissionManager;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
|
|
||||||
class OrderdetailVoter extends ExtendedVoter
|
final class OrderdetailVoter extends Voter
|
||||||
{
|
{
|
||||||
public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager, protected Security $security)
|
public function __construct(private readonly Security $security, private readonly VoterHelper $helper)
|
||||||
{
|
{
|
||||||
parent::__construct($resolver, $entityManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element'];
|
protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element'];
|
||||||
|
|
||||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||||
{
|
{
|
||||||
if (! is_a($subject, Orderdetail::class, true)) {
|
if (! is_a($subject, Orderdetail::class, true)) {
|
||||||
throw new \RuntimeException('This voter can only handle Orderdetail objects!');
|
throw new \RuntimeException('This voter can only handle Orderdetail objects!');
|
||||||
|
@ -73,7 +75,7 @@ class OrderdetailVoter extends ExtendedVoter
|
||||||
|
|
||||||
//If we have no part associated use the generic part permission
|
//If we have no part associated use the generic part permission
|
||||||
if (is_string($subject) || !$subject->getPart() instanceof Part) {
|
if (is_string($subject) || !$subject->getPart() instanceof Part) {
|
||||||
return $this->resolver->inherit($user, 'parts', $operation) ?? false;
|
return $this->helper->isGranted($token, 'parts', $operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Otherwise vote on the part
|
//Otherwise vote on the part
|
||||||
|
|
|
@ -22,6 +22,7 @@ declare(strict_types=1);
|
||||||
*/
|
*/
|
||||||
namespace App\Security\Voter;
|
namespace App\Security\Voter;
|
||||||
|
|
||||||
|
use App\Services\UserSystem\VoterHelper;
|
||||||
use Symfony\Bundle\SecurityBundle\Security;
|
use Symfony\Bundle\SecurityBundle\Security;
|
||||||
use App\Entity\Base\AbstractDBElement;
|
use App\Entity\Base\AbstractDBElement;
|
||||||
use App\Entity\Parameters\AbstractParameter;
|
use App\Entity\Parameters\AbstractParameter;
|
||||||
|
@ -40,16 +41,17 @@ use App\Entity\UserSystem\User;
|
||||||
use App\Services\UserSystem\PermissionManager;
|
use App\Services\UserSystem\PermissionManager;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
|
|
||||||
class ParameterVoter extends ExtendedVoter
|
final class ParameterVoter extends Voter
|
||||||
{
|
{
|
||||||
|
|
||||||
public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager, protected Security $security)
|
public function __construct(private readonly Security $security, private readonly VoterHelper $helper)
|
||||||
{
|
{
|
||||||
parent::__construct($resolver, $entityManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||||
{
|
{
|
||||||
//return $this->resolver->inherit($user, 'attachments', $attribute) ?? false;
|
//return $this->resolver->inherit($user, 'attachments', $attribute) ?? false;
|
||||||
|
|
||||||
|
@ -104,7 +106,7 @@ class ParameterVoter extends ExtendedVoter
|
||||||
throw new RuntimeException('Encountered unknown Parameter type: ' . (is_object($subject) ? $subject::class : $subject));
|
throw new RuntimeException('Encountered unknown Parameter type: ' . (is_object($subject) ? $subject::class : $subject));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->resolver->inherit($user, $param, $attribute) ?? false;
|
return $this->helper->isGranted($token, $param, $attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function supports(string $attribute, $subject): bool
|
protected function supports(string $attribute, $subject): bool
|
||||||
|
|
|
@ -41,31 +41,31 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Security\Voter;
|
namespace App\Security\Voter;
|
||||||
|
|
||||||
|
use App\Services\UserSystem\VoterHelper;
|
||||||
use Symfony\Bundle\SecurityBundle\Security;
|
use Symfony\Bundle\SecurityBundle\Security;
|
||||||
use App\Entity\Parts\Part;
|
use App\Entity\Parts\Part;
|
||||||
use App\Entity\Parts\PartLot;
|
use App\Entity\Parts\PartLot;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
use App\Services\UserSystem\PermissionManager;
|
use App\Services\UserSystem\PermissionManager;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
|
|
||||||
class PartLotVoter extends ExtendedVoter
|
final class PartLotVoter extends Voter
|
||||||
{
|
{
|
||||||
public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager, protected Security $security)
|
public function __construct(private readonly Security $security, private readonly VoterHelper $helper)
|
||||||
{
|
{
|
||||||
parent::__construct($resolver, $entityManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element', 'withdraw', 'add', 'move'];
|
protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element', 'withdraw', 'add', 'move'];
|
||||||
|
|
||||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||||
{
|
{
|
||||||
if (! is_a($subject, PartLot::class, true)) {
|
$user = $this->helper->resolveUser($token);
|
||||||
throw new \RuntimeException('This voter can only handle PartLot objects!');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_array($attribute, ['withdraw', 'add', 'move'], true))
|
if (in_array($attribute, ['withdraw', 'add', 'move'], true))
|
||||||
{
|
{
|
||||||
$base_permission = $this->resolver->inherit($user, 'parts_stock', $attribute) ?? false;
|
$base_permission = $this->helper->isGranted($token, 'parts_stock', $attribute);
|
||||||
|
|
||||||
$lot_permission = true;
|
$lot_permission = true;
|
||||||
//If the lot has an owner, we need to check if the user is the owner of the lot to be allowed to withdraw it.
|
//If the lot has an owner, we need to check if the user is the owner of the lot to be allowed to withdraw it.
|
||||||
|
@ -86,7 +86,7 @@ class PartLotVoter extends ExtendedVoter
|
||||||
|
|
||||||
//If we have no part associated use the generic part permission
|
//If we have no part associated use the generic part permission
|
||||||
if (is_string($subject) || !$subject->getPart() instanceof Part) {
|
if (is_string($subject) || !$subject->getPart() instanceof Part) {
|
||||||
return $this->resolver->inherit($user, 'parts', $operation) ?? false;
|
return $this->helper->isGranted($token, 'parts', $operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Otherwise vote on the part
|
//Otherwise vote on the part
|
||||||
|
|
|
@ -24,29 +24,36 @@ namespace App\Security\Voter;
|
||||||
|
|
||||||
use App\Entity\Parts\Part;
|
use App\Entity\Parts\Part;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
|
use App\Services\UserSystem\VoterHelper;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Voter that votes on Part entities.
|
* A Voter that votes on Part entities.
|
||||||
*
|
*
|
||||||
* See parts permissions for valid operations.
|
* See parts permissions for valid operations.
|
||||||
*/
|
*/
|
||||||
class PartVoter extends ExtendedVoter
|
final class PartVoter extends Voter
|
||||||
{
|
{
|
||||||
final public const READ = 'read';
|
final public const READ = 'read';
|
||||||
|
|
||||||
|
public function __construct(private readonly VoterHelper $helper)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected function supports($attribute, $subject): bool
|
protected function supports($attribute, $subject): bool
|
||||||
{
|
{
|
||||||
if (is_a($subject, Part::class, true)) {
|
if (is_a($subject, Part::class, true)) {
|
||||||
return $this->resolver->isValidOperation('parts', $attribute);
|
return $this->helper->isValidOperation('parts', $attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Allow class name as subject
|
//Allow class name as subject
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||||
{
|
{
|
||||||
//Null concealing operator means, that no
|
//Null concealing operator means, that no
|
||||||
return $this->resolver->inherit($user, 'parts', $attribute) ?? false;
|
return $this->helper->isGranted($token, 'parts', $attribute);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
* This use the syntax like "@permission.op"
|
* This use the syntax like "@permission.op"
|
||||||
* However you should use the "normal" object based voters if possible, because they are needed for a future ACL system.
|
* However you should use the "normal" object based voters if possible, because they are needed for a future ACL system.
|
||||||
*/
|
*/
|
||||||
class PermissionVoter extends Voter
|
final class PermissionVoter extends Voter
|
||||||
{
|
{
|
||||||
public function __construct(private readonly VoterHelper $helper)
|
public function __construct(private readonly VoterHelper $helper)
|
||||||
{
|
{
|
||||||
|
@ -62,7 +62,7 @@ class PermissionVoter extends Voter
|
||||||
$attribute = ltrim($attribute, '@');
|
$attribute = ltrim($attribute, '@');
|
||||||
[$perm, $op] = explode('.', $attribute);
|
[$perm, $op] = explode('.', $attribute);
|
||||||
|
|
||||||
$valid = $this->resolver->isValidOperation($perm, $op);
|
$valid = $this->helper->isValidOperation($perm, $op);
|
||||||
|
|
||||||
//if an invalid operation is encountered, throw an exception so the developer knows it
|
//if an invalid operation is encountered, throw an exception so the developer knows it
|
||||||
if(!$valid) {
|
if(!$valid) {
|
||||||
|
|
|
@ -41,6 +41,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Security\Voter;
|
namespace App\Security\Voter;
|
||||||
|
|
||||||
|
use App\Services\UserSystem\VoterHelper;
|
||||||
use Symfony\Bundle\SecurityBundle\Security;
|
use Symfony\Bundle\SecurityBundle\Security;
|
||||||
use App\Entity\PriceInformations\Orderdetail;
|
use App\Entity\PriceInformations\Orderdetail;
|
||||||
use App\Entity\Parts\Part;
|
use App\Entity\Parts\Part;
|
||||||
|
@ -48,22 +49,19 @@ use App\Entity\PriceInformations\Pricedetail;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
use App\Services\UserSystem\PermissionManager;
|
use App\Services\UserSystem\PermissionManager;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
|
|
||||||
class PricedetailVoter extends ExtendedVoter
|
final class PricedetailVoter extends Voter
|
||||||
{
|
{
|
||||||
public function __construct(PermissionManager $resolver, EntityManagerInterface $entityManager, protected Security $security)
|
public function __construct(private readonly Security $security, private readonly VoterHelper $helper)
|
||||||
{
|
{
|
||||||
parent::__construct($resolver, $entityManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element'];
|
protected const ALLOWED_PERMS = ['read', 'edit', 'create', 'delete', 'show_history', 'revert_element'];
|
||||||
|
|
||||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||||
{
|
{
|
||||||
if (!is_a($subject, Pricedetail::class, true)) {
|
|
||||||
throw new \RuntimeException('This voter can only handle Pricedetails objects!');
|
|
||||||
}
|
|
||||||
|
|
||||||
$operation = match ($attribute) {
|
$operation = match ($attribute) {
|
||||||
'read' => 'read',
|
'read' => 'read',
|
||||||
'edit', 'create', 'delete' => 'edit',
|
'edit', 'create', 'delete' => 'edit',
|
||||||
|
@ -74,7 +72,7 @@ class PricedetailVoter extends ExtendedVoter
|
||||||
|
|
||||||
//If we have no part associated use the generic part permission
|
//If we have no part associated use the generic part permission
|
||||||
if (is_string($subject) || !$subject->getOrderdetail() instanceof Orderdetail || !$subject->getOrderdetail()->getPart() instanceof Part) {
|
if (is_string($subject) || !$subject->getOrderdetail() instanceof Orderdetail || !$subject->getOrderdetail()->getPart() instanceof Part) {
|
||||||
return $this->resolver->inherit($user, 'parts', $operation) ?? false;
|
return $this->helper->isGranted($token, 'parts', $operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Otherwise vote on the part
|
//Otherwise vote on the part
|
||||||
|
|
|
@ -32,10 +32,14 @@ use App\Entity\Parts\Storelocation;
|
||||||
use App\Entity\Parts\Supplier;
|
use App\Entity\Parts\Supplier;
|
||||||
use App\Entity\PriceInformations\Currency;
|
use App\Entity\PriceInformations\Currency;
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
|
use App\Services\UserSystem\VoterHelper;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
|
|
||||||
use function get_class;
|
use function get_class;
|
||||||
use function is_object;
|
use function is_object;
|
||||||
|
|
||||||
class StructureVoter extends ExtendedVoter
|
final class StructureVoter extends Voter
|
||||||
{
|
{
|
||||||
protected const OBJ_PERM_MAP = [
|
protected const OBJ_PERM_MAP = [
|
||||||
AttachmentType::class => 'attachment_types',
|
AttachmentType::class => 'attachment_types',
|
||||||
|
@ -49,6 +53,10 @@ class StructureVoter extends ExtendedVoter
|
||||||
MeasurementUnit::class => 'measurement_units',
|
MeasurementUnit::class => 'measurement_units',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function __construct(private readonly VoterHelper $helper)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if the attribute and subject are supported by this voter.
|
* Determines if the attribute and subject are supported by this voter.
|
||||||
*
|
*
|
||||||
|
@ -62,7 +70,7 @@ class StructureVoter extends ExtendedVoter
|
||||||
if (is_object($subject) || is_string($subject)) {
|
if (is_object($subject) || is_string($subject)) {
|
||||||
$permission_name = $this->instanceToPermissionName($subject);
|
$permission_name = $this->instanceToPermissionName($subject);
|
||||||
//If permission name is null, then the subject is not supported
|
//If permission name is null, then the subject is not supported
|
||||||
return (null !== $permission_name) && $this->resolver->isValidOperation($permission_name, $attribute);
|
return (null !== $permission_name) && $this->helper->isValidOperation($permission_name, $attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -99,10 +107,10 @@ class StructureVoter extends ExtendedVoter
|
||||||
*
|
*
|
||||||
* @param string $attribute
|
* @param string $attribute
|
||||||
*/
|
*/
|
||||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||||
{
|
{
|
||||||
$permission_name = $this->instanceToPermissionName($subject);
|
$permission_name = $this->instanceToPermissionName($subject);
|
||||||
//Just resolve the permission
|
//Just resolve the permission
|
||||||
return $this->resolver->inherit($user, $permission_name, $attribute) ?? false;
|
return $this->helper->isGranted($token, $permission_name, $attribute);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,19 @@ declare(strict_types=1);
|
||||||
namespace App\Security\Voter;
|
namespace App\Security\Voter;
|
||||||
|
|
||||||
use App\Entity\UserSystem\User;
|
use App\Entity\UserSystem\User;
|
||||||
|
use App\Services\UserSystem\PermissionManager;
|
||||||
|
use App\Services\UserSystem\VoterHelper;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||||
|
|
||||||
use function in_array;
|
use function in_array;
|
||||||
|
|
||||||
class UserVoter extends ExtendedVoter
|
final class UserVoter extends Voter
|
||||||
{
|
{
|
||||||
|
public function __construct(private readonly VoterHelper $helper, private readonly PermissionManager $resolver)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if the attribute and subject are supported by this voter.
|
* Determines if the attribute and subject are supported by this voter.
|
||||||
*
|
*
|
||||||
|
@ -57,8 +66,10 @@ class UserVoter extends ExtendedVoter
|
||||||
*
|
*
|
||||||
* @param string $attribute
|
* @param string $attribute
|
||||||
*/
|
*/
|
||||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
|
||||||
{
|
{
|
||||||
|
$user = $this->helper->resolveUser($token);
|
||||||
|
|
||||||
if ($attribute === 'info') {
|
if ($attribute === 'info') {
|
||||||
//Every logged-in user (non-anonymous) can see the info pages of other users
|
//Every logged-in user (non-anonymous) can see the info pages of other users
|
||||||
if (!$user->isAnonymousUser()) {
|
if (!$user->isAnonymousUser()) {
|
||||||
|
@ -71,9 +82,9 @@ class UserVoter extends ExtendedVoter
|
||||||
|
|
||||||
//Check if the checked user is the user itself
|
//Check if the checked user is the user itself
|
||||||
if (($subject instanceof User) && $subject->getID() === $user->getID() &&
|
if (($subject instanceof User) && $subject->getID() === $user->getID() &&
|
||||||
$this->resolver->isValidOperation('self', $attribute)) {
|
$this->helper->isValidOperation('self', $attribute)) {
|
||||||
//Then we also need to check the self permission
|
//Then we also need to check the self permission
|
||||||
$tmp = $this->resolver->inherit($user, 'self', $attribute) ?? false;
|
$tmp = $this->helper->isGranted($token, 'self', $attribute);
|
||||||
//But if the self value is not allowed then use just the user value:
|
//But if the self value is not allowed then use just the user value:
|
||||||
if ($tmp) {
|
if ($tmp) {
|
||||||
return $tmp;
|
return $tmp;
|
||||||
|
@ -81,8 +92,8 @@ class UserVoter extends ExtendedVoter
|
||||||
}
|
}
|
||||||
|
|
||||||
//Else just check user permission:
|
//Else just check user permission:
|
||||||
if ($this->resolver->isValidOperation('users', $attribute)) {
|
if ($this->helper->isValidOperation('users', $attribute)) {
|
||||||
return $this->resolver->inherit($user, 'users', $attribute) ?? false;
|
return $this->helper->isGranted($token, 'users', $attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -107,4 +107,18 @@ final class VoterHelper
|
||||||
//Otherwise throw an exception
|
//Otherwise throw an exception
|
||||||
throw new \RuntimeException('The user could not be resolved.');
|
throw new \RuntimeException('The user could not be resolved.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the permission operation combination with the given names is existing.
|
||||||
|
* Just a proxy to the permission manager.
|
||||||
|
*
|
||||||
|
* @param string $permission the name of the permission which should be checked
|
||||||
|
* @param string $operation the name of the operation which should be checked
|
||||||
|
*
|
||||||
|
* @return bool true if the given permission operation combination is existing
|
||||||
|
*/
|
||||||
|
public function isValidOperation(string $permission, string $operation): bool
|
||||||
|
{
|
||||||
|
return $this->permissionManager->isValidOperation($permission, $operation);
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue