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.

13 thoughts on “Doctrine objects schizophrenia & the ACL!

    • Well it’s not a bug per se. But there’s about 5 issues about that on github and a few pull requests as well. But as long as the design is not ok for the guy responsible for the security component, this is blocked in the current status… So a temporary solution is still something

    • Fabpot said on the PR that he’s ok putting it in 2.0 if an “agreeable solution” is found. That’s what we’re lacking now: an “agreeable solution”

  1. Nice to know this can be solved overriding one service. Doing the check on each use of ACL was tiring.

    But the biggest problem with ACL is you can’t get objects with it, for example: get all blog post owned by user foo.

    I had to write ugly SQL code to retrieve my objects…

    • Well, you can. It’s just not convenient and you can’t do it through Doctrine. But clearly the ACL was not made with this goal in mind. You can check ProblematicACLManagerBundle on github. Problematic and I have tried to write a more usable interface to the ACL manager, so maybe you could contribute this kind of code there so others can use it directly.

    • Nope, we’re doing exactly the same thing except you’re using the entity manager that always “knows” what the real class name should be. And I implemented the logic of getting the parent class name myself.

      I was looking for the solution before you wrote it though! So please go back in time and post about this around November 2011 ;-)

      • Only 2 months late! Glad to see that we’ve both come up with a decent solution to the problem. I wonder if this class could be added as part of one of the Doctrine bundles, because it’s (almost) always going to come about when using Doctrine and the Security component together.

        • Not really. If you check a project using Doctrine with MongoDB ODM will have proxies inheriting this:
          \Doctrine\ODM\MongoDB\Proxy\Proxy
          So I guess this is also gonna be different for CouchDB, XML etc …

          And if you have other libraries that make use of proxies and for which you need to use the ACL, this is also not going to work because you can only have ONE identity resolver service. So if you use ORM & ODM together and both bundles provide a similar ID resolver class, you can’t use them together.

          A proposed solution was to have services tagged ‘security.acl.object_identity.resolver’ for example then the real id resolving service could find all of these and call on them one after the other to find which one should be used and use it.
          This solution has been blocked by @schmittjoh who is responsible for the ACL component. So we’re blocked on this front for now.

  2. I’m afraid the same thing happens also to UserSecurityIdentity, and sadly this solution doesn’t appear to cover that case. Is there some service which can be overriden in similar way?

    • In Symfony 2.1 most of these issues have now been resolved (or so I think). As the identity manager will now detect these.

Comments are closed.