Post: hidden: boolean
I want the logged in user could see all the posts, and the non-logged-in user only have access to posts whose hidden fields are false.
So I write like this in cancan's Ability Model:
if user_signed_in?
can :read, Post
else
can :read, Post, :hidden => false
end
but accessing the helper user_signed_in is not allowed in Model.
As stated in this question: Rails 3 devise, current_user is not accessible in a Model ?. While we could using some tricks to access the helper, its not proper to do that
So, how could I authorize the not-logged-in user properly? Or just use "Include" to use this helper ??
Or should I put this in authentication part? but how?
All you need to do is this:
def initialize(user)
user ||= User.new # guest user (not logged in)
can :read, Post, :hidden => false
if User.exists?(user)
can :read, Post
end
end
With devise, the current_user
helper method returns the current user when logged in but returns nil when not logged in. It is available in the controllers and views. By default, CanCan does all authorization checks against the return of the current_user
method.
Now whenever the can?
method is called from a view or a controller, the return of current_user
will be passed to a new instance of Ability
as the local variable user
.
To check if the user was logged in, I choose to useUser.exists?()
. It's a class method for ActiveRecord::Base
that will check if that user
object is persisted in the database. Any other way will work just as well though. For instance, this would work just as well or better:
if user.encrypted_password
can :read, Post
end
This will check if the default devise password field exists for the user
instance. If you haven't done anything too crazy, this will only return a value if the user is logged in. If the second option works for your situation, it may be superior because you won't even have to query the database.
Semi-relevant tip, check out a role handling gem like Rolify. It is great when used in conjunction with CanCan.