Version 18, last updated by kenmcd at June 11, 2011 12:39 UTC
ACL is the most difficult and common problem developers face today, yet one that isn't solved properly in any PHP framework yet.
ACL is so context sensitive it needs to be abstracted to its own layer in order for it to be reusable.
Not reusable as in making it so flexible that it can be configured to work with anything and everything. Those APIs are hardly "usable", so therefore not "reusable". But if it can't work with different types of table schemes then you can't reuse it as it only works in certain situations.
1. Controllers
Problem
Who gets to do what controller actions.
Solution
We don't want the ACL to make controllers aware of anything more than the actions being performed. So like what we have now, we only want the controllers to authorize requests (BREAD basically).
This base is already covered in a recent commit. Reason why we don't want to authorize like who gets to change the author of an article is because that would couple the controller to the data.
2. Models
Problem
What data returned depends on who the user is.
Solution
Models are dumb. They only know about states, and what data to get depending on those states. They know absolutely nothing about the data it returns to whoever is requesting it be it model or controller. And it needs to stay that way.
By default you wouldn't want filtering done here. You want your data to be readable to the public most of the time. So when you need filtering that can be considered a special case.
Those special cases are best solved by using the controller to pass states to the model (like for example the user access level and usergroup), and the model then builds its query based on those states. That way the model would return exactly the data the controller is asking for
There's no point in getting the data as usual, and then do iterations and filtering before returning it. Doing so is wasteful, as you're asking the database to return you data that you don't actually need. It's always better to always query exactly what data you really want.
So with all that said, from the model perspective there is no difference between querying articles based on a usergroup or article category. And it should stay like that.
3. Database Tables, Rowsets and Rows
Problem
Row fields need to be configurable in terms of who can read or write to a field. Sometimes you don't want anyone to be able to publish an article or change the author of an article. There's also cases where there's sensitive data you want hidden, like user passwords or emails.
Solution
Implement something similar to how filters work, learning from the simplicity of permissions on a filesystem.
For the sake of KISS we could call it "permissions". By default each column would be readable and writable (e.g. value would be 'write', readonly is 'read' and protected/hidden is 'protected'), and just like with the filters we've come to love it would be easy to customize.
Example in a database table __construct:
$config->permissions = array('created_on' => 'read') //we don't want user submitted data to modify this property, it's done by creatable);
It would also make sense to define these permissions from the model, using $config-->table_permissions or similar.
So again just to sum it up. Proposed solution is to add permissions for read and write on each table field. The row and rowset methods would now then only getData that are marked as 'read', setData only sets data that are on fields that are marked 'write'.
We would likely want to leave the ability to force the row to modify fields that aren't writable when needed (like for creatable that sets the created_by and created_on values, these need to be set even though you would likely want these columns to be readonly). We could have a getWritable method that works just like getModified, only that it returns an array over names of writable columns obviously.
4. Views and layouts
Problem
Based on the user permissions, you want to show different data and different user interface controls. There's also the need to more easily control what row fields are shown in other formats than html (html is easy that way)
Solution
Views + ACL usually result in a lot of decisions. Even if you litter your controller with form html logic, there's still a lot of things like "if $user is publisher or above, show $publish radio". The view should not need to know about access levels and usergroups in order to decide whether or not to show some form element. Views should be able to trust the data that the model gives it, and only decide how, not what.
With the ideas mentioned in (2) and (3) the only thing the view would need to do, in order to know whether to render a value or a text input with the value could be as easy as $item->getWritable(). As for the formats problem with json, csv and other views potentially outputting data that you don't want to reveal, that's solved in (3) as these the model wouldn't give these views data you don't want to show, at all. This does not cover the end-user problems yet, with configuring what users are able to do and so on.
With the proposed solutions above implemented, so we get a solid architecture, then the UI and UX problem will be much easier to solve.