We developed our current ACL type system before Acegi had its ACL system, and planned for this behavior from the beginning (we work with Hibernate as well). Our system has these abilities: 1. Property level ACLs. If the user does not have read access for a property, then somehow blank it out so that sensitive data is not transmitted on the wire. If the user does not have write access to a property and the client attempts to change a value on the property, then throw a security exception when they attempt to persist the object. 2. Instance level ACLs. If the user does not have read access to an instance, then filter that instance out: a. If the instance is the return value of a service method, throw access denied exception. b. If the instance appears in a collection, remove it from the collection. c. If the instance appears as the value of a property, secure the property (via the same process used in #1).
Apply these symantecs to all returned objects wherever they appear in an object graph, which, of course, implies recursion. Now consider the typical usage pattern for our rich client application: 1. Rich client makes remote invocation to server side service via service interface. The interface is a proxy that calls the remote service via HttpInvoker. 2. Enter server side: a. We first encounter the general security proxy that does basic role based security checks against the service method itself. b. Next, we encounter the transaction proxy which establishes a transaction context for the remainder of the method invocation. c. Invoke the actual service method. d. Service method returns object graph. e. Leave transaction proxy, meaning the transaction is committed (or rolled back in case of error/exception). f. If there was no error or exception, then we return back to the security proxy which now performs ACL security on the returned graph (note that this is outside of the transaction). The object graph may be mutated during this securing phase. As you can imagine, this gets real complicated when using POJOs and Hibernate (and your Hibernate model doubles as your DTOs), which is exactly what we use. If you retrieve an object graph from one service method, make modifications, and then persist those changes via another service method invocation you are dealing with two totally separate transactions and Hibernate Sessions. The ACL mechanism performs actual modifications to the POJOs in order to "secure" them, but you do not want these modifications persisted back to the DB as they were temporary and specific to the purpose of securing transmission of data. This is about when you start longing for the more dynamic nature of some other languages - it would be so much easier if I could set dynamic metadata against a property (a property property), or remove a property altogether. Anyway, you somehow have to merge the allowable mutations made by the client with the original object state before persisting to Hibernate. The version of Hibernate we use (< 3.0) does not make this any easier, though it is possible. There are a lot of various interactions that can bite you if you aren't very careful with your implementation. I don't have time now to elaborate on how we solved these various issues. For now, I'll say that we used a combination of AOP, Hibernate Interceptor, and special "secured" placeholders for objects. The solution is not optimal at the moment. Our version of Hibernate just does not provide any easy way to optimize things, so we end up reading each and every object from the DB before updating it. This means at the time of update we have two copies of each object: the one passed in from the client (which is mutilated, so to speak, because of the ACL mods), and one we just reloaded from the DB via Hibernate. We end up applying an algorithm to determine what allowable mutations were made by the client and then making those same mutations on the "real" object loaded from Hibernate. There are other ways to approach the solution, of course (such as proxying every object in the graph and recording deltas as the client changes things) - but each solution has its own set of advantages and disadvantages. The proxy solution gets harder to deal with when exposing your service as a web service to other types of clients. - Andy On Wednesday 09 February 2005 02:40 pm, Tim Kettering wrote: > Hi everyone, > > > > I've started work on implementing acegi's post-invocation security w/ ACLs. > I am also using Spring/Hibernate to handle the data, and tx layer. > > > > What I am attempting to do is have the post-invocation "scrub" an domain > object (which will have nested domain objects that need to be checked). > Making up an example, we have a Store object which can contain a collection > of Item objects. So when a service call is made load a store #233, the > post invocation chain would first check to see if the user has permission > to view the Store itself. Once that is allowed, the check goes deeper into > the Store itself, and checks each Item in turn to ensure the proper > permissions. So far it seems to work well. > > > > But however, since I am using Spring/Hibernate's session/tx management, I > found out that when Acegi was scrubbing the Store object, the "changes" > would be written back to the database by hibernate at the end of the > session (due to the automatic flush). Which in hindsight, is quite obvious > that would happen, and is indeed correct behavior by Hibernate/Spring. > > > > My next thought was to move the post-invocation stuff outside the > Hibernate/Spring transaction boundary, but then as a co-worker pointed out, > I would lose the benefit of having the proper version of data within the > transaction when doing the ACL checks against those items. Nested > transactions is an option, but I thought I'd write a email to the list and > inquire if anyone else here has run into this situation and perhaps be > willing to share a good solution? Or if there is none, we could discuss > this, and contribute to the knowledge base of good Acegi practices, since > this issue is likely to come up again when people start to adopt > post-invocation more widely. > > > > Some options that I thought of. > > > > 1. Nested Transactions. > 2. Force an hibernate eviction of object at start of post-invocation > chain (clunky though.. we have some straight jdbc calls too so I hate to > pollute chain w/ hibernate specific calls). > > > > Im hoping others might have ideas as well. > > > > -tim ------------------------------------------------------- SF email is sponsored by - The IT Product Guide Read honest & candid reviews on hundreds of IT Products from real users. Discover which products truly live up to the hype. Start reading now. http://ads.osdn.com/?ad_id=6595&alloc_id=14396&op=click _______________________________________________ Home: http://acegisecurity.sourceforge.net Acegisecurity-developer mailing list Acegisecurity-developer@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/acegisecurity-developer