I am trying to store a password after hashing it but it shows up as NULL in the database.I generated a scaffold for users using password string and name string, and then altered the mysql table to store hashed password instead using this :
ALTER TABLE users CHANGE password hashed_password CHAR(40) NULL;
my model:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :password
validates :name, :uniqueness => true
validates :password, :length => { :in => 6..20 }
def before_create
self.hashed_password = User.hash_password(self.password)
end
def after_create
#password = nil
end
private
def self.hash_password(password)
Digest::SHA1.hexdigest(password)
end
end
I am using Rails 3.2.13.
I think you should use
before_create :hash_the_password
after_create :nil_the_password
def hash_the_password
self.hashed_password = User.hash_password(self.password)
end
def nil_the_password
#password = nil
end
and NOT
#Wrong?
def before_create
...
end
so the callbacks can be the problem.
Related
I'm trying to use has_secure_password for user login, I've defined the User mode as below
require 'digest/md5'
class User < ActiveRecord::Base
has_secure_password
before_validation :prep_emailId
before_save :create_avatar_url
validates :emailId, presence: true, uniqueness: true, format: { with: /\A(|(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+#((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6})\z/i }
validates :first_name, presence: true
has_many :projects
belongs_to :nationality
belongs_to :category
scope :sorted, lambda{order("projects.position ASC")}
scope :newest_first, lambda{ "projects.created_at DESC"}
scope :oldest_first, lambda{order("projects.created_at ASC")}
scope :search, lambda{|query|
where(["name LIKE?", "%#{query}%"])
}
private
def prep_emailId
self.emailId = self.emailId.strip.downcase if self.emailId
end
def create_avatar_url
self.avatar_url = "http://www.gravatar.com/avatar/#{Digest::MD5.hexdigest(self.emailId)}?s=50"
end
end
I've declared strong parameters on the controller
def user_params
params.require(:user).permit(:category_id, :nationality_id, :first_name,
:last_name, :gender, :date_of_birth, :emailId, :password,
:password_confirmation, password_digest, :avatar_url)
end
Here's my create method.
def create
#user = User.new(user_params)
if #user.save
redirect_to user_path(#user.id)
#notice: "Thanks you for signing up !!!"
else
render ('new')
end
end
The error I'm getting when I try to save is as follows
Password digest missing on new record
Now if I take out attr_accessor from this code as suggested by many on stackoverflow this is what I end up getting.
Mysql2::Error: Data too long for column 'password_digest' at row 1: INSERT INTO `users` (`avatar_url`, `created_at`, `emailId`, `first_name`, `last_name`, `password_digest`, `updated_at`) VALUES ('http://www.gravatar.com/avatar/f76ca3885ff46187f3a216ba566623b9?s=50', '2014-03-17 10:39:01', 'funny#funnier.com', 'funny', 'funnier', '$2a$10$lJp6l70lHepWGz08f4O7luT3kE6Wj7bYzqD3o6G.EErkl0FTbAiHq', '2014-03-17 10:39:01')
You don't need the attr_accessors as has_secure_password handles that and the validation. You'll want password_confirmation in the view not password confirm.
I'm about two months in to learning RoR.
Trying to setup a subscription site using https://monospace-rails.herokuapp.com/ and want to add customer's physical address fields to the form. For some reason I can't find any info on this. I'm probably searching the wrong key words.
This is the user model:
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :stripe_token, :last_4_digits
attr_accessor :password, :stripe_token
before_save :encrypt_password
before_save :update_stripe
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :name
validates_presence_of :email
validates_uniqueness_of :email
validates_presence_of :last_4_digits
def stripe_description
"#{name}: #{email}"
end
def update_stripe
if stripe_id.nil?
if !stripe_token.present?
raise "We're doing something wrong -- this isn't supposed to happen"
end
customer = Stripe::Customer.create(
:email => email,
:description => stripe_description,
:card => stripe_token
)
self.last_4_digits = customer.active_card.last4
response = customer.update_subscription({:plan => "premium"})
else
customer = Stripe::Customer.retrieve(stripe_id)
if stripe_token.present?
customer.card = stripe_token
end
# in case they've changed
customer.email = email
customer.description = stripe_description
customer.save
self.last_4_digits = customer.active_card.last4
end
self.stripe_id = customer.id
self.stripe_token = nil
end
def self.authenticate(email, password)
user = self.find_by_email(email)
if user && BCrypt::Password.new(user.hashed_password) == password
user
else
nil
end
end
def encrypt_password
if password.present?
self.hashed_password = BCrypt::Password.create(password)
end
end
end
I'm assuming I need to add the address fields to the db, attr_accessible and within the Stripe:: Customer.create add something. I'm a little confused on how to write it and if that is all require to make it work.
Any help or even links in the right direction would be huge.
I have the following model and am trying to turn off password validation for the User model using this Rails Validates Prevents Save
class User < ActiveRecord::Base
before_save :encrypt_password
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 4..12 },
:if => :password_required?
def password_required?
self.new_record? or self.password?
end
#
# where we encrypt on creation
#
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
I get an error for:
undefined method `password?' for #<User:0x007fc8e0473be0>
Should I just check for self.password_hash? There's another form for updating the password. What is the best strategy for turning off validation in that instance?
thx
You should add this to your user model:
attr_accessor :password
That will give your user model a password attribute -- however, it is not persisted to the database or remember in your application in any way. Only the salt and hash are stored.
To get password? in your model too, do this:
attr_accessor :password
alias :password? :password
You can use password? like a gimpy Boolean field: if it returns anything, then password is set. Otherwise password hasn't been set.
I am extrapolating from a User model given in the Rails tutorial found here to learn more about creating models. I am trying to give a user a confirmation flag, which is initially set false until the user confirms their identity through clicking a link in an automated email sent after registration.
Everything worked before I added the confirmed attribute. I have added a confirmed column to the database through a migration, so it seems to me the error happens somewhere in the before_save :confirmed_false logic.
Can someone help me? The user model is below.
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email, :password, :password_confirmation
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
before_save :encrypt_password
before_save :confirmed_false
def has_password?(submitted_password)
encrypted_password == encrypt(submitted_password)
end
def self.authenticate(email, submitted_password)
user = find_by_email(email)
return nil if user.nil?
return user if user.has_password?(submitted_password)
end
private
def confirmed_false
self.confirmed = false if new_record?
end
def encrypt_password
self.salt = make_salt if new_record?
self.encrypted_password = encrypt(password)
end
def encrypt(string)
secure_hash("#{salt}--#{string}")
end
def make_salt
secure_hash("#{Time.now.utc}--#{password}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
1,1 Top
In your migration, if you set the confirmed column to be a boolean and the default value to be false then you don't need the before_save :confirmed_false callback at all as it will always be false when it's a new record.
Updated
class User < ActiveRecord::Base
# unlike before_save it's only run once (on creation)
before_create :set_registration_date
def set_registration_date
registration_date = Time.now # or Date.today
end
end
Can't really figure out what you're trying to do here. It seems like you want to set the default to be confirmed = false, then change it to confirmed = true if the user clicks on the appropriate link and sends you the correct token, or something like that.
So the flow would be something like this:
A user record is created with confirmed = false
There is no need for a before_filter do do anything yet
A user does some action that permits his confirmed column to be set to true
Still no need for a before_filter
What's the before_filter for? Are you trying to use it to set a default?
This code is from the agile web development with rails book.. I don't understand this part of the code...
User is a model which has name,hashed_password,salt as its fields. But in the code they are mentioning about password and password confirmation, while there are no such fields in the model. Model has only hashed_password. I am sure mistake is with me. Please clear this for me :)
User Model has name,hashed_password,salt. All the fields are strings
require 'digest/sha1'
class User < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name
attr_accessor :password_confirmation
validates_confirmation_of :password
validate :password_non_blank
def self.authenticate(name, password)
user = self.find_by_name(name)
if user
expected_password = encrypted_password(password, user.salt)
if user.hashed_password != expected_password
user = nil
end
end
user
end
def password
#password
end
def password=(pwd)
#password = pwd
return if pwd.blank?
create_new_salt
self.hashed_password = User.encrypted_password(self.password, self.salt)
end
private
def password_non_blank
errors.add(:password,"Missing password")if hashed_password.blank?
end
def create_new_salt
self.salt = self.object_id.to_s + rand.to_s
end
def self.encrypted_password(password, salt)
string_to_hash = password + "wibble" + salt
Digest::SHA1.hexdigest(string_to_hash)
end
end
attr_accessor is used to create getter/setter methods for an instance variable. For example:
attr_accessor :foo
# is equivalent to
def foo
#foo
end
def foo=(value)
#foo = value
end
In the code you pasted, def password and def password= are defined manually. However, I'm a bit confused by the use of:
attr_accessor :password_confirmation
validates_confirmation_of :foo automatically creates a foo_confirmation accessor, so this should be:
attr_accessor :password
validates_confirmation_of :password
Add a simple before_save callback to encrypt the password and you're all done.
require 'digest/sha1'
class User < ActiveRecord::Base
# attrs
attr_accessor :password
# class methods
class << self
def encrypt(password, salt)
Digest::SHA1.hexdigest("--#{salt}--#{password}--");
end
end
# validation
validates_presence_of :name
validates_confirmation_of :password
# callbacks
before_save :encrypt_password
protected
def encrypt_password
return if password.blank?
if new_record?
self.salt = Digest::SHA1.hexdigest("--#{Time.now}--#{name}--")
end
self.encrypted_password = User.encrypt(password, salt)
end
end
Since you don't want to store the password in the database in plaintext, you create a virtual attribute called password. You do this when you write:
def password=(pwd)
#password = pwd
return if pwd.blank?
create_new_salt
self.hashed_password = User.encrypted_password(self.password, self.salt)
end
That way, when you call password="wibble" it is actually encrypting "wibble" and storing the encrypted value in the database instead.
In the example, password_confirmation property is added to the model using the attr_accessor helper which sets up a getter and mutator for you in one line of code:
attr_accessor :password_confirmation
That one line is the same as if you had written this:
def password_confirmation
#password_confirmation
end
def password_confirmation=(pwd_conf)
#password_confirmation = pwd_conf
end
password's accessors are defined explicitly in the model:
def password
#password
end
def password=(pwd)
#password = pwd
return if pwd.blank?
create_new_salt
self.hashed_password = User.encrypted_password(self.password, self.salt)
end
These virtual attributes are defined in the model code, because they don't exist in the db table the model gets the rest of it's attributes from, because you don't want them stored in the db.
I was having the same question.
I found the item of validates_confirmation_of at Rails API website useful: http://ar.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M000081.