ruby-on-rails-3declarative-authorization

how to check if_attribute :id => is_in { array of ids from recursive method }


I have a resource database where resources can belong to different locations. Users and groups (self-referential user table) can have different roles on different locations. Groups can be inside other groups. Authorization works well for single users using 'if_attribute' to check if the location_id is among the location_ids that the user is allowed to show, edit, etc.:

has_permission_on :locations do
  to [:show, :new, :create, :edit, :update, :destroy] 
  if_attribute :id => is_in { (user.permissions.where(:role => "admin").collect {|i| Location.find_by_id(i.location_id).subtree_ids}.flatten.uniq )}
end

Since the groups can be "nested" inside each other, I have figured that I'll have to use a recursive method to find all the "legal" location ids. I tried this:

 has_permission_on :locations do
  to [:show, :new, :create, :edit, :update, :destroy] 
  if_attribute :id => is_in { (user.permissions.where(:role => "admin").collect {|i| Location.find_by_id(i.location_id).subtree_ids} + find_group_location_ids(user)).flatten.uniq }
end

with the method defined outside the 'authorization do'-routine:

def find_group_location_ids(user)
  location_ids = []
  nested_group_location_ids(user)
  def nested_group_location_ids(user)
    user.group_memberships.each do |gm|
      location_ids = location_ids + gm.user.location.id
      nested_group_location_ids(gm.user)
    end
  end
  return location_ids
end

The problem is that the method call doesn't find the method. I get this error:

NoMethodError (undefined method `find_group_location_ids' for (Authorization::Engine::AttributeValidator:0x7fb63448e2b0)

I have tried to place the method definition on a lot of different places, but with no luck.

How can I use if_attribute to see if an id is inside an array from a recursive method?


Solution

  • I got som help from steffenb at the declarative_authorization group at google groups (he is the author of declarative_authorization). The solution was to move the method to the user model:

    def find_group_location_ids
      location_ids = []
      nested_group_location_ids(self)
      def nested_group_location_ids(user)
        user.group_memberships.each do |gm|
          location_ids = location_ids + gm.user.location.id
          nested_group_location_ids(gm.user)
        end
      end
      return location_ids
    end
    

    and to call it in this way from autorization_rules.rb:

    has_permission_on :locations do
      to [:show, :new, :create, :edit, :update, :destroy] 
      if_attribute :id => is_in { (user.memberships.where(:role_id => "admin").collect {|i| Location.find_by_id(i.location_id).subtree_ids}  + user.find_group_location_ids).flatten.uniq }
    end