In this post, I’ll be describing my current approach to implementing a major feature into an existing web application. In the software development world, everyone knows that there are many different techniques to implement the same set of features. We describe these methods as ‘design patterns’, and the type of design pattern you chose to employ will depend on several factors – some of these will include current skill and knowledge level of the developer, the sprint length, the complexity of the existing code base and the potential future requirements of the application. I also need to make sure that I don’t over-engineer the solution and allow enough flexibility for future extendibility. Usually this means finding and then implementing the simplest solution.
In the real world, I’m constrained to a set budget and a specific deadline to deliver this feature in a working state. Before I start with my thought processes, I need to put out a brief disclaimer. This is by no means a guide, in the broadest sense. There are many guides out there on the Internet that will help you to implement many features, some of which will even teach you how to build a Twitter or Yelp clone. This blog post serves a dual purpose. The first purpose is that I get to write content for the aspiring developer – someone like myself who is a journeyman software craftsman.The second purpose is a means to expose my ignorance to a wider audience (as opposed to just within my team or on StackOverFlow), in the hopes of gaining constructive feedback.
The blogpost will most likely be too long for one sitting, so it’ll be broken up into three parts, to be written and published later this year.
The existing web application for which this feature is to be implemented serves as a data visualisation platform for an industrial ‘Internet of Things’ energy monitoring device. For the technically minded, our platform is based on a MongoDB database with PHP Slim framework running on Apache webserver.
The application has been built from the ground up and has faced some major design changes – one such change I oversaw was the replacement of the back-end database from MySQL to MongoDB, which involved rewriting the existing SQL queries into MongoDB’s aggregation pipelines. The goals were clear – the API calls had to return the exact same data and it was just a case of figuring out how to best store and access the data from the new NoSQL database.
The feature to be implemented is role-based access control (RBAC). The existing web application platform provides one type of account – essentially a ‘superuser’ that can view and administer all monitoring devices and their locations. Obviously, this is not a viable solution for providing our different end users access to the web app on a multi-tenancy platform.
Therefore, I need to ensure limited access is provided based on roles, but also restrict data access based on the client. The existing superuser role will remain for the overall administrators of the application (HSSMI), with two additional user types, both restricted to a single client/account – a normal admin user, that can add/edit sites and devices, and a basic user that has viewing rights only.
The Implementation Approach
The first port of call was to discuss the feature implementation with my team lead, Abdi, who alluded that such an implementation would possibly need to be inserted as middleware. Since the application was built with MVC in mind, it does make sense to write a call to the middleware function that will either grant or restrict access, and then directly insert this call into the control layer. By doing this I’ll reduce the amount of changes needed to implement the feature, as I expect that both the model and view layers will need minimal, if any at all, changes.
So far so good, I now have a plan. So what is the next stage? I use google to find answers. It’s pretty much common knowledge that software developers spend a lot of time googling for answers on how to implement things. This is because most features that you’re required to implement have either been many times before in part or complete. The more well-established the programming language and community are, the greater the chance of finding a workable solution in your chosen language. In the case of PHP, both are of no concern.
After a bit of time spent searching, and a few potential leads on StackOverFlow, I manage to find the correct terminology for this specific feature – ‘Access Control List’ or ACL.
“An Access Control List, or ACL, defines the set of rules that determines which group of users have access to which routes within your Slim application”
I then identify a possible solution on Github – slim-auth by jeremykendall, which is an open source authorization and authentication library for the Slim Framework. It’s based on another PHP framework Zend, but has been adapted to work with Slim. One quick look at the requirements to make sure I am good to go…
“Slim Auth has not been tested against the upcoming Slim 3 release.”
Unfortunately our app is running on Slim 3, and I don’t have the time to try to implement a solution that could potentially introduce further problems. Going back to google, further searching reveals the following Github repository:
A brief study of the usage example reveals Slim 3 implementation, as well as a simple, easy to read method for assigning resources, roles and access assignments – either allow or deny. This definitely sounds like a nice and simple solution to implement.
For this library to work, I need to think about how the specific roles will be passed into the middleware, when the user is logged in. The answer is by using the session – a temporary storage for user data that persists only for the amount of time a user stays logged in. Since the session was already being utilized in the application, the only modification required here is to manually add the role types to each user account in the users collection in MongoDB, and then make sure that this value is being passed to the session during login authentication.
With that said, I start to integrate this library into our application. As with many feature implementations from open-source libraries, it’s a constant state of adding code, breaking things, looking at the errors and stack trace, and then resolving the issues one by one. It’s far too much to expect a complete Dummies guide set of instructions or examples since the developers of these libraries are in most cases, not being compensated and are rather doing this for the good of the development community.
Saying that, when I did manage to hit a snag in the road that I simply can’t figure out for myself, I reached out directly to the developer by opening an issue on his repo. Within a day I get a response from him, suggesting that I try an alternative approach. What transpired was a two-way conversation in which I manage to figure out the source of my issue (which happened to be due to an error that I had inadvertently introduced myself – oops), however I gained an understanding that the library he was providing did not exactly suit our requirements.
In short, his library required every single route to be explicitly stated for approval, when in our case we needed the flip side which only needs to deny access to a few specified routes (in particular, just the admin page). Hence, to implement this library would not be the simplest thing to do.
To my surprise, after spending roughly a day spiking and debugging, having again spoken to my team lead for further clarification, the simplest thing to do would be to write my own access control method. The irony of this is that this would have not become apparent, had I not taken the above approach.