Custom password field with devise (ruby) - 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

Related

Update multiple records using one form

I have a following situation: user has multiple assets and each asset has one asset_detail record.
Models:
class User < ActiveRecord::Base
has_many :assets
end
class Asset < ActiveRecord::Base
has_one :asset_detail
belongs_to :user
accepts_nested_attributes_for :asset_detail,
:allow_destroy => false
attr_accessible # ...
end
class AssetDetail < ActiveRecord::Base
belongs_to :asset
attr_accessible # ...
end
Controller actions:
def edit
#user = current_user
#assets = Asset.all
end
def update
#user = current_user
#user.update_attributes(params["user"])
end
View:
= form_for #user, url: 'update action url' do |f|
= f.fields_for :assets do |ff|
= ff.text_field :title
= ff.fields_for :asset_detail do |fff|
= fff.text_field :value
The problem is that all the form fields are populated properly but i'm not able to save them. The form is sent without any error but the data is not updated.
I think your models should look like this:
class User < ActiveRecord::Base
attr_accessible :assets_attributes #...
has_many :assets
accepts_nested_attributes_for :assets_attributes
end
class Asset < ActiveRecord::Base
attr_accessible :asset_detail_attrbutes # ...
has_one :asset_detail
belongs_to :user
accepts_nested_attributes_for :asset_detail_attributes,
:allow_destroy => false
end
the reason being, you need to be able to set the attributes via the attributes hash passed to each model. HTH!

Relationships in sqllite database no foreign key added

I want to create relationship from Post to User model. So I create:
class Post < ActiveRecord::Base
attr_accessible :text, :title, :image, :user_id
has_many :comments
belongs_to :user
mount_uploader :image, ImageUploader
end
and in User.rb:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :remember_me
has_many :posts
end
Then I add file to database migration:
class AddUserIdToPost < ActiveRecord::Migration
def change
add_column :posts, :user_id, :integer
end
end
And made migration via rake db:migration.
When I add new post the value of user_id (in dataBase) is empty, but column exists.
Part of Post_Controller:
def create
#post = Post.new(params[:post])
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render json: #post, status: :created, location: #post }
else
format.html { render action: "new" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
What should I do with #post = Post.new(params[:post])? I think that everything is fine...
attr_accessible :text, :title, :image, :user_id
or, instead of attr_accessible, just do this:
attr_protected

Can't mass assign protected attributes - it's mass assigning fields in attr_accessor

Can't mass-assign protected attributes: password, password_confirmation
Both of those fields are not mapped in the database, they are just fields in the form that I want to use to enable some nice validations.
Here is my model class:
class User < ActiveRecord::Base
attr_accessible :email, :password_hash, :password_salt
attr_accessor :password, :password_confirmation
before_save :encrypt_password
validates_confirmation_of :password
validates :password, presence: true
validates :email, presence: true
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
end
I was under the impression that by placing password and password_confirmation in the attr_accessor method they would not be mass assigned, yet here I am with this little issue.
Any suggestions?
Here's my migration field so you can see what fields are actually in my database.
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
t.string :password_hash
t.string :password_salt
t.timestamps
end
end
end
What am I missing here?
attr_accessible specifies a white list of model attributes that can be set via mass-assignment.
attr_accessor creating an instance variable (#name) and a corresponding access method to read it. Also creates a method called name= to set the attribute.
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation
attr_accessor :password
before_save :encrypt_password
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates :email, presence: true
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
end
You need to add :password_confirmation and :password in attr_accessible

Why is assign_attributes with array of ids for has_many association only associating one object?

I have a form where users can upload assets using ajax. This creates many Asset objects that I then want to associate with a Post object when it is created. I have a form_field named asset_ids that I update with the created Asset ids as they are created. When I create the Post object and populate its data with assign_attributes, only ONE association is created, no matter how many ids are there.
Asset model:
class Asset < ActiveRecord::Base
attr_accessible :caption, :image
belongs_to :post
has_attached_file :image, :styles => { :large => "600x600>", :medium => "300x300>", :thumb => "100x100>" }
end
Post model:
class Post < ActiveRecord::Base
attr_accessible :content, :post_date, :status, :title, :tag_list, :asset_ids, :as => :admin
has_many :assets, :dependent => :destroy
has_and_belongs_to_many :tags
validates :content, :post_date, :title, :presence => true
end
An example of the posted data hash:
{"title"=>"Test post", "status"=>"true", "post_date"=>"01/02/2013", "content"=>" Some content", "tag_list"=>"", "asset_ids"=>"97,102"}
The example above assigns only one Asset (id 97) to the new Post, when I assign_attributes like so:
#post = Post.new
#post.assign_attributes params[:post], :as => :admin
I had to make sure I was assigning the ids as an array:
#post.asset_ids = params[:post][:asset_ids].split(",")

undefined method `password_hash' error with datamapper

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.

Resources