NameError in AccessController

I have been learning Rails 3 from a video on Lynda.com, and it has been going well up until lately. What i’m trying to do currently is restrict access to certain pages unless a session is present where the user is authorized (has logged in). This is working correctly because when I try to access the pages that I want restricted, I get redirected to the login form. The problem comes up when I try to login. I put the correct username in, but when I put the password in, any password at all, it doesn’t even need to be the correct one, I get an error page I made a user admin account for development purposes, and when I login I get this error:

NameError in AccessController#attempt_login
undefined local variable or method `salt’ for #AdminUser:0x41a6a68

Application Trace:
app/models/admin_user.rb:62:in password_match?' app/models/admin_user.rb:52:in authenticate’
app/controllers/access_controller.rb:19:in `attempt_login’

Now, I don’t know a ton about debugging as of right now, but based on the trace the method attempt_login in the access controller is the following:

def attempt_login
    authorized_user = AdminUser.authenticate(params[:username], params[:password])
    if authorized_user
      session[:user_id] = authorized_user.id
      session[:username] = authorized_user.username
      flash[:notice] = "You are now logged in."
      redirect_to(:action => 'menu')
    else
      flash[:notice] = "Invalid username/password combination."
      redirect_to(:action => 'login')
    end

  end

I don’t know if there is an error in there that I am missing?

Anyway thanks in advance, and let me know if you need more of my code to find a solution because I am totally stuck at this point.

Elementax

Hi,

Let’s look at your error message:

Application Trace:
app/models/admin_user.rb:62:in `password_match?'
app/models/admin_user.rb:52:in `authenticate'
app/controllers/access_controller.rb:19:in `attempt_login'

You are correct that the original error is being triggered by the method attempt_login in access_controller.rb.

However, when we inspect that, we see:

authorized_user = AdminUser.authenticate(params[:username], params[:password])

This line is calling the authenticate method on your AdminUser object.

The authenticate method seems to be defined here: app/models/admin_user.rb:52
And this method is in turn calling the password_match? method defined here: app/models/admin_user.rb:62

It is the password_match? method that is triggering the error:

undefined local variable or method `salt' for #<AdminUser:0x41a6a68>

There could be a number of reasons for this happening.
Could you post the complete code from admin_user.rb and we’ll see if we can get to the bottom of it.

Thanks a lot, here is the complete admin_user.rb. I’m assuming I just made a foolish error somewhere, but I still cant seem to find out where:


require 'digest/sha1'
class AdminUser < ActiveRecord::Base
  
  has_and_belongs_to_many :pages
  has_many :section_edits
  has_many :sections, :through => :section_edits
  
  attr_accessor :password
  
  EMAIL_REGEX = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$/i

  validates :first_name, :presence => true, :length => { :maximum => 25 }
  validates :last_name, :presence => true, :length => { :maximum => 50 }
  validates :username, :length => { :within => 8..25 }, :uniqueness => true
  validates :email, :presence => true, :length => { :maximum => 100 }, 
    :format => EMAIL_REGEX, :confirmation => true


  validates_length_of :password, :within => 8..25, :on => :create

  before_save :create_hashed_password
  after_save :clear_password

  scope :named, lambda {|first,last| where(:first_name => first, :last_name => last)}
  scope :sorted, order("admin_users.last_name ASC, admin_users.first_name ASC")
  
  attr_protected :hashed_password, :salt
  
  def name
    "#{first_name} #{last_name}"
  end
  
  def self.authenticate(username="", password="")
    user = AdminUser.find_by_username(username)
    if user && user.password_match?(password)
      return user
    else
      return false
    end
  end

  def password_match?(password="")
    hashed_password == AdminUser.hash_with_salt(password, salt)
  end
  
  def self.make_salt(username="")
    Digest::SHA1.hexdigest("Use #{username} with #{Time.now} to make salt")
  end
  
  def self.hash_with_salt(password="", salt="")
    Digest::SHA1.hexdigest("Put #{salt} on the #{password}")
  end
  
  private
  
  def create_hashed_password
    unless password.blank?
      self.salt = AdminUser.make_salt(username) if salt.blank?
      self.hashed_password = AdminUser.hash_with_salt(password, salt)
    end
  end

  def clear_password
    self.password = nil
  end
  
end


It seems the error is being thrown by this line:

hashed_password == AdminUser.hash_with_salt(password, salt)

Do you have a “salt” field in your “adminusers” database table?
If so, are there any pending migrations that you have forgotten to carry out?

Failing those two things, what is your adminusers table structure?