【问题标题】:How do I setup my CanCanCan permissions correctly?如何正确设置我的 CanCanCan 权限?
【发布时间】:2014-12-11 00:21:04
【问题描述】:

我对如何正确配置 CanCanCan 有点困惑。

对于初学者,我是否必须将load_and_authorize_resource 添加到我想要限制访问的每个控制器资源?

这是我想做的:

  • 管理员可以管理和访问所有控制器和操作
  • 编辑可以阅读所有,管理:新闻室,并可以管理所有帖子
  • 会员可以阅读每个帖子,可以创建和更新帖子(不能编辑/删除/其他任何内容),无法访问新闻编辑室。我们的业务规则中更新和编辑帖子之间的区别在于,更新是创建一个新帖子,它是当前帖子的子帖子。所以不是编辑。只是与祖先关联的新记录。
  • 访客可以阅读每个帖子,但无法创建帖子或访问新闻编辑室。

这就是我的ability.rb 的样子:

class Ability
  include CanCan::Ability
  def initialize(user)
    user ||= User.new # guest user (not logged in)
    #Admin
   if user.has_role? :admin
        can :manage, :all
        can :manage, :newsroom
   # Editor
    elsif user.has_role? :editor
      can :read, :all
      can :manage, :newsroom
      can :manage, Post
    #Member
    elsif user.has_role? :member
        can :read, :all
        can :create, Post
        can :status, Post
        can :update, Post do |post|
            post.try(:user) == user
        end
    #Guest
    else
        can :read, :all
        can :create, Post
        can :status, Post
    end    
  end
end

在我的routes.rb 我有这个:

  authenticate :user, lambda { |u| u.has_role? :admin or :editor } do
    get 'newsroom', to: 'newsroom#index', as: "newsroom"
    get 'newsroom/published', to: 'newsroom#published'
    get 'newsroom/unpublished', to: 'newsroom#unpublished'    
  end

但发生的情况是,当我使用尚未分配任何角色的用户(即我想成为“访客”)登录时,他们可以访问新闻编辑室。

当我尝试编辑具有:member 角色的帖子时,它给了我一个“未授权编辑帖子”错误(这是正确的)。

我无法完全锁定Newsroom,我不知道为什么。

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-4 cancan cancancan


    【解决方案1】:

    您不需要在每个控制器中都使用load_and_authorize_resource。这是一个方便的宏,它做两件事。首先,它使用为当前控制器和操作假定的记录分配一个实例变量。然后它授权资源。对于某些控制器操作,第一步可能是错误的,因此您需要加载资源,然后手动对其进行授权。 Railscasts episode about CanCan 的一个例子是这样的:

    def edit
      @article = Article.find(params[:id])
      unauthorized! if cannot? :edit, @article
    end
    

    你也可以像CanCan Wiki for authorizing controllers上的例子那样做:

    def show
      @project = Project.find(params[:project])
      authorize! :show, @project
    end
    

    或者您也可以使用authorize_resource 并自行负责加载。最后,您必须确保 CanCan 以某种方式用于授权(控制器宏或在每个操作中)。关于你的能力,我认为你想要这样的东西:

    class Ability
      include CanCan::Ability
      def initialize(user)
        user ||= User.new # guest user (not logged in)
        #Admin
        if user.has_role? :admin
          can :manage, :all
        # Editor
        elsif user.has_role? :editor
          can :read, :all
          can :manage, :newsroom
          can :manage, Post
        #Member
        elsif user.has_role? :member
          can :read, :all
          can :create, Post
          can :status, Post
          can :update, Post do |post|
            post.try(:user) == user
          end
        #Guest
        else
          can :read, :all
          cannot [:index, :published, :unpublished], :newsroom
        end
      end
    end
    

    下面是一个示例,例如您可以如何授权您的新闻编辑室:

    class ToolsController < ApplicationController
      authorize_resource :class => false
      def show
      # automatically calls authorize!(:show, :tool)
      end
    end
    

    关于 CanCan 的最后一点个人说明是,我不建议将它用于新项目,因为它不再被积极维护,而且我发现它在定义能力时有点违反直觉。也就是说,CanCan 是我使用过的文档记录最完整的 gem 之一,尤其是 the wiki 有大量示例和解释。

    【讨论】:

    • 现在有一个名为 CanCanCan 的社区版本正在积极维护 - github.com/CanCanCommunity/cancancan。不过,感谢您的回答,将尝试一下并告诉您。唯一有点奇怪的是你的最后一个例子:授权新闻编辑室。所以我应该只使用authorize_resource :class =&gt; false 来授权我的Newsroom 类?
    • 这个答案实际上并没有解决我的问题。主要问题是我仍然可以使用我不应该能够访问的角色访问newsroom。例如。我不希望角色为 :member 的用户能够访问它,但他们仍然可以。
    • 很抱歉直到现在才看到您的 cmets。感谢您提供指向维护的 CanCan 项目的链接。我看到你终于自己动手了,对吗?
    【解决方案2】:
    can :read, :all
    

    表示用户有权读取您应用的所有资源。应该是

    can :read, Post
    

    还加

    cannot :manage, :newsroom
    

    您不想访问新闻编辑室的地方。您指定权限的顺序很重要。 正如其他人已经提到的,“load_and_authorize_resource”是可选的。只需要“授权资源”来授权控制器的所有操作。如果您跳过这些,那么您可以“授权”单个控制器操作。

    除非绝对必要,否则避免使用技能块。例如,如果 Post 中有一个 user_id,那么你可以这样做

    can :update, Post, user_id: user.id
    

    最后,“class=> false”用于没有模型支持控制器的情况。 即您没有名为“Newsroom”的模型,但您有一个名为“NewsroomsController”的控制器。

    【讨论】:

      【解决方案3】:

      为了它的价值,我不得不像这样设置我的NewsroomController

      class NewsroomController < ApplicationController
        authorize_resource :class => false
      

      这是我的ability.rb 的工作版本在我获得所需权限后的样子:

      #Roles
      #Admin
       if user.has_role? :admin
            can :manage, :all
       # Editor
        elsif user.has_role? :editor
          can :manage, :newsroom
          can :manage, Post
        #Member
        elsif user.has_role? :member
            can [:read, :create, :status], Post
            can :update, Post do |post|
              post.try(:user) == user
            end
        #Guest
        else
            can [:read, :status], Post
        end
      

      【讨论】:

        【解决方案4】:

        对于初学者,我是否必须将 load_and_authorize_resource 添加到我想要限制访问的每个控制器资源?

        是的。

        但是,当我使用具有 没有被分配任何角色(即我想成为“客人”),他们 可以访问新闻编辑室。

        来自上面的来宾角色:

        ...
        #Guest
        else
            can :read, :all
            can :create, Post
            can :status, Post
        end    
        

        这使访客可以读取所有内容以及创建帖子的能力。 如果您希望您的客人只能阅读帖子,则应该是:

        ...
        #Guest
        else
            can :read, Post
            # can :status, Post # maybe you want this aswell
        end   
        

        【讨论】:

        • 此外,@marcamillion 可以摆脱路由中的身份验证,因为如果从访客中删除 can :read, :all,CanCanCan 将拒绝访问新闻编辑室。很好的删除重复。
        • 我想要一个Guest 能够阅读所有帖子、创建帖子和状态帖子。但我不希望他们能够访问Newsroom,也不希望他们能够编辑/更新/删除任何Post
        猜你喜欢
        • 1970-01-01
        • 2011-04-16
        • 2015-11-14
        • 1970-01-01
        • 2013-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多