Rails and LDAP gotchas
I’ve spent some time over the last few days trying to access a Microsoft Active Directory (AD) using LDAP from a Rails app. Although there are some libraries and a few blog posts, this is still a very painful thing to do.
Here’s some things I worked through so you don’t have to.
LDAP paths are location dependent, but not case sensitive
Not a Ruby or Rails issue, but something you need to know – “dc=foo,dc=com” is not the same as “dc=com,dc=foo”. However, DC=Foo,DC=com is the same as dc=foo,dc=com
Pre-built Windows ruby-ldap needs no other LDAP libraries
Chris Scharf has built the ruby ldap libraries for Windows Although the ruby-ldap site says the code relies on other libraries, on Windows, that code is built in.
Here’s some ruby-ldap code that works with my AD server:
require 'ldap'
conn = LDAP::Conn.new( '<domain-server>', 389 )
conn.set_option( LDAP::LDAP_OPT_PROTOCOL_VERSION, 3 )
conn.bind( '<domain>\<username>', '<password>' ) do |conn|
base = 'ou=Users,ou=<container>,dc=<domain>,dc=local'
results = conn.search2(base, LDAP::LDAP_SCOPE_SUBTREE, '(cn=*)')
results.each { |entry| puts "#{entry['dn']}: #{entry['telephoneNumber']}" }
endNote: I’m using a Small Business Server, and the container for me is MyBusiness. Also, my full domain name is
Most ActiveLDAP document is wrong
ActiveLDAP is getting closer to ActiveRecord in the way it works, and a bunch of the initialization code has changed to do this, but the documentation is not entirely correct. I have version 0.8 and here’s what I know:
- Use ActiveLdap::Base.establish_connection to setup the connection
- :password can now be used instead of :password_block
- :user isn’t yet used (but is documented) – use :bind_as instead
Here’s my code from environment.rb:
require 'active_ldap'
ActiveLdap::Base.establish_connection(
:host => '<domain-server>',
:port => 389,
:base => 'dc=<domain-name>,dc=local',
:bind_format => '%s',
:bind_dn => '<domain-name>\\<username>',
:password_block => Proc.new { '<password>' },
:allow_anonymous => false
)ActiveLDAP has some bugs in a Rails app
There are some bugs in ActiveLDAP 0.8 that I had to work around
Implement to_param
If you implement in your model code:
def to_param
id
endThen the Rails scaffold code will work great!
Schema processing broke for me
At line 44 of ActiveLDAP’s schema.rb file, I had to update to this to stop the code crashing:
while @entries[group] && schema = @entries[group].shiftThe DN value for saving is broken
I poked around and commented out some code, but I really didn’t understand what I was doing! Part of my confusion is that ActiveLdap::Base has two implementations of the base method that interact weirdly. The problem is that the base part of the DN was being added twice, and the DN attribute name (in my case CN) was on the front of the string. Here’s an example of what I was seeing:
cn=cn=<my name>,ou=Users,ou=MyBusiness,dc=<domain>,dc=local,ou=Users,ou=MyBusiness,dc=<domain>,dc=localThe string that did work was:
cn=<my name>,ou=Users,ou=MyBusiness,dc=<domain>,dc=localnTSecurityDescriptor isn’t supported
After getting past the DN string building, this was my next problem with save (I actually disabled validation to get here, as the validation does insist this is needed)
Ok, so this is mostly the fault of AD, but if you want to save changes back to AD, then you need to be able to get the nTSecurityDescription attribute for your objects. It seems that AD has implemented an extension to retrieve this attribute, but I have no idea how to do that.
What else can go wrong?
I was planning on being able to build a live search against my users in the AD using LDAP, but the performance sucks, even for only a dozen users, it takes over a second on my unloaded server with everything in memory.
Trackbacks
Use the following link to trackback from your own site:
http://blog.craz8.com/trackbacks?article_id=rails-and-ldap-gotchas&day=28&month=02&year=2007
[...] Rails and LDAP gotchas » CRAZ8 Example environment.rb for RoR using ActiveLDAP (tags: rails LDAP ActiveLDAP rubyonrails) [...]
I tried to do scaffold with activeldap (0.8.2), but I get the following error:
Code for Group:
And the controller looks like this:
This finally worked for me … just use your username, password & hostname. Note that CORP is my domain name. Also note that the username and password should be contained in SINGLE QUOTES unless you have some reason to use double quotes…. that messed me up for a while. ============================= require ‘rubygems’ require ‘net/ldap’
user = ‘CORP\my_username’ pass = ‘my_password’ ldap = Net::LDAP.new :host => “x.x.x.x”, :port => 389, :auth => {:method => :simple, :username => user, :password => pass} p (ldap.bind) ? “Authorization Succeeded!” : “Authorization Failed: #{ldap.get_operation_result.message}”
[...] åƒè€ƒè³‡æ–™ï¹”rails and ldap gotchas [...]
hi,
i also try to check user against ldap. in my case i try to use the LDAP::SSLConn for secure connection to afar ldap-server.
to do it i added this to /etc/ldap/ldap.conf:
BASE dc=my_firm_name,dc=com URI ldaps://foo.my_firm_name.com ldaps://bar.my_firm_name.com TLS_CACERT /etc/ssl/certs/nag02_ca.pem TLS_REQCERT never
then i included the ldap-library of ian macdonald: http://raa.ruby-lang.org/project/ruby-ldap/
my checking-method looks like this:
class ApplicationController > tmp = ApplicationController.new => # >> tmp.foo RuntimeError: no result returned by search from /home/crolle/work/CEIS-Typ/src/app/controllers/application.rb:83:in `search’ from /home/crolle/work/CEIS-Typ/src/app/controllers/application.rb:83:in `foo’ from (irb):2
the conn.bound? returns true in to the logfile. so the connection seems to work. i checked the traffic between the ldap-client and the ldap-server. the ssl-handshake was made. a ssl-request was made too and the client got some response. so that looks o.k. but what throws the error? maybe you have an idea what the reason for the error might be? every hint would be helpful and great.
best regards chris
arrgh. you also can check my code here:
http://pastie.caboo.se/147306