I'm working on a Rails 3.2 app where I use Devise for authentication. I decided to try single table inheritance for managing user roles, but I quickly ran into a problem. I currently have three User models, User < ActiveRecord
, Admin < User
and Collaborator < User
. Admin and Collaborator share most User columns, but they have slightly different behaviors and privileges. My models currently looks like this:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :token_authenticatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :name, :password, :password_confirmation, :remember_me
before_save :ensure_authentication_token
[...]
end
class Admin < User
has_one :account, dependent: :destroy
attr_accessible :account_attributes
accepts_nested_attributes_for :account
end
class Collaborator < User
has_one :account
end
class Account < ActiveRecord::Base
attr_accessible :name
validates_presence_of :name
has_many :projects, dependent: :destroy
has_many :users
end
The problem aries when I try to authenticate Admins and Collaborators in my ProjectController (and other controllers where I need authentication):
# Causes problem, no one can access anything.
before_filter :authenticate_admin!
before_filter :authenticate_collaborator!
A similar problem I had was with devise's helper methods for ie. current_user, now I have current_admin and current_collaborator, I "solved" that by creating a before filter and method:
def set_current_user
@current_user = current_admin || current_collaborator
end
Is there a similar or simple solution for my authentication problem with Devise, or would you recommend another approach than Single Table Inheritance, and what would that be?
My goal is, 1. when new users signs up, they become Admins, when they create their account, an Account model is also created. 2. The new (Admin) user can then invite additional users to the Account, which will be Collaborators. 3. Admins and Collaborators should have different privileges. Collaborators won't create new "Accounts" when they sign up (company could be a better name for my Account model) so Admin and Collaborators will need slightly different forms for signing up and editing.
Thanks.
Update
I kinda "solved" it by creating a similar before filter:
def authenticate!
if @current_user == current_admin
:authenticate_admin!
elsif @current_user == current_collaborator
:authenticate_collaborator!
end
end
Suggestions on possibly more elegant solutions would still be appreciated.
You can solve this using the following solution
def authenticate! if modelA_user_signed_in? @current_user = current_modelA true else authenticate_modelB! end end
Not sure if this is still needed a solution to this...
A more elegant way to have the double authentications could be to do the following:
private
def authenticate!
:authenticate_admin! || :authenticate_collaborator!
@current_user = admin_signed_in? ? current_admin : current_collaborator
end
Then call before_filter :authenticate!
If you dont need a universal '@current_user' variable just leave out the second line.
Hope this helps.
You may separate all common logic to module and use only same table.
module UserMethods
#...
end
class User < ActiveRecord::Base
include UserMethods
devise ...
end
class Admin < ActiveRecord::Base
include UserMethods
self.table_name = "users"
devise ...
end
And configure all devise model separately in routes, views(if necessary, see Configuring Views). In this case, you may easy process all different logic.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With