Welcome to the first in what should be a series of posts about the IBBRE project. I’m David Fowler, one of the IBBRE software developers. I haven’t really been given any specific remit as to what to blog about, so I’m going to talk about what I find interesting – which this week will be about the knowledge required to keep IBBRE up and running. I joined the project a few months ago, and was faced with a large amount of existing code to be worked with and added to. There was a steep learning curve in figuring out how all this worked, and I shall use examples from my own experience where possible for illustration.
What I plan to do today is just give a couple of examples of where things have gone wrong (ok, they were fixed fairly quickly, but should have been spotted in testing). In future posts, I’ll try to draw some conclusions.
First some background information which may help to understand the later examples. The IBBRE server (what you interact with when you go to http://www.lifeguideonline.org) is written using Grails. If you’ve done any programming before, I can recommend going to http://www.grails.org/Community, downloading Grails, and working through the tutorials. Otherwise, or if you’re pressed for time, hopefully the following explanation will help.
The G in Grails stands for Groovy, a Java compatible scripting language. Rails is a web development framework. I guess the easiest way to explain Rails is that it’s a set of conventions, or best practices, which help you design web systems in a straightforward way. The main convention is that of Model, View, Controller (MVC) which forces you to separate the data itself (the model) from how the data is displayed (the view), and from the actions that alter the data (the controller). The main resaon for all this is that by separating the different elements, the resulting code is easier to understand and maintain. In IBBRE we have a model describing intervention data held in a file Intervention.groovy, with a controller called InterventionController.groovy, and several views (for the creation, editing, listing, etc, of interventions). Views are written in GSP (basically HTML with some extra features). All these files are stored in a directory structure, and as long as you know the conventions, it’s fairly easy to find the bit of code that does a particular job.
Another important feature is that the models (i.e. the data descriptions) map to database tables, so that if you create a new intervention in InterventionController, it will be saved as a row in a table in the database. This mapping process is called GORM (Grails Object Relational Mapping) and saves a lot of effort on the part of the programmer. A very useful feature is that if you modify the model, the database gets updated (maybe extra columns are added to a table), with no extra work needed from the programmer.
I want to describe a couple of issues that can catch out a novice user (e.g. me). When you’re developing and testing new code, you don’t want to use the main database that holds all the important intervention data. One mistake would result in a deluge of irate emails and loss of confidence from the end users. So we use a local Grails server with a local database, which is typically wiped when the server is restarted. In order to avoid having to enter sample test data every time, there is a file Bootstrap.groovy that adds all this when the server starts. Now, even though it sounds really obvious when I say it, it’s very easy to add some code to this bootstrap file, see that the new data appears on the development server – and then completely forget to modify the main database when the time comes to update the main server.
A case in point with IBBRE is access permissions. We have a database table called requestmap which specifies which types of user can access which URLs. A couple of entries in this table are shown in the next figure. The first row says that moderators can make new announcements, and the second says that any user can list tutorial interventions.
| id | config_attribute | url |
| 12 | ROLE_MODERATOR | /admin/announcement/** |
| 19 | ROLE_USER | /manager/intervention/listTutorial/** |
In the Bootstrap.groovy file there are lines that add these to the development database (but not to the main database, as that database isn’t wiped between restarts). Something like this:
new Requestmap(url: '/manager/intervention/listTutorial/**', configAttribute: defaultRole).save()
So, it’s easy to get the development version working, create some tutorial interventions, log in as different users, check that everything works as expected, and add the code to the main server. But, you also have to manually add the row to the database table (which I forgot, of course). And eventually, someone will complain getting an error message when they try to look at tutorial interventions.
A second, related, example is also to do with access permissions. In addition to the checking of the requestmap table, there are various places in the code that check that the logged in user has the correct permissions. For example (and don’t worry if you don’t understand the code, I’ll explain it below):
if (intervention.owner.id!=userid &&
!(intervention.type.id == InterventionType.findByName("demo").id) &&
!(intervention.type.id == InterventionType.findByName("tutorial").id)
This is a test to see if the logged in user has certain permissions (in fact, it’s checking if they don’t have certain permissions, and will output a warning message if not). It looks to see if (a) the user is not the owner of the intervention (b) the user is not a researcher (c) the user is not an admin (d) the intervention is not a demo intervention (e) the intervention is not a tutorial intervention. If all of these are true (i.e. the user fails all the checks), then subsequent code (not shown here) will give the warning.
Why is this important? Well, if you’ve been asked to add a new category of interventions (similar to demo or tutorial), you have to remember to add a line to the test. (And of course, when I recently added the tutorial category, I omitted to add the line:
!(intervention.type.id == InterventionType.findByName("tutorial").id)
And why did I forget? Because I had added the row to the requestmap table, logged in to the server and checked to see if everything worked. But when I logged in to check it, it must have been as either an admin or the intervention owner, as there was no warning message. Easy to do if you’re in a hurry, I guess. But also, because I had taken the action to add the requestmap entry that lets all users look at interventions, I assumed that there wasn’t anything else to do. But the main lesson is that you should log in as every possible type of user, and owner or non-owner of the intervention, before accepting the code as OK.
OK, I’ve made myself look stupid enough for one day… Next time, I’ll try to draw some conclusions – and maybe add some more examples as I think of them (or if I manage to make some new real life ones).