【问题标题】:Rails cancan and State Machine - Authorizing statesRails canscan 和状态机 - 授权状态
【发布时间】:2012-12-19 02:30:09
【问题描述】:

我最近在我的 rails 应用程序中使用了两个很棒的 gem,state_machine 和 cancan,但我很好奇将它们干净地集成的最佳方法。 目前,我已经在按钮上放置了状态转换,这些按钮可以执行控制器授权的操作。这非常有效,我可以限制谁可以执行该操作。

我也想让用户能够在编辑表单中更改对象状态。我注意到 state_machine 将获取散列中的 state_event 键,以及要执行的操作的值(因此它将通过所有 state_machines 回调)。 这可以通过 update_attributes 中的 params 散列传递。太棒了。

但是,只有某些用户才能将对象更改为某些状态。我将如何实现这一点?这个想法是

params['state_event']=='move_to_x'

应该为某些用户提供救助,但允许其他用户。我也很担心,因为在我实施之前,这是一个聪明的用户可以在状态事件中发布任何内容的授权部分,即使他们也不应该被允许!

【问题讨论】:

    标签: ruby-on-rails ruby authorization cancan state-machine


    【解决方案1】:

    您可以通过两种方式做到这一点。通过在转换上设置条件。像这样的:

     transition :from => :parked, :to => :idling, :if => :valid_user
    

    并在您的模型中创建一个 valid_user 方法。

    def valid_user
      if User.current_user.has_role?(xyz)
        do baa
      end
    end
    

    (User.current_user.has_role?(xyz)) 不是有效的测试 - 您需要自己的测试。

    或者您可以使用自定义状态机验证:

     state :first_gear, :second_gear do
       validate :speed_is_legal
    

    在文档中有一个警告:

    http://rdoc.info/github/pluginaweek/state_machine/master/StateMachine/Integrations/ActiveModel

    这里还有一个有趣的帖子:

    State Machine, Model Validations and RSpec

    我们在应用程序中成功地使用了这两种方法。

    -- 为大众编辑--

    思考关于在模型中使用 current_user 的评论。我们考虑过重新阅读我们的代码。在我们使用它的一两个示例中,我们意识到我们可以完全删除 current_user 方法,从而消除任何安全风险。

    我们没有调用 User.current_user,而是改为:

     self.users.first 
    

    这显然假设模型 has_many users。然后你可以调用这个用户的能力

    【讨论】:

    • 我明白你想做什么。但我的印象是在模型级别使用 Current_user 之类的方法很糟糕。
    • 这还不错,在我看来,这只是不常见的做法。我们这样做是因为我们必须使用状态机之类的东西。搜索“当前用户模型”,你会发现一堆关于它为什么不好以及为什么不好的帖子。
    • 我想只有很大一部分我不喜欢在模型层中使用当前用户。我最担心的是,如果我离开了铁轨怎么办?或者我正在尝试使用 rails c 中的模型。然后我需要在那里使当前的用户方法可用,并且还需要在单元测试中使其可访问。似乎很尴尬。话虽如此,我对我的解决方案感到满意,它阻止了授权代码接触我的模型。它停留在控制器中,类似于 cancan 工作的感觉。
    • 我也喜欢你的回答,并重构了我的一些方法。状态机有一个问题,因为它很难做一些不涉及控制器但需要用户的授权事情。我正在尝试找出解决此问题的其他方法。
    • 我很高兴我的问题/思考帮助重构了一些代码:)。是的,我明白这一点。我最大的感觉是,最好的方法是将 state_machine 和任何类型的授权完全分开。我不确定你想出了什么棘手的案例。如果由于某种原因您绝对需要 state_machine 中的用户,我会说获得使用的最佳方法是从控制器传递它。这样它就可以是任何用户,因此您的模型与当前用户的概念无关。
    【解决方案2】:

    这实际上并没有我想象的那么糟糕,而且我这样做的方式是让代码相当短,并且 current_user 在我的模型之外(如此大的优势)。

    诀窍是在控制器中再次调用授权。

    基本上

    def update
      authorize! params[:object][:state_event].to_sym, @object unless params[:object][:state_event].empty?
      .... etc
    end
    

    这样我就可以在 Ability.rb 中添加别名。因此,可以执行该操作的用户将获得授权,而不能执行该操作的用户将获得异常。这也很棒,因为它与我将在基于按钮的操作上使用的功能相同。

    唯一需要注意的是,您不能使用 @object.state_transistions 来获取用户可以转换到的可用状态的列表,但应该可以通过某种辅助方法来做到这一点。

    更新:虽然在类似层的视图中获取这些状态很容易

    我使用的是简单的形式,所以我只是一个集合输入,这样

     ..... collection: @object.status_transistions.select{|t| can? t.event, @object}
    

    这使得选择具有对象可以通过的所有转换,并且用户也被授权这样做:)。

    【讨论】:

      猜你喜欢
      • 2014-06-15
      • 1970-01-01
      • 2020-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-25
      • 2020-08-12
      • 1970-01-01
      相关资源
      最近更新 更多