I have just now significantly refactored authorisation or access control in Phosphorus Five, as you can see from its code. In addition, I’ve also removed a couple of “anomalies”, which arguably were bugs in its code – Some quite severe too may I add. Hence, I wanted to write up about how access to an object is granted or denied in Phosphorus Five, hopefully allowing you to more easily create your own access objects, granting or denying access to specific parts of your Phosphorus Five installation.
First things first, an access object in Phosphorus Five determines whether or not one or more roles have access to some part of your app. The role is the name of the access object’s root node, and whether or not it grants access or denies access to that role to the part in question, is determined according to its name, which can end with either “.deny” or “.allow”. Secondly access objects are “cascading”. What I mean by that, is that they obey to similar rules as a CSS selector. For instance, if I deny access to the path “/foo/” to some role, then unless explicitly overridden in another access object, that same role will be denied access to also “/foo/bar/”.
In addition you can create access objects that are referencing all non-root roles, by creating an access object with a role name of “*”. This implies that the access object is for all roles in the system, except the “root” role, which has access to everything always, and cannot be restricted in any ways. In addition each access object have a “type”. The type declaration of my access objects allows me (or you) to extend the access system, by creating your own types of access objects. By default though, there are the following access objects in Phosphorus Five.
- p5.module – Determines if access to module is given or not
- p5.io.read-file – Determines read access to files or folders on disc
- p5.io.write-file – Determines write access to files or folders on disc
- p5.hyper-core – Used in Hyper Core to determine access
- p5.system.platform.execute-file – Used to determine if user has shell execute access to a file on disc
All of the above types are expected to have one of “.allow” or “.deny” after their names. If I wanted to grant access to the “foo” user to write to the files within the folder “/foo/bar/” for instance, I could create an access object resembling the following.
The above would allow all users belonging to the “foo” role to write to all files beneath “/foo/bar/”. Though it presents us with a dilemma, which is that it also allows the user delete the folder or rename it. This might not necessarily be what you want, so you can further restrict this operation, by adding another (parametrised) access object to your list of access objects.
foo p5.io.write-file.allow:/foo/bar/ foo p5.io.write-file.deny:/foo/bar/ exact:true
Notice the [exact] parts above. Since a “deny” object always have precedence when two access objects have the same path and role name, if the user tries to rename or delete the “/foo/bar/” folder itself, the last access object from above will have precedence, and hence prevent the user from deleting or renaming the folder itself. However, since the last access object from above has an [exact] argument, it will only match the specified path, if it is exactly “/foo/bar/”. Hence, in our above example we first allows the user to write to everything inside of the folder “/foo/bar/”, for then to deny him to change the “/foo/bar/” folder itself. This gives the “foo” user complete control over everything inside of the “/foo/bar/” folder, but not the folder itself. An access object can be parametrised with the following arguments.
- [exact] – Requires an exact match
- [file-type] – A list of pipe separated file extensions, such as e.g. “hl|md|js”
- [folder] – Requires the path to end with a “/” for the access object to be a match
designer p5.io.write-file.allow:/foo/bar/ file-type:html|css|png
If you in addition want to allow the designer role to create folders too, you can accomplish that with the following.
designer p5.io.write-file.allow:/foo/bar/ file-type:html|css|png designer p5.io.write-file.allow:/foo/bar/ folder:true designer p5.io.write-file.deny:/foo/bar/ exact:true
* p5.io.write-file.allow:/foo/bar/ file-type:html guest p5.io.write-file.deny:/foo/bar/
The above logic will work since an explicitly named access object (our “guest” object from above), will always have precedence over an “*” object. Since all IO operations will check to see if the user has access to the file according to the access object list, this creates a fairly secure way to grant or deny users access to parts of your Phosphorus Five installation. You can also create your own types of access objects, extending the authorisation features of Phosphorus Five with your own logic – However, that is the subject of another article later …