POSTS

Doctrine Objects Schizophrenia & the Acl

There’s a problem many people seem to encounter in Symfony2 and that appears when you start using the ACL component for security along with Doctrine. The ACL defines an object’s identity based on its class name and an id retrieved from a ‘getId’ method if one exists or from a getIdentifier method if the object implements the DomainObject interface.

The problem: object schizophrenia

The problem is that some objects are subject to schizophrenia. They might be the exact same thing for you. But for a computer system, they’re not. A typical case is Doctrine proxy objects. If you retrieve your objects without lazy loading, they have the real class object. If you retrieve them through lazy loading, they have a ‘proxy’ class that extends your domain object’s class. So:

Will be of class Khepin\AclTestBundle\Entity\Category sometimes and of class Proxies\KhepinAclTestBundleEntityCategoryProxy if it is retrieved as a proxy object through Doctrine lazy loading. The proxy class will be created in the cache like this:

So using instanceof or other means of testing your object’s class will still work fine. But if you set the ACL’s rights for a user on an object while dealing with the “real” domain object, but later test it against an object that was lazily loaded, you won’t retrieve the correct rights because for the ACL manager, the proxy version of the object is a completely different thing.

A solution

So the solution is to use the parent’s class name when dealing with the proxy. The automatic way to do this within symfony however is currently not available / blocked over architecture issues.

Because Doctrine let’s this problem appear in its own way where we need the parent’s class name. But other libraries might put us in front of similar issues for different reasons and that could be implemented in a completely different way. Maybe there will be more than one inheritance level, maybe it’s not an inheritance problem this time etc…

It means every bundle for such a library should provide an identity resolver service and the people responsible for symfony’s security component are not willing to go this route and would rather find a different solution. So for now all the PRs that have been proposed to “fix” this haven’t been validated.

So we’re screwed?

Nope! The good news is that this object identity resolving logic lives inside of a service defined in the DIC. So we can override that service to provide our own logic for now — hoping a more standardized solution can appear in the future.

To override the given service, we specify a service with the same name in our config.yml:

Then our class needs to implement the ObjectIdentityRetrievalStrategyInterface and return an ObjectIdentity object. In the case of Doctrine, here’s how you can do it:

If you have more than just Doctrine objects requiring this kind of logic, you can add your logic to this class. For example if you’re using the MongoDB Document Manager as well. As there’s only one class used for the ObjectIdentity retrieval service, it’s not possible right now to have many bundles each provide their own and have them used automatically. However this solution allows you to use your own code to get around this issue.