Rails 4 + Pundit:在has_many中加入模型授权:通过关联

时间:2015-09-11 16:51:50

标签: ruby-on-rails ruby-on-rails-4 authorization has-many-through pundit

在我的Rails应用程序中,有3个模型,由has_many :through关联定义:

class User < ActiveRecord::Base
  has_many :administrations
  has_many :calendars, through: :administrations
end

class Calendar < ActiveRecord::Base
  has_many :administrations
  has_many :users, through: :administrations
end

class Administration < ActiveRecord::Base
  belongs_to :user
  belongs_to :calendar
end

加入Administration模型具有role属性,我们用它来定义给定user的给定calendar的角色 - 所有者,编辑者或查看者

实际上,在应用程序中,用户可以是日历的所有者,也可以是其他日历的查看者。

我使用Devise实现了身份验证。

我还开始使用Pundit实施授权:授权目前适用于calendars,其中users可以根据roles执行不同的操作。

更新:这是当前的CalendarPolicy

class CalendarPolicy < ApplicationPolicy

  attr_reader :user, :calendar

  def initialize(user, calendar)
    @user = user
    @calendar = calendar
  end

  def index?
    user.owner?(calendar) || user.editor?(calendar) || user.viewer?(calendar)
  end

  def create?
    true
  end

  def show?
    user.owner?(calendar) || user.editor?(calendar) || user.viewer?(calendar)
  end

  def update?
    user.owner?(calendar) || user.editor?(calendar)
  end

  def edit?
    user.owner?(calendar) || user.editor?(calendar)
  end

  def destroy?
    user.owner?(calendar)
  end

end

现在,我想为管理模型实施Pundit策略,如下所示:

  • 如果用户是日历的所有者,那么他可以执行IndexShowCreateNewEdit,{{1} }和Update对此日历管理的操作。
  • 但是,如果用户是日历的编辑或查看者,那么他只能做两件事:1。执行Destroy操作以查看日历的所有用户,然后执行Index对他自己的政府采取行动,以及#34;离开日历&#34;。

我的问题如下:

  • 管理实例仅作为Destroyuser之间的连接存在,如上所述。
  • 因此,要对管理实例执行操作,我需要三个上下文:calendaradministration_iduser_id
  • 但是,Pundit只接受策略中的两个上下文,通常是calendar_id和实际user(这里是管理)。

在Pundit的GitHub页面上,在Additional context部分,我们可以阅读以下内容:

  

附加背景

     

Pundit强烈建议您以这种方式为应用程序建模   您授权所需的唯一上下文是用户对象和   要检查授权的域模型。如果你找到了   你需要更多的背景,考虑一下你自己   授权正确的域模型,也许是另一个域模型(或者   围绕多个域模型的包装器)可以提供您的上下文   需要。

     

Pundit不允许您将其他参数传递给策略   正是这个原因。

     

但是,在极少数情况下,您可能需要根据更多内容进行授权   上下文,而不仅仅是当前已验证的用户。假设为   例如,授权依赖于IP地址   经过身份验证的用户。在这种情况下,一个选项是创建一个   包含用户和IP并将其传递给的特殊类   政策。

record关联是否构成了非常罕见的情况之一&#34;上面提到过或者是否有更简单的方法来实现我的has_many :through联接模型的授权?

2 个答案:

答案 0 :(得分:1)

是的,这是极少数情况之一。

# calendar controller show
@calendar = something    
@administration = @calendar.administration_of_current_user
authorize CalendarAdministrationContext

# pundit CalendarAdministrationContext
def initialize(user, administration, calendar)
  @user = user
  @administration = administration
  @calendar = calendar
end

答案 1 :(得分:1)

我不认为,这是一个例外情况。 打破良好做法的原因是什么,所以在您提供的链接中明确说明了什么?

您只需在CalendarController中添加add_vieweradd_editorremove_viewerremove_editor个动作即可。

可以使用旧的CalendarPolicy授权前两个。

class CalendarPolicy
  # old staff here

  def add_viewer?
    user.is_owner?(calendar)
  end

  def add_editor?
    user.is_owner?(calendar)
  end
end

对于删除操作,您需要AdministrationPolicy(我错了,说日历策略是enogh):

class AdministrationPolicy
  attr_reader :user, :authorization

  def remove_viwer?
    authorization.viewer? and authorization.user == user
  end

  def remove_editor?
    authorization.editor? and authorization.user == user
  end
end