I’m working on a legacy project with a lot of ancient staffs. There’re huge numbers of actions which are used really rare. Half a year ago we upgraded from Symfony 2.8 to Symfony 4.4. All worked pretty well until a manager tried to use one of the old action that now returns AccessDeniedException: Access Denied.
I’ve checked Symfony documentation and all seems pretty straightforward for me.
The documentation says:
Checking to see if a User is Logged In (IS_AUTHENTICATED_FULLY)
If you only want to check if a user is logged in (you don’t care about roles), you have two options. First, if you’ve given every user ROLE_USER, you can check for that role.
There is app/config/security.yml with the next configuration:
security:
access_decision_manager:
strategy: unanimous
allow_if_all_abstain: true
encoders:
FOSUserBundleModelUserInterface:
algorithm: sha512
encode_as_base64: false
iterations: 1
role_hierarchy:
ROLE_CUSTOMER_ADMIN: ROLE_USER
ROLE_ADMIN: ROLE_USER
ROLE_ADMIN_CAN_EDIT_PERMISSIONS: ROLE_ADMIN
ROLE_SUPER_ADMIN: ROLE_ADMIN_CAN_EDIT_PERMISSIONS
providers:
fos_user:
id: fos_user.user_provider.username
firewalls:
main:
pattern: ^/
form_login:
provider: fos_user
default_target_path: /user-post-login
always_use_default_target_path: true
login_path: user_security_login
check_path: fos_user_security_check
logout:
path: /logout
target: /login
handlers: [mp.logout_handler]
invalidate_session: false
anonymous: ~
remember_me:
secret: "%secret%"
lifetime: 31536000 # 365 days in seconds
path: /
domain: ~ # Defaults to the current domain from $_SERVER
access_control:
- { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }
...
- { path: ^/api, role: IS_AUTHENTICATED_ANONYMOUSLY }
...
- { path: ^/admin/select-customer-status, role: [ROLE_CUSTOMER_ADMIN, ROLE_SUPER_ADMIN] }
...
- { path: ^/admin, role: ROLE_USER }
My current user has the role ROLE_SUPER_ADMIN, according to role_hierarchy this role has ROLE_USER in ancestors, but when I try to open http://localhost:8080/admin/select-customer-status/1 I get this Access Denied exception.
I tried to debug and figured out this exception arose in SymfonyComponentSecurityHttpFirewallAccessListener
But the real problem is that Voter is checking for IS_AUTHENTICATED_FULLY under the hood, but this one isn’t present in $attributes.
Another interesting thing is when I add this configuration directly to Action it works as expected and no exception is thrown:
/**
* @Route("/admin/update-user-permissions/{id}", name="update_user_permissions")
* @Method({"POST"})
*
* @IsGranted("ROLE_SUPER_ADMIN")
* @IsGranted("ROLE_CUSTOMER_ADMIN")
*/
Can anybody help with this strange behaviour?
P.S. There is a similar question, but it’s for Symfony 2 and not reliable for me.
Advertisement
Answer
what apparently happens, is, that all of the roles mentioned are required, i.e. both ROLE_SUPER_ADMIN as well as ROLE_CUSTOMER_ADMIN. As far as I understand your problem description, your user is either, but not both.
The screenshot you provided shows the access manager handling the request at the bottom:

which clearly shows, that the AuthenticatedVoter is NOT the problem, because it abstains (since it only votes on IS_AUTHENTICATED_* roles (for example IS_AUTHENTICATED_FULLY), which is also easily seen in its code.
However, as can be easily seen as well, both roles are checked separately(!). The RoleHierarchyVoter now tries to see, if the ROLE_CUSTOMER_ADMIN is granted, and I assume your user has the ROLE_SUPER_ADMIN, which the RoleHierarchyVoter will expand by using the hierarchy to ROLE_SUPER_ADMIN,ROLE_ADMIN_CAN_EDIT_PERMISSIONS, ROLE_ADMIN and ROLE_USER. None of which is ROLE_CUSTOMER_ADMIN.
As I have suggested in a comment, you might want to choose to add a new ROLE that both extended admin roles hold. if ROLE_ADMIN is not appropriate, maybe a ROLE_EXTENDED_ADMIN or something.
ROLE_CUSTOMER_ADMIN: [ROLE_ADMIN, ROLE_EXTENDED_ADMIN] ROLE_SUPER_ADMIN: [ROLE_ADMIN_CAN_EDIT_PERMISSIONS, ROLE_EXTENDED_ADMIN]
and then in access control:
- { path: ^/admin/select-customer-status, roles: ROLE_EXTENDED_ADMIN }
