Skip to content

Instantly share code, notes, and snippets.

@vesan
Forked from jeffjohnson9046/ruby-ldap-sample.rb
Created November 14, 2019 14:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vesan/0a2201ac3ecfee906864a503033d08a5 to your computer and use it in GitHub Desktop.
Save vesan/0a2201ac3ecfee906864a503033d08a5 to your computer and use it in GitHub Desktop.
Some VERY basic LDAP interaction in Ruby using Net::LDAP.
#######################################################################################################################
# This Gist is some crib notes/tests/practice/whatever for talking to Active Directory via LDAP. The (surprisingly
# helpful) documentation for Net::LDAP can be found here: http://net-ldap.rubyforge.org/Net/LDAP.html
#######################################################################################################################
require 'rubygems'
require 'net/ldap'
#######################################################################################################################
# HELPER/UTILITY METHOD
# This method interprets the response/return code from an LDAP bind operation (bind, search, add, modify, rename,
# delete). This method isn't necessarily complete, but it's a good starting point for handling the response codes
# from an LDAP bind operation.
#
# Additional details for the get_operation_result method can be found here:
# http://net-ldap.rubyforge.org/Net/LDAP.html#method-i-get_operation_result
#######################################################################################################################
def get_ldap_response(ldap)
msg = "Response Code: #{ ldap.get_operation_result.code }, Message: #{ ldap.get_operation_result.message }"
raise msg unless ldap.get_operation_result.code == 0
end
#######################################################################################################################
# SET UP LDAP CONNECTION
# Setting up a connection to the LDAP server using .new() does not actually send any network traffic to the LDAP
# server. When you call an operation on ldap (e.g. add or search), .bind is called implicitly. *That's* when the
# connection is made to the LDAP server. This means that each operation called on the ldap object will create its own
# network connection to the LDAP server.
#######################################################################################################################
ldap = Net::LDAP.new :host => # your LDAP host name or IP goes here,
:port => # your LDAP host port goes here,
:encryption => :simple_tls,
:base => # the base of your AD tree goes here,
:auth => {
:method => :simple,
:username => # a user w/sufficient privileges to read from AD goes here,
:password => # the user's password goes here
}
#######################################################################################################################
# ALTERNATIVE FOR OPENING LDAP CONNECTION
# Instead of using .new, you can call .open. Within .open's code block, you can perform whatever LDAP operations you
# need in the context of a single network connection.
#######################################################################################################################
host = # your LDAP host name or IP goes here
port = # your LDAP host port goes here
base = # the base of your AD tree goes here
credentials = {
:method => :simple,
:username => # a user w/sufficient privileges to read from AD goes here,
:password => # the user's password goes here
}
Net::LDAP.open(:host => host, :port => port, :encryption => :simple_tls, :base => base, :auth => credentials) do |ldap|
# Do all your LDAP stuff here...
end
#######################################################################################################################
# SOME SIMPLE LDAP SEARCHES
#######################################################################################################################
# GET THE DISPLAY NAME AND E-MAIL ADDRESS FOR A SINGLE USER
search_param = # the AD account goes here
result_attrs = ["sAMAccountName", "displayName", "mail"] # Whatever you want to bring back in your result set goes here
# Build filter
search_filter = Net::LDAP::Filter.eq("sAMAccountName", search_param)
# Execute search
ldap.search(:filter => search_filter, :attributes => result_attrs) { |item|
puts "#{item.sAMAccountName.first}: #{item.displayName.first} (#{item.mail.first})"
}
get_ldap_response(ldap)
# ---------------------------------------------------------------------------------------------------------------------
# GET THE DISPLAY NAME AND E-MAIL ADDRESS FOR AN E-MAIL DISTRIBUTION LIST
search_param = # the name of the distribution list you're looking for goes here
result_attrs = ["sAMAccountName", "displayName", "mail"] # Whatever you want to bring back in your result set goes here
# Build filter
search_filter = Net::LDAP::Filter.eq("sAMAccountName", search_param)
group_filter = Net::LDAP::Filter.eq("objectClass", "group")
composite_filter = Net::LDAP::Filter.join(search_filter, group_filter)
# Execute search
ldap.search(:filter => composite_filter, :attributes => result_attrs) { |item|
puts "#{item.sAMAccountName.first}: #{item.displayName.first} (#{item.mail.first})"
}
get_ldap_response(ldap)
# ---------------------------------------------------------------------------------------------------------------------
# GET THE MEMBERS OF AN E-MAIL DISTRIBUTION LIST
search_param = # the name of the distribution list you're looking for goes here
result_attrs = ["sAMAccountName", "displayName", "mail", "member"]
# Build filter
search_filter = Net::LDAP::Filter.eq("sAMAccountName", search_param)
group_filter = Net::LDAP::Filter.eq("objectClass", "group")
composite_filter = Net::LDAP::Filter.join(search_filter, group_filter)
# Execute search, extracting the AD account name from each member of the distribution list
ldap.search(:filter => composite_filter, :attributes => result_attrs) do |item|
puts "#{item.sAMAccountName.first}: #{item.displayName.first} (#{item.mail.first})"
item.member.map { |m| puts "\taccount: #{m.match(/(?<=\().+?(?=\))/)}" }
end
get_ldap_response(ldap)
# ---------------------------------------------------------------------------------------------------------------------
# GET THE DISPLAY NAME AND E-MAIL ADDRESS FOR ALL E-MAIL DISTRIBUTION LISTS
# Build filter
# This stackoverflow article was a HUGE help: http://stackoverflow.com/questions/6434752/better-way-to-query-an-ldap-users-via-ruby-net-ldap
group_filter = Net::LDAP::Filter.eq("objectClass", "group")
proxy_address_filter = Net::LDAP::Filter.eq("proxyAddresses", "*")
composite_filter = Net::LDAP::Filter.join(group_filter, proxy_address_filter)
# Execute search
ldap.search(:filter => composite_filter, :attributes => result_attrs) { |item|
puts "#{item.sAMAccountName.first}: #{item.mail.first}"
}
get_ldap_response(ldap)
#######################################################################################################################
# LDAP FILTER EXAMPLES
#######################################################################################################################
# CONSTRUCT AN OR FILTER WITH SEVERAL EQUALS
# If you come across a situation where you need to search LDAP for this == x | this == y | this == z, there isn't an
# easy way to deal with it. The Filter.intersect method takes two arguments, but that isn't enough (because we have
# 3 "OR" conditions). Fortunately, Net::LDAP::Filter has a .construct method that will build a valid query string for
# us (with a little help):
names = ["lstarr", "barf", "dmatrix", "pvespa", "yogurt"]
filters = names.map { |name| Net::LDAP::Filter.eq("sAMAccountName", name) }
search_filter = Net::LDAP::Filter.construct("(|#{ filters.join("") })")
# search_filter => (|(|(|(|(sAMAccountName=lstarr)(sAMAccountName=barf))(sAMAccountName=dmatrix))(sAMAccountName=pvespa))(sAMAccountName=yogurt))
# Ugly, probabaly inefficient, but it'll work. Now we can do this:
emails = []
ldap.search(:filter => search_filter, :attributes => ["mail"], :return_result => false) do |result|
emails << (result.mail.is_a?(Array) ? result.mail.first : result.mail)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment