Git Product home page Git Product logo

gldap's Introduction

gldap

Go Reference Go Coverage Go Report Card


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:

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{"[email protected]"})
		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.


Go Reference

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:

// 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....
}

gldap's People

Contributors

jimlambrt avatar xunleii avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

gldap's Issues

Support Bind request controls

Is your feature request related to a problem? Please describe.

Currently, there's no way to receive Controls on inbound Bind requests or send Controls back with Bind responses.

Describe the solution you'd like

Add support for Controls on both Bind requests and responses.

TLS Version check for every request

Hi Jim,
i am working on a project to create an ldap server. i am using your modules to run a startTLS handshake on port 389. i want my server to run on a particular version. conn variable is private hence the TLS version is not reachable to be checked.

request.go:
type Request struct { // ID is the request number for a specific connection. Every connection has // its own request counter which starts at 1. ID int TLSVersion int // conn is needed this for cancellation among other things. conn *conn message Message routeOp routeOperation extendedName ExtendedOperationName }

a new variable is added - TLSVersion : a public data member
use case: from the request sent by the client, if the server wants to support a particular version say only TLS1.1, we can check the version by doing a r.TLSVersion which sends an integer value ranging from 769 - 772 based on TLS protocol version. This helps the server to lock a particular version and unnecessary requests could be ignored.

it will be great if the code is looped into any of the existing branches.

Thanks,
Rohith

Support for mapping user after a Bind request

Is your feature request related to a problem? Please describe.
I am creating an LDAP server using gldap and I can't figure out how to ensure that a user performing a search operation has been successfully authenticated just before.

Describe the solution you'd like
It might be helpful to provide at least the connection ID on each message to know "who" (i.e. which connection) made the request.

Describe alternatives you've considered
I tried to use any accessible information on the request message to bypass that, but couldn't find anything that would help.

intermittent server.Stop() error

Describe the bug
Occasionally, we see the following error on shutdown:

2023-05-08T12:14:01.550-0400 [ERROR] testdirectory-default-logger: error stopping directory: %s: op="testdirectory.(Directory).Stop" err="gldap.(Server).Stop: no listener: invalid state"

Expected behavior

If the listener is already closed or nil, we shouldn't try to close it.

Support search request Controls

Is your feature request related to a problem? Please describe.

Currently, there's no way to receive Controls on inbound Search requests or send Controls back with Search responses.

Describe the solution you'd like

Add support for Controls on both Search requests and responses.

Support for both LDAP and LDAP3 clients

Is your feature request related to a problem? Please describe.
Currently, we only support binding with an LDAP3 client and clients using the previous version of the protocol are unable to bind.

Describe the solution you'd like
Support both LDAP and LDAP3 clients but allow the developer an option to only support LDAP3 if they wish via a new Option when creating a NewServer. This new WithRequiredVersion option will have to be passed along from the Server to each new conn and then to each new request, so it can be enforced when the new message is created.

Support for attach and fetch custom data with connection

Is your feature request related to a problem? Please describe.
Currently, in order to maintain connection session data, we can use Request.GetConnectionID() to build an extra session table. Normally the session will be cleared when an unbind request is received. But when the connection is closed without receiving an unbind request, the related session cannot be correctly cleared.

Describe the solution you'd like
Provide methods to attach and fetch custom data with each connection:

type conn struct {
        ...
	extra interface{}
}

func (r *Request) SetConnectionExtra(value interface{}) {
	r.conn.extra = value
}
func (r *Request) GetConnectionExtra() interface{} {
	return r.conn.extra
}

Support for Unbind

Is your feature request related to a problem? Please describe.
Currently, there's no way to unbind from a server. Even if you send an unbind request, the server will continue to serve requests for the connection

Describe the solution you'd like
Add support for an Unbind request and stop serving requests for any connection that receives an unbind request.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.