feat: improved assignment access checks with dedicated messages

This commit is contained in:
Björn Fromme
2025-12-03 09:22:59 +01:00
parent e54e3e85b9
commit 2e34aea17e
2 changed files with 41 additions and 59 deletions
@@ -31,6 +31,27 @@ class CreateController extends AbstractController
/** @var User $user */
$user = $this->getUser();
$teamer = $user->getTeamer();
// Only allow applications on empty slots
$availableSlots = (int) $assignment->getAvailableDispositions();
if (0 < $availableSlots) {
$this->addFlash('error', 'Auf diesem Einsatz sind bereits alle Plätze belegt');
return $this->redirectToRoute('app_teamer_index');
}
// Only teamers without existing applications may apply to the assignment
$existingApplication = $this
->entityManager
->getRepository(Application::class)
->findOneBy(['teamer' => $teamer, 'assignment' => $assignment]);
if (null !== $existingApplication) {
$this->addFlash('error', 'Du hast dich bereits auf diesen Einsatz beworben');
return $this->redirectToRoute('app_teamer_index');
}
$application = new Application($assignment, $teamer);
$form = $this->createForm(TeamerApplicationType::class, $application);
+18 -57
View File
@@ -2,10 +2,7 @@
namespace App\Security\Voter;
use App\Entity\Application;
use App\Entity\Assignment;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
@@ -18,10 +15,8 @@ class AssignmentVoter extends Voter
public const PUBLISH = 'PUBLISH';
public const CALL_OFF = 'CALL_OFF';
public function __construct(
private readonly Security $security,
private readonly EntityManagerInterface $entityManager,
) {
public function __construct(private readonly Security $security)
{
}
protected function supports(string $attribute, mixed $subject): bool
@@ -30,65 +25,31 @@ class AssignmentVoter extends Voter
return false;
}
return in_array($attribute, [static::VIEW, static::EDIT, static::APPLY, static::PUBLISH, static::CALL_OFF]);
return in_array($attribute, [self::VIEW, self::EDIT, self::APPLY, self::PUBLISH, self::CALL_OFF], true);
}
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
if (null === $token->getUser()) {
return false;
}
/** @var Assignment $assignment */
$assignment = $subject;
// All authenticated users may view assignments
if (static::VIEW === $attribute && null !== $token->getUser()) {
return true;
}
// Only users with administrative role may edit assignments
if (static::EDIT === $attribute) {
return $this->security->isGranted('ROLE_ADMINISTRATIVE');
}
// Only users with administrative role may publish assignments currently in draft
if (static::PUBLISH === $attribute) {
return Assignment::STATUS_DRAFT === $assignment->getStatus()
&& $this->security->isGranted('ROLE_ADMINISTRATIVE');
}
// Only users with administrative role may call off assignments
if (static::CALL_OFF === $attribute) {
return Assignment::STATUS_CALLED_OFF !== $assignment->getStatus()
&& $this->security->isGranted('ROLE_ADMINISTRATIVE');
}
// Only allow applications to published, not deleted and not called off assignments
if (
true === in_array($assignment->getStatus(), [
return match ($attribute) {
self::VIEW => true,
self::EDIT => $this->security->isGranted('ROLE_ADMINISTRATIVE'),
self::PUBLISH => Assignment::STATUS_DRAFT === $assignment->getStatus()
&& $this->security->isGranted('ROLE_ADMINISTRATIVE'),
self::CALL_OFF => Assignment::STATUS_CALLED_OFF !== $assignment->getStatus()
&& $this->security->isGranted('ROLE_ADMINISTRATIVE'),
self::APPLY => false === in_array($assignment->getStatus(), [
Assignment::STATUS_DRAFT,
Assignment::STATUS_DELETED,
Assignment::STATUS_CALLED_OFF,
])) {
return false;
}
// Only allow applications on empty slots
$availableSlots = (int) $assignment->getAvailableDispositions();
if (0 < $availableSlots) {
return $assignment->getDispositions()->count() < $availableSlots;
}
// Only teamers without existing applications may apply to the assignment
if ($this->security->isGranted('ROLE_TEAMER')) {
/** @var User $user */
$user = $token->getUser();
$teamer = $user->getTeamer();
$application = $this
->entityManager
->getRepository(Application::class)
->findOneBy(['teamer' => $teamer, 'assignment' => $assignment]);
return null === $application;
}
return false;
], true),
default => false,
};
}
}