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 }