Active Directory authentication with local active record persistance without password

Tagged with AD active directory LDAP net%2Fldap

Language: Ruby

View as text

#This is the user.rb for a rails system that needs to authenticate to Active Directory. 
#You need LDAP services turned on (usually this is the case by default)
#You will need to check with your AD administrator anyway to get all the connection information. 

#To get started you will need to install the 'ruby-net-ldap' gem
#At your command prompt type: sudo gem install ruby-net-ldap

#See the documentation here: http://rubyfurnace.com/docs/ruby-net-ldap-0.0.4/
#You'll want to start by reading the section on "Net::LDAP" (See link list on the left)

#This code is a drop in replacement for the self.authenticate method in the restful_authentication plugin. 
#After adding the line:  require 'net/ldap' at the top, the only other change 
#is to remove the existing self.authenticate method and replace it with the 2 shown here.
#Optionally, you can remove all the password code that is no longer in use. 
#The exceptions are the "self.encrypt" method and the "encrypt" method 
#which are still in use by the "remember_me_until" method.



#place at the top of the user.rb model
require 'net/ldap'


  ###########################################################################################################
  #LDAP AD Authentication
  def self.authenticate(username,password)
    ldap_con = initialize_ldap_con(username + "@domain.com", password) #passing in the user with the dc attached... you should also be able to use the full CN
    treebase = "DC=domain,DC=com" 
    user_filter = Net::LDAP::Filter.eq( "sAMAccountName", username ) 
    # op_filter = Net::LDAP::Filter.eq( "objectClass", "organizationalPerson" ) #could be implemented at a future point in time

    #ldap will automatically bind when trying to preform a search or modification, so we don't call .bind here. .bind is only if you want to just return true or false, but we want to look up some attributes!
    if ldap_con.search( :base => treebase, :filter => user_filter, :attributes=> ['dn','sAMAccountName','displayname','SN','givenName']) do |ad_user|      
        #try to find the user locally and if they aren't there then create them. If they are there, update their name and email fields locally
        local_user = find_by_login(ad_user.samaccountname.to_s)
        if !local_user
          local_user = User.new(:login => ad_user.samaccountname.to_s, :name => ad_user.displayname.to_s, :email => ad_user.givenname.to_s + '.' + ad_user.sn.to_s + '@domain.com')
          local_user.save false #pass false to skip validations
          return local_user
        else
          #this is inefficient since each time we login I'm updating some fields, but I want the local db's name and email fields to stay sync'd.
          local_user.update_attributes(:name => ad_user.displayname.to_s, :email => ad_user.givenname.to_s + '.' + ad_user.sn.to_s + '@domain.com')
          local_user.save false
          return local_user
        end
      end            
    else
      return nil #they didn't authenticate to AD
    end
  end  
    
  
  def self.initialize_ldap_con(user_name, password)
    ldap = Net::LDAP.new
    ldap.host = "your_ldap_server_IP"
    ldap.port = 636 #required for SSL connections, 389 is the default plain text port
    ldap.encryption :simple_tls #also required to tell Net:LDAP that we want SSL
    ldap.auth "#{user_name}","#{password}"
    ldap #explicitly return the ldap connection object
  end
  ###########################################################################################################
Original snippet written by Jon Kinney
Last updated at 23:13 PM on Sep 09, 2008

SnippetStash costs money to host and develop. The service is free for everyone to use
but if you found it useful please consider making a small donation.