undefined method `password_hash' error with datamapper - ruby

I'm trying to build [Ryan Bates' "authenticaion from scratch"][1] using sinatra but authentication does not work. I get a "undefined method password_hash" error.
Everything else is working. I checked datamapper documentation, especially for lazy loading, but couldn't find anything useful. The code is below. What mistake am I doing?
main.rb (only relevant part)
post "/login" do
user = User.authenticate params[:email], params[:password]
if user
session[:user_id] = user.id
redirect "/"
else
session[:errors] = "No such user or bad password."
redirect "/login"
end
end
user.rb
require 'data_mapper'
require 'dm-validations'
require 'bcrypt'
module Kimsin
DataMapper.setup :default, 'sqlite3:///home/barerd/RProjects/kimsin/users.db'
class User
attr_accessor :password, :confirm_password
include DataMapper::Resource
property :id, Serial
property :email, String, :required => true, :unique => true, :format => :email_address,
:messages => { :presence => "We need your email address.", :is_unique => "We already have that email.", :format => "Doesn't look like an email adress.."}
property :password_salt, String
property :password_hash, String, :length => 80
validates_presence_of :password, :confirm_password, :messages => { :presence => "You have to type a password and confirm it." }
validates_format_of :password, :confirm_password, :with => /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])([\x20-\x7E]){8,40}$/, :messages => { :format => "The password should be 8 to 40 characters long and contain at least one digit, one lowercase and one uppercase letter and one special character." }
before :save, :encrypt_password
def self.authenticate email, password
user = User.all :email.like => email
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
def encrypt_password
if password != nil
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret password, password_salt
end
end
end
DataMapper.finalize
end

Adding a attr_reader :password_hash solved it.

Related

How would I store passwords in CouchDB using CouchRest & BCrypt?

Although, this isn't CouchDB specific, the library I'm using is. I'm using couchrest_model in Ruby to create a User model like this below:
class User < CouchRest::Model::Base
use_database 'sample'
property :_id, String
property :email, String
# property :password
timestamps!
end
I'm confused with password storing here. I'd like to use BCrypt but when I do something like this:
class User < CouchRest::Model::Base
include BCrypt
use_database 'sample'
property :_id, String
property :email, String
property :password, BCryptHash
timestamps!
end
I get told that User::BCryptHash is an uninitialised constant. I of course require the bcrypt library beforehand. Could you help me resolve that BCrypt issue or suggest a different way of storing passwords in CouchDB? I've looked at hashing passwords however I'm not sure how to implement this.
require 'couchrest_model'
class User < CouchRest::Model::Base
include ActiveModel::SecurePassword
use_database 'sample'
has_secure_password
property :username, String
property :email, String
property :password_digest, String
timestamps!
design { view :by_email }
end
User.create(:username => 'rafalchmiel', :email => 'hi#rafalchmiel.com', :password => 'password', :password_confirmation => 'password')
User.create(:username => 'bar', :email => 'hi#bar.com', :password => 'password213', :password_confirmation => 'password213')
User.create(:username => 'foo', :email => 'hi#foo.com', :password => 'password12111', :password_confirmation => 'password12111')
More info as to why this works in this GitHub Issue.

Custom password field with devise (ruby)

I'm using a database shared between 2 rails apps.
A webapp using BCrypt and has_secure_password to authenticate user, and my app, an REST API, using Devise to authenticate users. Password hash is the same.
So, I would like to use the field password_digest instead of encrypted_password to authenticate via Devise and I don't know how ! (I'm seeking in the documentation but find nothing). So, I have to copy / paste my password hash from password_digest to encrypted_password yet.
Here my session controller Code :
class SessionsController < Devise::SessionsController
before_filter :ensure_params_exist
def create
build_resource
resource = User.find_for_database_authentication(:email => params[:email])
return invalid_login_attempt unless resource
if resource.valid_password?(params[:password])
#resource.ensure_authentication_token! #make sure the user has a token generated
sign_in("user", resource)
render :json => { :authentication_token => resource.authentication_token, :lastname => resource.lastname, :firstname => resource.firstname, :last_sign_in => resource.last_sign_in_at }, :status => :created
return
end
invalid_login_attempt
end
#def destroy
# # expire auth token
# #user=User.where(:authentication_token=>params[:auth_token]).first
# #user.reset_authentication_token!
# render :json => { :message => ["Session deleted."] }, :success => true, :status => :ok
#end
protected
def ensure_params_exist
return unless params[:email].blank?
render :json=>{:success=>false, :message=>"missing email parameter"}, :status=>422
end
def invalid_login_attempt
warden.custom_failure!
render :json => { :errors => ["Invalid email or password."] }, :success => false, :status => :unauthorized
end
end
And then my User Model
class User < ActiveRecord::Base
before_save :ensure_authentication_token
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :trackable, :token_authenticatable#, :registerable,
#:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :client_id, :firstname, :group_id, :lastname, :password, :password_confirmation, :role_id, :group_ids, :auth_token, :password_digest, :encrypted_password
# Relations dans la base de données
belongs_to :client
belongs_to :role
has_many :memberships
has_many :groups, :through => :memberships
end
I am not aware about how BCrypt/has_secure_password works, but you can either
use virtual attributes as follows
def encrypted_password
return password_digest
end
def encrypted_password= value
return password_digest
end
Or even better, use alias methods
set encrypted_password and encrypted_password= as alias methods for password_digest and password_digest=.
I think the best way to get this work is to use alias_attribute. This problem might be resolved a long time ago but I still want to come up with this.
alias_attribute :encrypted_password, :password_digest

ActiveRecord format validation not firing

I'm trying to apply a format validation to a model, but when I create the model it isn't coming back as invalid. I added a length validation and it works just fine. What am I doing wrong?
require 'rubygems'
require 'active_record'
class User < ActiveRecord::Base
validates :username, :format => { :with => /[A-Za-z]+/, :message => "Only letters a-z are allowed" }
validates :username, :length => { :maximum => 20, :too_long => "%{count} letters is too many"}
end
ActiveRecord::Base.establish_connection( ... )
user = User.create!(:username => 'johnsmith1234', :signupdate => '2010-11-12')
puts user.valid?
The output is always true unless I have a length of over 20 characters, then I get an error on the length. So why doesn't the format validation fire?
/[A-Za-z]/ checks for one or more alphabets in given string. If you want only alphabets you need to specify ^ and $. (i.e) /^[A-Za-Z]$/
validates :username, :format => { :with => /^[A-Za-z]+$/, :message => "Only letters a-z are allowed" }
One more thing use new to create new user. Because create or create! will throw error if your validation fails.
user = User.new(:name => "john123")
if user.valid?
#do something
else
#do something `user.errors.full_messages` will have your validation messages if it has error
end

RoR Tutorial (Chapter 10.4) - admin attributes tests fail

I'm a RoR-Learner and I made my way to the RoR-Tutorial by Michael Hartl, but actually I'm facing a problem at Chapter 10.4. I did all the Listings up to 10.42, but 3 of my Rspec-Tests are failing.
It says:
1) Users admin attribute should respond to admin
Failure/Error: #user = User.create!(#attr)
ActiveRecord::RecordInvalid: Validation failed: Name can't be blank,
Email can't be blank, Email is invalid, Password can't be blank,
Password is too short (minimum is 6 characters)
./spec/requests/users_spec.rb:52:in `block (3 levels) in
2) Users admin attribute should not be an admin by default
Failure/Error: #user = User.create!(#attr)
ActiveRecord::RecordInvalid: Validation failed: Name can't be blank,
Email can't be blank, Email is invalid, Password can't be blank,
Password is too short (minimum is 6 characters)
./spec/requests/users_spec.rb:52:in `block (3 levels) in
3) Users admin attribute should be convertible to an admin
Failure/Error: #user = User.create!(#attr)
ActiveRecord::RecordInvalid: Validation failed: Name can't be blank,
Email can't be blank, Email is invalid, Password can't be blank,
Password is too short (minimum is 6 characters)
./spec/requests/users_spec.rb:52:in `block (3 levels) in
I already took a careful look on my users_controllers_spec.rb and my user_spec.rb, but I couldn't find an explanation for the mistakes. Does anyone have an idea?
require 'spec_helper'
describe UsersController do
render_views
describe "GET 'index'" do
describe "for non-signed-in users" do
it "should deny access" do
get :index
response.should redirect_to(signin_path)
flash[:notice].should =~ /sign in/i
end
end
describe "for signed-in users" do
before(:each) do
#user = test_sign_in(Factory(:user))
second = Factory(:user, :name => "Bob", :email => "another#example.com")
third = Factory(:user, :name => "Ben", :email => "another#example.net")
##users = [#user, second, third]
#users = [#user, second, third]
30.times do
#users << Factory(:user, :name => Factory.next(:name),
:email => Factory.next(:email))
end
end
it "should be successful" do
get :index
response.should be_success
end
it "should have the right title" do
get :index
response.should have_selector("title", :content => "All users")
end
it "should have an element for each user" do
get :index
#users[0..2].each do |user|
response.should have_selector("li", :content => user.name)
end
end
it "should paginate users" do
get :index
response.should have_selector("div.pagination")
response.should have_selector("span.disabled", :content => "Previous")
response.should have_selector("a", :href => "/users?escape=false&page=2",
:content => "2")
response.should have_selector("a", :href => "/users?escape=false&page=2",
:content => "Next")
end
end
end
describe "GET 'show'" do
before(:each) do
#user = Factory(:user)
end
it "should be successful" do
get :show, :id => #user
response.should be_success
end
it "should find the right user" do
get :show, :id => #user
assigns(:user).should == #user
end
it "should have the right title" do
get :show, :id => #user
response.should have_selector("title", :content => #user.name)
end
it "should include the user's name" do
get :show, :id => #user
response.should have_selector("h1", :content => #user.name)
end
it "should have a profile image" do
get :show, :id => #user
response.should have_selector("h1>img", :class => "gravatar")
end
end
describe "GET 'new'" do
it "should be successful" do
get 'new'
response.should be_success
end
it "should have the right title" do
get 'new'
response.should have_selector("title", :content => "Sign up")
end
end
describe "POST 'create'" do
describe "failure" do
before(:each) do
#attr = { :name => "", :email => "", :password => "",
:password_confirmation => "" }
end
it "should not create a user" do
lambda do
post :create, :user => #attr
end.should_not change(User, :count)
end
it "should have the right title" do
post :create, :user => #attr
response.should have_selector("title", :content => "Sign up")
end
it "should render the 'new' page" do
post :create, :user => #attr
response.should render_template('new')
end
end
describe "success" do
before(:each) do
#attr = { :name => "New User", :email => "user#example.com",
:password => "foobar", :password_confirmation => "foobar" }
end
it "should create a user" do
lambda do
post :create, :user => #attr
end.should change(User, :count).by(1)
end
it "should redirect to the user show page" do
post :create, :user => #attr
response.should redirect_to(user_path(assigns(:user)))
end
it "should have a welcome message" do
post :create, :user => #attr
flash[:success].should =~ /welcome to the sample app/i
end
it "should sign the user in" do
post :create, :user => #attr
controller.should be_signed_in
end
end
end
describe "GET 'edit'" do
before(:each) do
#user = Factory(:user)
test_sign_in(#user)
end
it "should be successful" do
get :edit, :id => #user
response.should be_success
end
it "should have the right title" do
get :edit, :id => #user
response.should have_selector("title", :content => "Edit user")
end
it "should have a link to change the Gravatar" do
get :edit, :id => #user
gravatar_url = "http://gravatar.com/emails"
response.should have_selector("a", :href => gravatar_url,
:content => "change")
end
end
describe "PUT 'update'" do
before(:each) do
#user = Factory(:user)
test_sign_in(#user)
end
describe "failure" do
before(:each) do
#attr = { :email => "", :name => "", :password => "",
:password_confirmation => "" }
end
it "should render the 'edit' page" do
put :update, :id => #user, :user => #attr
response.should render_template('edit')
end
it "should have the right title" do
put :update, :id => #user, :user => #attr
response.should have_selector("title", :content => "Edit user")
end
end
describe "success" do
before(:each) do
#attr = { :name => "New Name", :email => "user#example.org",
:password => "barbaz", :password_confirmation => "barbaz" }
end
it "should change the user's attributes" do
put :update, :id => #user, :user => #attr
#user.reload
#user.name.should == #attr[:name]
#user.email.should == #attr[:email]
end
it "should redirect to the user show page" do
put :update, :id => #user, :user => #attr
response.should redirect_to(user_path(#user))
end
it "should have a flash message" do
put :update, :id => #user, :user => #attr
flash[:success].should =~ /updated/
end
end
end
describe "authentication of edit/update pages" do
before(:each) do
#user = Factory(:user)
end
describe "for non-signed-in users" do
it "should deny access to 'edit'" do
get :edit, :id => #user
response.should redirect_to(signin_path)
end
it "should deny access to 'update'" do
put :update, :id => #user, :user => {}
response.should redirect_to(signin_path)
end
end
describe "for signed-in users" do
before(:each) do
wrong_user = Factory(:user, :email => "user#example.net")
test_sign_in(wrong_user)
end
it "should require matching users for 'edit'" do
get :edit, :id => #user
response.should redirect_to(root_path)
end
it "should require matching users for 'update'" do
put :update, :id => #user, :user => {}
response.should redirect_to(root_path)
end
end
end
describe "DELETE 'destroy'" do
before(:each) do
#user = Factory(:user)
end
describe "as a non-signed-in user" do
it "should deny access" do
delete :destroy, :id => #user
response.should redirect_to(signin_path)
end
end
describe "as a non-admin user" do
it "should protect the page" do
test_sign_in(#user)
delete :destroy, :id => #user
response.should redirect_to(root_path)
end
end
describe "as an admin user" do
before(:each) do
admin = Factory(:user, :email => "admin#example.com", :admin => true)
test_sign_in(admin)
end
it "should destroy the user" do
lambda do
delete :destroy, :id => #user
end.should change(User, :count).by(-1)
end
it "should redirect to the users page" do
delete :destroy, :id => #user
response.should redirect_to(users_path)
end
end
end
end
This is my user_spec.rb
require 'spec_helper'
describe User do
before(:each) do
#attr = {
:name => "Example User",
:email => "user#example.com",
:password => "foobar",
:password_confirmation => "foobar"
}
end
it "should create a new instance given valid attributes" do
User.create!(#attr)
end
it "should require a name" do
no_name_user = User.new(#attr.merge(:name => ""))
no_name_user.should_not be_valid
end
it "should require an email address" do
no_email_user = User.new(#attr.merge(:email => ""))
no_email_user.should_not be_valid
end
it "should reject names that are too long" do
long_name = "a" * 51
long_name_user = User.new(#attr.merge(:name => long_name))
long_name_user.should_not be_valid
end
it "should accept valid email addresses" do
addresses = %w[user#foo.com THE_USER#foo.bar.org first.last#foo.jp]
addresses.each do |address|
valid_email_user = User.new(#attr.merge(:email => address))
valid_email_user.should be_valid
end
end
it "should reject invalid email addresses" do
addresses = %w[user#foo,com user_at_foo.org example.user#foo.]
addresses.each do |address|
invalid_email_user = User.new(#attr.merge(:email => address))
invalid_email_user.should_not be_valid
end
end
it "should reject duplicate email addresses" do
# Put a user with given email address into the database.
User.create!(#attr)
user_with_duplicate_email = User.new(#attr)
user_with_duplicate_email.should_not be_valid
end
it "should reject email addresses identical up to case" do
upcased_email = #attr[:email].upcase
User.create!(#attr.merge(:email => upcased_email))
user_with_duplicate_email = User.new(#attr)
user_with_duplicate_email.should_not be_valid
end
describe "password validations" do
it "should require a password" do
User.new(#attr.merge(:password => "", :password_confirmation => "")).
should_not be_valid
end
it "should require a matching password confirmation" do
User.new(#attr.merge(:password_confirmation => "invalid")).
should_not be_valid
end
it "should reject short passwords" do
short = "a" * 5
hash = #attr.merge(:password => short, :password_confirmation => short)
User.new(hash).should_not be_valid
end
it "should reject long passwords" do
long = "a" * 41
hash = #attr.merge(:password => long, :password_confirmation => long)
User.new(hash).should_not be_valid
end
end
describe "password encryption" do
before(:each) do
#user = User.create!(#attr)
end
it "should have an encrypted password attribute" do
#user.should respond_to(:encrypted_password)
end
it "should set the encrypted password" do
#user.encrypted_password.should_not be_blank
end
describe "has_password? method" do
it "should be true if the passwords match" do
#user.has_password?(#attr[:password]).should be_true
end
it "should be false if the passwords don't match" do
#user.has_password?("invalid").should be_false
end
end
end
describe "authenticate method" do
it "should return nil on email/password mismatch" do
wrong_password_user = User.authenticate(#attr[:email], "wrongpass")
wrong_password_user.should be_nil
end
it "should return nil for an email address with no user" do
nonexistent_user = User.authenticate("bar#foo.com", #attr[:password])
nonexistent_user.should be_nil
end
it "should return the user on email/password match" do
matching_user = User.authenticate(#attr[:email], #attr[:password])
matching_user.should == #user
end
end
describe "admin attribute" do
before(:each) do
#user = User.create!({
:name => "Example User",
:email => "user#example.com",
:password => "foobar",
:password_confirmation => "foobar"
})#(#attr)
//EDIT: I tried both, the fill-in by hand an the original code with (#attr) --> nothing changes, still the same mistake
end
it "should respond to admin" do
#user.should respond_to(:admin)
end
it "should not be an admin by default" do
#user.should_not be_admin
end
it "should be convertible to an admin" do
#user.toggle!(:admin)
#user.should be_admin
end
end
end
EDIT: User.rb
== Schema Information
Table name: users
id :integer not null, primary key
name :string(255)
email :string(255)
created_at :datetime
updated_at :datetime
//it's outcommented
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
def has_password?(submitted_password)
encrypted_password == encrypt(submitted_password)
end
def self.authenticate_with_salt(id, cookie_salt)
user = find_by_id(id)
(user && user.salt == cookie_salt) ? user : nil
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(email, submitted_password)
user = find_by_email(email)
return nil if user.nil?
return user if user.has_password?(submitted_password)
end
private
def encrypt_password
self.salt = make_salt unless has_password?(password)
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
end
If some important details are missing, please ask, I'm prepared to update this post (just didn't want to overload it, because the code is already huge enough).
Thanks for your attention!
I have just recently come across this same issue and resolved it by removing the bang (!) from the create method call:
#user = User.create(#attr)
HTH
Your user_spec.rb does not match with the one from Listing 10.34 in the tutorial.
More specifically, the before(:each) block.
your user_spec.rb:
before(:each) do
#user = User.create!({
:name => "Example User",
:email => "user#example.com",
:password => "foobar",
:password_confirmation => "foobar"
})#(#attr)
end
Listing 10.34:
before(:each) do
#user = User.create!(#attr)
end

How best to deal with HTTP response in Ruby?

I'm using ActiveModel instead of ActiveRecord. And my model is:
class User
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
validates :name, :presence => true, :length => { :maximum => 50 }
validates :email, :presence => true,
:format =>
{
:with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
}
validates :password, :presence => true, :confirmation => true,
:length =>
{
:within => 6..40
}
attr_accessor :name, :email, :password, :password_confirmation
def initialize(attributes = {})
#name = attributes[:name]
#email = attributes[:email]
#password = attributes[:password]
#password_confirmation = attributes[:password_confirmation]
end
def persisted?
false
end
def save
# createUser calls RESTful HTTP server and gets back JSON in http response's body
response = createUser(self.name, self.email, self.password)
end
end
And in my users_controller.rb below when I try to process this response returned by save method above, it messes up my model's validations for password and password_confirmation.
def create
#user = User.new(params[:user])
response = #user.save
parsed_response_body = ActiveSupport::JSON.decode(response.body)
# response body I have is {"ok":"ok message"} OR {"error":"error message"}
message = parsed_response_body["error"]
if #user.valid? && message.nil?
flash[:success] = message
redirect_to signup_path
else
#user.password = ""
#user.password_confirmation = ""
flash[:error] = message
render :action => 'new'
end
end
And below is controller code that doesn't break the validations; where #user.save in this case is returning true.
def create
#user = User.new(params[:user])
if #user.valid? && #user.save
flash[:success] = "Done"
redirect_to signup_path
else
#user.password = ""
#user.password_confirmation = ""
flash[:error] = "Not Done"
render :action => 'new'
end
end
I'd be thankful if someone can help me with this..
It looks to me like you need to call super in save or it won't actually try to validate:
def save
super
# createUser calls RESTful HTTP server and gets back JSON in http response's body
response = createUser(self.name, self.email, self.password)
end

Resources