Add propper access control for snips with public option
This commit is contained in:
@ -5,6 +5,7 @@ namespace App\Controller;
|
||||
use App\Entity\Snip;
|
||||
use App\Form\SnipType;
|
||||
use App\Repository\SnipRepository;
|
||||
use App\Security\Voter\SnipVoter;
|
||||
use App\Service\SnipServiceFactory;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
@ -33,6 +34,8 @@ class SnipController extends AbstractController
|
||||
#[Route('/single/{snip}', name: '_single')]
|
||||
public function single(Snip $snip): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(SnipVoter::VIEW, $snip);
|
||||
|
||||
return $this->render('snip/single.html.twig', [
|
||||
'snip' => $snip,
|
||||
'content' => $this->snipServiceFactory->create($snip)->get(),
|
||||
@ -42,6 +45,8 @@ class SnipController extends AbstractController
|
||||
#[Route('/raw/{snip}', name: '_raw')]
|
||||
public function raw(Snip $snip, Request $request): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(SnipVoter::VIEW, $snip);
|
||||
|
||||
$response = new Response(
|
||||
$this->snipServiceFactory->create($snip)->get(),
|
||||
Response::HTTP_OK,
|
||||
@ -67,6 +72,8 @@ class SnipController extends AbstractController
|
||||
#[Route('/edit/{snip}', name: '_edit')]
|
||||
public function edit(Snip $snip, Request $request): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted(SnipVoter::EDIT, $snip);
|
||||
|
||||
$form = $this->createForm(SnipType::class, $snip);
|
||||
$form->add('Save', SubmitType::class);
|
||||
if ($snip->getId()) {
|
||||
|
@ -19,6 +19,9 @@ class Snip
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $name = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $public = null;
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->name ?? '';
|
||||
@ -40,4 +43,16 @@ class Snip
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isPublic(): ?bool
|
||||
{
|
||||
return $this->public;
|
||||
}
|
||||
|
||||
public function setPublic(bool $public): self
|
||||
{
|
||||
$this->public = $public;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ class SnipType extends AbstractType
|
||||
'attr' => ['rows' => 20],
|
||||
'mapped' => false,
|
||||
])
|
||||
->add('public')
|
||||
;
|
||||
}
|
||||
|
||||
|
47
src/Security/Voter/SnipVoter.php
Normal file
47
src/Security/Voter/SnipVoter.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Security\Voter;
|
||||
|
||||
use App\Entity\Snip;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
class SnipVoter extends Voter
|
||||
{
|
||||
public const EDIT = 'edit';
|
||||
public const VIEW = 'view';
|
||||
|
||||
protected function supports(string $attribute, mixed $subject): bool
|
||||
{
|
||||
// replace with your own logic
|
||||
// https://symfony.com/doc/current/security/voters.html
|
||||
return in_array($attribute, [self::EDIT, self::VIEW])
|
||||
&& $subject instanceof Snip;
|
||||
}
|
||||
|
||||
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
|
||||
{
|
||||
/** @var Snip $subject */
|
||||
$user = $token->getUser();
|
||||
// if the user is anonymous, do not grant access
|
||||
if (!$user instanceof UserInterface) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ... (check conditions and return true to grant permission) ...
|
||||
switch ($attribute) {
|
||||
case self::VIEW:
|
||||
if ($subject->isPublic()) {
|
||||
return true;
|
||||
}
|
||||
case self::EDIT:
|
||||
if ($subject->getCreatedBy() === $user) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user