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 }