【问题标题】:Get a string that represents a user's CanCan abilities获取代表用户 CanCan 能力的字符串
【发布时间】:2012-03-21 16:52:37
【问题描述】:

我想缓存一个Post 视图,但该视图取决于当前用户的权限(例如,如果current_user.can?(:edit, @post),我只显示“编辑”链接)

所以我希望我的缓存键包含当前用户的 CanCan 能力的表示,以便当用户的能力发生变化时我可以使缓存无效

SO:我怎样才能得到一个代表当前用户能力的字符串,以便两个具有相同能力的不同用户生成相同的“能力字符串”?

我试过user.ability.inspect,但这不会为具有相同能力的不同用户生成相同的字符串

【问题讨论】:

    标签: caching authorization cancan cache-invalidation


    【解决方案1】:

    编辑:为 CanCanCan 修订

    从 CanCanCan 的 1.12 版(CanCan 的社区延续)开始,Ability.new(user).permissions 返回具有给定用户所有权限的哈希值。

    上一个答案(CanCan):

    这可能有点复杂......但它就是这样......

    如果您将指定的用户传递给 CanCan 所需的能力模型,则可以使用 instance_variable_get 访问该用户角色的定义,然后将其分解为您想要的任何字符串值。..

    >> 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"]
    

    如果您想知道施加这些规则的模型,您可以访问 @subjects 实例变量以获取其名称。

    这是我使用的能力模型布局 (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)]>
    ]
    

    【讨论】:

    • 我想总结一下,您必须使用您选择的内容构建数组,保存它,然后再次使用 to_s 将其内容转换为字符串! :)
    • 这是一个依赖于实现细节的 hack。
    • 唯一依赖的是用户正在使用 CanCan gem,这是 OP 正在使用的。使用 CanCan 时,Ability 和 User 类本来就可用
    • 其实例变量未记录在案,可能会在较新版本中更改。它还掩盖了潜在的重要细节(例如输出中最后一条规则的条件)。这就是为什么您应该坚持使用公共 API 或寻找其他方式的原因!
    • 从 CanCanCan 的 1.12 版(CanCan 的社区延续)开始,Ability.new(user).permissions 返回具有给定用户所有权限的哈希值。
    【解决方案2】:

    我想将我的能力发送给 JS,并跟进这篇文章,这是我的帮助方法,您可以使用它来将用户能力转换为控制器中的数组。然后我在数组上调用 .to_json 并将其传递给 javascript。

    def ability_to_array(a)
      a.instance_variable_get("@rules").collect{ |rule| 
      { 
        :subject => rule.instance_variable_get("@subjects").map { |s| s.name }, 
        :actions => rule.instance_variable_get("@actions").map { |a| a.to_s }
      }
    }
    end
    

    这是我实现 can() 方法的 Backbone.js 模型:

    var Abilities = Backbone.Model.extend({
      can : function(action, subject)
      {
        return _.some(this.get("abilities"), function(a) {
          if(_.contains(a["actions"], "manage") && _.contains(a["subject"], "all")) return true;
          return _.contains(a["actions"], action) && _.contains(a["subject"], subject);
        });
       }
    });
    

    【讨论】:

    • 你也可以只使用 :subject => rule.instance_variable_get("@subjects").map { |s| s.to_s},就好像你使用 s.name 并且你的主题包含它会出错的符号
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多