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’).

Complications of not running SSH on port 22

Traditionally ssh runs on port 22, but that does not mean it has to. Where I work each machine that runs ssh runs it on a different port. I do not know why, but it causes quite a bit of fun when trying to use common ssh based commands that all expect ssh to run on the default port. I have had to figure out more then a few of the workarounds and will share them because I had trouble finding a few of them.

SSH

SSH is easy to connect to a non-standard port, simply use the -p flag followed by the port number. Following is an example:

ssh-copy-id

For those who are not aware, you can create an identity file and copy it to the remote machine to allow you to log in without the need of a password (Link to the guide that taught me). ssh-copy-id assumes that the remote machine is running on port 22 and you have to trick it to change the port. We use the same -p flag as with ssh, but we need to put the whole of the arguments into single quotes for it to work. See the example below:

rsync

rsync has a -e flag for setting which remote shell to use. after the -e flag you put the ssh options like is you were using ssh normally. Be sure to put them inside double quotes or they will get confused with rsync options. See the following example:

scp

SCP is very easy, simply use the -P flag to set the port number, like in the following example:

Updating an iPhone inside VirtualBox

Today at work (I started writing this the same day, but it took a bit longer then I thought to finish), we needed to test a new calendar feature with the various devices our sales reps use.  Android was easy, the entire IT staff have android phones, and most even have an android tablet. iPhone is another matter, no one had an apple device we could test on. Luckily we happened to have a few old iPhone 3 in our storage closet from when we switched phone carriers a few years ago. Unluckily, they were using an old iOS version and no longer activated. Activation and upgrading require iTunes, which runs on Windows, but I sadly run a linux machine. For windows only tasks I run a virtual Windows XP machine in VirtualBox.

VirtualBox can pass USB devices from the host machine to the virtual machine, but doing so requires an extension package. You can download the extension file from Oracle’s website, but there are several versions available. I use VirtualBox v4.1.12. The website says that the 4.1.xxx extension package only works with version 4.1.xxx but it installed properly on my system anyways. You can install extensions from the Preferences panel under File > Preferences (Or Ctrl+G).

VirtualBox Settings

The settings panel for extensions.

Once you have the extension installed, plug in your iPhone. Next, open up the settings panel for your virtual machine and go to the USB tab. Enable the USB controller. You also need to add a new filter for the iPhone. The USB filter will connect the iPhone to the virtual device for you automatically when it is connected. Now we can close the panel and start the virtual machine.

On the right side of the panel are the buttons that allow you to add and remove USB filters

On the right side of the panel are the buttons that allow you to add and remove USB filters

Setting the USB filter for the iPhone

After adding the iPhone filter

With the virtual machine running you can install iTunes and open it up, the iPhone should connect like it would on a physical machine. Once it has finished setting itself up, iTunes should offer any available updates to iOS, let it start the update. About half way through, the update will fail, this is because the iPhone has rebooted into recovery mode, which changes the USB identification. We need to go back to the settings panel and add the recovery mode iPhone to the USB filters.

Setting the USB filter for the recovery mode iPhone

After adding the recovery mode filter

Once that is done, we can manually connect the recovery mode iPhone to the virtual machine to reconnect the iPhone to the virtual machine. This is done by right clicking the USB button at the bottom right of the Virtual Machine and selecting the iPhone from the list. Once this is done iTunes will recognize the troubled iPhone and ask if you want to reset and update it.

Once you select the iPhone, it will have a check in the box to show that it is connected.

Once you select the iPhone, it will have a check in the box to show that it is connected.

At this point iTunes should be able to complete the update. As the iPhone shifts between normal and recovery mode, VirtualBox will use the filters you set earlier to automatically connect the device to the virtual machine, just like if it was a physical box. Once this is done you can remove the USB filters, or leave them as is so you can continue to use iTunes to load music and the like to your iPhone.

Software and versions used:
Oracle’s VirtualBox 4.1.12
Oracle VM VirtualBox Extension Pack 4.1.26
iTunes

Hardware used:
iPhone 3g

Resizing VirtualBox disk images

Recently I had the misfortune of having my windows virtual test machine fill up it’s hard drive. When I had created it, my host system had limited space and I only needed to test things in a windows environment inside a browser, so it got a small hard disk image. One day, while installing a new program, it simply ran out.

I could make a new virtual machine and re-install  but that requires configuration, whereas resizing only takes time.

In command line, navigate to the directory with the troubled system’s hard disk image. Since I had exported and imported this machine when I moved from a desktop to my nice new (to me) laptop my disk image was in vmdk format, but you need a vdi format to resize. So first we convert the disk:

Once cloned, you can resize the vdi you just made. Please note that the size is in MB.

The disk is now resized, but not attached to our virtual machine. I simply edited the virtual machine to point to the resized copy. Alternatively you can convert the image back into vmdk and copy it over the original.

(Don’t forget to delete extraneous copies, these files take up a lot of space)

The virtual machine should now have the new resized attached to it and it should be bootable, but the windows install can still only use the original size. We need to alter the partition to make it use the full disk. For this we need to boot into a live Linux disk with GParted. I used the Ubuntu 12.04 install CD, but GParted also have a live image available if you lack an already downloaded disk. Once you have saved the partition, you can shutdown the live disk and reboot into windows, which now has access to the entire resized hard disk image.

Software and versions used:
Ubuntu 12.04 LTS 64-bit (Xubuntu Desktop)
VirtualBox 4.1.12
Xubuntu Desktop 12.04 LTS Live CD
GParted