Tag Archives: CakePHP 2.x

CakePHP 2.x routing

One of the larger issues I’ve had to deal with after being rehired is that the commissioning system we created had stopped producing reports, you could load the page, but no data would populate. These reports are populated via ajax, so I checked the request and found the error “No data found”. Several hours of digging and google slowly uncovered what was happening behind the scenes.

The root cause ended up being the url of the ajax request to pull the data. There were colons in the url. They were already encoded, but once you removed the time section of the datetime string, the reports worked again. These reports have remained unchanged since they were first developed over a year ago. The issue lay in the core of CakePHP. In particular the way the routing for requests had changed. We had been using 2.1 before and are now using 2.7. The two routing classes simply decode the url in different places, and the new one does it before the elements are turned into arguments passed to the controller.

I ended up coming up with two fixes. The first was to simply rename the old CakeRoute and use it as a custom routing class. This worked, but I didn’t test it heavily so there may have been issues. In addition, I was unable to find a method to restrict the custom routing class to only the controllers that needed it. The second method was to simply write a router connection in Config/routes.php where I defined the argument elements. This required a call for each controller action I wanted to fix and would end up being time intensive if you needed to manage a large number of routes, I only had four. You can see my routing code below.

Software and versions used or mentioned:
CakePHP 2.1 & 2.7

Resources:
CakePHP 2.x Routing Documentation
CakePHP GitHub

Multiple Role Authentication in CakePHP

As I have said before, I use CakePHP for the web system at my job. Only a few dozen people use the system, but the each do different jobs and need different access permissions (We don’t want a call center rep performing a manager approval of a purchase order, etc). This is where the Auth/ACL system built into CakePHP comes in.

The Auth/ACL system works very well, but the one Role (or Group if you prefer) per user set-up makes it difficult to fine tune a user’s permissions unless you create a role for each job title. What I needed was the ability to assign multiple roles to a user to more easily build a set of permissions of what they could do. A quick Google search later and I had several ‘solutions’, of which zero worked. It was obvious I needed to make my own solution.

To be honest, I didn’t understand the Auth/ACL system when I started, and I still feel like my understanding now is questionable (Filed under “Magic” along-side File Systems and loan interest calculations). My first misunderstanding was that Auth and ACL were one connected system. When you look at them as two systems it makes more sense.

ACL is simply the interface to the rules connecting ARO (Access Request Objects: users and roles) to ACO (Access Control Objects: the controllers and actions). It isn’t concerned with how many roles a user has, and it will even check multiple AROs at once without modification. We can leave it alone and trust that it will do it’s job for this.

Auth is what you really need to modify to get multiple roles working. Auth uses Authorize Adapters to check if a user is authorized to view a page. There are a few different flavors of these adapters, but you are probably using ActionsAuthorize, which takes the user data and tosses it to ACL for checking. When we look at Auth itself we see that this data is pulled from the session, which is loaded during login.

Now it is time for the example code.

In my example code I have used the model Role for the roles, you could easily replace Group or whatever your preferred name is in it’s place quite easily. Users has a HABTM (Has And Belongs To Many) relationship with Roles.

This first set of code should go into Controller/Component/AuthExtComponent.php
Instead of re-writing the AuthComponent itself to load the roles when logging in, we are going to create a new Component that extends the AuthComponent. In this we override the login function. When it is called, we call the parent login function, and if the user is properly authenticated, we load the role data for the user and store it in the user’s session.

This next bit of code is just an edited ActionsAuthorize. It should go into Controller/Component/Auth/ActionsMultiAuthorize.php
All this code does is use the role data we saved to the session during login and pass it to ACL for checking.

Last, you need to configure your app to use the AuthExt we coded above. In the AppController, you should configure your included $components list like the following.
By using className instead of calling AuthExt directly, we ensure that other bits of code that depend on Auth can still find it (Even though they are technically using AuthExt).

Sadly, I hard-coded the roles model name into the above code. If I had done it properly I would have used configurable settings.

While writing this post and re-reading the code, I realized that you could have had a much easier time and written a Authenticate Adapter to load the data instead. I will probably write and release that code later.

Update: (2013-09-30) In the course of using this code I have found that while this code allows you to build allow permissions, a single deny in any of a user’s roles, regardless of if that user has allows elsewhere, will deny the permission. A simple fix to prevent this from happening is to clear any sql rows with -1 permissions from the aros_acos table in your database. Note that this will clear any deny permissions you may have purposefully set.

Postgres sequences in CakePHP

Recently I added my company’s legacy database system to our new CRM system we are building with CakePHP. It worked wonderfully, until I tried to inject something into the legacy DB. As it turns out our old DB was built using sequences for the primary keys of all the tables. When this was combined with the fact that it used one of several schema in the same db, CakePHP simply could not find the sequence and the SQL failed.

As it turns out it is easy to configure the model to use a sequence. I ended up with the following as an example:

Take note that you need to include the schema and double quotes in the sequence string for it to work (At least with a schema that is not ‘public’).