I’ve developed a php application using an MVC design (rolled my own…) and am at the point that I need to implement a role based access system: there will only be 3 different types of user roles. Prior to using an MVC approach (aka spaghetti code), my method of access control was at the “page” level; since all of the html, javascript, and php for a given web page co-mingled, I simply first did a check for their role via a session, and if they didn’t have access, they got the boot.
Now that I’m using MVC and everything is OOP, it seems like I should have a check within every one of my methods to see whether a user should actually have access to that method. And, if this is the case, would the correct approach to create a series of tables in my database, one with the methods and what roles can access them, one table with the roles, and another table with the users and their associated roles?
Finally, if I do have a good conceptual understanding, I have to admit that it seems like a bit of a difficult process to maintain: every time I add a new method to my system, it means that I will have to think about what roles should have access to the method. Or, is this the price of security?
I would appreciate comments that would help me to just get started with the proper design.
You might want to take a look at the authorization part of Symfony’s security component. It holds a lot of good info and methods for exactly what you are looking for, especially with the ACL system. It is also a “pluggable” component, which means you can use it separately from Symfony and just plug it into your framework.
Basically, you want to try and breakup your Request/Response flow into a set of discrete steps.
Gather request information
Extract current user from request object (perhaps a header) or the session.
Match request against your routing table.
Determine access requirements for the matched route
4.a. Each individual request can specify allowable roles
4.b. Or you could use some sort of firewall to require, for example, all routes starting with /admin needs an admin role.
Determine if the user has the proper access. Toss not allowed exception if not.
Process the controller for the current request. Or however you generate the response.
With this approach, authorization is run on all requests so you need not check in your action methods.
Each step is a little chunk of isolated code. For many applications, you can hard code the flow right in your application object.
However, there are many other steps that you may or may not need later. Perhaps a Cross Origin Resource Sharing step. Perhaps cache control. Lot’s of possibilities. It’s worthwhile to be able configure the flow for different applications and to then plugin processors as needed.
There are two basic approaches that I am aware of.
The first approach emits events at various points in the processing cycle. You can then attach listeners to these events. More details here:
The event approach is very popular and widely used.
The second approach based on the notion of middleware. Each step becomes a standalone bit of middleware. Instead of events, middleware objects are chained together with each one calling the next one. In theory, this approach is more flexible and could perform better than an event driven approach. It’s widely used in other languages (nodejs express, ruby etc) but is just starting to catch on in php.
gives me a very clear overview of the process. And, I appreciate the additional resources, though I think that it might be a bit too much for what I need at this point.
And while I can see how I would not in fact need to check in every method (I can do so in the Controller before I get to the method using the user information), I’m still trying to imagine the best way to see if a user does in fact have access to a particular resource. To give one concrete example, in my website, there are “students” who can access specific “courses”. Do I
a) Have a database that gets updated with the student ids and the course ids every time a new student/course is created, then just check if that user/course exists as a query? In other words, a database which exists with specific users (or groups) and the resources associated with them for which they have permission.
or
b) Have a class which consists of checks for specific resource access “on the fly”. For example, if the resource requested is something like get-student-course/7, then I have a permissions class which has a method to specifically check whether that user has access to that course by seeing if they’re enrolled in that course. In other words, there’s no master table of permissions, but rather a class which consists of the necessary checks for each user as they try to access a resource based on some business logic.
No matter what, you are going to need some form of persistence, and more than likely, in a database. So your point one is a given no matter what solution you decide to have.
The next decision is, do you want a solution for one scenario, the student / course scenario, or do you want a solution, which will work generally across any object and any role at any time? If you want the latter, then you want to look at Symfony’s ACL system. If you want the former, then you should look at Symfony’s Voters system.