Active Directory authentication with local active record persistance without password
#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