smartcoderx
smartcoderx

Reputation: 1081

Is granted for other user

After i read this chapter in cookbook

http://symfony.com/doc/current/cookbook/security/entity_provider.html

i create a entity "User" which implements the "AdvancedUserInterface" and a entity "Roles" which implements the "RoleInterface". Also i create a role structure in my "security.yml".

The relation between user and roles is a "ManyToMany" relation.

Everything is fine.

For the logged in user i can check a grant like this:

$this->get('security.context')->isGranted("ROLE_EDITOR");

But how can i check this grant for other user in database?

There is something like?

$this->get('security.context')->isGranted("ROLE_EDITOR", $user);

Upvotes: 6

Views: 6746

Answers (4)

ttskch
ttskch

Reputation: 131

Following code works completely with Voters as well.

namespace App\Security;

use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken;

class SecurityManager
{
    public function __construct(private AccessDecisionManagerInterface $adm)
    {
    }

    public function isGranted(UserInterface $user, mixed $attribute, mixed $subject = null): bool
    {
        $tokenStorage = new TokenStorage();
        $token = new PostAuthenticationToken($user, 'main', $user->getRoles());
        $tokenStorage->setToken($token);
        $authorizationChecker = new AuthorizationChecker($tokenStorage, $this->adm);

        return $authorizationChecker->isGranted($attribute, $subject);
    }
}
use App\Security\SecurityManager;

class Foo
{
    public function __construct(private SecurityManager $sm)
    {
    }
    
    public function bar(UserInterface $user, Baz $baz): void
    {
        if ($this->sm->isGranted($user, 'EDIT', $baz)) {
            // do something
        }
    }
}

Upvotes: 1

Erdal G.
Erdal G.

Reputation: 3000

Warning: this code only checks if the specified user has a given role. It is useful for example for showing users' roles, but cannot be used to effectively grant access to another user, as voters aren't used. This is the case for all answers here.

Symfony 5 answer

namespace App\Controller;

...
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
use Symfony\Component\Security\Core\Role\RoleHierarchy;

class UserController extends AbstractController
{
    private $roleHierarchy;

    /**
     * @Route("/users", name="users")
     */
    public function usersIndex(RoleHierarchyInterface $roleHierarchy)
    {
        $this->roleHierarchy = $roleHierarchy;

        // your user service or your Doctrine code here
        $users = ...

        foreach ($users as $user) {
            $roles = $roleHierarchy->getReachableRoleNames($user->getRoles());
            \dump($roles);

            if ($this->hasRole($user, 'ROLE_SUPER_ADMIN')) {
                ...
            }
        }
        ...
    }

    private function hasRole(User $user, string $role): bool 
    {
        $reachableRoles = $this->roleHierarchy->getReachableRoleNames($user->getRoles());

        foreach ($reachableRoles as $reachableRole) {
            if ($reachableRole === $role) {
                return true;
            }
        }

        return false;
    }
}

Note: I put everything in the controller for the sake of simplicity here, but of course I'd recommend to move the Role Management code into a separate service like @leberknecht's answer.

Upvotes: 4

spackmat
spackmat

Reputation: 1005

Within PHP code, I just inject the the RoleHierarchyInterface and use this simple expression:

if (in_array($role, $this->roleHierarchy->getReachableRoleNames($user->getRoles()))) {
  // whatever
}

In Twig, I wrote this simple extension (works with current Versions: Symfony 5.2 and Twig 3.3):

<?php
// /src/Twig/AccessCheckExtension.php
declare(strict_types=1);
namespace App\Twig;

use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;

class AccessCheckExtension extends AbstractExtension
{
    public function getFilters(): array
    {
        return [
            new TwigFilter('has_role', [AccessCheckRuntime::class, 'hasRole'])
        ];
    }
    
    public function getFunctions(): array
    {
        return [
            new TwigFunction('has_role', [AccessCheckRuntime::class, 'hasRole'])
        ];
    }
}

and

<?php
// /src/Twig/AccessCheckRuntime.php
declare(strict_types=1);
namespace App\Twig;

use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Twig\Extension\RuntimeExtensionInterface;

class AccessCheckRuntime implements RuntimeExtensionInterface
{
    /**
     * this uses PHP 8.0 constructor property promotion to reduce the needed lines
     **/
    public function __construct(
        protected RoleHierarchyInterface $roleHierarchy
    ){}

    public function hasRole(UserInterface $user, string $role): bool
    {
        return in_array($role, $this->roleHierarchy->getReachableRoleNames($user->getRoles()));
    }
}

This can be used in a Twig template as following:

{% if user|has_role('ROLE_WHATEVER') %}
Hello {{ user.name }}
{% endif %}

or

{% if has_role(user, 'ROLE_WHATEVER') %}
Hello {{ user.name }}
{% endif %}

Pretty straightforward. I prefer the filter variant.

Upvotes: 2

leberknecht
leberknecht

Reputation: 1736

Dont know if there is something build-in by now, but its pretty straight-forward to build:

class RoleCheckerService
{
    private const PROVIDER_KEY = 'role-check';
    /**
     * @var RoleHierarchyInterface
     */
    private $roleHierarchy;

    public function __construct(RoleHierarchyInterface $roleHierarchy)
    {
        $this->roleHierarchy = $roleHierarchy;
    }

    final public function isGranted(User $user, string $role): bool 
    {
        $token = new PreAuthenticatedToken($user, null, self::PROVIDER_KEY, $user->getRoles());
        $reachableRoles = $this->roleHierarchy->getReachableRoles($token->getRoles());

        foreach ($reachableRoles as $reachableRole) {
            if ($reachableRole->getRole() === $role) {
                return true;
            }
        }

        return false;
    }
}

Upvotes: 1

Related Questions