The following is a step by step tour through the process I went through to get Ruby to talk to Window Server 2003’s ActiveDirectory. Disclaimer: I am not a Windows guy, and I’m CERTAINLY not an ActiveDirectory/LDAP guy, so … yeah ..
Getting the required files
First thing we need are the Ruby/LDAP bindings from http://sourceforge.net/projects/ruby-ldap/. Follow the README on how to build this, or for the lazy:
ruby extconf.rb
make
sudo make install
Lets test. Pop open irb and try:
require 'ldap'
If everything installed properly, this should return true. Now the tough part …
Deciphering LDAP
I am new to LDAP. This made it even tougher to decipher LDAP’s cryptic naming scheme. Here is a glossary of terms you should probably not reel at the site of.
CN (Common Name) For example, Britt Selvitelle, John Doe.DC (Domain Component) Name for domain/DNS entries.
sAMAccountName (Who Knows) Your windows login.
OU (Organizational Unit) Users groups.
Gathering the Necessary Information
Ok, lets get some basic information about your users and domains. This is an easy step, or at least it was for me. From your Windows machine, open up cmd and type:
dsquery user
This should give you some good information. such as the CN, OU, and DC values.
Establishing a Connection and Doing Fancy Things
Alrighty, fire up irb or an editor and try the following:
require 'ldap'
host = 'miro' # host name of the ldap server
port = 389
# NOTE! The order of the values in your BASE string is important!
base = 'ou=VoltaicUsers,dc=voltaiccommerce,dc=com'
name = 'Britt Selvitelle'
password = 'woohoo'
conn = LDAP::Conn.new(host, port)
conn.set_option( LDAP::LDAP_OPT_PROTOCOL_VERSION, 3 )
conn.bind("cn=#{name},ou=VoltaicUsers,dc=voltaiccommerce,dc=com", password)
# Now lests try a query!
results = conn.search2(base, LDAP::LDAP_SCOPE_SUBTREE, '(cn=*)',['cn', 'sAMAccountName'])
puts results.inspect
Unfortunately, error messages returned are not the most descriptive, so if your bind parameters are wrong it can be difficult to diagnose the problem. For example:
conn.bind("", "")
followed by a query will return:
LDAP::ResultError: Operations error
from (irb):23:in `search2'
from (irb):23
If you’re incredibly stubborn and keep trying like a monkey with a hammer, you should get it eventually.
Authenticating Based on Your Windows Login
You may have noticed that the only way to bind to a connection is via the CommonName. Not very handy when all your users are used to logging in with their regular logins. My solution has been to connect as an extremely unprivileged user, query for the sAMAccountName/CN pair, and reconnect with the appropriate CN. This is slightly (read ‘horribly’) ugly, so if anyone knows a better way to validate using a user’s normal login, please let me know!
Hi,
ruby: error.c:264: ldap_parse_result: Assertion `( (ld)->ld_options.ldo_valid == 0×2 )’ failed.
Any Ideas ?
This was running beautifully with Apache2+fcgi until very recently. I am now receiving errors regarding connection to the LDAP server, and I haven’t had a chance to dig into it yet. I’d love to see how you’ve composed your script (the connection portion), if you don’t mind emailing a copy.
No problem. This is a smaller version of the real thing. It’s pretty much the same as the one you showed in your blog. However enough to toast fcgi on that box…We are connecting to Netcape LDAP from a linux box, with the latest ruby-ldap and openldap installed. Pretty bleak docs and suppport out there to resolve this kind of stuff. Looks like the ruby-ldap project might be dead. Yikes !!
!/usr/local/bin/ruby
require ‘ldap’
def print_entry(entry) puts ”>>>> Entry #{entry.to_s} #{entry.class}” entry.each_key { |k| puts “Key: #{k}—> #{entry[k]}” } end
ldap_conn = LDAP::Conn.new(‘fred.blee.com’, 389) ldap_conn.bind{ i = 0 ldap_conn.search2(‘cn=bozo,ou=Fred Groups,o=blee.com’, LDAP::LDAP_SCOPE_SUBTREE, ‘(cn=*)’){|entry| i += 1 print_entry(entry) } }
It is possible to connect using the login name.
Bind using:
conn.bind(‘login_name@voltaiccommerce.com’, ‘password’)
Neat! well written little guide!
How would you go about adding a user to the LDAP. i assume using
ldap_conn.add() ?