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.
Related
im new into Rails 4 development and rails in general. I red official "getting started with rails" guide where it is shown how to create blog. So I want to do my own from 0 registration, auth system.
While creating new user I get this error. I do not understand what I am doing wrong. Any advice?
Git repo:
https://github.com/pumpurs/auth
ActiveRecord::UnknownAttributeError in UsersController#create
unknown attribute: password
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user].permit(:password, :email, :password_confirmation))
end
private
def user_params
params.require(:user).permit(:password, :email, :password_confirmation)
end
end
Model file:
class User < ActiveRecord::Base
before_save :encrypt_password
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :email
validates_uniqueness_of :email
def enrypt_password
if password.present?
self.password_salt = BCript::Engine.generate_salt
self.password_hash = BCript::Engine.generate.hash_seret(password, password_salt)
end
end
end
You need to add attr_accessor :password to your User model, to provide a non-db-backed attribute to use to base your db-backed password_hash attribute off of.
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.
I got the error " unknown attribute: password" while logging into the app. It doesn't seem to save the password. I am new to rails, can anyone help me on this?
This is the message that I get while running the app. Here are is the code from model and control files.
ActiveRecord::UnknownAttributeError in UsersController#create
unknown attribute: password
I am trying to create a login/password web app. Getting the following error while running the app.
undefined method encrypt_pasword'
app/models/user.rb:32:inpassword='
app/controllers/users_controller.rb:43:in new'
app/controllers/users_controller.rb:43:increate'
Here is the code from control file.
require 'digest/sha2'
class User < ActiveRecord::Base
include ActiveModel::MassAssignmentSecurity
attr_accessible :name, :password, :password_confirmation, :hashed_password, :salt
attr_accessor :name
attr_accessor :salt
validates :name, :presence => true, :uniqueness => true
validates :password, :confirmation => true
attr_accessor :password_confirmation
attr_reader :password
validate :password_must_be_present
def User.authenticate(name, password)
if user = find_by_name(name)
if user.hashed_password == encrypt_password(password, user.salt)
user
end
end
end
def User.encrypt_password(password, salt)
Digest::SHA2.hexdigest(password + "wibble" + salt)
end
# 'password' is a virtual attribute
def password=(password)
#password =password
if password.present?
generate_salt
self.hashed_password =self.class.encrypt_password(password, salt)
end
end
private
def password_must_be_present
errors.add(:password, "Missing password") unless hashed_password.present?
end
def generate_salt
self.salt = self.object_id.to_s + rand.to_s
end
end
Here is my model file
require 'digest/sha2'
class User < ActiveRecord::Base
include ActiveModel::MassAssignmentSecurity
attr_accessible :name, :password, :password_confirmation, :hashed_password, :salt
attr_accessor :name
attr_accessor :salt
attr_accessor :password
validates :name, :presence => true, :uniqueness => true
validates :password, :confirmation => true
attr_accessor :password_confirmation
attr_reader :password
validate :password_must_be_present
def encrypt_password
self.encrypted_password
end
def User.authenticate(name, password)
if user = find_by_name(name)
if user.hashed_password == encrypt_password(password, user.salt)
user
end
end
end
def User.encrypt_password(password, salt)
Digest::SHA2.hexdigest(password + "wibble" + salt)
end
private
def encrypt_password
self.salt = make_salt if new_record?
self.encrypted_password = encrypt(password)
end
# 'password' is a virtual attribute
def password=(password)
#password =password
if password.present?
generate_salt
self.hashed_password =self.class.encrypt_password(password, salt)
end
end
private
def password_must_be_present
errors.add(:password, "Missing password") unless hashed_password.present?
end
def generate_salt
self.salt = self.object_id.to_s + rand.to_s
end
end
encrypt_pasword is missing an s.
The User.authenticate method is returning nil even though the user exists in the database with the correct email and password. This happens when calling the authenticate method from the Create action in the Sessions controller or from the Rails Console (irb).
Any help with this problem will be greatly appreciated.
class SessionsController < ApplicationController
def new
end
def create
user = User.authenticate(params[:session][:email],
params[:session][:password])
if user.nil?
flash.now[:error] = "Invalid email/password combination"
render 'new'
else
sign_in user
redirect_to user
end
end
def destroy
sign_out
render 'pages/options'
end
end
Here is my User model:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :first_name, :last_name, :email, :password, :password_confirmation,
:account_type, :email_confirmed, :weight
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
before_save :encrypt_password
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
def self.authenticate_with_salt(id, cookie_salt)
user = find_by_id(id)
(user && user.salt == cookie_salt) ? user : nil
end
private #################################################
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
def generate_email_conf_code
email_conf_code = secure_hash("#{Time.now.utc}")
self.email_conf_code = email_conf_code
end
end
Try to check your server log. You can also monitor them directly on the terminal. Look for the session email received on the server. It looks something like this on Rails 3.1.1
Parameters: {"session"=>{"email"=>"xxx#yyy.com", "password"=>"[FILTERED]"}}
See that you're getting the email correctly. If not, I guess you know what to do.
does your database store a password_digest or encrypted_password column? Older tutorials by michael hartl used password_digest, now they seem they are encrypted_password.
I'm following Iteration 13 of Agile Web development, for User login.
I create a migration to add 2 columns to my User model : hashed_password and salt.
Creation of Users works
login fails, due to this error undefined method 'hashed_password' in method 'authenticate'
The problem is that :
In rails console, I can fetch User.first.hashed_password, what seems OK :-)
I outputted the User that I fecth, and it is NOT nil
I tried to output user.hashed_password as I did in the rails console, but that throws always the same error :
NoMethodError (undefined method hashed_password' for #<ActiveRecord::Relation:0x00000003e6c3c0>):
app/models/user.rb:21:inauthenticate'
app/controllers/sessions_controller.rb:6:in `create'
This is my User model :
require 'digest/sha2'
class User < ActiveRecord::Base
has_and_belongs_to_many :products
has_many :created_products, :class_name => "Product", :foreign_key => :product_id
default_scope :order => "username ASC"
# Attributs pour le login (Livre)
validates :username, :presence => true, :uniqueness => true
validates :password, :confirmation => true
attr_accessor :password_confirmation
attr_reader :password
validate :password_must_be_present
def User.authenticate(name, password)
logger.debug "---------- Beginning of Authenticate"
if user = User.where(:username => name)
logger.debug "utilisateur = #{user.inspect}" # THIS IS OK AND NOT NIL
logger.debug "utilisateur hashed PW = #{user.hashed_password}" # ERROR
if user.hashed_password == encrypt_password(password, user.salt)
return user
end
end
end
def User.encrypt_password(password, salt)
Digest::SHA2.hexdigest(password + "wibble" + salt)
end
def password=(password)
#password = password
if (password.present?)
generate_salt
self.hashed_password = self.class.encrypt_password(password, salt)
end
end
private
def password_must_be_present
errors.add(:password, "Mot de passe manquant") unless hashed_password.present?
end
def generate_salt
self.salt = self.object_id.to_s + rand.to_s
end
end
User.where(:username => name) is returning an ActiveRecord::Relation object (hence the error message you are seeing). Try changing your if statement to:
if user = User.where(:username => name).first
That will set take the first matching user, which will be an instance of User and so will have a hashed_password field. If no user matches, you'll get nil.
put this in your model
private
def self.hash_password(password)
Digest::SHA1.hexdigest(password)
end