Devise validates_uniqueness fails silently - ruby-on-rails-3

This is my User model:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :token_authenticatable, :confirmable, :timeoutable,
:recoverable, :rememberable, :trackable, :validatable,
:email_regexp => /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
attr_accessible :email, :password, :password_confirmation, :remember_me, :name, :confirmed_at, :confirmation_token, :category_ids
validates_uniqueness_of :email, :case_sensitive => false
validates_confirmation_of :password
end
When I enter an email address that exists in the DB, my page doesn't re-load and I don't see any error messages or anything.
This is what I see in the log:
Started GET "/validators/uniqueness?case_sensitive=false&user%5Bemail%5D=abc%40email.com" for 127.0.0.1 at 2012-06-21 03:59:57 -0500
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" LIKE 'abc#email.com') LIMIT 1
Started GET "/validators/uniqueness?case_sensitive=false&user%5Bemail%5D=abc%40email.com" for 127.0.0.1 at 2012-06-21 03:59:57 -0500
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" LIKE 'abc#email.com') LIMIT 1

Why are you trying to overwrite devise validations? Devise will handle everything.
Logs suggest that you are trying to make some kind of JS form validation, right? If so, try checking for existing user differently:
email = params[:email] || ""
#exists = User.where(:email=>email.downcase).count

"The solution is to perform fully customized validation instead, or to
override email validation like you did in your example"
See
https://github.com/bcardarella/client_side_validations/issues/455#issuecomment-12170527

Related

ActiveAdmin: ActiveRecord::RecordNotFound in Admin::UsersController#show when editing current user

When I am logged in as an admin on my rails app, I go to the admin page and then to the Users column in activeadmin (Which includes admin users). When I click edit for the admin-user that is currently logged in, I edit the fields, and then when I click Update User I get the following error:
ActiveRecord::RecordNotFound in Admin::UsersController#update
Couldn't find User with id=5 [WHERE ('t'='f')]
This only happens when I do the above procedure for the current_user and not for any other user.
This my admin/users.rb file:
ActiveAdmin.register User do
index do
column :email
column :current_sign_in_at
column :last_sign_in_at
column :sign_in_count
default_actions
end
filter :email
form do |f|
f.inputs "Admin Details" do
f.input :email
f.input :initials
f.input :password
f.input :password_confirmation
end
f.actions
end
end
And Here is my user.rb model file:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_associated_audits
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me ,:name, :initials
has_many :entries
end
[WHERE ('t'='f')] Condition can never be true !

Devise - mass assignment error when changing other users passwords in specific password change page

In my RoR application I'm using devise and the client requires a bit of customisation - basically he requires that the administrator be able to change passwords of other users and that the password change page be different than the page to edit profile details. I've set up custom actions to handle this namely my own change_password action in users controller.
Users Controller Actions
def change_password
#user = User.find(params[:id])
end
def update_password # I post to this
#user = User.find(params[:id])
if #user.update_attributes!(params[:user])
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
Heres the routes.rb entries
devise_for :users, :skip => [:registrations]
as :user do
get 'users/edit' => 'devise/registrations#edit', :as => 'edit_user_registration'
put 'users' => 'devise/registrations#update', :as => 'user_registration'
end
resources :users
...
match "/users/:id/change_password" =>"users#change_password", :as=>:change_password_user, :via=>:get
match "/users/:id/update_password" => "users#update_password", :as=>:update_password_user, :via=>:post
And this is my users model
class User < ActiveRecord::Base
rolify
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable, :registerable,
devise :database_authenticatable, #:registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :role_ids, :as => :admin
attr_protected :username, :name, :email, :password, :password_confirmation, :remember_me
validates_uniqueness_of :username
validates_presence_of :username, :email
validates_uniqueness_of :email
end
however I keep getting this mass attributes assignment error
Can't mass-assign protected attributes: password, password_confirmation
the weird thing is that I've set all these attributes to accessible_protected. I can edit other users details but can't edit their passwords. Whats going on here?
There are many ways you can fix this problem. I'll try to explain a few.
I think the key to your problem is that you are mixing up the MassAssignmentSecurity roles. You've defined a Whitelist for the admin role and a Blacklist for the default role. The error says that you tried to assign something that was on the Blacklist for the default role.
Since you are defining different roles, I assume you probably want to fix it this way:
Change your admin Whitelist
attr_accessible :role_ids, :password, :password_confirmation, as: :admin
Then assign as the admin:
if #user.update_attributes!(params[:user], as: :admin)
(If your controller action includes fields other than the password fields, this may cause new violations.)
A different option is to stick to the default role. You can bypass security a couple ways.
The first option which I don't recommend is to not pass the password and password confirmation as part of the User params, and send them separately in your view. You can then manually set those fields like so:
#user.assign_attributes(params[:user])
#user.password = params[:password]
#user.password_confirmation = params[:password_confirmation]
if #user.save!
However, it's even easier to do the following to just skip protection:
#user.assign_attributes(params[:user], without_protection: true)
if #user.save!
For more information, this guide is fairly good:
http://guides.rubyonrails.org/security.html#mass-assignment
I hope that helps.

Not able to update my foreign key in the new method,using Devise for Users Table

I have Two models Users and Profiles ( Users is from devise ). A user can have many profiles , so i created a migration to add user_id to Profiles.
class Profile < ActiveRecord::Base
belongs_to :user
attr_accessible :description, :name, :product, :image, :price , :user_id
mount_uploader :image, ImageUploader
validates_presence_of :name, :product,:image, :price
end
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
# attr_accessible :title, :body
has_many :profiles
validates_associated :profiles
end
In my profiles controller , i have a new method to create a new profile. I want that when user logs in, he is able to see only his profiles not all.
def new
#profile = current_user.profiles.build
##profile = #user.profile.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #profile }
end
end
Ideally, my user_id column should be updated with build, but my user_id does not get updated and that is why profiles are not displayed . i am using postgresql with pg gem and when i manually add the user_id for that profile , i am able to see the profiles.
Please help to solve this, i am able to figure this out since a long time.Let me know if you want more information to solve this.
Maybe try something like this in your controller:
if current_user.profiles.count == 0
profile = Profile.create
current_user.profiles << profile
end

Uniqueness of users with devise and acts_as_tenant in rails 3

I am using the acts_as_tenant gem to manage multi-tenancy, and I'm using devise to manage users.
I have only setup devise User model and Account model for tenants.
I can create users against multiple tenants - this is all working fine EXCEPT when I attempt to create two users with the same email against different tenant ID's I get a uniqeness error.
I am using the validates_uniqueness_to_tenant option as described.
User model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :remember_me
acts_as_tenant(:account)
validates_uniqueness_to_tenant :email
end
Account model
class Account < ActiveRecord::Base
attr_accessible :name
end
Application Controller
class ApplicationController < ActionController::Base
set_current_tenant_by_subdomain(:account, :subdomain)
protect_from_forgery
end
This looks like it should be working based on all documentation in acts_as_tenant, do I need to override something at the devise level instead?
EDIT: After some head-scratching and a bit of a break, the problem is I believe because by default Devise has added a unique index to the Email column.
This obviously does not gel with what acts_as_tenant wants to do...
I will try removing the index and see whether Devise pukes or not.
EDIT 2: OK, have officially given up on this for now. I have hand-rolled authentication for the main site and this is working properly with acts_as_tenant.
I can only assume some incompatibility between acts_as_tenant and Devise at some layer - beyond me to find it at this stage.
The only way to do this is by removing the validatable module from devise and run your own validations like so:
class User < ActiveRecord::Base
acts_as_tenant :account
attr_accessible :email, :password, :remember_me
#remove :validatable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable
#run own validations
#I've omitted any emailformatting checks for clarity's sake.
validates :email,
presence: true,
uniqueness: { scope: :account_id, case_sensitive: false }
validates :password,
presence: true,
length: { :in => 6..20 },
:if => :password_required?
protected
# copied from validatable module
def password_required?
!persisted? || !password.nil? || !password_confirmation.nil?
end
end
I haven't tested it, but I wonder if changing the order might help acts_as_tenant do its thing before devise takes over.
class User < ActiveRecord::Base
acts_as_tenant(:account)
validates_uniqueness_to_tenant :email
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :remember_me
end
Just came across this question. Sweam's solution is pretty good.
But I prefer to not override the default behaviour. So I came up with this solution:
validate :remove_old_email_validation_error
validates_uniqueness_of :email, :allow_blank => true, :if => :email_changed?, :scope => [:account_id]
private
def remove_old_email_validation_error
errors.delete(:email)
end
We remove the default validation error for email, therefore ignoring the validation check, and we do our own validation again.
What I have added is from the Validatable module, but i have added :scope to it.
Its important to keep the order. Add the above code after the devise command.
I resolved it as:
validate :remove_old_uniquess_email_error
private
def remove_old_uniquess_email_error
errors.delete(:email) if self.company_id.nil? && errors[:email].present? && errors[:email] == ["already taken"]
end

Devise remember me not storing cookie

I've set up a simple sample app that uses devise for user authentication.
When I select the rember me checkbox on login I get the following in the console:
Processing by Devise::SessionsController#create as HTML
Parameters: {"commit"=>"Sign in", "authenticity_token"=>"Vm+2aWEmCRnnjdXAOpXdI5dGChUKlEcmfGNXNoGUkwc=", "utf8"=>"Γ£ô", "user"=>{"remember_me"=>"1", "password"
=>"[FILTERED]", "email"=>"test#example.com"}}
←[1m←[35mUser Load (1.0ms)←[0m SELECT "users".* FROM "users" WHERE "users"."email" = 'test#example.com' LIMIT 1
←[1m←[36mAREL (1.0ms)←[0m ←[1mUPDATE "users" SET "last_sign_in_at" = '2011-10-06 21:36:46.439511', "updated_at" = '2011-10-06 21:43:59.298269', "sign_in_coun
t" = 15, "current_sign_in_at" = '2011-10-06 21:43:59.297269' WHERE "users"."id" = 1←[0m
I have the following in my Posts controller:
before_filter :authenticate_user!
However, when I close the browser and open it again I am forced to login again before I can view posts. It doesn't seem that the cookie is being stored. I found the following cookie in firefox for my page:
Cookie Name: remember_user_token
Expires: at end of session
Is this the cookie thats supposed to be stored and expire at a future date but instead expires at the end of session? Can someone verify this?
I've tried both commenting and uncommenting config.remember_for = 2.weeks in devise.rb to different settings and nothing seems to fix the problem.
My User.rb file looks like this:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
end