Today, I pushed a branch to my fork of authlogic_example: with-activeldap.
This branch shows a way of implementing pass-through authentication to an LDAP server using ActiveLdap and Authlogic, with just some small changes to the User and UserSession models.
First, we’ll need to bring in the net-ldap and activeldap gems. We edit config/environment.rb to include the following two lines:
config.gem "net-ldap", :lib => false, :version => '>=0.0.5' config.gem "activeldap", :lib => "active_ldap", :version => '1.0.9'
If you use sudo gem install net-ldap, you’ll get 0.0.4. Here, I’ve built the 0.0.5 gem from the GitHub repo. This is because there are some bug fixes in the GitHub master that aren’t in the RubyForge gem. In the with-activeldap branch, the two gems are vendored.
Now, we create config/ldap.yml to configure ActiveLdap’s connection to our LDAP server. Here’s mine:
development: host: 127.0.0.1 base: dc=dev,dc=Asuka,dc=local bind_dn: cn=Manager,dc=dev,dc=Asuka,dc=local password: secret test: host: 127.0.0.1 base: dc=test,dc=Asuka,dc=local bind_dn: cn=Manager,dc=Asuka,dc=local password: secret production: host: 127.0.0.1 base: dc=prod,dc=Asuka,dc=local bind_dn: cn=Manager,dc=Asuka,dc=local password: secret
This tells ActiveLdap the server/port to connect to, what the base entry for our LDAP objects is, and what user to bind as for operations on the server. Now, we create a LdapUser class to represent user entries in the LDAP:
class LdapUser < ActiveLdap::Base ldap_mapping :dn_attribute => "uid", :scope => :sub, :prefix => "o=users" end
This defines an LDAP user as being an entry from the o=users organization, where all of the entries are distinguished by the uid attribute. Now, we should be able to use the console to list all of our LDAP users like so:
>> LdapUser.all
Now, we’ll add some methods to the User model that allow us to look up users by login in the LDAP and create entries in the database if they don’t already exist. We’ll also need a method for forwarding the credentials provided on the login form to the LDAP and see if they are valid:
class User < ActiveRecord::Base acts_as_authentic do |c| # Don't validate password, since that will be held in the LDAP c.validate_password_field = false end def ldap_entry LdapUser.find(self.login) end # Tries to find a User first by looking into the database and then by # creating a User if there's an LDAP entry for the given login def self.find_or_create_from_ldap(login) find_by_login(login) || create_from_ldap_if_valid(login) end # Creates a User record in the database if there is an entry in the LDAP # with the given login def self.create_from_ldap_if_valid(login) begin User.create(:login => login) if LdapUser.find(login) rescue ActiveLdap::EntryNotFound nil # Don't do anything since we can't find an entry end end protected # Authenticates the user against the LDAP. def valid_ldap_credentials?(password_plaintext) ldap_entry.bind(password_plaintext) ldap_entry.remove_connection true rescue ActiveLdap::AuthenticationError, ActiveLdap::LdapError::UnwillingToPerform false end end
With this, there is no longer a need for the crypted_password and password_salt columns in the users table, so if those columns exist, you’ll have to write a migration to remove them (or at least allow NULL values for them). Now, we modify the UserSession to use our custom methods for looking up users and validating their credentials:
class UserSession < Authlogic::Session::Base find_by_login_method :find_or_create_from_ldap verify_password_method :valid_ldap_credentials? end
With that, we should now be able to log in as a user by providing her uid as the login and the password. LDAP pass-through authentication achieved! There is a downside though: ActiveLdap is not particularly efficient with its queries but this can be mitigated by storing the user’s LDAP entry (the LdapUser object) in the User instance when it is first looked up.
def ldap_entry @ldap_entry ||= LdapUser.find(self.login) end
In a future post, I will extend this further by modeling LDAP groups and bringing in declarative_authorization to implement role-based access control based on LDAP group membership.





Excellent article, very useful. I’ll be waiting for your post on declarative_authorization.
– nachokb
To put:
config.gem “net-ldap”, :lib => false, :version => ‘>=0.0.5′
fails, but when y try to do:
Net::LDAP.new it works.
And when i try to
sudo gem install net-ldap for the 0.0.4 version it said:
ERROR: could not find gem net-ldap locally or in a repository
Only i try to declare in my config.gem that is needed net-ldap 0.0.5 version.
@maxjgon: Yeah, the 0.0.5
net-ldapgem needs to be built from the GitHub repository. If you want to try using 0.0.4 (which might work depending on your particular schema), you actually want to use the command below.sudo gem install ruby-net-ldapAnd change your
config.gemline inconfig/environment.rbaccordingly. Turns out the RubyForge-released gem is calledruby-net-ldapwhile the GitHub-built one is callednet-ldap. This threw me for a bit of a loop.If you need more help with this, fire me an e-mail and I’ll try and help you out as much as I can.
Hi,
I am having a problem with getting any return value from “LdapUser.all”. I am using OpenDS and intend to authenticate against users in a group in it. Any help with OpenDS would really help.
Hi! I’m need two diferents forms to authenticate. Firts without ldap, second within ldap. Any idea?
Best Rails + Authlogic + LDAP solution I’ve found. Thanks for the post. I did find that I needed to use ActiveLdap version 1.0.9, because in 1.1.0+ they added gettext_activerecord which didn’t work for me. This may be because of my use of formtastic.
Thanks for the post.
@Ademola: I have little experience with OpenDS and I’m not sure what the nature of your errors are based on your description. If you still need help with this, send me an e-mail and I’ll see what I can do.
@Javier: I don’t think that’s possible with the method that I’m using because I’m overriding the mechanism by which Authlogic validates the username and password as opposed to providing an entirely different (parallel) method of authentication like Authlogic plugins do.
@Adam: Yeah, at the time I wrote the post, 1.0.9 was the latest version. Now I’m using 1.2.0 and gettext_activerecord does work for me, but I’m not using formtastic. You might also be interested in seeing how I integrated it all with declarative_authorization to get RBAC through LDAP groups: http://github.com/enricob/authlogic_example/tree/ldap-rbac