Search code examples
symfonysecuritysymfony-3.3

Symfony Role and security explained


I'm trying to learn the Symfony roles and security. My current security.yml file, looks like this:

role_hierarchy: 
    ROLE_USER: ROLE_DO_ALMOST_NOTHING
    ROLE_EDITOR: [ ROLE_USER, ROLE_ALLOWED_TO_EDIT ]
    ROLE_CONTRIBUTOR: [ ROLE_EDITOR, ROLE_ALLOWED_TO_CONTRIBUTE ]
    ROLE_ADMIN: [ ROLE_CONTRIBUTOR ]
    ROLE_SUPER_ADMIN: [ ROLE_ADMIN, ROLE_ALLOWED_TO_DO_ANY_THING ]

access_control: 
    - { path: ^/admin, roles: ROLE_USER }
    - { path: ^/admin/editor, roles: ROLE_ADMIN }
    - { path: ^/editor, roles: ROLE_EDITOR }
    - { path: ^/contributor, roles: ROLE_CONTRIBUTOR }
    - { path: ^/super, roles: ROLE_SUPER_ADMIN }

And I'm using this setup for my users:

providers:
    in_memory:
        memory: 
            users:
                person:
                    password: password!
                    roles: 'ROLE_USER'
                admin: 
                    password: password2
                    roles: 'ROLE_ADMIN'

Here is my problem. I'm been missing around with the access_control portion of security, however, the path ^/admin/editor with the roles marked as ROLE_ADMIN will allow the user person to access the route even though the person user didn't have the role of ROLE_ADMIN. I was wondering if this is because the route itself is shared by the same controller as the ^admin route? Or done someone see where I might have gone wrong with the code, since the user person can access the route that I they shouldn't.

The other routes:

- { path: ^/editor, roles: ROLE_EDITOR }
- { path: ^/contributor, roles: ROLE_CONTRIBUTOR }
- { path: ^/super, roles: ROLE_SUPER_ADMIN }

Work as expected.


Solution

  • The issue is you are matching /admin before you match admin/editor, and that only requires the ROLE_USER role. When you have:

    - { path: ^/admin, roles: ROLE_USER }
    

    That matches everything that starts with /admin, including admin/editor. As soon as Symfony finds the appropriate route it will not check the first of them. So your ^/admin/editor/ check is never reached. Try this instead:

    access_control: 
        - { path: ^/admin/editor, roles: ROLE_ADMIN }
        - { path: ^/admin, roles: ROLE_USER }
        - { path: ^/editor, roles: ROLE_EDITOR }
        - { path: ^/contributor, roles: ROLE_CONTRIBUTOR }
        - { path: ^/super, roles: ROLE_SUPER_ADMIN }
    

    As a good rule of thumb, your most granular/specific routes should be put first. Any sub-routes should always be put ahead of the main route.