4

Is it possible to set up guards with AASM that control event access by user role?

This seems like a fairly common use case, but I can't find a well agreed upon answer. Many people seem to suggest keeping the permission logic in the controllers, which certainly works, but means bleeding the state machine logic into several controllers. This is bad for several reasons, but the most critical is that any updates to the SM will require finding all usages to modify along with the model.

The solution I've come up with works, but I'm very curious if anyone has found a better one.

My solution:

Within my AASM class, I have included;

aasm do

    before_all_events set_user

    event :sample_event, :guard => :user_can? do
        transition ...
    end

end



private
def set_user user
    @user = user
    @user ||= User.new
end

def user_can?
    @user.some_check_on_attributes?
end

Which in turn allows me to do within my controller:

aasm_class.sample_event current_user

To check against the current user, or alternately;

aasm_class.sample_event

To check against a default user.

Is this the best way of approaching this issue? Does anyone have a better suggestion?

Jake Haller-Roby
  • 6,335
  • 1
  • 18
  • 31

2 Answers2

1

I use the sentient_user gem which does break MVC, but allows you to access User.current from your models.

swrobel
  • 4,053
  • 2
  • 33
  • 42
  • 1
    I rarely run across a mention of one of my projects, but when I do it makes my day! The sentient_user gem was written for exactly this purposes back in the rails 1.2 -> 2.0 transition days. It still works exactly as intended, with no fundamental changes since then. I understand why you say its a violation of MVC, but arguably, since your business logic varies depends on who is doing it, current_user is relevant and not a violation. We pass data between layers all the time, and thats all this does. – David Bock May 23 '16 at 17:03
0

Accessing current_user in guard clauses appears to be a thing (running AASM 4.10.0):

event :sample_event, guard: user_can? do
    transition ...
end

def user_can?(current_user)
    current_user.some_check_on_attributes?
end

or even

event :sample_event, guard: -> (current_user) { current_user.some_check_on_attributes? } do
    transition ...
end