Reputation: 1081
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
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
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.
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
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
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