A better permissions model? Use cases? Comments?

Permalink
Hi All --

I've never been a fan of how the permissions work within c5. I doubt I'm alone. But I jumped back in today because I needed to do permissions for one of my models, but the core is not incredibly extensible, either (unlike a lot of the rest of the platform). So, I had to write some permissions for my model, abstracted a bit, and went a little too far.

I really like the way that you have a permissions class that you can pass an object to, not really caring what the object is, and let the permissions class figure everything out. So I'm keeping with that design, but with a relatively simple class that can handle most objects (any object, really).

I'll explain it further in a minute, but I'd like to get comments from everybody, see if it fulfills your use cases (am i missing something big?), and if I should continue fleshing this out.
--------------------------------------------------
Basically, there's a core Permissions object / factory. You pass it an object instance (page, user, file, attribute, it doesn't matter). In MOST cases it will return a generic Permissions object. You use assign($perm), remove($perm), and has($perm) with constants in order to manipulate and check permissions. It wouldn't be difficult to set up a magic method so that you don't need to use the constants. The constants are integers, so bitwise operations are easy.

There's a database table with the following rows: ID, objName, objPK, perms, uID, gID. That's the beautiful thing -- NO MATTER what object is passed in, I can take the class type and the PK (the object needs to have an ->ID or ->getPrimaryKey()) and can store the permissions in a single table.

Typically, this will work with a user. When you assign/clear it will affect the user row. When you check, it checks by XORing with all applicable group rows. If you pass in a group, though, it will assign/clear/check the group row.

So... let's say I want to further extend it. There are two potential reasons (or combined). One is that the permission constants don't apply. The other is that I need to do something very special.

So I extend the Permissions object. In the simplest case I can declare my own constants. I don't have to change ANYTHING else. I can call assign() or has() with the new constants and all the existing storage / checking works.

Slightly more complicated -- I want to offer task permissions (like the site does). So I extend the model and I create a handful of new constants plus I override the method that sets the SQL keys (ie, the objName = 'ModelNameHere' and the objPK = ID). I replace objName with '!core-tasks' (so it won't conflict with anything else), and objPK = null. When assigning or checking the parent class works just like usual, but using a non classname in the SQL where there's usually a classname.

Furthermore, I might want something extra special that might calculate permissions based on the object's properties. So I extend the core class, and name it [object to handle permissions for]Permissions. The Permissions constructor/factory checks if there are any of these classes in existence and returns that instead. Then, the applicable methods are overridden to not even touch the database.

I've put together the following rough code (it won't work, but it'll show what i'm talking about). It provides an example of the "override to change the objName" and "override to completely change the functionality" use cases that I described. I welcome all comments.

class AmpPermissions {
    const PERM_READ = 1;
    const PERM_WRITE = 2;
    const PERM_APPROVE = 4;
    //future core permissions can be: 8,16,32,64,128,256,512,1024,2048,4096. an int of 4 bytes unsigned can be up to 4294967295
    protected $origObj = null;
    protected $perms = 0;
    protected $user = null;
    protected $group = null;
    protected $userGroups = array();
    protected $objPK = null;
    protected $objName = null; //typically the classname, but non classname keys should start with ! and include packagename or other unique identifier
    protected $sqlCoreWhere = '';
    protected $sqlSelectWhere = '';
    public static function getPermissionsObject (&$object) {

jshannon