-
Notifications
You must be signed in to change notification settings - Fork 783
How to use CanCan when the permission has multiple conditions? #453
Comments
I'm not Ryan, but I hope my idea would be useful. |
Thanks nimf using that and solving for the user stories you mentioned above I now have: can :show, Room if user.try(:is_a_room_member, room) || room.password_required == false unless user.try(:is_a_banned_room_member, room) That is cleaner. Problem here is that when this gets kicked to the applicationController's rescue_from CanCan::AccessDenied do |exception| I have no way of knowing if it was because the user was banned or if the Room requires a password. As all CanCan::AccessDenied gets if the Model not the object being access: Room(id: integer, user_id: integer, title: string, password_required: boolean, password: string) etc... Any ideas where I'll know why the user wasn't given permission to view the Room? Thanks |
Oh, I see. At first, def room_eligible?(user, room)
unless room
session[:cannot_reason] = "no such room"
return nil
end
if user.try(:is_a_banned_room_member, room)
session[:cannot_reason] = "user banned in this room"
return nil
end
if room.password_required
unless user.try(:is_a_room_member, room)
session[:cannot_reason] = "room password required"
return nil
end
end
return true
end Then you can check session[:cannot_reason] in your can :show, Room if room_eligible?(user, room) And I don't know if user is nil can the room be viewed if password is not required? With my example it is allowed. You can also move |
Thanks nimf, that's interesting, I'd also need to make another session variable to store the room.id... @ryan, does this problem go away with the new CanCan 2.0? Thanks |
@nimf one more thing. Where would the helper live? It can't be in the model as the model can't use session. Ability.rb won't work as it to is a model. Where did you have in mind? Thanks |
Another challenge here is that I'm not sure how to right controller specs that can see where CanCan Rescue redirects. Is that possible? Right now if CanCan sends an Access Denied the test spec test ends there and doesn't continue with what is in the "rescue_from CanCan::AccessDenied do |exception|" block ... Thoughts? |
Shame on me! Things are much simpler: #/app/models/ability.rb
def initialize(user)
user ||= User.new # guest user (not logged in)
can :read, Room do |room| room_eligible?(user, room) end
end
def room_eligible?(user, room)
unless room
raise CanCan::AccessDenied.new("Not such room!", :read, Room)
end
if user.try(:is_a_banned_room_member?, room)
raise CanCan::AccessDenied.new("User banned in this room!", :read, Room)
end
if room.password_required
unless user.try(:is_a_room_member?, room)
raise CanCan::AccessDenied.new("Password required!", :read, Room)
end
end
return true
end Then just look for exception.message in rescue_from. |
Thanks, I like that but it doesn't let the CanCan Rescue know which Room.id we are talking about. I ended up using a Session variable in the controller to track that but it feels dirty, inelegant. @ryan, any thoughts? |
nobody will restrict you from adding id into the exception message, i.e.: def room_eligible?(user, room)
unless room
raise CanCan::AccessDenied.new("Not such room!", :read, Room)
end
if user.try(:is_a_banned_room_member?, room)
raise CanCan::AccessDenied.new("User banned in the room #{room.id}!", :read, Room)
end
if room.password_required
unless user.try(:is_a_room_member?, room)
raise CanCan::AccessDenied.new("Password required for room #{room.id}!", :read, Room)
end
end
return true
end Then just parse it in rescue_from. |
@nimf Using that method was extremely helpful, thanks. I added a wiki page as I couldn't find any that had examples of using custom methods, https://github.com/ryanb/cancan/wiki/Custom-Ability-Methods |
Ryan, I have a model that's permission is based on a variety of conditions. I've been going in circles to define the CanCan condition correctly and it continues to get very messy. I was wondering if you had a suggestion that might be helpful here?
Models:
User
Room (id, password_required (boolean)
UserRoom (user_id, room_id, banned (boolean)
User Stories:
So far I have:
def initialize(user, room, room_password)
.....
can :show, Room if room && user.try(:is_a_room_member, room) == true && user.try(:is_a_banned_room_member, room) == false
As you can see the above doesn't solve all the stories and it's already getting ugly.
Any suggestions on how this can be simplified/cleaner and work?
Thank you
The text was updated successfully, but these errors were encountered: