I said it would never happen but here is another Ruby blogpost for the
ThoughtWorks pool. In my defense, there really wasn't much help out there on this topic so my
pair and I decided this had to be posted. His post on what we tried in order to make
Ruby talk to LDAP is
over there.
The Bits
OpenDS is Sun's LDAP server. We chose this as our LDAP server because it is the one most likely to sit behind the
OpenSSO Single Sign-On implementation we are populating, querying and binding to. It is written purely in Java and provides a bunch of useful shell scripts and a Java API to do the things we want to do. This will allow us to test our Ruby-LDAP implementation independently of our solution.
OpenDS has a nice installer with a friendly setup wizard. If you want to
script the install then run this from the root of the installation directory:
>$ './setup --cli --no-prompt -p 1389 -D "cn=Directory Manager" -w "password" -b dc=example,dc=com -a --doNotStart'
Ruby-LDAP was the friendliest and most sensible choice because it was the most pure Ruby choice which allowed us to do things
the Ruby way.
RJB let us use the OpenDS libraries but was too much like writing Java in Ruby and involved running another JVM on the production server it is destined for. If we were to go that far then we'd have opted for JRuby to do the bridging. After
ActiveLDAP flat out refused to acknowledge OpenDS and would only play well with OpenLDAP, that was written off. Ruby-LDAP is a written in C so it isn't a nice gem install but instead takes a bit of making and shaking to get going.
Here is how to install it from the command line once it is
downloaded and unzipped:
>$ ruby
ruby-ldap-0.9.7
/extconf.rb "."
>$ make
>$ make install
The Required Basic LDAP KnowledgeLDAP is like a very simple and slightly stupid database for storing Identity information. It does not have transactions. You can't query it with SQL. It is a tree. It does have a schema.
Understand your LDAP schema. You can customise it. It is made up of nodes that are defined by
object classes and attributes. These include but are not restricted to: Organization; OrganizationalUnit; and Person.
Distinguished Name
(DN) appears everywhere when you are dealing with LDAP. It is the unique name given to a node in the LDAP tree and describes the exact path from the node to the root. A
DN starts at the node and walks up the tree. It looks like:
uid=damana,ou=australia,dc=mycompany,dc=com
This is an
LDAP glossary that was useful in deciphering the LDAP maze and nomenclature.
Adding an Organizational UnitTo add an organizational unit:
#/usr/bin/ruby -w
require 'ldap'
connection = LDAP::Conn.new('localhost', 1389)
connection
.bind('cn=Directory Manager','password')
record = [
LDAP.mod(LDAP::LDAP_MOD_ADD,'objectclass',['organizationalUnit']),
LDAP.mod(LDAP::LDAP_MOD_ADD,'ou',['australia
']),
]
connection.add("ou=branches, dc=
mycompany
, dc=com", record)
connection.unbind
Adding a UserTo add a user to an organizational unit:
#/usr/bin/ruby -w
require 'ldap'
connection = LDAP::Conn.new('localhost', 1389)
connection.bind('cn=Directory Manager','password')
record = [
LDAP.mod(LDAP::LDAP_MOD_ADD,'objectclass',['person']),
LDAP.mod(LDAP::LDAP_MOD_ADD,'cn',['Damana Madden']),
LDAP.mod(LDAP::LDAP_MOD_ADD,'cn',['dmadden']),
LDAP.mod(LDAP::LDAP_MOD_ADD,'sn',['Damana Madden']),
]
connection.add("cn=Damana Madden ou=australia, dc=mycompany, dc=com", record)
connection.unbind
Deleting a UserTo delete a user:
#/usr/bin/ruby -w
require 'ldap'
connection
= LDAP::Conn.new('localhost', 1389)
connection
.bind('cn=Directory Manager','password')
connection
.delete("cn=Damana Madden ou=australia, dc=mycompany, dc=com")
connection
.unbind