Rails cancan和State Machine – 授权状态

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

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

但是,只有某些用户应该能够将对象更改为某些状态。 我该如何实现呢? 这个想法就是这样

params['state_event']=='move_to_x' 

应该为一些用户纾困,但允许其他用户。 它也让我担心,直到我实现这是授权部分,一个聪明的用户可以在状态事件中发布任何内容,即使它们也不应该被允许!

你可以用两种方式做到这一点。 通过在过渡上设置条件。 像这样的东西:

  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))不是有效的测试 – 您需要自己的测试。

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

  state :first_gear, :second_gear do validate :speed_is_legal 

在文档中发现了一个警告:

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

这里有另一篇有趣的post:

状态机,模型validation和RSpec

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

– 为大众编辑 –

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

我们没有调用User.current_user,而是交换到:

  self.users.first 

这显然假设模型has_many用户。 然后,您可以调用此用户的能力

这实际上并不像我想象的那么糟糕,而且我已经以一种使代码相当短的方式完成它并且将current_user从我的模型中取出(这么大的优点)。

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

基本上

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

这样我就可以在Ability.rb中使用别名。 因此,可以执行操作的用户将被授权,而无法获得exception的用户将被授权。 这也很棒,因为它与我基于按钮的动作使用的能力相同。

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

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

我正在使用简单的表单,所以我只是一个集合输入

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

这使得select具有对象可以经历的所有转换,并且用户也被授权执行:)。