cachingauthorizationcancancache-invalidation

Get a string that represents a user's CanCan abilities


I want to cache a Post view, but the view depends on the permissions of the current user (e.g., I only show the "edit" link if current_user.can?(:edit, @post))

So I'd like my cache key to include a representation of the current user's CanCan abilities, so that I can invalidate the cache when the user's abilities change

SO: how can I get a string that represents the current user's abilities such that 2 different users with the same abilities will generate the same "ability string"?

I've tried user.ability.inspect, but this doesn't produce the same string for different users who have the same abilities


Solution

  • EDIT: revised for CanCanCan

    As of version 1.12 of CanCanCan (the community continuation of CanCan), Ability.new(user).permissions returns a hash with all permissions for the given user.

    Previous answer (CanCan):

    This might be a little complex...but here it goes..

    If you pass the specified User into the Ability model required by CanCan, you can access the definition of that users role using instance_variable_get, and then break it down into whatever string values you want from there..

    >> u=User.new(:role=>"admin")
    >> a=Ability.new(u)
    >> a.instance_variable_get("@rules").collect{ 
          |rule| rule.instance_variable_get("@actions").to_s
       }
    => ["read", "manage", "update"]
    

    if you want to know the models in which those rules are inflicted upon, you can access the @subjects instance variable to get its name..

    here is the model layout for Ability from which I worked with (pp)

    Ability:0x5b41dba @rules=[
      #<CanCan::Rule:0xc114739 
        @actions=[:read], 
        @base_behavior=true, 
        @conditions={}, 
        @match_all=false, 
        @block=nil, 
        @subjects=[
          User(role: string)]>, 
      #<CanCan::Rule:0x7ec40b92 
        @actions=[:manage], 
        @base_behavior=true, 
        @conditions={}, 
        @match_all=false, 
        @block=nil, 
        @subjects=[
          Encounter(id: integer)]>, 
      #<CanCan::Rule:0x55bf110c 
        @actions=[:update], 
        @base_behavior=true, 
        @conditions={:id=>4}, 
        @match_all=false, 
        @block=nil, 
        @subjects=[
          User(role: string)]>
    ]