Ruby on Rails
HowtoAuthenticateViaLdap

This page needs wiki gardening.
It needs more than gardening; 98 percent of information here is outdated and invalid now.

While you’re starting the lawnmower, could you at least give simple pointers to how this thing is done currently?

Using the ActiveLDAP library, it’s relatively easy to set up your Rails app to authenticate via LDAP. Of course, you’ll need to have ActiveLDAP installed on your system, which you can get via RubyGems:

  gem install ruby-activeldap

ActiveLDAP requires the Ruby-LDAP extension library which can be downloaded here.

It also requires the ‘log4r’ rubygem which you will be prompted to install as a dependency.

I was having issues with log4r not loading so I had to alter the require line in lib/activeldap/base.rb to use the log4r rubygem per the log4r documentation.

So instead of

  require 'log4r'

You have

  require 'rubygems'
  require_gem 'log4r'

In order to get this working, I actually had to unpack the gem once it was installed and move the contents of its “lib” folder inside my app’s “lib”. I don’t know if that’s the easiest way to do it, but it worked for me.

Example: You could do something like the following, from your application root:


cd lib
gem unpack ruby-activeldap
cp -a ruby-activeldap-<version>/lib/* .
rm -rf ruby-activeldap-<version>

Once you have ActiveLDAP installed on your system, the next step is to add it to the environment in your app. Add the following line somewhere in your config/environment.rb or config/root.rb (depending on your version of Rails):

  require 'active_ldap'

Now, create a model (File app/models/ldap_user.rb) for your LDAP-user, and define a method for authenticating against the LDAP server:

  class LdapUser < ActiveLdap:Base
    ldap_mapping :prefix => "cn=users"
    def self.login(username, password)
      ActiveLdap::Base.connect \
        :host => "ldap.yourserver.com",
        :port => 389,
        :base => "dc=your,dc=base",
        :bind_format => "uid=#{username},cn=users,dc=your,dc=ldap,dc=binding,dc=config,dc=com",
        :password_block => Proc.new { password },
        :allow_anonymous => false
      
      return true
    rescue ActiveLdap::AuthenticationError
      return false
    ensure
      ActiveLdap::Base.close
    end
  end

In this case, the method takes the user’s login name and password as parameters, and uses them to attempt to connect to the LDAP server. If the login succeeds, it closes the connection and returns true. If it fails, it catches the error and returns false.

Now you have an LDAP-based login method that you can build into your controllers. Most of the rest of my code was based on the HowtoAuthenticate page, which gives a good overview of how to protect your controllers. The only other difference with my code is that I used cookies rather than session variables so my users could stay logged in longer. :). Do not use cookies, this makes your site vulnerable to attack.

For what it’s worth, here’s how my controllers look.

Application:


# The filters added to this controller will be run for all controllers in the application.
# Likewise will all the methods added be available for all controllers.
class ApplicationController < ActionController::Base

protected def authenticate #changed from cookie to session unless session[:login] @session[:return_to] = @request.request_uri redirect_to :controller => “login” return false end end end

Concern: I’m not an expert, but isn’t using the presence of the plaintext username in a cookie as the indicator of authenticated state vulnerable to dishonest cookies? Seems like storing it in the session hash would be secure. Fixed

Login Controller:


class LoginController < ApplicationController
def index
render :action => “login”
end

def login # the actual login form if @params[:login] @values = @params[:login] if LdapUser.login(@values[:un], @values[:pw]) cookies[:login] = {:value => @values[:un], :expires => Time.now+31536000} else flash[:notice] = “Incorrect!” end else flash[:notice] = “no params?” end redirect_to @session[:return_to] end end

Once you have all that in place, all you need to do is add a call to before_filter to the top of each controller you want to password protect, like so:

  class ProtectedController < ApplicationController
    before_filter :authenticate
  end

That’s it!


note: There is now a pure ruby LDAP library that is much more simple to use than activeldap. See HowtoAuthenticateWithRubyNetLdap.


Great!
Q: So, what does your login.rhtml look like?

A: Here’s a basic example that works for me (LeahCunningham):


        <div class='portlet'>
                <table border='0' cellspacing='0' cellpadding='5'>
                        <tr>
                                <caption>
                                        Authentication

<%= form_tag :action => “login” %> User <%= text_field(:login, :un) %> Password <%= password_field(:login, :pw) %> <%= end_form_tag %>

begin 2006-04-08 DelayedGreen

I have some problems with this authentication method:

  1. It seems to me to be entirely vulnerable to a user generated login cookie — better to use session variables. Fixed
  2. Its use of ActiveLDAP seems to preclude any other use of ActiveLDAP in your application — I think any Rails app can only maintain one directory server connection at a time through ActiveLDAP, so a login like this interrupts everything else using ActiveLDAP
  3. It’s doing way too much with LDAP. All you really want to do is a test bind; you don’t want to pull a bunch of schema and create myriad objects that you’ll never use.

Forunately, ruby/ldap can maintain multiple connections, and to mutiple servers. No need to use ActiveLDAP for this simple task.

end 2006-04-08 DelayedGreen

BEGIN 8/30/06 paul@nelkin.net

I used the following for my LDAP Authentication (less than 10 Lines of Code!!)


require 'ldap'

ldap_host = ‘mail.host.com’
ldap_port = 389

begin
ldap_conn = LDAP::Conn.new(“mail.host.com”, 389)
ldap_conn.set_option( LDAP::LDAP_OPT_PROTOCOL_VERSION, 3 )
pass=“yourpassword”
ldap_conn.bind( “uid=youruserid,cn=users,dc=yourbase,dc=com”, pass )
end

Of course for uid and pass you would specify either a specific username and password, or define a variable.
**END 8/30/06


Questions

  1. Q. Where does this “10 lines of code” fit into the above?

Answer:

It doesn’t. It uses only Ruby’s built in ldap support. If all you wish to do is authenticate login
using LDAP, active_ldap is overkill, as all you really
need to do is some variation of the above.


  1. Q. Is there a better way to install/get ActiveLDAP and Ruby-LDAP?

Answer:

No Answer Yet.



Not sure what you mean by an easier way. If you are familiar with RoR this shouldn’t be too difficult. Just use paul@nelkin.net code, it worked for me!

eric-at-erichayes-dot-net note:
With ActiveLDAP 0.8.3.1 I had to require with an underscore:


require ‘active_ldap’

Dear Guys,
I tried this example,but its not working.It throws an error called
“undefined method `close’ for ActiveLdap::Base:Class”
I would like to know the solution for this error.And One more thing is ,I could not install “ldap-0.9.5-mswin32.gem”
Can anybody help me to ge this work done.
Regards
Hussain

Updated on October 20, 2008 06:55 by Anonymous Coward (212.226.87.142)