我是否应该使用 CanCan 跳过对实例化资源的操作的授权?
我正在编写一个网络应用程序,用于从更大、完整的卡片组中随机选择卡片列表。我有一个 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我发现了我的问题。 CanCan 根本不是问题!我的 CardSet 控制器中的以下行抛出异常并将我的访客(未登录)用户重定向到登录页面:
我将其更改为:
现在代码按预期工作: 访客用户可以查看创建的新随机集,但不能“保存”它们,除非他们首先登录。
所以,我真正的问题是 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:
I changed it to read:
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.
嗯,正确的做法是使用 CanCan 能力类来定义正确的授权规则。
在Ability.rb
等
虽然 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
etc
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