0

I'm trying to slowly migrate an old CakePHP site to Rails 4. Because of the massive effort required, the site needs to be moved over gradually, only migrating a single piece at a time, and starting with Active Admin. I've seen plenty of questions about doing things like this, but there are a few tricky extra things I need to consider:

  1. I cannot modify the CakePHP code
  2. I cannot break the CakePHP site
  3. The CakePHP site stores its hashed passwords in a database column named "password", and Devise doesn't like that. It expects "password" to be the plaintext password, and has gotten into a nasty habit of attempting to override that DB column as well.

Once the entire site has been migrated to Rails, I can certainly start following any of the myriad number of answers about how to migrate an app to Devise, but for now I need to work with the existing app

Thanks

charredUtensil
  • 153
  • 1
  • 11

2 Answers2

1

I was able to resolve this issue by installing the ignorable gem, and SELECTing the password column AS encrypted_password in the default scope. It's ugly, but it works

class User < ActiveRecord::Base
  ignore_columns :password

  default_scope :select => "#{User.quoted_table_name}.*, #{User.quoted_table_name}.password AS encrypted_password"

  devise :database_authenticatable

  def valid_password?(password)
    hash = ::Digest::MD5.hexdigest("[SALT REDACTED]#{password}").downcase
    return hash == self.encrypted_password
  end
end
charredUtensil
  • 153
  • 1
  • 11
  • I'm in the same situation you're in (existing PHP app) and I wish I could've seen this sooner. I spent the past hour looking around and have went so far as looking into Devise source code and adding breakpoints in my code to debug before finally remembering that Devise adds in a virtual attribute named password which overrides my DB password. – Rystraum Mar 10 '14 at 00:10
  • I feel your pain. I'm trying to develop a gem to help out with this kind of thing. If you come up with any other weird issues, I'd love to hear them. – charredUtensil Mar 10 '14 at 23:37
  • The default_scope, unfortunately, has the side effect of altering relationship calls (e.g. role habtm users, calling role.user_ids returns everything and not just the user's ids). I'm working around it by re-declaring role.user_ids to only return the ids. – Rystraum Mar 11 '14 at 03:17
0

I found this: http://www.slideshare.net/napcs/rails-and-legacy-databases-railsconf-2009 and followed his advise in creating sql views to wrap up the legacy table.

This is what I used to run on mine (I'm adding an application on top of Limesurvey):

CREATE VIEW `users` AS
SELECT `uid` as `id`,
       `users_name` as `username`,
       `email` as `email`,
       `password` as `encrypted_password`,
       `full_name` as `full_name`,
       `role_names` as `role_names`,
       `parent_id` as `parent_id`,
       `lang` as `lang`,
       `one_time_pw` as `one_time_pw`,
       `created` as `created_at`,
       `modified` as `updated_at`
FROM `lime_users`;"

The catch is that you have to include in the sql view all columns that do not have default values in the base table for you to insert into the view. This is a good way to normalize 'ugly' column names into rails-friendly column names.

My user model:

class User < ActiveRecord::Base
  before_save :save_encrypted_password
  self.primary_key = "id" # This needs to be declared, for some reason.

  def sha2(password)
    (Digest::SHA2.new << password).to_s
  end

  def valid_password?(password)
    return false if encrypted_password.blank?
    return Devise.secure_compare(sha2(password), self.encrypted_password)
  end
protected
  def save_encrypted_password
    if password == password_confirmation
      self.encrypted_password = sha2(password)
    else
      errors.add :password_confirmation, "has to match password"
    end
  end
end

It's also a good idea to reflect in the model constraints that are in the database (not null, unique values, etc) to avoid any more gotchas. (Learned this the hard way after almost an hour of slogging through arcane error messages.)

Hope this helps.

Rystraum
  • 1,985
  • 1
  • 20
  • 32