Rich Waters

Ext, Javascript, Notes/Domino, Ext.nd, Ruby on Rails

I’ve had this article sitting around in Draft for quite some time now and finally got around to putting some finishing touches on. Needless to say, I’ve jumped onto the Ruby on Rails bandwagon along with a lot of other people, and continue to be amazed as I learn more and more about the framework. Currently I’m in the process of mocking up a new application in Rails that will need to tie back into Notes quite a bit. The first step in this process was to get Notes logins to work in the Rails app which was wasn’t too rough.

Our Domino server was already configured to run LDAP and allowed anonymous access which makes things a little tougher. I had to search around quite a bit and piece together information on how to connect to LDAP via Ruby and how to create a simple authentication system and login page.

To start things off you need the Net::LDAP library, you can download it from the ruby forge site, or just grab it with Gem.

gem install ruby-net-ldap

Next I created a module in the Rails’ /lib folder to hold the LDAP connection and authenticate method.
EDIT 9/20/2007:It seems I wasn’t very clear here, this code below was placed into “ldap_connect.rb” within the lib folder. It is then included into the rails application with the require statement below.

require 'net/ldap' module LDAP def self.authenticate(user,password) if user == "" || password == "" return false end ldap_con = Net::LDAP.new({:host => 'your.dominoserver.com', :port => 389,:auth=>{:method=>:simple,:username => user, :password => password } } ) return true if ldap_con.bind return false end end

This code is pretty simple, its going to first do a reality check to make sure that a name and password were entered at all (necessary because we allow anonymous access), then it will attempt to create (bind) a connection to the ldap server using the username and password entered. Now on to the controller, I let rails generate the shell with a quick

script generate controller login

Instead of starting from scratch I found a nice simple base on the Rails Wiki. Really the only parts to worry about are the call to the LDAP.authenticate function, and the mention up top to skip_before_filter :authenticate (I’ll go into this more in a moment).

require 'ldap_connect' class LoginController < ApplicationController skip_before_filter :authenticate def index end def authenticate if session[:person] = LDAP.authenticate(params[:login][:name], params[:login][:password]) session[:username] = params[:login][:name].capitalize_each if session[:return_to] redirect_to_path(session[:return_to]) session[:return_to] = nil else redirect_to :controller => "inspections" end else flash[:notice] = "Login failed!" redirect_to :action => "index" end end # logout # clears the session def logout session[:person] = nil session[:username] = nil flash[:notice] = "Logged out" redirect_to :action => "index" end end

This is a pretty simple controller, it’s basically passing through the values entered to the authenticate method above and then redirecting the user depending on the result of the authentication. It does have some niceness where it previously stored the page the user was trying to access and redirects them to that page if they successfully login. Something is still missing… we need to tell the application to force people to login if they haven’t already. Since we want this to happen on any page they might go to we place this code in the application.rb controller. A few quick lines will do the trick

class ApplicationController < ActionController::Base before_filter :authenticate filter_parameter_logging "password" protected def authenticate unless session[:person] session[:return_to] = @request.request_uri redirect_to :controller => "login" return false end end end

before_filter will take care of calling authenticate before displaying any page. Authenticate checks to see if they have our session variable set and will redirect them to login if need be. A quick note about the filter_parameter_logging, this is good to have in place otherwise all passwords will be recorded in plain text log files…:P

The final piece needed is a simple view to display the login fields. By default rails is configured to look for the index method of any controller (this can be changed). To keep things simple I made the login page views\login\index.rhtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en"> <head> <title>Login</title> </head> <body> <%= start_form_tag :action => 'authenticate' %> <fieldset> <div class="form-element"> <label for="name">Username </label> <%= text_field 'login', 'name' , {:class=>"text-input"} %> </div> <div class="form-element"> <label for="password">Password </label> <%= password_field 'login', 'password' , {:class=>"text-input"} %> </div> </body> </html>

That’s it… not entirely simple, but really not bad once you get the hang of Rails.

Share and Enjoy:
  • Digg
  • Facebook
  • Google Bookmarks
  • Posterous
  • RSS
  • Twitter
U Comment, I Follow - Heavily moderated, SPAM will be dealt with.

28 Responses to “Using domino logins (LDAP) for a Ruby on Rails app”

  1. hey tried your method …gives an error for ldap_connect ..can you please help me with that.

    raghu

  2. What sort of error are you getting? I think most of that code was copy/paste straight from what I have that’s functional for me.

    Rich Waters

  3. hi.,

    am new to rails., is there any server i need to install for net ldap., or is this gem enough to implement ldap in my application??.,can u pls explain me in detail??? if i need to install or configure anyhthing , how to do that?? please help me…

    Carolin Raji

  4. Hi Carolin,
    It really depends on your setup. My experience specifically has been with hooking up Rails to an already existing LDAP server which is integrated with our Lotus Notes/Domino system. You could certainly tie into another LDAP source, but somewhere there needs to be an LDAP server configured and allowing connections from your Rails application. If you don’t already have an LDAP server up and running you could look into a more simple Rails-only form of authentication or perhaps Open LDAP (http://www.openldap.org/)

    Rich Waters

  5. Hi,
    your code is not accepted by some IDE like Aptana

    it didn’t take your code please copy correct format

    Ravi Vardhan

  6. Hi
    i cannot understand two require statements in two files
    please explain
    require ‘net/ldap’ in lib folder
    and reuire ldap_connect in loginController

    i have got error

    uninitialized constant LoginController::LDAP

    please help on this

    bye

    bewan

  7. Ravi,

    I’m surprised the code isn’t functional, there’s nothing specific that should cause issues with Aptana. Perhaps it is copying and pasting the html so you may need to convert over characters like <

    Rich Waters

  8. Bewan,

    That seems to be an issue I wasn’t extremely clear on. The ‘net/ldap’ require is what pulls in the ruby-net-ldap gem that was required. ‘ldap_connect’ is what I named my module in the lib folder, you may need to adjust this to whatever filename you chose. I’ll make a quick edit above to make sure that section is clear. Not having the correct name there would cause the error you stated.

    Rich Waters

  9. Hi,
    i have followed your steps as it is but i got below error.please respond to my problem

    thank u

    “invalid binding information”

    RAILS_ROOT: script/../config/..
    Application Trace | Framework Trace | Full Trace

    /var/lib/gems/1.8/gems/ruby-net-ldap-0.0.4/lib/net/ldap.rb:1097:in `bind’
    /var/lib/gems/1.8/gems/ruby-net-ldap-0.0.4/lib/net/ldap.rb:701:in `bind’

    Bharath

  10. Hi,
    I have followed your steps as it is but i got below error.please respond to my problem

    thank u

    Net::LDAP::LdapError in LoginController#authenticate

    “invalid binding information�

    RAILS_ROOT: script/../config/..
    Application Trace | Framework Trace | Full Trace

    /var/lib/gems/1.8/gems/ruby-net-ldap-0.0.4/lib/net/ldap.rb:1097:in `bind’
    /var/lib/gems/1.8/gems/ruby-net-ldap-0.0.4/lib/net/ldap.rb:701:in `bind’

    Bharath

  11. Bharath,
    Are you sure that you configured the connection information to your server properly? Also if it is a Notes server, do you have LDAP turned on? It sounds like there’s a problem even connecting to your LDAP server.

    Rich Waters

  12. Hi Rich Waters,
    This is Bharath.Thansks for gave response to my problem.

    But i have used below information to connect LDAP in lib/ldap_connect.rb

    require ‘net/ldap’
    module LDAP
    def self.authenticate(user,password)
    if user == “” ||password == “”
    return false
    end
    ldap_con = Net::LDAP.new({:host =>’192.168.0.12′,:port =>389,
    :auth => {:method=>:simple,:ou=>”People”,:uid=>user,:password=>password}})
    return true if ldap_con.bind
    return false
    end
    end

    Bharath

  13. is there any wrong please suggest me.(Ldap Server is working fine with above data.no problem in LDAP)

    Bharath

  14. i am getting an error showing
    uninitialized constant LoginController::LDAP
    /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:477:in `const_missing’
    app/controllers/login_controller.rb:8:in `authenticate’
    -e:4:in `load’
    -e:4
    could you plz solve my comment
    thanks ,,

    sri

  15. Bharath,
    I don’t know that you can include the ou=>”People” bit in the initial connection. They way I had it functioning we allowed all LDAP users to bind to the server and begin querying. I would check out http://wiki.rubyonrails.org/rails/pages/HowtoAuthenticateWithRubyNetLdap for some more info.

    Rich Waters

  16. Sri,

    not quite sure what the issue is, you’re using a separate controller where I put it in application.rb for my example. What is line 8 in your login_controller?

    Rich Waters

  17. hi Rich,
    i am getting error like this
    NameError in LoginController#authenticate

    uninitialized constant LDAP::NET
    and my line 8 contains
    def authenticate
    if session[:person]=LDAP.authenticate(params[:login][:name],params[:login][:password])
    session[:username]=params[:login][:name].capitalize_each
    if session[:return_to]

    sri

  18. hi,
    how can we know that the application was connected to ldap server or not ,where the code takes place that you mentioned

    sri

  19. hi rich waters,
    and now i am getting error like this
    Net::LDAP::LdapError in LoginController#authenticate

    invalid binding information

    RAILS_ROOT: script/../config/..
    My LDAP server was working fine
    could you suggest me immediately its urgent

    sri

  20. hi Rich waters,
    finally i got connected to server

    but the thing is How to check whether particular account is existing or not ,i mean how to search ant retrieve attributes of existing user accounts

    thank u..

    sri

  21. Sri,
    There has been a lot of interest in this area, I may write some additional future articles about LDAP in Rails.

    Here’s a method that I use to grab a single record and have access to it’s attributes.

    def self.get_user(user)
    ldap = Net::LDAP.new( { :host => “blah”, :port => 389, :base => “” } )

    cn_filter = Net::LDAP::Filter.eq( “cn”, user )
    person = ldap.search( :filter => cn_filter )
    return person[0]
    end

    Rich Waters

  22. Hi Rich,
    Thanks for the great article, it’s very helpful. I haven’t attempted to implement this idea yet but I was wondering if you’ve done any particular ACL checking? For example if you have an application that you wish to allow access only to users in the group “Administrators”, does that information come back in the ldap search?

    Thanks!
    -Irene

    Irene Ros

  23. Irene,

    I have taken this further, and have used groups for a basic role system in an application I’m currently working on. I’ll work on a follow up article and can provide a function to easily test if someone is a member of a specific group.

    Rich Waters

  24. I was able to get LDAP authentication up and running. However, if ldap.bind never seems to return false. If I have valid login info, it will return true sometimes, and if I use invalid info, it just hangs indefinitely.

    Brad

  25. Thanks for the Article Rich,

    Is there a way to make the username param search based on uid instead of DN? I would rather have someone login with a uid instead of their full name… Which seems to be the only thing I can get working.

    Cheers,
    Jay

    Jay

  26. Hi rich i am also getting this error
    invalid binding information/home/mubashir/.gem/ruby/1.8/gems/ruby-net-ldap-0.0.4/lib/net/ldap.rb:1097:in `bind’
    /home/mubashir/.gem/ruby/1.8/gems/ruby-net-ldap-0.0.4/lib/net/ldap.rb:701:in `bind’
    /home/mubashir/mis/lib/ldap_connect.rb:11:in `authenticate’
    /home/mubashir/mis/app/controllers/login_controller.rb:10:in `authenticate’
    can you plz guide me how i can resolve this problem.

    mushi

  27. This code doesn’t work, and even if u by chance get the login screen it doesn’t have a submit button, ruby on the rails is making me very sad and it’s a big dissappiontment

    florian

  28. Well for me the error was simple:
    :uid=>user is false
    :username=>user
    and it works fine.

    Claus-Christian Ude

Leave a Reply