Safest and Railsiest way in CanCan to do Guest, User, Admin permissions
I'm relatively new to rails (3), and am building an application, using CanCan, where there are 3 tiers of users.
- Guest - unregistered visitor User
- registered and logged in visitor
- Admin - registered and logged in visitor with admin flag
My ability is bog-stock right now, copied from cancan docs, basically defining the guest role and the admin role
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # Guest user
if user.is_admin?
can :manage, :all
else
can :read, [Asana,Image,User,Video,Sequence]
end
end
end
I'm looking to add in the user role. Since I'm creating that throwaway user model, I thought about using new_record? to determine if the user is logged in or not. Something like:
class Ability
include CanCan::Ability
def initialize(user)
user 开发者_开发知识库||= User.new # Guest user
if !user.new_record? and user.is_admin?
can :manage, :all
elsif !user.new_record? and !user.is_admin?
can {registered user-y permissions}
else
can :read, [Asana,Image,User,Video,Sequence]
end
end
end
But, it just doesn't feel right. Seems kind of disassociated from, like, actual logged-in-ed-ness, and have concerns about whether its actually secure.
Looking for advice on a more elegant way to doing this.
Thanks!
Good question, I use a lower to higher permissions approach:
class Ability
include CanCan::Ability
def initialize(user)
# Guest User
unless user
can :read, [Asana,Image,User,Video,Sequence]
else
# All registered users
can {registered user-y permissions}
# Admins
if user.is_admin?
can :manage, :all
end
end
end
end
This way if tomorrow you have other roles to integrate you can do it adding a case statement like so:
class Ability
include CanCan::Ability
def initialize(user)
# Guest User
unless user
can :read, [Asana,Image,User,Video,Sequence]
else
# All registered users
can {registered user-y permissions}
# Different roles
case user.role
when 'admin'
can :manage, :all
when 'manager'
can :manage, [Video, Image, Sequence]
end
end
end
end
So what you basically want is abilities for none logged in users, abilities for a logged in user and then abilities for a logged in admin?
Because the current user model is passed into the initialize you're going to have to test based on a property of the user and it would make sense to use a basic role property stored on the user model, eg
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.role == 'admin'
# Admin roles
can :manage, :all
elsif user.role == 'user'
# Signed in user permissions
else
# Guest permissions
can :read, :all
end
end
So when a user signs up/registers you can default the role value to 'user' and then allow for some method to update this to 'admin' in a management interface. You could use a single admin? check on a user since this would be false for guests as well as normal logged in users.
If the user object isn't a new_record it means it's stored in the database. To me that's sufficient enough to accept that it's a logged in user.
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
# Guest users
can :create, User
# Members
unless user.new_record?
can [:edit, :update], User, :id => user.id
end
# Admins
if user.admin?
can :manage, :all
end
end
end
Furthermore, sometimes admin functionality doesn't have to be so fancy - perhaps this will be all you need?
class User < ActiveRecord::Base
# Alternatively, you can add an admin attribute flag
def admin?
["theadmin@yourservice.com", "hisspouse.yourservice.com"].include?(email)
end
end
Finally, although you are new to rails, I'll suggest that you code your own authentication from scratch. Using gems like Devise and Authlogic almost always seems to have it's downsides. And it doesn't really have to be that complicated. Ryan (the author of cancan) did a great screencast on this topic: http://railscasts.com/episodes/250-authentication-from-scratch
If you use role inheritance, this pattern works well:
Ability
user ||= User::GUEST # guest user (not logged in)
# anyone
can [:read], Post
# any registered user
if user.role? :user
can [:comment], Post
end
# editor
if user.role? :editor
can [:create], Post
end
# admin
if user.role? :admin
can [:manage], Post
end
User
GUEST = User.new.tap {|u| u.role = 'guest'}
ROLES = %w[guest user editor admin]
def role?(base_role)
begin
ROLES.index(base_role.to_s) <= ROLES.index(role.to_s)
rescue
raise "invalid role query '#{base_role}' against user role '#{role}'"
end
end
If you actually have a persisted user model, then that is a logged in user, otherwise they are a guest. Maybe you are over-thinking this?
class Ability
include CanCan::Ability
def initialize(user=nil)
if user && user.is_admin?
can :manage, :all
elsif user
can {registered user-y permissions}
else # guest
can :read, [Asana,Image,User,Video,Sequence]
end
end
end
精彩评论