What's the best approach to apply role-based access authorization in Active Admin?

Viewed 1590

If only using ActiveAdmin I know that I can do something like this to limit the access of a certain user group to a resource:

ActiveAdmin.register Resource do
  menu :parent => "Super Admin Only", :if => proc { current_admin_user.super_admin? }
end

However, if I have a dynamic access/permission control form that gets updated depending on the needs of a certain user group, I need to be able to update my ActiveAdmin respectively.

I'm saving my permission data as json (key-value pair). At first, I was thinking of using ActiveAdmin::AuthorizationAdapter

class StaffAuthorization < ActiveAdmin::AuthorizationAdapter

  def authorized?(action, subject = nil)
    return true if resource.try(:name) == 'Dashboard'
    return false if action == :destroy

    retrieve_policy(action).authorized?
  end

  def role
    user.role
  end

  def policy_klass
    "policy/staff/#{role}".classify.constantize
  rescue NameError
    Policy::Staff::Default
  end

  def retrieve_policy(action)
    policy_klass.new(user, resource, action)
  end
end

For exmaple a Processing adminuser:

module Policy::Staff
  class Processing < Base
    def authorized?
      return true if resource.try(:resource_class) == ::Borrow
      return true if resource.try(:resource_class) == ::User && action == :read
      return true if [ 'Reports', 'Categorize' ].include?(resource.try(:name))
      false
    end
  end
end

I can always generate a policy file by looping through the json on save but the function looks dirty. Always on a format returning true for a certain resource or action on it.

Any better way to do this kind of feature in ActiveAdmin?

Limitations: Running ActiveAdmin 1.0.0 Ransack 1.8.4

  • If I change/update any of the two I get painful list of errors.
1 Answers

I recommend the cancan adapter, which appears to be available in ActiveAdmin 1.0.0.

First, install cancancan (the successor to cancan) by adding it to your Gemfile. Then set up the adapter:

config.authorization_adapter = ActiveAdmin::CanCanAdapter

Then define an app/models/ability.rb class.

I have something like this (borrowing from your example and extending it a little):

class Ability
  include CanCan::Ability

  def initialize(user)
    can :read, ActiveAdmin::Page, name: 'Dashboard'

    case user.role
    when 'superuser'
      # superusers can do everything, no need to specify
      can :manage, :all
    when 'processing'        
      basic(user)
      processing(user)
    when 'another-role'
      basic(user)
    end
  end


  private

  def basic(user)
    # The user can read all users
    can :read, User

    # and they can manage themselves
    can :manage, user
  end

  def processing(user)
    can :manage, Borrow

    # The conditions hash allows cancan to generate a query
    # to load accessible records as well as check individual
    # records.
    can :manage, OtherThing, name: ['Reports', 'Categorize']
  end
end

I've had good luck with cancancan, for fairly complex authorization, but I haven't tried the other built-in adapter: pundit, so I don't know how it compares.

Related