Lashae
Lashae

Reputation: 1382

Symfony2 access control with multiple role requirement

On my site, except some spesific pages (login, register, pwd reset) users are required to login. I have implemented remember me feature and it works well.

What I would like to achieve is, for administration pages, users should have admin role and not remembered. To check this requirement I used allow_if in the relevant access_control rule, however it denies my admin user's access, although session is not remembered and I can confirm that session has UsernamePasswordToken on debug toolbar.

My access_control rules are as follows: (4th one doesn't work)

access_control:
    - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/user/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/user/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/admin, allow_if: "has_role('ROLE_ADMIN') and has_role('IS_AUTHENTICATED_FULLY')" }
    - { path: ^/, role: IS_AUTHENTICATED_REMEMBERED }

If I remove and has_role('IS_AUTHENTICATED_FULLY') part from the relevant access control rule, user can pass authorization so the problem seems to be this part.

What is the problem with has_role('IS_AUTHENTICATED_FULLY') ?

Symfony Version: 2.7.5

Upvotes: 1

Views: 3154

Answers (2)

Lashae
Lashae

Reputation: 1382

Well, I found a workaround to fix the issue.

How to use Expressions in Security, Routing, Services, and Validation cookbook article on official website has a relevant section and states the possible use of is_remember_me() and is_fully_authenticated() methods for checking the existence IS_AUTHENTICATED_REMEMBERED and IS_AUTHENTICATED_FULLY roles respective.

is_remember_me is different than checking IS_AUTHENTICATED_REMEMBERED

The is_remember_me and is_authenticated_fully functions are similar to using IS_AUTHENTICATED_REMEMBERED and IS_AUTHENTICATED_FULLY with the isGranted function - but they are not the same. The following shows the difference:

use Symfony\Component\ExpressionLanguage\Expression;
// ...

$ac = $this->get('security.authorization_checker');
$access1 = $ac->isGranted('IS_AUTHENTICATED_REMEMBERED');

$access2 = $ac->isGranted(new Expression(
'is_remember_me() or is_fully_authenticated()'
));

Here, $access1 and $access2 will be the same value. Unlike the behavior of IS_AUTHENTICATED_REMEMBERED and IS_AUTHENTICATED_FULLY, the is_remember_me function only returns true if the user is authenticated via a remember-me cookie and is_fully_authenticated only returns true if the user has actually logged in during this session (i.e. is full-fledged).

Using that documentation section, I revised my access control rules as follows and it works now:

access_control:
    - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/user/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/user/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/admin, allow_if: "has_role('ROLE_ADMIN') and is_fully_authenticated()" }
    - { path: ^/, role: IS_AUTHENTICATED_REMEMBERED }

However, I still think that this is an issue and is not the expected behaviour. So, I opened an issue in the official issue tracker:

Github Issue: #16096 - Expression engine has_role() function can't process implicit roles

Upvotes: 1

Dmitry Malyshenko
Dmitry Malyshenko

Reputation: 3051

Documentation:

Add Code to Deny Access There are two ways to deny access to something:

  1. access_control in security.yml allows you to protect URL patterns (e.g. /admin/*). This is easy, but less flexible;
  2. in your code via the security.authorization_checker service.

You're trying to use the first way, but it's possible to make only by second.

You need to perform an authorization checking in your controller by putting inside it a code

if (!$this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
    throw $this->createAccessDeniedException();
}

Also you could use SensioFrameworkExtraBundle and just add an annotation to your controller

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;

...

/**
 * @Security("is_granted('IS_AUTHENTICATED_FULLY')")
 */
public function showAction()
{
}

Upvotes: 0

Related Questions