A simple Go library providing an easy-to-use wrapper around go-ldap/ldap/v3 for common LDAP and Active Directory operations.
This package was extracted from netresearch/raybeam to provide a standalone, reusable LDAP client library.
Working with go-ldap/ldap/v3 directly can be challenging for common LDAP operations. This library solves the pain points you'll encounter:
❌ Complex Connection Management - Manual connection pooling, health checks, and retry logic ❌ Manual DN Construction - Error-prone string building with security risks (DN injection) ❌ No Caching - Repeated LDAP queries for the same data slow down applications ❌ Verbose Error Handling - Generic LDAP errors without context or specific types ❌ AD vs OpenLDAP Differences - Different APIs and attributes require separate code paths ❌ Security Pitfalls - Easy to introduce vulnerabilities without proper input validation ❌ Boilerplate Code - Simple operations require dozens of lines of setup and teardown
✅ Automatic Connection Pooling - Built-in connection management with health checks and auto-retry
✅ Safe DN Handling - Automatic escaping and validation prevents injection attacks
✅ Built-in Caching - Intelligent caching layer reduces LDAP server load
✅ Comprehensive Error Types - Specific errors like ErrUserNotFound
with detailed context
✅ Unified API - Same methods work seamlessly with Active Directory and OpenLDAP
✅ Security by Default - Input validation, proper escaping, and secure connection handling
✅ Simple API - Common operations in just a few lines of code
Raw go-ldap: Finding and authenticating a user (50+ lines)
import "github.com/go-ldap/ldap/v3"
// Connect and bind
conn, err := ldap.DialURL("ldaps://ldap.example.com:636")
if err != nil {
return err
}
defer conn.Close()
err = conn.Bind("cn=admin,dc=example,dc=com", "password")
if err != nil {
return err
}
// Search for user (manual filter construction)
searchReq := ldap.NewSearchRequest(
"dc=example,dc=com",
ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases,
0, 0, false,
fmt.Sprintf("(&(objectClass=user)(sAMAccountName=%s))",
ldap.EscapeFilter(username)), // Manual escaping required
[]string{"dn", "cn", "mail"},
nil,
)
sr, err := conn.Search(searchReq)
if err != nil {
return err
}
if len(sr.Entries) == 0 {
return errors.New("user not found") // Generic error
}
userDN := sr.Entries[0].DN
// Authenticate user (separate bind operation)
err = conn.Bind(userDN, password)
if err != nil {
return err
}
// ... additional validation and error handling
Simple LDAP Go: Same operation (3 lines)
import ldap "github.com/netresearch/simple-ldap-go"
client, _ := ldap.New(config, "cn=admin,dc=example,dc=com", "password")
user, err := client.CheckPasswordForSAMAccountName("username", "password")
// Returns structured User object with automatic error handling
- 🔐 User Authentication - One-line password verification with automatic DN resolution and secure binding
- 👥 User Management - Type-safe user operations with automatic attribute mapping and validation
- 🏢 Group Operations - Simplified group queries and membership management across AD and OpenLDAP
- 💻 Computer Management - Active Directory computer object support with automatic schema detection
- 🔑 Password Management - Secure password changes with automatic LDAPS enforcement and policy validation
- 🛡️ Active Directory Support - Native AD features like SAMAccountName, UPN, and nested group resolution
- ⚡ Connection Pooling - Automatic connection management with health checks, retry logic, and resource cleanup
- 🎯 Smart Caching - Configurable caching layer that reduces server load for repeated queries
- 🔒 Security by Default - Built-in DN injection protection, input validation, and secure connection handling
- 📊 Structured Errors - Context-rich error types that make debugging and error handling straightforward
- 🌐 Context Support - Full
context.Context
integration for timeouts, cancellation, and request tracing - 📝 Structured Logging - Integrated slog support for comprehensive operational visibility
go get github.com/netresearch/simple-ldap-go
package main
import (
"fmt"
"log"
ldap "github.com/netresearch/simple-ldap-go"
)
func main() {
// Configure LDAP connection
config := ldap.Config{
Server: "ldaps://ldap.example.com:636",
BaseDN: "dc=example,dc=com",
IsActiveDirectory: true, // Set to false for generic LDAP
}
// Create client with service account credentials
client, err := ldap.New(config, "cn=admin,dc=example,dc=com", "password")
if err != nil {
log.Fatal(err)
}
// Authenticate a user
user, err := client.CheckPasswordForSAMAccountName("username", "password")
if err != nil {
log.Printf("Authentication failed: %v", err)
return
}
fmt.Printf("Welcome, %s!\n", user.CN())
}
Comprehensive examples are available in the examples directory:
- Basic Usage - Finding users, groups, and computers
- Authentication - User authentication and password changes
- User Management - Creating, updating, and managing users
Config
- LDAP server configurationLDAP
- Main client for LDAP operationsUser
- Represents an LDAP user with common attributesGroup
- Represents an LDAP group with member informationComputer
- Represents a computer object (Active Directory)
// Basic client creation
client, err := ldap.New(config, username, password)
// Enhanced client with connection pooling and caching
client, err := ldap.NewHighPerformanceClient(config, username, password)
// Or with custom configuration:
client, err := ldap.NewCachedClient(config, username, password, 1000, 5*time.Minute)
// Convenience constructors
client, err := ldap.NewHighPerformanceClient(config, username, password)
client, err := ldap.NewCachedClient(config, username, password, 1000, 5*time.Minute)
// User authentication
user, err := client.CheckPasswordForSAMAccountName("jdoe", "password")
// Find users (standard methods)
user, err := client.FindUserBySAMAccountName("jdoe")
users, err := client.FindUsers()
// Find users (caching is transparent if enabled in config)
user, err := client.FindUserBySAMAccountNameContext(ctx, "jdoe")
// Caching happens automatically if config.EnableCache is true
// User management
err := client.CreateUser(fullUser, "ou=Users,dc=example,dc=com")
err := client.DeleteUser("cn=John Doe,ou=Users,dc=example,dc=com")
// Group operations (caching is transparent if enabled)
group, err := client.FindGroupByDNContext(ctx, "cn=Admins,dc=example,dc=com")
// Caching happens automatically if config.EnableCache is true
err := client.AddUserToGroup(userDN, groupDN)
See the Go Reference for complete API documentation.
config := ldap.Config{
Server: "ldap://ldap.example.com:389",
BaseDN: "dc=example,dc=com",
IsActiveDirectory: false,
}
config := ldap.Config{
Server: "ldaps://ad.example.com:636", // LDAPS recommended
BaseDN: "dc=example,dc=com",
IsActiveDirectory: true, // Enables AD-specific features
}
Enable optimization features using configuration flags:
config := ldap.Config{
Server: "ldaps://ad.example.com:636",
BaseDN: "dc=example,dc=com",
IsActiveDirectory: true,
// Performance optimizations
EnableOptimizations: true, // Enable all optimizations
EnableCache: true, // Enable caching separately
EnableMetrics: true, // Enable performance metrics
EnableBulkOps: true, // Enable bulk operations
}
Or configure specific features:
// High-performance client with all optimizations
client, err := ldap.NewHighPerformanceClient(config, username, password)
// Custom configuration with specific features
// Use config flags to enable features:
config.EnableOptimizations = true // Enable all optimizations
config.EnableCache = true // Enable caching
config.EnableMetrics = true // Enable metrics
config.EnableBulkOps = true // Enable bulk operations
// Then create client with convenience constructors:
client, err := ldap.NewHighPerformanceClient(config, username, password)
// Or:
client, err := ldap.NewCachedClient(config, username, password, 1000, 5*time.Minute)
// Or:
client, err := ldap.NewPooledClient(config, username, password, 20)
- ✅ Use LDAPS (TLS encryption) in production environments
- ✅ Use service accounts with minimal required permissions
- ✅ Store credentials securely using environment variables or key management
- ✅ Validate certificates in production deployments
⚠️ Password changes require LDAPS when using Active Directory
The library provides specific error types for common scenarios:
// Check for specific errors
_, err := client.FindUserBySAMAccountName("username")
if err == ldap.ErrUserNotFound {
// Handle user not found
} else if err != nil {
// Handle other errors
}
Available error types:
ErrUserNotFound
- User lookup failedErrGroupNotFound
- Group lookup failedErrComputerNotFound
- Computer lookup failedErrSAMAccountNameDuplicated
- Account name already existsErrMailDuplicated
- Email address already existsErrActiveDirectoryMustBeLDAPS
- LDAPS required for AD operations
- Go 1.23.0 or later
- Access to an LDAP server (OpenLDAP, Active Directory, etc.)
- Appropriate credentials and permissions for desired operations
Tests require a live LDAP server. Set the following environment variables:
export LDAP_SERVER="ldaps://your-server:636"
export LDAP_BASE_DN="dc=example,dc=com"
export LDAP_READ_USER="cn=service,dc=example,dc=com"
export LDAP_READ_PASSWORD="password"
Then run tests:
go test -v ./...
This package is licensed under the MIT License. See the included LICENSE file for details.
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Follow Conventional Commits for commit messages
- Use
gofmt
for code formatting - Add tests for new functionality
- Submit a pull request
- go-ldap/ldap - The underlying LDAP library
- netresearch/raybeam - Original project this was extracted from