Skip to content

Ruby Ruby/LDAP Tutorial

LDAP (Lightweight Directory Access Protocol) is a protocol for accessing and maintaining distributed directory information services. Ruby can interact with LDAP servers using the net-ldap gem, which is a pure Ruby LDAP client library.

The net-ldap gem supports common LDAP operations like connect, bind, search, add, modify, and delete entries. It aims to be compatible with RFCs related to LDAP.

Before using net-ldap, you need to install it. This is typically done using RubyGems:

$ gem install net-ldap

Ensure you have a running LDAP server (e.g., OpenLDAP, Active Directory, or a cloud-based LDAP service) for testing the examples. The examples will assume a local OpenLDAP server, but connection details can be easily adjusted.

Connecting to an LDAP server involves creating a Net::LDAP object and then binding to the directory.

You initialize a Net::LDAP object with server details:

require 'net/ldap'
ldap = Net::LDAP.new host: '127.0.0.1', # Your LDAP server IP or hostname
port: 389, # Standard LDAP port (636 for LDAPS)
auth: {
method: :simple,
username: "cn=admin,dc=example,dc=com", # Your admin DN
password: "password" # Your admin password
}
  • host: The hostname or IP address of the LDAP server.
  • port: The port number for LDAP service (default is 389, or 636 for LDAPS/SSL).
  • auth: A hash specifying the authentication method and credentials. :simple is common.

This creates an object but doesn’t immediately connect or bind.

Binding authenticates your session. net-ldap often performs an implicit bind when an operation is attempted if auth details are provided during initialization. You can also explicitly bind using ldap.bind.

if ldap.bind
# Successfully bound to LDAP server
puts "Authentication successful!"
# ... perform operations ...
else
# Bind failed
puts "Authentication failed!"
puts "LDAP Error Code: #{ldap.get_operation_result.code}"
puts "LDAP Error Message: #{ldap.get_operation_result.message}"
end

It’s good practice to check the result of ldap.bind or any other operation. ldap.get_operation_result provides details about the last operation.

#!/usr/bin/env ruby
require 'net/ldap'
ldap_config = {
host: 'localhost', # Or your LDAP server IP/hostname
port: 389,
auth: {
method: :simple,
username: 'cn=admin,dc=example,dc=com', # Replace with your bind DN
password: 'admin_password' # Replace with your password
}
}
ldap = Net::LDAP.new(ldap_config)
if ldap.bind
puts "Successfully connected and bound to the LDAP server."
# Operations like search, add, modify, delete would go here.
else
puts "Connection or bind failed."
puts "Error code: #{ldap.get_operation_result.code}"
puts "Error message: #{ldap.get_operation_result.message}"
end

To add an entry, you specify its Distinguished Name (DN) and attributes.

dn = "uid=user1,ou=users,dc=example,dc=com"
attributes = {
objectclass: ["top", "person", "inetOrgPerson"],
cn: "User One",
sn: "One",
givenName: "User",
uid: "user1",
mail: "user1@example.com",
userPassword: "new_password"
}
if ldap.add(dn: dn, attributes: attributes)
puts "Entry '#{dn}' added successfully."
else
puts "Failed to add entry '#{dn}'."
puts "Error: #{ldap.get_operation_result.message}"
end

Ensure the objectclass attributes are correct for your LDAP schema and the entry type you are creating.

To modify an entry, specify the DN and an array of operations (add, delete, replace attribute values).

dn_to_modify = "uid=user1,ou=users,dc=example,dc=com"
operations = [
[:replace, :mail, "user.one@example.com"], # Replace existing mail
[:add, :telephoneNumber, "+1234567890"] # Add new attribute
]
if ldap.modify(dn: dn_to_modify, operations: operations)
puts "Entry '#{dn_to_modify}' modified successfully."
else
puts "Failed to modify entry '#{dn_to_modify}'."
puts "Error: #{ldap.get_operation_result.message}"
end

To delete an entry, provide its DN.

dn_to_delete = "uid=user1,ou=users,dc=example,dc=com"
if ldap.delete(dn: dn_to_delete)
puts "Entry '#{dn_to_delete}' deleted successfully."
else
puts "Failed to delete entry '#{dn_to_delete}'."
puts "Error: #{ldap.get_operation_result.message}"
end

Modifying the RDN (Relative Distinguished Name) of an entry is done using ldap.rename. This operation might not be supported by all LDAP servers or might have restrictions.

old_dn = "uid=user1,ou=users,dc=example,dc=com"
new_rdn = "uid=newuser1"
# Optional: new_superior_dn = "ou=new_users,dc=example,dc=com"
# delete_old_rdn defaults to true
if ldap.rename(olddn: old_dn, newrdn: new_rdn, delete_attributes: true)
# The new DN will be uid=newuser1,ou=users,dc=example,dc=com
puts "Entry RDN modified successfully."
else
puts "Failed to modify RDN."
puts "Error: #{ldap.get_operation_result.message}"
end

Searching requires a base DN, scope, filter, and optionally, attributes to retrieve.

base_dn = "ou=users,dc=example,dc=com"
filter = Net::LDAP::Filter.eq("objectclass", "person") # Find all persons
# More complex filter: Net::LDAP::Filter.pres("mail") & Net::LDAP::Filter.eq("sn", "Doe")
attributes_to_fetch = ["cn", "sn", "mail", "uid"]
ldap.search(base: base_dn, filter: filter, attributes: attributes_to_fetch, return_result: false) do |entry|
puts "DN: #{entry.dn}"
entry.each do |attribute, values|
puts " #{attribute}: #{values.join(', ')}"
end
end
# Check for search errors after the block or if return_result: true
result = ldap.get_operation_result
if result.code != 0
puts "Search failed: #{result.message}"
end
  • base: The DN to start the search from.
  • filter: An LDAP filter string or Net::LDAP::Filter object. E.g., "(uid=user1)" or Net::LDAP::Filter.eq("uid", "user1").
  • attributes: An array of attribute names to return. If empty or nil, typically returns all user attributes.
  • scope: Can be Net::LDAP::SearchScope_BaseObject, Net::LDAP::SearchScope_SingleLevel, or Net::LDAP::SearchScope_WholeSubtree (default).
  • return_result: false (default with block) processes entries in a block. If true, ldap.search returns an array of entries.

net-ldap methods usually return true on success or false/nil on failure. After an operation, ldap.get_operation_result provides a Net::LDAP::Result object containing the operation’s code, message, and matched DN.

A common pattern for error checking:

if ldap.some_operation(...)
# Success
else
op_result = ldap.get_operation_result
puts "Operation failed!"
puts "Code: #{op_result.code}"
puts "Message: #{op_result.message}"
# op_result.error_message and op_result.matched_dn might also be useful
end

Net::LDAP::Error can be raised for some critical issues, so begin...rescue Net::LDAP::Error => e can also be used for broader error handling.

For comprehensive details on net-ldap and its features: