Uniqueness of users with devise and acts_as_tenant in rails 3 - ruby-on-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

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 !

Remember Me option with devise rails3

I have used devise 2.2.4, On login page remember_me option is not working. It is also not throwing any error, neither it is saving anything in remember_at field in user table.
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,:token_authenticatable,:recoverable, :rememberable, :trackable, :validatable#,:authentication_keys => [:login]
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :dealer_detail_id
validates :username, :password, :password_confirmation, :presence => true
validates_uniqueness_of :username #, :if => :email_changed?
end

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

Devise validates_uniqueness fails silently

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

Devise doesn't validate registration form after I change the default registration routes

I've got a simple app (Rails 3.2) with a User modal that's set up with devise(version 2.0.4).
My User model has the devise default settings:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :remember_me
end
Everything seems to work fine when I use the default devise routes like so:
devise_for :users
But I didn't to use the default so I changed it to this ones:
devise_for :users, :path => '', :path_names => { :sign_in => "login", :sign_out => "logout", :sign_up => "sign_up" }
After I changed this routes and restarted my server they seem to work fine but for some reason the validation don't work now, and even if I remove the "validatable" feature from my model and add my own custom validation they does not seem to work as well.
Would love your help on this!
- Paz
Solved this. The reason why it didn't work is because devise uses the "users/login" to know on what scope to work on.
So in order to fix this I changed my routes file to this:
devise_for :users
devise_scope :user do
get "/login" => "devise/sessions#new"
get "/logout" => "devise/sessions#destroy"
get "/register" => "devise/registrations#new"
end
Reference: https://github.com/plataformatec/devise/wiki/How-To:-Change-the-default-sign_in-and-sign_out-routes