1

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

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

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

params['state_event']=='move_to_x'

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

4

2 回答 2

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

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

状态机、模型验证和 RSpec

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

-- 为大众编辑 --

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

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

 self.users.first 

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

于 2012-12-19T08:32:05.297 回答
0

这实际上并没有我想象的那么糟糕,而且我这样做的方式是让代码相当短,并且 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}

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

于 2012-12-20T03:57:44.153 回答