我是否应该使用 CanCan 跳过对实例化资源的操作的授权?

发布于 2024-09-01 08:32:01 字数 2434 浏览 5 评论 0原文

我正在编写一个网络应用程序,用于从更大、完整的卡片组中随机选择卡片列表。我有一个 Card 模型和一个 CardSet 模型。两种模型都有 7 个完整的 RESTful 操作集(:index、:new、:show 等)。 CardSetsController 有一个用于创建随机集的额外操作::random

# app/models/card_set.rb
class CardSet < ActiveRecord::Base
  belongs_to :creator, :class_name => "User"
  has_many :memberships
  has_many :cards, :through => :memberships

# app/models/card.rb
class Card < ActiveRecord::Base
  belongs_to :creator, :class_name => "User"
  has_many :memberships
  has_many :card_sets, :through => :memberships

我添加了用于身份验证的 Devise 和用于授权的 CanCan。我有具有“编辑”角色的用户。编辑者可以创建新的卡片集。访客用户(未登录的用户)只能使用 :index:show 操作。这些授权正在按设计运行。编辑者目前可以毫无问题地使用 :random:new 操作。正如预期的那样,来宾用户不能。

# app/controllers/card_sets_controller.rb
class CardSetsController < ApplicationController
  before_filter :authenticate_user!, :except => [:show, :index]
  load_and_authorize_resource

我想允许访客用户使用 :random 操作,但不允许使用 :new 操作。换句话说,他们可以看到新的随机集,但不能保存它们。 :random 操作视图上的“保存”按钮对访客用户隐藏(按设计)。问题是,:random 操作所做的第一件事是构建 CardSet 模型的新实例来填充视图。当 cancan 尝试 load_and_authorize_resource 一个新的 CardSet 时,它会抛出 CanCan::AccessDenied 异常。因此,视图永远不会加载,并且访客用户会看到“您需要登录或注册才能继续”消息。

# app/controllers/card_sets_controllers.rb
def random
  @card_set = CardSet.new( :name => "New Set of 10", :set_type => "Set of 10" )

我意识到我可以通过传递 : except => 来告诉 load_and_authorize_resource 跳过 :random 操作。 :random 到调用中,但出于某种原因,这感觉“错误”

执行此操作的“正确”方法是什么?我应该创建新的随机集而不实例化新的 CardSet 吗?我应该继续添加例外吗?

更新

我上面没有包括我的能力类别。我已将其更新为包含“:随机”操作,但它仍然无法正常工作。

class Ability
  include CanCan::Ability

  def initialize( user )
    user ||= User.new # User hasn't logged in

    if user.admin?
      can :manage, :all if user.admin?
    else
      # All users, including guests:
      can :read, [Card, CardSet]
      can :random, CardSet

      # All users, except guests:
      can :create, [Card, CardSet] unless user.role.nil?
      can :update, [Card, CardSet] do |c|         
        c.try( :creator ) == user || user.editor?
      end

      if user.editor?
        can [:create, :update], [Card, CardSet]
      end
    end
  end
end

I am writing a web app to pick random lists of cards from larger, complete sets of cards. I have a Card model and a CardSet model. Both models have a full RESTful set of 7 actions (:index, :new, :show, etc). The CardSetsController has an extra action for creating random sets: :random.

# app/models/card_set.rb
class CardSet < ActiveRecord::Base
  belongs_to :creator, :class_name => "User"
  has_many :memberships
  has_many :cards, :through => :memberships

# app/models/card.rb
class Card < ActiveRecord::Base
  belongs_to :creator, :class_name => "User"
  has_many :memberships
  has_many :card_sets, :through => :memberships

I have added Devise for authentication and CanCan for authorizations. I have users with an 'editor' role. Editors are allowed to create new CardSets. Guest users (Users who have not logged in) can only use the :index and :show actions. These authorizations are working as designed. Editors can currently use both the :random and the :new actions without any problems. Guest users, as expected, cannot.

# app/controllers/card_sets_controller.rb
class CardSetsController < ApplicationController
  before_filter :authenticate_user!, :except => [:show, :index]
  load_and_authorize_resource

