Wednesday, December 24. 2008Applying ACLs to ModelsIn my last post, I discussed using Zend_Form as a combination input filter/value object within your models. In this post, I'll discuss using Access Control Lists (ACLs) as part of your modelling strategy. ACLs are used to indicate who has access to do what on a given resource. In the paradigm I will put forward, your resource is your model, and the what are the various methods of the model. If you finesse a bit, you'll have "user" objects that act as your who. Just like with forms, you want to put your ACLs as close to your domain logic as possible; in fact, ACLs are part of your domain.
First up, however, let's review Zend_Acl in a Nutshell
class Spindle_Model_Acl_Spindle extends Zend_Acl { public function __construct() { // Define roles: $this->addRole(new Spindle_Model_Acl_Role_Guest) ->addRole(new Spindle_Model_Acl_Role_User, 'guest') ->addRole(new Spindle_Model_Acl_Role_Developer, 'user') ->addRole(new Spindle_Model_Acl_Role_Manager, 'developer'); // Deny privileges by default; i.e., create a whitelist $this->deny(); // Define resources and add privileges $this->add(new Spindle_Model_Acl_Resource_Bug) ->allow('guest', 'bug', array('list', 'view')) ->allow('user', 'bug', array('add', 'comment', 'link', 'close')) ->allow('developer', 'bug', array('update', 'delete')); $this->add(new Spindle_Model_Acl_Resource_Comment) ->allow('guest', 'comment', array('view', 'list')) ->allow('user', 'comment', array('add')) ->allow('developer', 'comment', array('delete')); } } In this example, we do several things:
Resources and Roles in // A simple role: class Spindle_Model_Acl_Role_Guest implements Zend_Acl_Role_Interface { public function getRoleId() { return 'guest'; } } // A simple resource: class Spindle_Model_Acl_Resource_Bug implements Zend_Acl_Resource_Interface { public function getResourceId() { return 'bug'; } } As you may notice, these are trivial to implement -- and the point is that they can be mixed in to your model classes to give them semantic meaning. That said, there's one caveat: when defining the actual ACL rules -- which map roles and resources -- the specified roles and resources must already exist in the ACL tree. As such, I find it convenient to define my roles early, and then add resources and privileges on an ad hoc basis. By grouping the base ACL definition in an object, we now have a re-usable ACL that we can pass around or use within other contexts, finally bringing us to our model. Using Zend_Acl in ModelsRoles
Typically in Zend Framework, you'll authenticate a user using
Let's define a "User" object that implements the role interface. Internally,
we'll store the user's defined role as part of the object, and have the
class Spindle_Model_UserManager_User implements Zend_Acl_Role_Interface { /* ... */ public function getRoleId() { if (!isset($this->role)) { return 'guest'; } return $this->role; } /* ... */ } You'll notice that not only does this provide the user's current role, but it also provides a contingency for when none is set ("guest" is our lowest level of access). I'll revisit this user class in later articles. ResourcesA model is a resource. As such, it should implement the resource interface. Furthermore, it likely should know which roles are allowed which rights. Finally, it should be able to verify access before performing an action. So, we need a little code. First, let's make our model a resource. class Spindle_Model_BugTracker implements Zend_Acl_Resource_Interface { public function getResourceId() { return 'bug'; } /* ... */ } Now, let's allow injecting an ACL object, or lazyloading it if none is found. In each case, we should then setup the access list for our resource. We'll limit the ACL object to one of a known type -- which ensures that particular roles will be present. class Spindle_Model_BugTracker implements Zend_Acl_Resource_Interface { /* ... */ protected $_acl; public function setAcl(Spindle_Model_Acl_Spindle $acl) { if (!$acl->has($this->getResourceId())) { $acl->add($this) ->allow('guest', $this, array('list', 'view')) ->allow('user', $this, array('save', 'comment', 'link', 'close')) ->allow('developer', $this, array('delete')); } $this->_acl = $acl; return $this; } public function getAcl() { if (null === $this->_acl) { $this->setAcl(new Spindle_Model_Acl_Spindle()); } return $this->_acl; } /* ... */ }
You'll notice that we pass
Next, we need a way to determine the current role. As noted earlier when
discussing roles, you'll typically authenticate a user with
class Spindle_Model_BugTracker implements Zend_Acl_Resource_Interface { /* ... */ protected $_identity; public function setIdentity($identity) { if (is_array($identity)) { if (!isset($identity['role'])) { $identity['role'] = 'guest'; } $identity = new Zend_Acl_Role($identity['role']); } elseif (is_scalar($identity) && !is_bool($identity)) { $identity = new Zend_Acl_Role($identity); } elseif (null === $identity) { $identity = new Zend_Acl_Role('guest'); } elseif (!$identity implements Zend_Acl_Role_Interface) { throw new Spindle_Model_Exception('Invalid identity provided'); } $this->_identity = $identity; return $this; } public function getIdentity() { if (null === $this->_identity) { $auth = Zend_Auth::getInstance(); if (!$auth->hasIdentity()) { return 'guest'; } $this->setIdentity($auth->getIdentity()); } return $this->_identity; } /* ... */ }
You'll note that Now that we have our roles and our resources, we can address how to add checks in our methods to verify user rights prior to executing code.
An expedient way to do this is to use class Spindle_Model_BugTracker implements Zend_Acl_Resource_Interface { /* ... */ public function checkAcl($action) { return $this->getAcl()->isAllowed( $this->getIdentity(), $this, $action ); } }
Now, let's' hook this into various methods. As an example, consider the
We'll consider insufficient privileges an exceptional condition for this example: class Spindle_Model_BugTracker implements Zend_Acl_Resource_Interface { /* ... */ public function save(array $data) { if (!$this->checkAcl('save')) { throw new Spindle_Model_Acl_Exception("Insufficient rights"); } /* ... */ } /* ... */ } When instantiating our model now, we need to either pass in the current identity, or set it after instantiation, but prior to calling an ACL-controlled action: // At instantiation: $bugModel = new Spindle_Model_BugTracker(array('identity' => $user)); // Following instantiation: $bugModel = new Spindle_Model_BugTracker(); $bugModel->setIdentity($user); $bugModel->save($data); (Of course, it will also pull it automatically from the authentication session, but it's good to know we can also inject it!) ACLs RevisitedNow that the resource and privilege definition has been moved to the model, we can simplify the actual ACL object a bit so that it only defines roles and initializes the whitelist: class Spindle_Model_Acl_Spindle extends Zend_Acl { public function __construct() { // Define roles: $this->addRole(new Spindle_Model_Acl_Role_Guest) ->addRole(new Spindle_Model_Acl_Role_User, 'guest') ->addRole(new Spindle_Model_Acl_Role_Developer, 'user') ->addRole(new Spindle_Model_Acl_Role_Manager, 'developer'); // Deny privileges by default; i.e., create a whitelist $this->deny(); } } We still define the roles here, as our user object is only used for validating access; we still need to define roles, first. Summary
In the next installment, I'll look at how "Return Values are Part of Your Model, Too." Comments
Display comments as
(Linear | Threaded)
Wow, this is making my head spin. Then again I remember that LiveUser had a similar effect on people.
One thing I was sort of waiting to see in your blog post was to a way to easily integrate the ACL checks into the data retrieval process. That is a solution that hooks into the step where I fetch the ressources a given user may be able to view, which then adds whatever magic it needs to add the to SQL query. This is what I always was most missing from LiveUser as I had to manually deal with this. Hopefully one day I will get away from being stuck on PHP 5.1.6 (thanks RedHat) so that I can use Doctrine, which should enable me to do exactly that. That should actually be possible with this setup -- you would query the identity to get the current role, and then do branching on the query.
Oh, as for RHEL and PHP 5.1.6... get used to it. Next RHEL version is going to be in *2010*, and at that point will likely use PHP 5.2.4 or 5.2.6 -- which should be fairly obsolete by that point as well.
Nice article! I used once Zend_Acl in the past, and I liked it, but I had to extend it a little for my needs:
http://blog.felho.hu/extending-zend_acl-to-support-custom-roles-and-resources.html that's all very interesting, however all examples i find using ACLs are very simple: what about when i for example allow a user to edit it's own bugs, but prevent editing to everyone else? or if i want to have a moderator for some forum which is allowed to modify posts in forum 'a' but not in forum 'b'?
thanks & merry christmas! In both cases, you would define custom assertions. My stategy would be to have a custom role object that aggregates multiple roles, and in your assertion class, only validate roles of that class.
Then, in your assertion logic, check the various roles and/or other metadata against the access list for your resource. harald, try adding assertions to your rules:
http://framework.zend.com/manual/en/zend.acl.advanced.html#zend.acl.advanced.assertions Assertions are not the solution. They also only work for simple problems. Also they are difficult to store in a database and don't allow for dynamic manipulation.
How would one do this with Zend_Acl and assertions?: get latest 10 posts a user is allowed to view, keeping in mind the user is not allowed to view posts in some categories nor when they are tagged with some tags. you could easily implement a custom assertion language with the composite pattern and throwing in some spicy composite AND, OR, NOT objects.
Very nice idea Matthew.
I only thought of mapping modules/controllers and actions to resources and privileges, but breaking it down to the model and method level would allow for more granular access control. But I think it only makes sense this way, if your model consists of those straight forward methods you used in the example ('view', 'save', 'delete', 'close'), that more or less correspond to similar controller-actions. And what if one action includes multiple changes in multiple models. I guess it could become hard to define and configure what methods should be allowed in what case. Regards, Marc You're on fire, Matthew - another excellent post =)
This is hardly a full solution. Where's the rule persistence? And what if I want different business objects to be used as different resources, instead of one resource for one type? What if I want to disallow a single user access to a specific resource?
In other words, where is the true power and flexibility of an ACL? Moving the rule definition from hard coded in the ACL class, to hard coded in a resource type diverts from the problem, but doesn't solve it. It's a nice little SRP violation as well. No, it should NOT know "which roles are allowed which rights". That information should stay in the ACL, loaded from store. I have been able to create a persistent ACL, but using Doctrine and Nested Set, Zend_Db_Table is just not up to the task (which is the source of the lack of ACL persistence in ZF, I'd imagine). Sorry if I come across rude, but the lack of proper support of ACL persistence, ZF's attitude towards it, and the poor, no, very poor Zend_Db_Table / Row is just twisting my nuts. Am I the only one bothered by this? Everything's just fine and dandy for you all? Hard to believe. I've personally thrown Zend_Db_Table in the dustbin, and I would definitely have done so a long time ago if I had anything to say at ZF. It's a freaking blamage for an otherwise great framework. I think you can find the answer in my blog post already linked here:
http://blog.felho.hu/extending-zend_acl-to-support-custom-roles-and-resources.html That's still very limited as it doesn't allow nested groups and resources as a full ACL would support. Also pardon me for saying, it is really messy (I'm really making friends today).
This is a snippet from what I used to load ACL rules from store using Doctrine and Nested Set (I hope it will be somewhat readable after posting): public function loadRoleBranche(AclRole $role) { if($this->_acl->hasRole($role->name)) { return; } if($role->getNode()->isRoot()) { $this->_acl->addRole($role); return; } $parent = null; foreach($role->getNode()->getAncestors() as $ancestor) { if(! $this->_acl->hasRole($ancestor->name)) { $this->_acl->addRole($ancestor, $parent); } $parent = $ancestor; } $this->_acl->addRole($role, $parent); } public function loadRules() { $query = Doctrine_Query::create() ->select('re.*, ru.*, pr.*, ro.*') ->from('Rules ru') ->innerJoin('ru.AclResource re') ->innerJoin('ru.Privilege pr') ->innerJoin('ru.Role ro') ->innerJoin('ro.Users usr') ->where( "usr.id = ?", $this->getUser()->id ); $rules = $query->execute(); if(!count($rules)) { return; } foreach($rules as $rule) { $this->loadResourceBranche($rule->Resource); $this->loadRoleBranche($rule->Role); $privilege = $rule->Privilege->name ? $rule->Privilege->name : null; if($rule->is_allow) { $this->_acl->allow($rule->Role, $rule->Resource, $privilege); } else { $this->_acl->deny($rule->Role, $rule->Resource, $privilege); } } } BTW since you mention Doctrine. I think there was once a sister project of Doctrine that among other things tried to take the ideas from LiveUser and make them available form Doctrine. I think the core feature set of LiveUser was really sweet. It was just too hard to use them. With integration on the ORM level the main issue should go away.
http://sensei-project.com As I have mentioned, I am mostly stuck on RHEL5 aka PHP 5.1.6, which means I personally cannot use Doctrine. But I wish that someone would pick up sensei again for the day when I finally can .. then of course I would be happy to contribute with code .. until then anyone interested can prick my brain about LiveUser to get the features into some Doctrine behavior. Can you explain "messy" in more detail?
If you hate Zend_Acl and Zend_Db so much, then my challenge for you is to do something constructive, and contribute. I've said it in several public forums, but I'll say it again here: coming out to rant when you dislike how a piece of open source software works, but not offering to help fix it (either through bug reports, patches, or code contributions) is unconstructive and counter-productive. While ZF does have several full-time developers, the bulk of our contributions come from volunteers, who are contributing on their own time or their company's time. You're only going to build up resentment if you lay the smack down on the project without contributing anything of your own back to it.
Yes, ZF does not offer ACL persistence. Should we? Maybe. I personally feel that it's a rare case where an application actually *needs* a database for ACL persistence, and that there are easier, more performant solutions. But for those cases where it *may* be necessary, then it may make sense to have a solution in place. Am *I* ready to create teh requirements for it? No. Are you? Probably. So, make that the beginning of *your* contributions. Matthew,
I appreciate the “invite”, but I'll have to decline. But I don't think the fact I don't have time away from my own projects should invalidate any criticism I have. After all, I am a *user* of ZF. Open Source or not, you can't do away with criticism from your users with a "fix it yourself or live with it" argument. Or at least that is my own conviction. IMHO the point of using Zend is when you want to convert an existing PHP application to use a big, supported OSS framework.
If you want something that has ACL storage, doctrine integration, a RAD environment and the works - use Symfony. Thanks to Mr. O'Phinney for writing these tutorials. They are appreciated. I completely agree that anybody can criticise a project. I have no problem with *good* criticism -- criticism that clearly outlines problems and which indicates expectations.
However, statements like yours, "the poor, no, very poor Zend_Db_Table / Row," and "the lack of proper support of ACL persistence, ZF's attitude towards it," offer *no* direction whatsoever. You're not stating anything about what your expectations are -- what use case is not represented, or where the current implementation is buggy. We are given no indication how *you* are using the code -- to see if either (a) there may be ways to use the code to meet your expectations (i.e., user error), or (b) if perhaps we now have a new use case to address. There are plenty of posts like yours that I have termed "drive-by critiques." I get them practically daily. But if you want to be taken seriously, you need to engage the community appropriately. You need to do as I have noted: indicate your expectation, show how you're trying to solve it, and show how it fails. In the case of improvements or new features, indicate what you want, and how you would like to use the software. Dropping in with an open-ended critique and then saying, "I don't have time," is pointless and rude. This is "Open Source Software 101", as far as I'm concerned. Finally, as a courtesy, when you post comments to blogs where you critique software that competes with yours, you should indicate your own affiliations. (Tarjei Huse is a developer on the Midgard CMS.) You know what, you're right. I realize what I'm saying can be perceived as rude, which is not my intention. Forward and to the point, yes, but not rude. You are right to say I did not give any arguments, I basically just said it sucked and that was that. It's because I suspect you know very well what I'm aiming at.
I seriously doubt I can provide you with any practical reasons I dislike Zend_Db_Table you haven't heard yet. Apparently ZF thinks Table Data Gateway is an appropriate solution for projects of any significant complexity. I dispute that it is, and even Fowler himself hints at Table Data Gateway being a solution applicable only to projects with a very basic domain model. As for my criticism of ZF's attitude towards ACL persistence: it's in the manual. "Storage of ACL data is therefore left as a task for the developer, since use cases are expected to vary widely for various situations. " This to me says "figure it out yourself" (which I'm starting to believe to be the general mantra). It would be a different story if it said something in the spirit of “we're still working it”. Which is how I have interpreted it for a long time. The variety of possible use cases is hardly an argument. After waiting for something to change, I've come to the conclusion that nothing will happen in this regard any time soon, because of Zend_Db_Table or more generally because of the lack of proper data mapping. Realistically, it should say “it isn't feasible with our mapping pattern of choice”. Which brings me back to Zend_Db_Table. I love the way things are organized in ZF, and I love ZF in general. I even convinced my (now previous) employer to switch to ZF, I like it that much. But using Table Data Gateway was a huge mistake, and the functionality of Zend_Db_Table is lacking, even after the framework is supposed to be well mature. Zend_Db_Table doesn't support class table inheritance, it doesn't support any type of hierarchy model (such as needed for ACL persistence), but most importantly, it doesn't support joining tables, making it pretty much useless for any moderately complex application. Yes, those things are the minimum I expect from a mature data integration layer. And despite attempts by users to get Zend_Db_Table to work more like an actual ORM, and masses of others turning to Propel and Doctrine, the same kind of denial seems to surround the issues with Zend_Db_Table as do Zend_Acl persistence. These issues remain un-addressed, while all sort of “nice to haves” are added. Yes, I could create some use cases to illustrate what I would expect from the data integration layer in ZF, but would that tell you anything you do not already know? I doubt it. Unless you've covered your ears and chanted “you don't contribute so I don't have to listen to you” every time somebody brought up the limitations of Zend_Db_Table. First off, I'm sorry I called you out -- but I feel that if I don't, we just encourage more similar behavior.
Second, our internal ZF team is *very* small. We are an architect and two engineers doing coding. We rely very much on the community for contributions. Have I held my hands over my ears so as not to hear criticism? No. Have I not subscribed to every list we own? Yes. Why? Because I have my own areas of responsibility, and no single one of us can have expertise in all areas of the framework. As it is, we're likely stretched fairly thin. The principal author of Zend_Acl is no longer with Zend, so I do not know what his plans were for the component. I do know that there have been a couple of contributors advancing some ideas towards ACL persistence both in the past and in the present; we've rejected at least one such proposal due to having a very unclear scope, but a newer one looks like it could be promising. Otherwise, believe it or not, I've not heard many complaints about it, other than the "how do I store the ACLs in the database?" variety. Believe it or not, there are many different ways to do this, and this was one reason that Zend_Acl was originally written without any persistence (I myself have seen ACLs persisted in configuration files, LDAP, and databases, each with a very different schema). If someone were to propose a comprehensive solution for a somewhat standard ACL hierarchy, I'd be all ears. As for Zend_Db, honestly, I have never subscribed to the Zend_Db mailing lists as while I am competent with databases, they are not my area of expertise. I myself have used Table Data Gateways for quite some years, and find them quite easy to work with; that said, I'm not typically working with enormous databases, either. I've heard a number of people complain about Zend_Db and Zend_Db_Table, but honestly, nobody has ever shared any specifics with me, even when I've asked. ORMs are tricky beasts to get correct. To do so, we'd need somebody with significant database experience to step forward and help architect the solution. One solution that has been proposed is for us to adopt a third party ORM solution and have formal integration points; a number of contributors are already contributing integration with Doctrine, and this may be the direction we go. Finally, I never said, "you don't contribute so I don't have to listen to you." I said that if you're going to raise issues and criticism, you need to provide appropriate detail. It's one thing to say "Zend_Db_Table doesn't support class table inheritance" and a very different one to say "Zend_Db_Table does not support class table inheritance, which would allow me to be able to do X, Y, and Z. This is the API I would like to see..." One sounds like ungrateful whining, and does not give those contributing/developing anything to work with, the other lays out a fairly clear plan of action. This is all I, or any open source developer, asks. Yeah, getting an ORM right is quite a challenge. So much so that its hard to get it right inside a community process. Its just so complex and it takes a few iterations that its impossible for a community like ZF or PEAR or whatever to really provide a fostering ground.
At the same time any project that does end up getting it right will likely have too large an established user base once it gets to the point where its mature enough. As such I the approach of simply cooperating with a 3rd party is a good idea and maybe for the next major version it could be integrated (not sure what the plans are for ZF 2.x and if they for example align with Doctrine 2.x) Some may find this hard to believe, but I'm not a big fan of the NIH (Not Invented Here) syndrome. I think in places like this, if we can cooperate with third parties, it's a huge win for *both* communities. I'm certainly leaning in this direction, and others on the internal Zend team are as well.
Kudos!
And like I was saying .. for some aspects its just very hard to get it right and I think it makes sense for ZF to be cautious here and rather provide a solid bit in itself consistent, albeit limit solution, than one that is broken and rushed. One tricky aspect with ZF is the "clean IP" angle .. this makes this kind of cooperation a bit more difficult. Reading back over this, I think the idea of hard coding the role names into the model is something which I'd rather not do. I prefer to have my models and what specific roles can take an action (or not) on the resource decoupled.
That's certainly an option -- and a particularly good one when you may or may not know what roles exist. However, at some point, the map needs to exist -- or rules for defining the map from a data source.
One approach is to have some basic roles available, and then use Zend_Acl's ability to have a role inherit from multiple roles to make it possible to define the map in your models (users could then have custom roles that inherit from one or more of these roles). This keeps the actual role/resource/rights mapping in a correct semantic location, while still keeping flexibility. However, separating the map into your ACL class is a good option as well, and one that I have used. This is just another technique that many overlook. Er:
} elseif (!$identity implements Zend_Acl_Role_Interface) { Replace implements with instanceof? Nothing wrong with the idea or intention though Er, yes, that's what I meant. I really need to find some time in the next couple of days to update all the examples in here.
What would you do if your models didn't handle persistance by themselves? If the persistance was handled by an external object like a DAO or DataMapper, where would you put the ACL checking code?
That's part of what I was trying to demonstrate here and the other posts on models -- using a data access layer (or DAO) with your models. ACLs then either become part of your entities or part of your service layer.
Do you also implement ACL checks in controller? For example, have an Action Helper that checks if a person can access a particular controller or controller method/action? Or do you put checks in the view or a combination of the aforementioned?
Actually, I usually put my ACL checks in my models and/or service layers -- not in the controllers or views at all.
Very interesting Matt. Personally I put mine in as an action helper checking controller/action permissions. Is your ACL setup to check permissions against all of your tables? also is it still statically applied or do you have a table of file which the ACL reads for its data.
Very interesting read Matthew. I have currently implemented this in my own application and it works like a charm! There is however one thing that I find hard to solve. Maybe you can give some pointers to steer me in the correct direction.
Sometimes my views do have the need for ACL like behaviour, for example whether or not to show an 'edit' or 'add' button. How I solve this now is by having a view helper query my Zend_Auth instance, to check if the user has the required 'role'. However, it struck me that this is exactly what ACL does. So what way would you suggest to query the ACL from a view? You will usually inject your ACLs into the service layer, or at least have setters/getters for the ACL object. What this means is that you can either pull the ACL object from the service object and pass it to your view, or pass your model to the view and pull the ACL object from it there. Once you have the ACL object, you run your queries.
Another possibility is to serve different views based on ACLs. One way to do this is to have a main view into which you inject your model and/or ACL object. Based on a lookup, you then determine which specific partial view script to render (either using render() or partial()). By 'pass your model to the view' you mean set the model to a view variable? If that is the case, I think that option would fit best in my architecture.
I do this however from my controller since I don't really have a service layer. Any good articles (from you perhaps) where I can read about a service layer implemented in ZF? (Im always a bit confused on the distinction between a controller and a service layer). Thanks for the quick reply, means a lot!
#11.1.2.1.1
Chris Vermegen
on
2010-03-12 09:09
(Reply)
Yes, exactly -- $this->view->modelName = $model;
As for service layers, they're part of your domain model. I've presented on the topic a few times; you can find my presentations at http://slideshare.net/weierophinney -- look for the slides about "architecting models" and "play-doh". Thanks!!!
btw. When/where will your next presentation be? Anywhere in Europe by any chance?
#11.1.2.1.1.1.1
Chris Vermegen
on
2010-03-12 09:29
(Reply)
I'm speaking in May at TekX in Chicago, and in June at the Dutch PHP Conference in Amsterdam.
I’ve read a lot about Zend_Acl for last couple days, but I still can’t figure out is there a way to manage access based not on role/resource names (classes) but on their instances? In other words, different instances of the same class (User) can have different relations to different instances of resource (Document). There can be merely "guest", "member" or "admin" with appropriate permissions to all documents, but also a certain user is the "owner" of the document and has to have a separate set of relatively to it.
I’ve seen a proposal for it at http://blog.felho.hu/extending-zend_acl-to-support-custom-roles-and-resources.html, but the proposed solutions store is’s access rules separatelly from the model data. As a proposal, in order to cover the described case, I think, Zend_Acl_Role_Interface#getRoleId() has to accept an optional Zend_Acl_Resource_Interface argument in relation to which the role (ex. just "member" or "owner") is determined. Does this make sense or it can be achieved in some more simple way? You can use assets for this scenario (http://framework.zend.com/manual/en/zend.acl.advanced.html). For example, in the assert you can query the model to see which user originally created the document, and only allow that user to delete it. Make sure you use an updated ZF, as earlier versions did not pass in the actual model/resource into the assert (http://zendframework.com/issues/browse/ZF-1721).
Add Comment
|
CalendarQuicksearchLinks
ArchivesCategoriesSyndicate This BlogShow tagged entries |






The part where I complain you can ignore While most of you have been sitting under the Christmas tree and happily unpacking presents surrounded by your loved ones, I was reading Matthew's blog post about Zend_ACL. What? I finally got time to to these kin
Tracked: Jan 02, 13:06