https://github.com/jimlambrt/gldap
  
  
    Build LDAP services w/ Go 
    https://github.com/jimlambrt/gldap
  
go golang ldap ldap-library ldap-server ldap-service ldap-testing
        Last synced: 7 months ago 
        JSON representation
    
Build LDAP services w/ Go
- Host: GitHub
 - URL: https://github.com/jimlambrt/gldap
 - Owner: jimlambrt
 - License: mit
 - Created: 2022-01-11T23:57:45.000Z (almost 4 years ago)
 - Default Branch: main
 - Last Pushed: 2024-10-18T22:33:06.000Z (about 1 year ago)
 - Last Synced: 2025-03-29T02:09:14.166Z (7 months ago)
 - Topics: go, golang, ldap, ldap-library, ldap-server, ldap-service, ldap-testing
 - Language: Go
 - Homepage:
 - Size: 230 KB
 - Stars: 114
 - Watchers: 4
 - Forks: 5
 - Open Issues: 1
 - 
            Metadata Files:
            
- Readme: README.md
 - Contributing: CONTRIBUTING-add-commands.md
 - License: LICENSE
 - Code of conduct: CODE_OF_CONDUCT.md
 - Security: SECURITY.md
 
 
Awesome Lists containing this project
- awesome-go - gldap - gldap provides an ldap server implementation and you provide handlers for its ldap operations. (Networking / Transliteration)
 - zero-alloc-awesome-go - gldap - gldap provides an ldap server implementation and you provide handlers for its ldap operations. (Networking / Transliteration)
 - go-awesome - gldap - LDAP service (Open source library / The Internet)
 - awesome-golang-repositories - gldap
 - awesome-go-extra - gldap - 01-11T23:57:45Z|2022-06-01T13:18:36Z| (Networking / Uncategorized)
 - awesome-go - jimlambrt/gldap
 - awesome-go - jimlambrt/gldap
 
README
          # gldap
[](https://pkg.go.dev/github.com/jimlambrt/gldap)
[](https://raw.githack.com/jimlambrt/gldap/main/coverage/coverage.html)
[](https://goreportcard.com/report/github.com/jimlambrt/gldap)
`gldap` is a framework for building LDAP services.  Among other things, it defines abstractions for:
* `Server`: supports both LDAP and LDAPS (TLS) protocols as well as the StartTLS
  requests. 
* `Request`: represents an LDAP request (bind, search, extended, etc) along with
  the inbound request message. 
* `ResponseWriter`: allows you to compose request responses.
* `Mux`: an ldap request multiplexer. It matches the inbound request against a
  list of registered route handlers. 
* `HandlerFunc`: handlers provided to the Mux which serve individual ldap requests.
Example:
```go
package main
import (
	"context"
	"log"
	"os"
	"os/signal"
	"syscall"
	"github.com/jimlambrt/gldap"
)
func main() {
	// create a new server
	s, err := gldap.NewServer()
	if err != nil {
		log.Fatalf("unable to create server: %s", err.Error())
	}
	// create a router and add a bind handler
	r, err := gldap.NewMux()
	if err != nil {
		log.Fatalf("unable to create router: %s", err.Error())
	}
	r.Bind(bindHandler)
	r.Search(searchHandler)
	s.Router(r)
	go s.Run(":10389") // listen on port 10389
	// stop server gracefully when ctrl-c, sigint or sigterm occurs
	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
	defer stop()
	select {
	case <-ctx.Done():
		log.Printf("\nstopping directory")
		s.Stop()
	}
}
func bindHandler(w *gldap.ResponseWriter, r *gldap.Request) {
	resp := r.NewBindResponse(
		gldap.WithResponseCode(gldap.ResultInvalidCredentials),
	)
	defer func() {
		w.Write(resp)
	}()
	m, err := r.GetSimpleBindMessage()
	if err != nil {
		log.Printf("not a simple bind message: %s", err)
		return
	}
	if m.UserName == "alice" {
		resp.SetResultCode(gldap.ResultSuccess)
		log.Println("bind success")
		return
	}
func searchHandler(w *gldap.ResponseWriter, r *gldap.Request) {
	resp := r.NewSearchDoneResponse()
	defer func() {
		w.Write(resp)
	}()
	m, err := r.GetSearchMessage()
	if err != nil {
		log.Printf("not a search message: %s", err)
		return
	}
	log.Printf("search base dn: %s", m.BaseDN)
	log.Printf("search scope: %d", m.Scope)
	log.Printf("search filter: %s", m.Filter)
	if strings.Contains(m.Filter, "uid=alice") || m.BaseDN == "uid=alice,ou=people,cn=example,dc=org" {
		entry := r.NewSearchResponseEntry(
			"uid=alice,ou=people,cn=example,dc=org",
			gldap.WithAttributes(map[string][]string{
				"objectclass": {"top", "person", "organizationalPerson", "inetOrgPerson"},
				"uid":         {"alice"},
				"cn":          {"alice eve smith"},
				"givenname":   {"alice"},
				"sn":          {"smith"},
				"ou":          {"people"},
				"description": {"friend of Rivest, Shamir and Adleman"},
				"password":    {"{SSHA}U3waGJVC7MgXYc0YQe7xv7sSePuTP8zN"},
			}),
		)
		entry.AddAttribute("email", []string{"alice@example.org"})
		w.Write(entry)
		resp.SetResultCode(gldap.ResultSuccess)
	}
	if m.BaseDN == "ou=people,cn=example,dc=org" {
		entry := r.NewSearchResponseEntry(
			"ou=people,cn=example,dc=org",
			gldap.WithAttributes(map[string][]string{
				"objectclass": {"organizationalUnit"},
				"ou":          {"people"},
			}),
		)
		w.Write(entry)
		resp.SetResultCode(gldap.ResultSuccess)
	}
	return
}
```
## Road map
### Currently supported features:
* `ldap`, `ldaps` and `mTLS` connections
* StartTLS Requests
* Bind Requests
  * Simple Auth (user/pass) 
* Search Requests
* Modify Requests
* Add Requests
* Delete Requests
* Unbind Requests
### Future features
At this point, we may wait until issues are opened before planning new features
given that all the basic LDAP operations are supported. 
## [gldap.testdirectory](testdirectory/README.md)
[](https://pkg.go.dev/github.com/jimlambrt/gldap/testdirectory) 
The `testdirectory` package built using `gldap` which provides an in-memory test
LDAP service with capabilities which make writing tests that depend on an LDAP
service much easier.  
`testdirectory` is also a great working example of how you can use `gldap` to build a custom
ldap server to meet your specific needs.
Example:
```go
// this testdirectory example demonstrates how can start a test directory for 
// your unit tests which will automatically stop when the test is complete. 
func TestExample(t *testing.T) {
	// start a test directory running ldaps on an available free port (defaults)
	// that allows anon binds (a default override)
	td := testdirectory.Start(t,
		testdirectory.WithDefaults(&testdirectory.Defaults{AllowAnonymousBind: true}),
	)
	// create some test new user entries (using defaults for ou, password, etc)
	users := testdirectory.NewUsers(t, []string{"alice", "bob"})
	// set the test directories user entries
	td.SetUsers(users...)
	// INSERT your tests here....
}
```