I want to allow guest users to use the :random action, but not the :new action. In other words, they can see new random sets, but not save them. The "Save" button on the :random action's view is hidden (as designed) from the guest users. The problem is, the first thing the :random action does is build a new instance of the CardSet model to fill out the view. When cancan tries to load_and_authorize_resource a new CardSet, it throws a CanCan::AccessDenied exception. Therefore, the view never loads and the guest user is served a "You need to sign in or sign up before continuing" message.

# app/controllers/card_sets_controllers.rb
def random
  @card_set = CardSet.new( :name => "New Set of 10", :set_type => "Set of 10" )

I realize that I can tell load_and_authorize_resource to skip the :random action by passing :except => :random to the call, but that just feels "wrong" for some reason.

What's the "right" way to do this? Should I create the new random set without instantiating a new CardSet? Should I go ahead and add the exception?

Update

I didn't include my Ability class above. I've updated it to include the ':random' action, but it still isn't working quite right.

class Ability
  include CanCan::Ability

  def initialize( user )
    user ||= User.new # User hasn't logged in

    if user.admin?
      can :manage, :all if user.admin?
    else
      # All users, including guests:
      can :read, [Card, CardSet]
      can :random, CardSet

      # All users, except guests:
      can :create, [Card, CardSet] unless user.role.nil?
      can :update, [Card, CardSet] do |c|         
        c.try( :creator ) == user || user.editor?
      end

      if user.editor?
        can [:create, :update], [Card, CardSet]
      end
    end
  end
end

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

顾忌 2024-09-08 08:32:01

我发现了我的问题。 CanCan 根本不是问题!我的 CardSet 控制器中的以下行抛出异常并将我的访客(未登录)用户重定向到登录页面:

before_filter :authenticate_user!, :except => [:show, :index]

我将其更改为:

before_filter :authenticate_user!, :except => [:show, :index, :random]

现在代码按预期工作: 访客用户可以查看创建的新随机集,但不能“保存”它们,除非他们首先登录。

所以,我真正的问题是 Devise(或者实际上是我的 Devise 配置),而不是 CanCan。

I found my problem. CanCan was not the issue at all! The following line in my CardSet controller was throwing the exception and redirecting my guest (not logged in) users to the log in page:

before_filter :authenticate_user!, :except => [:show, :index]

I changed it to read:

before_filter :authenticate_user!, :except => [:show, :index, :random]

And now the code works as intended: Guest users can view the new random sets created, but cannot 'Save' them unless they first log in.

So, my real problem was with Devise (or, actually, my Devise configuration) and not with CanCan at all.

掩耳倾听 2024-09-08 08:32:01

嗯,正确的做法是使用 CanCan 能力类来定义正确的授权规则。

在Ability.rb

  def initialize(user)
#everyone
    can [:read, :random], [CardSet]
#everyone who is editor
    if user.editor?
      can [:new, :create], [CardSet]

问题是,:random 操作所做的第一件事是构建 CardSet 模型的新实例来填充视图。当 cancan 尝试加载新的 CardSet 时,它会抛出 CanCan::AccessDenied 异常。

虽然 CanCan 授权控制器操作,但以随机操作(即 CardSet.new)构建新实例并不在 CanCan 的范围内。您可能会收到错误,因为您没有在Ability.rb 中为随机操作定义规则。我上面的例子应该可以解决你的问题

Well, the right thing would be to use CanCan ability class to define the correct authorization rules.

in Ability.rb

  def initialize(user)
#everyone
    can [:read, :random], [CardSet]
#everyone who is editor
    if user.editor?
      can [:new, :create], [CardSet]

etc

The problem is, the first thing the :random action does is build a new instance of the CardSet model to fill out the view. When cancan tries to load_and_authorize_resource a new CardSet, it throws a CanCan::AccessDenied exception.

While CanCan authorizes controller action, building a new instance in random action (i.e. CardSet.new) is not in the scope of CanCan. You probably get the error because you have no rules defined in Ability.rb for random action. My example above should solve your problem

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文