Ldaptic

This is an object-oriented LDAP wrapper library I started back in 2007 but only recently polished up and released. It’s unique in that it creates a class hierarchy (in a namespace your provide) that exactly mirrors the class hierarchy on the server. For example, on a typical server, you’ll get an InetOrgPerson class which inherits from OrganizationalPerson which inherits from Person which inherits from Top. You can reopen any of these classes to add additional client side behavior.

Ldaptic started as mainly a tool to interact with my company’s Active Directory server, and I lost interest in it when I left that job. Recently, I’ve become motivated to work on it again, as some of the blocking issues I faced are now potentially solvable with Active Model.

Getting Started

You need to have either the ruby-ldap or net-ldap gem installed. The former is preferred because it’s faster native C. Ldaptic is configured by including a dynamically created module into a namespace of your choosing.

module Example
  include Ldaptic::Module(
    :adapter => :ldap_conn,
    :base => 'ou=Users,dc=example,dc=com',
    :host => 'example.com',
    :username => 'cn=admin,ou=Users,dc=example,dc=com',
    :password => 'password'
  )
end

The adapter can usually be omitted as it defaults to :ldap_conn or :net_ldap, based on which of the above two gems can be found. If the base is omitted, it will use the first naming context on the server (usually what you want).

Entries are retrieved using the search method. Named parameters include :base, :scope, :filter, :attributes, :scope, and :limit. All are optional.

entries = Example.search(
  :filter => {:objectClass => 'inetOrgPerson'},
  :limit => 10
)

A Ruby class is created for each objectClass defined on the server. Entries are instances of these classes.

>> entry = Example.find('cn=admin,ou=Users,dc=example,dc=com')
=> #<Example::InetOrgPerson cn=admin,ou=Users,dc=example,dc=com ...>
>> entry.class.superclass
=> Example::OrganizationalPerson

Predictably, entries have attribute readers and writers.

>> entry.cn
=> <["admin"]>
>> entry.cn = "root"
>> entry[:cn]
=> <["root"]>
>> entry[:cn] = "admin"

The returned object is an attribute set and is similar to an array. Some attributes are marked by the server as “single value;” those will return the first element on method access but an attribute set on indexing access, for programmatic convenience.

>> entry.uidNumber
=> 0
>> entry[:uidNumber]
=> <[0]>

The indexing syntax can also be used to create and fetch children.

>> users
=> #<Example::OrganizationalUnit ou=Users,dc=example,dc=com ...>
>> users[:cn=>'admin'] = Example::InetOrgPerson.new
=> #<Example::InetOrgPerson cn=admin,ou=Users,dc=example,dc=com ...>
>> users[:cn=>'admin']
=> #<Example::InetOrgPerson cn=admin,ou=Users,dc=example,dc=com ...>

Entry also implements many of the standard methods you’ve come to expect in an Active Record world (save, valid?, errors, to_param, attributes, …). In fact, it is fully Active Model compliant.

For more information, see in particular Ldaptic::Methods (for namespace methods like search), Ldaptic::Entry, and Ldaptic::AttributeSet.

To Do

  • The test suite (reflecting my fledgling testing abilities from 2007) is more smoke test than BDD. Perhaps switch to RSpec in the quest to rectify this.

  • Potential new features (mostly along the lines of “make it more like Active Record”) are in the GitHub issue tracker. Vote for and comment on the ones you would find useful, as most are on hold until someone has a real use case.