Abdel5
Abdel5

Reputation: 1120

Symfony2 check user role in Service

How can I check user role in code of a symfony2 Service? Should I simply send a user roles object to a Service or is there solution which allows me to do the check from Service level?

Upvotes: 4

Views: 8896

Answers (5)

Francesco Abeni
Francesco Abeni

Reputation: 4265

Assuming that:

  • you inject security.context in your service

    public function __construct(SecurityContext $securityContext) {
      $this->securityContext = $securityContext;
    }
    
  • you are using FosUserBundle

  • I understood correctly your question :-)

you should be able to do:

$token = $this->securityContext->getToken();
if (empty($token)) {
    return [];
}
$user = $token->getUser();
if (empty($user)) {
    return [];
}
$roles = $user->getRoles();

Updates 2016/11/28

  1. Answer updated to pass the specific dependency instead of the whole container, as that is a bad practice (see comment by @Shady)
  2. Note that starting from Symfony 2.6, SecurityContext is deprecated, you should use TokenStorage instead.

Upvotes: 2

Mick
Mick

Reputation: 31919

BE CAREFUL...

Be very careful about what you are doing when it comes to Security.. DO NOT DO THIS.

This is because, you need to understand that the TokenInterface is not the same as UserInterface.

TokenInterface is the interface for the user authentication information.

You must use it - If you don't have the token made available in your current service, use the SecurityContext or the AccessDecisionManager.


Note: In Symfony > 2.6, some improvements were made, and SecurityContext was deprecated, and split into TokenStorage and AuthorizationChecker. So, you can go ahead and use AuthorizationChecker now.


Look! Even FOSUserBundle implementation talks about it:

/**
 * Never use this to check if this user has access to anything!
 *
 * Use the SecurityContext, or an implementation of AccessDecisionManager
 * instead, e.g.
 *
 *         $securityContext->isGranted('ROLE_USER');
 *
 * @param string $role
 * @return Boolean
 */
function hasRole($role);

Look how it works, look at things like getRoles, Hierarchical roles for example.

ROLES in the database are not to be trusted - if you get a User object from the database, and use your getRoles() or even worse the special isGranted($role) you have created, they will NOT have the roles you expect your user to have.

This should be your bible, and now you can understand the differences between:

1 $user->getRoles()

array(2) { 
    [0]=> string(10) "ROLE_ADMIN" 
    [1]=> string(9) "ROLE_USER" 
}

2 $this->token->getRoles()

array(2) { 
    [0]=> object(Symfony\Component\Security\Core\Role\Role) 
        (1) { ["role":"Symfony\Component\Security\Core\Role\Role":private]=> string(10) "ROLE_ADMIN" } 
    [1]=> object(Symfony\Component\Security\Core\Role\Role)
        (1) { ["role":"Symfony\Component\Security\Core\Role\Role":private]=> string(22) "ROLE_ALLOWED_TO_SWITCH" } 
    [2]=> object(Symfony\Component\Security\Core\Role\Role)
        (1) { ["role":"Symfony\Component\Security\Core\Role\Role":private]=> string(9) "ROLE_USER" } 

} 

3 $this->token->getUser->getRoles()

array(2) { 
    [0]=> string(10) "ROLE_ADMIN" 
    [1]=> string(22) "ROLE_ALLOWED_TO_SWITCH" 
    [2]=> string(9) "ROLE_USER" 
}

When you have security.yml

security:
    role_hierarchy:
        ROLE_ADMIN: [ROLE_ALLOWED_TO_SWITCH]

Upvotes: 0

YONGSOO KIM
YONGSOO KIM

Reputation: 1300

EDIT @Mick - DO NOT EVER DO THE FOLLOWING

See here for more info.


I think it would be much easier if you implemented an isGranted function in the User entity:

Class User implements UserInterface {
    ...
    public function isGranted($role)
    {
        return in_array($role, $this->getRoles());
    }
}

You can now easily check for granted roles in every layer of your application. In PHP:

$user->isGranted("USER_ADMIN")

Or in Twig:

user.granted("USER_ADMIN")

If you need to check a role for the current user, you can do this in Twig:

app.user.granted("USER_ADMIN")

Note: the variable "app" is globally defined.

Note 2: this code may throw an exception if you use it outside the secured area of your app, since app.user would be NULL.

Upvotes: 0

Yamiko
Yamiko

Reputation: 5453

The other answers have you pass the container instead of the authorization checker. While they work they create a tight dependancy on the container making it harder to migrate your code to other projects. Instead you should only pass the authorization checker.

Here is an example taken from the symfony docs.

app/config/services.yml

services:
    newsletter_manager:
        class:     "AppBundle\Newsletter\NewsletterManager"
        arguments: ["@security.authorization_checker"]

src/Appbundle/Newsletter/NewsletterManager.php

namespace AppBundle\Newsletter;

use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
// ...

class NewsletterManager
{
    protected $authorizationChecker;

    public function __construct(AuthorizationCheckerInterface $authorizationChecker)
    {
        $this->authorizationChecker = $authorizationChecker;
    }

    public function sendNewsletter()
    {
        if (false === $this->authorizationChecker->isGranted('ROLE_NEWSLETTER_ADMIN')) {
            throw new AccessDeniedException();
        }

        // ...
    }

    // ...
}

Upvotes: 13

Abdel5
Abdel5

Reputation: 1120

The answer is that to be able to use Symfony2 classes in service you need to provide service with a container.

Service definition:

services:
  /.../:
    class: /.../
    arguments: ['@service_container']

Service file:

use Symfony\Component\DependencyInjection\ContainerInterface as Container;

class globalHelper {    

    private $container;

    public function __construct(Container $container) {
        $this->container = $container;
    }

Upvotes: 0

Related Questions