Search code examples
symfonypermissionssymfony4

Symfony4 Access to the site based on permissions in the entity associated with the User


I have question about Symfony Voters. I have entity UserPermissionList which looks like that:

class UserPermissionList {

/**
 * @ORM\Id()
 * @ORM\GeneratedValue()
 * @ORM\Column(type="integer")
 */
private $id;

/**
 * @ORM\Column(type="string", length=255)
 */
private $name;

/**
 * @ORM\Column(type="string", length=50)
 */
private $path;

/**
 * @ORM\Column(type="smallint")
 */
private $view;

public function getId(): ?int {
    return $this->id;
}

public function getName(): ?string {
    return $this->name;
}

public function setName(string $name): self {
    $this->name = $name;

    return $this;
}

/**
 * @return mixed
 */
public function getPath() {
    return $this->path;
}

/**
 * @param mixed $path
 */
public function setPath($path): void {
    $this->path = $path;
}

/**
 * @return mixed
 */
public function getView() {
    return $this->view;
}

/**
 * @param mixed $view
 */
public function setView($view): void {
    $this->view = $view;
}
}

Path property is actual path to a specific route e.g: /desktop or /mobile

My User entity looks like that:

class User implements UserInterface
{
public const ROLE_USER = 'ROLE_USER';

/**
 * @ORM\Id()
 * @ORM\GeneratedValue()
 * @ORM\Column(type="integer")
 */
private $id;

/**
 * @ORM\ManyToMany(targetEntity="App\Entity\UserPermissionList")
 * @ORM\JoinTable(name="user_permisions")
 */
private $permissions;

public function __construct()
{
    $this->permissions = new ArrayCollection();
}

public function getId(): ?int
{
    return $this->id;
}

/**
 * @return Collection|UserPermissionList[]
 */
public function getPermissions(): Collection
{
    return $this->permissions;
}

public function addPermission(UserPermissionList $permission): self
{
    if (!$this->permissions->contains($permission)) {
        $this->permissions[] = $permission;
    }

    return $this;
}

public function removePermission(UserPermissionList $permission): self
{
    if ($this->permissions->contains($permission)) {
        $this->permissions->removeElement($permission);
    }

    return $this;
}}

And now, when User is related to specific permission in UserPermissionList I need to make logic allowing him access to route specified in this Entity.

Eg. I have permission with name: Desktop, path: /dekstop and view: 2 (means that permission can be displayed only on desktop devices).

Only User who has relation to this permission in UserPermissionList can visit page /desktop.

How my Voter should looks like?


Solution

  • EDIT. Ok, i made Voter like this:

    class ViewVoter extends Voter
    {
    /**
     * @var UserPermissionListRepository
     */
    private $permissionListRepository;
    
    public function __construct(UserPermissionListRepository $permissionListRepository)
    {
        $this->permissionListRepository = $permissionListRepository;
    }
    
    protected function supports($attribute, $subject)
    {
        $permissions = $this->permissionListRepository->findAll();
        $permissionList = [];
    
        foreach ($permissions as $permission) {
            $permissionList[] = $permission->getPath();
        }
    
        return in_array($attribute, $permissionList)
            && $subject instanceof User;
    }
    
    protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
    {
        $permissions = $subject->getPermissions();
        $permissionList = [];
    
        foreach ($permissions as $permission) {
            $permissionList[] = $permission->getPath();
        }
    
        return in_array($attribute, $permissionList);
    }
    }
    

    And than in my Controllers, e.g. in DesktopController i add annotation:

    @Security("is_granted('/desktop', user)")