Sharing data with CakePHP & Rails

 Dec 12, 2013

A while ago I was involved in a project that wanted to convert a legacy CakePHP (v1.3) app into a Rails app. One of the problems we had to solve was the differences between how each framework dealt with timestamp fields on tables, the other was how each stored hashed passwords. Here I outline how we solved each of these.

1. Different timestamp field names

CakePHP has the same feature as Rails in managing the created and last modified date and time of any record (as I am sure many web frameworks do). However, while CakePHP uses created and modified as the field names, Rails uses created_at and updated_at

At first we looked at changing Rails to use the legacy format so that during the transition we could bring up some modules in Rails (such as the backend admin) to replace the ageing CakePHP solution.

To do this in Rails meant overriding the ActiveRecord module with the new field names. While not hugely difficult in itself, one of our concerns was for the future. Would this become a maintenence pain, or confuse future developers who came in thinking this was a normal Rails app.

Alternatively, CakePHP makes if relatively easy to change the timestamp field names. Simply adding the following attributes to the applications AppModel (from which all other models inherit) allowed us to convert the entire legacy app over to the Rails idiom and let things take their natural course.

<?php
class AppModel extends Model
{
    public $createField = array('created_at'); 
    public $updateField = array('updated_at'); 

To prevent drastic changes throughout the app, we applied this change to each model individually as its table was updated to the new field names. Once they were all complete and the app thoroughly tested, we were safe to run with the new Rails solution.

2. Password storage hashes

In our CakePHP application we were using SHA256 for the password hash, along with CakePHP’s salt.

In order for the new Rails app to work with the existing users and allow them to login, we did 2 things.

First we created a method to allow a match on the original CakePHP password using SHA256 and the salt copied from the CakePHP app/config/core.php file.

Then we used the plaintext password submitted by the user to generate a new bcrypted and salted password in the database using Rails, if they were authenticated via their old CakePHP password.

This meant our users had a completely transparent login process, as far as they were concerned nothing changed. Meanwhile in the background we were able to authenticate them from legacy data, update the security of their password storage and move to a Rails default with minimal fuss.

The crux of this process was the authentication with a CakePHP password. Here is the method we used to hash incoming passwords from the login form and compare them against CakePHP hashes in the database.

require 'digest'
def cake_password(plaintext)
  cake_salt = '' # Configure::write('Security.salt') value from core.php
  Digest::SHA256.hexdigest("#{cake_salt}#{plaintext}")
end