I am trying to create a Custom Access Decision Manager by implementing AccessDecisionManagerInterface
on Symfony 5.2.
Symfony recognizes the decision manager and instantiate it, but it doesn’t pass Voters array to decision manager. So it can’t collect votes, however the default decision manager works fine. I have followed the information provided on Symfony website. I have also copied the decision manager file to my AppSecurity and just changed the class+file name but it didn’t work. Adding the custom decision manager to service.yml
didn’t help too.
Has anyone encountered the same problem? Any chance to fix it?
UPDATE #1
With reference to the issue https://github.com/symfony/symfony/issues/41123
I encountered this issue with different symptoms!
Although I knew the service is automatically registered but I did this in service.yml:
AppSecuritySpecialAccessDecisionManager: class: AppSecuritySpecialAccessDecisionManager autowire: false autoconfigure: false public: false
then I added decision manager to security.yml:
security: access_decision_manager: service: AppSecuritySpecialAccessDecisionManager
As you can see below, arguments haven’t been passed to the decision manager:
[root@dev7 rouzonline.test]# bin/console debug:container debug.security.access.decision_manager.inner --show-arguments // This service is a private alias for the service // AppSecuritySpecialAccessDecisionManager Information for Service "AppSecuritySpecialAccessDecisionManager" =================================================================== Description of SpecialAccessDecisionManager ---------------- ------------------------------------------- Option Value ---------------- ------------------------------------------- Service ID AppSecuritySpecialAccessDecisionManager Class AppSecuritySpecialAccessDecisionManager Tags - Public no Synthetic no Lazy no Shared yes Abstract no Autowired no Autoconfigured no ---------------- ------------------------------------------- ! [NOTE] The "debug.security.access.decision_manager.inner" service or alias has been removed or inlined when the ! container was compiled. [root@dev7 rouzonline.test]#
When I use default AccessDecisionManager voters are passed. I need to implement special authorization so I need to create my own decision manager. By the way, my decision manager implements AccessDecisionManagerInterface
I also checked SecurityBundleDependencyInjectionCompilerAddSecurityVotersPass->process()
, it is not being called when my decision manager is set.
UPDATE #2 For testing, I temporarily renamed default AccessDecisionManager.php and placed my decision manager, it surprisingly worked. But it is not the solution. Still looking for the correct solution.
Advertisement
Answer
Thank you guys for your replies and helps, I figured it out.
Workaround
When Symfony compiles the code, it (somehow) has its own service definitions, so it doesn’t get confused. I traced the code into the compiled cache and I found that it can’t determine the service definitions of my ADM. So I changed my service.yml
and defined arguments as follows:
security.access.decision_manager: class: AppSecuritySpecialAccessDecisionManager arguments: - [] - 'affirmative' - false - true
Inside vendor/symfony/security-bundle/DependencyInjection/Compiler/AddSecurityVotersPass.php
I found that there is argument replacement which enumerates service arguments by number, that is why I added unnamed service arguments.
$container->getDefinition('security.access.decision_manager') ->replaceArgument(0, new IteratorArgument($voterServices)); }
The traces showed that Symfony collects all voters correctly up to this point, so it is not needed to manage dependencies manually.
By using this service.yml, I didn’t need to define the access_decision_manager
in security.yml
.
After finding the problem, I tried to use access_decision_manager
but it caused the same error, means it didn’t receive list of voters. Then I decided to stop working on the problem as it is working for me now.
Conclusion
I know AccessDecisionManager is somehow a deep Symfony concept which is rarely used for common projects, but:
- Documentation doesn’t adequately cover this part
- When a developer implements an interface and injects it to Symfony, it is supposed either to be treated exactly same as the native code or provide enough descriptive logs or details to show noncompliance
- The method I used to fix the problem is not standard and might need modification in the future
- Symfony misbehaves when it is processing
access_decision_manager
fromsecurity.yml
Probably this part of Symfony needs to be reviewed.