Rails cancan and State Machine - Authorizing states

若如初见. 提交于 2019-12-01 01:23:49
simonmorley

You could do this in two ways. Either by putting a condition on the transition. Something like this:

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

And create a valid_user method in your model.

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

(User.current_user.has_role?(xyz)) is not a valid test - you'll need your own.

Or you can use a custom state machine validation:

 state :first_gear, :second_gear do
   validate :speed_is_legal

There's a caveat found about this in the docs:

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

There's another interesting post here:

State Machine, Model Validations and RSpec

We use both methods successfully in our application.

-- EDIT FOR THE MASSES --

Thinking about the comment about using current_user in a model. We thought about it a re-read our code. In the one or two examples we were using this, we realised we could remove the current_user method completely, thus eliminating any security risks.

Instead of calling User.current_user, we swapped to:

 self.users.first 

This obviously assumes the model has_many users. You can then call this user's abilities

This wasn't actually as terrible as I thought, and I've done it in a way that keeps the code rather short and current_user out of my models (so big plus).

The trick was to call authorize again in the controller.

basically

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

This way I can just aliases in Ability.rb. So users that can do the action will be authorized, and users that cannot will get the exception. This is also awesome as it's the same ability I'll be using on the button based actions.

The only caveat, is that you can't use @object.state_transistions to get a list of the available states the user can transition to, but it should be possible to do this via some sort of helper method.

UPDATE: though getting those states in a view like layer is easy enough

i'm using simple form so I just a collection input such that

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

Which leaves the select with all the transistions the object can go through, and that the user is also authorized to do :).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!