1

I have a model User and when I create one, I want to pragmatically setup some API keys and what not, specifically:
@user.apikey = Digest::MD5.hexdigest(BCrypt::Password.create("jibberish").to_s)

I want to be able to run User.create!(:email=>"something@test.com") and have it create a user with a randomly generated API key, and secret.

I currently am doing this in the controller, but when I tried to add a default user to the seeds.rb file, I am getting an SQL error (saying my apikey is null).

I tried overriding the save definition, but that seemed to cause problems when I updated the model, because it would override the values. I tried overriding the initialize definition, but that is returning a nil:NilClass and breaking things.

Is there a better way to do this?

Mitch Dempsey
  • 38,725
  • 6
  • 68
  • 74

5 Answers5

4

use callbacks and ||= ( = unless object is not nil ) :)

class User < ActiveRecord::Base
  before_create :add_apikey #or before_save

  private
  def add_apikey
    self.apikey ||= Digest::MD5.hexdigest(BCrypt::Password.create(self.password).to_s)
  end
end

but you should definitely take a look at devise, authlogic or clearance gems

Tadas T
  • 2,492
  • 20
  • 20
  • I think `before_create` could be good but if he's validating presence of apikey, he'll need to do it in `before_validation`. – theIV May 22 '10 at 21:46
  • If I do `User.new(params)` then `@user.save` is the `before_create` filter run? – Mitch Dempsey May 22 '10 at 21:47
  • Yes, because the save is really acting as a create in this instance since `@user.new_record?` will return true. – theIV May 22 '10 at 21:54
  • Thanks! I ended up going with this solution, it seems to be fairly elegant and worked great! Thanks! – Mitch Dempsey May 22 '10 at 21:57
  • @webdestroya: What if two users have the same password? – Ryan Bigg May 23 '10 at 00:08
  • @Ryan Bigg - Im not sure what you mean.. I am not using a password, it is just an APIKey used to authenticate. The APIkeys are checked to be unique – Mitch Dempsey May 23 '10 at 00:57
  • @webdestroya: The example shows `self.password`. I assume BCrypt doesn't do anything special for the same value, so if two users have the same password it's going to generate the same apikey for them. – Ryan Bigg May 23 '10 at 02:45
  • @Ryan Bigg - No, everytime you run Bcrypt it will give you a new hash the salt just adds extra randomness – Mitch Dempsey May 23 '10 at 03:18
1

What if, in your save definition, you check if the apikey is nil, and if so, you set it?

davidtbernal
  • 13,434
  • 9
  • 44
  • 60
1

Have a look at ActiveRecord::Callbacks & in particular before_validation.

theIV
  • 25,434
  • 5
  • 54
  • 58
  • What I am trying to do is be able to pass an email to a new user object, and then create it. I want the model itself to generate an APIkey and a Secret, so that anytime I create a user, I know that the API key will be there. – Mitch Dempsey May 22 '10 at 21:45
  • I understand that. My point in the comment to Tadas' response is that if you are not accepting Users that have no apikey, you need to do it before_validation, and not before_create because before_create happens after validation. – theIV May 22 '10 at 21:56
0
class User
    def self.create_user_with_digest(:options = { })
        self.create(:options)
        self.apikey = Digest::MD5.hexdigest(BCrypt::Password.create("jibberish").to_s)
        self.save
        return self
    end
end

Then you can call User.create_user_with_digest(:name => "bob") and you'll get a digest created automatically and assigned to the user, You probably want to generate the api key with another library than MD5 such as SHA256 you should also probably put some user enterable field, a continuously increasing number (such as the current date-time) and a salt as well.

Hope this helps

Schneems
  • 14,918
  • 9
  • 57
  • 84
0

I believe this works... just put the method in your model.

def apikey=(value)  
  self[:apikey] = Digest::MD5.hexdigest(BCrypt::Password.create("jibberish").to_s)  
end
Ju Nogueira
  • 8,435
  • 2
  • 29
  • 33
  • I still need to be able to populate the user from the database, which stores the APIkey... will this solution present a problem, when I essentially try to assign a value to a function? – Mitch Dempsey May 22 '10 at 21:51