ldap
Version, currently 1.0.03 versions
- 1.0.0latestJul 16, 2026
- 0.9.1not indexedJul 16, 2026
- 0.9.0not indexedJul 16, 2026
github.com/spider-gazelle/crystal-ldap
a Crystal lang LDAP client
20 stars
0 dependents
License: MIT
Installation
# Add this to your shard.yml
dependencies:
ldap:
github: spider-gazelle/crystal-ldap
version: ~> 1.0.0Then run:
shards installshard.yml
- Crystal
>= 1.2.0
Dependencies
Runtime Dependencies
Development Dependencies
- ameba*github: veelenga/amebadev
README
# LDAP Support for Crystal Lang
[](https://github.com/spider-gazelle/crystal-ldap/actions/workflows/ci.yml)
## Installation
Add the dependency to your `shard.yml`:
```yaml
dependencies:
ldap:
github: spider-gazelle/crystal-ldap
```
## Usage
### Connecting and Binding
Passing a TLS context will upgrade the connection using [start tls](https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol#StartTLS)
```crystal
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)`
```crystal
# 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
```crystal
# 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):
```crystal
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:
```crystal
begin
entries = client.search(base: "dc=example,dc=com")
rescue ex : LDAP::Client::SearchLimitError
partial = ex.entries # incomplete, but usable
end
```
Documentation
Built from the current release. The first visit to a release nobody has asked for starts its build.
Links
This release
- Version
1.0.0- Tagged
- Jul 16, 2026
- Commit
3cae787386a7- Crystal
>= 1.2.0- Indexed
- yes
Dependents
No indexed shard depends on this one yet.
Repository
github.com/spider-gazelle/crystal-ldap
Metadata
- Created
- Aug 12, 2026
- Updated
- Aug 12, 2026
- Synced
- Aug 12, 2026
- Versions
- 3