If you followed my previous tutorial on implementing pass-through authentication to LDAP with Authlogic, you might be wondering how it can be extended to give different permissions to members of different LDAP groups. ActiveLdap and declarative_authorization make this incredibly simple.

As with the first tutorial, I’ve pushed a branch based on authlogic_example that you can use as a concrete example.

First, let’s bring in the declarative_authorization gem:

# My example code was written before GitHub decided that they don't want to
# build gems anymore but this line should do the trick.
config.gem "declarative_authorization", :source => 'http://gemcutter.org'

Now, we need to make an ActiveLdap model class like LdapUser but for groups. We’ll call this LdapGroup:

class LdapGroup < ActiveLdap::Base
  ldap_mapping :dn_attribute => "cn",
    :scope => :sub,
    :prefix => "ou=groups,o=users"
 
  has_many :members, :class_name => "LdapUser", :wrap => "member",
    :primary_key => "dn", :foreign_key => "dn"
end

The ldap_mapping part is very similar to the previous one for LdapUser, but the has_many is where the real ActiveLdap magic starts!

We are letting ActiveLdap know that an LdapGroup has LdapUser members, which are defined in the multi-valued attribute member, where each of the values is the DN of an LdapUser. If you’re looking for a bit more detail, refer to the ActiveLdap documentation. Keep in mind that the code above is for the specific schema used by my example application and you may have to change it to match yours.

With that, we should be able to list all of our LDAP groups and find the members of any particular one in the console:

>> LdapGroup.all.collect { |g| g.cn }
=> ["super_admin"]
>> LdapGroup.find("super_admin").members.collect { |m| m.dn }
=> ["uid=ebianco,o=users,dc=example,dc=com"]

Now we’ll create an association between LdapUser and LdapGroup so that given a user, we can figure out which groups he/she is a member of. We’re going to tell ActiveLdap that a user’s groups are those groups in which the user’s DN appears in the group’s member attribute:

belongs_to :groups, :class_name => "LdapGroup", :many => "member",
  :foreign_key => "dn"

Let’s check our handiwork in the console:

>> LdapUser.find("ebianco").groups.collect { |g| g.cn }
=> ["super_admin"]

Now why did we go through all of this effort? declarative_authorization allows us to provide an instance method for User, #role_symbols, that returns an array of symbols representing the list of roles that the user has. With our ActiveLdap setup, mapping a user’s roles to LDAP groups becomes a one-liner:

def role_symbols
  ldap_entry.groups.collect { |g| g.cn.to_sym }
end

From here, we can define the authorization rules for our LDAP-based roles. declarative_authorization reads these from config/authorization_rules.rb:

authorization do
  role :super_admin do
    has_permission_on :users, :to => :manage
  end
  role :guest do
    has_permission_on :users, :to => :read
    has_permission_on :users, :to => [:create, :update, :destroy] do
      if_attribute :id => is {user.id}
    end
  end
 
end
 
privileges do
  privilege :manage do
    includes :create, :read, :update, :destroy
  end
end

Now we can use declarative_authorization as usual. I won’t go through all the details but if you want a primer on using declarative_authorization, you can start here and browse the commits on my authlogic_example branch.