Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

51 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LDAP Support for Crystal Lang

CI

Installation

Add the dependency to your shard.yml:

dependencies:
  ldap:
    github: spider-gazelle/crystal-ldap

Usage

Connecting and Binding

Passing a TLS context will upgrade the connection using start tls

require "ldap"

host = "ldap.forumsys.com"
port = 389
user = "cn=read-only-admin,dc=example,dc=com"
pass = "password"

# Standard LDAP port with unencrypted socket
socket = TCPSocket.new(host, port)

# Providing a context will upgrade to encrypted comms using start tls (official method)
tls = OpenSSL::SSL::Context::Client.new
tls.verify_mode = OpenSSL::SSL::VerifyMode::NONE

# Bind to the server
client = LDAP::Client.new(socket, tls)
client.authenticate(user, pass)

# Can now perform LDAP operations

To use the non-standard LDAPS (LDAP Secure, commonly known as LDAP over SSL) protocol then pass in a OpenSSL::SSL::Socket::Client directly: LDAP::Client.new(tls_socket)

# LDAPS method
socket = TCPSocket.new(host, port)
tls = OpenSSL::SSL::Context::Client.new
tls.verify_mode = OpenSSL::SSL::VerifyMode::NONE
socket = OpenSSL::SSL::Socket::Client.new(socket, context: tls, sync_close: true, hostname: host)

# Bind to the server
client = LDAP::Client.new(socket)
client.authenticate(user, pass)

# Can now perform LDAP operations

Querying

You can perform search requests

# You can use LDAP string filters directly
client.search(base: "dc=example,dc=com", filter: "(|(uid=einstein)(uid=training))")

# There are options to select particular attributes and limit response sizes
filter = LDAP::Request::Filter.equal("objectClass", "person")
client.search(
  base: "dc=example,dc=com",
  filter: filter,
  size: 6,
  attributes: ["hasSubordinates", "objectClass"]
)

# Filters can be combined using standard operations
filter = (
          LDAP::Request::Filter.equal("objectClass", "person") &
          LDAP::Request::Filter.equal("sn", "training")) |
          LDAP::Request::FilterParser.parse("(uid=einstein)"
         )
client.search(base: "dc=example,dc=com", filter: filter)

A search returns Array(LDAP::Entry). Each Entry exposes its dn separately from its attributes; attribute values are stored as raw Bytes (LDAP octet strings are not necessarily UTF-8):

entries = client.search(base: "dc=example,dc=com", filter: "(uid=einstein)")

entry = entries.first
entry.dn                  # => "uid=einstein,dc=example,dc=com"
entry["cn"]               # => ["Albert Einstein"]   (values decoded as String)
entry["mail"]?            # => ["einstein@ldap.forumsys.com"] or nil if absent
entry.bytes("objectGUID") # => Array(Bytes)          (raw, for binary attributes)
entry.keys                # => ["objectClass", "cn", "sn", "uid", "mail", ...]

A server-side size or time limit raises LDAP::Client::SearchLimitError, which carries the partial entries the server returned before stopping:

begin
  entries = client.search(base: "dc=example,dc=com")
rescue ex : LDAP::Client::SearchLimitError
  partial = ex.entries # incomplete, but usable
end

About

a Crystal lang LDAP client

Resources

Stars

20 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages