Giter Club home page Giter Club logo

ldapserver's Introduction

ldapserver

A LDAPv3 server framework for custom integrations or full-blown LDAP servers, with no external dependencies. Focus on the logic of your integration and forget the low-level details.

go get github.com/merlinz01/ldapserver

Why another LDAP library?

I needed to integrate an LDAP-compatible web app with my website database's stored login information. I didn't want to try to remotely manipulate an off-the-shelf LDAP server from my website's code, so a custom LDAP server was what I was looking for. The existing LDAP server frameworks I found threw obscure errors when I tested them. Being suspicious of the security implications of such errors, I decided to write a new framework, specifically focused on enabling the building of custom integrations.

Usage

This package provides an interface similar to that of net/http. See test/main.go for a working demo implementation.

Create a handler

Create an object implementing the Handler interface. The recommended way to do this is to define a struct that inherits the BaseHandler type, which provides default handling for all methods, and also handles StartTLS extended requests.

type MyHandler struct {
    ldapserver.BaseHandler
}
handler := &MyHandler{}

Create a LDAP server

Create a LDAPServer object using NewLDAPServer().

server := ldapserver.NewLDAPServer(handler)

Set up TLS

Provide a key pair to the server using SetupTLS().

err := server.SetupTLS("cert.pem", "key.pem")
if err != nil {
    log.Println("Error setting up TLS:", err)
    return
}

Or you can set/modify the server's TLSConfig field for more specific configuration. The server's TLSConfig must not be nil if you want to support StartTLS or initial TLS.

Start the server

Use ListenAndServe() for a ldap:// server, or ListenAndServeTLS() for a ldaps:// server.

server.ListenAndServe(":389")
server.ListenAndServeTLS(":636")

Shut down the server

If you need to shut down the server gracefully, call its Shutdown() method.

server.Shutdown()

Implementing LDAP operations

To enable more functionality, define your own methods on the handler.

func (h *MyHandler) Bind(conn *ldapserver.Conn, msg *ldapserver.Message, req *ldapserver.BindRequest) {
    // Put your authentication logic here
    result := &ldapserver.BindResponse{}
    result.ResultCode = ldapserver.LDAPResultSuccess
    conn.SendResult(result)
}

Extended operations

The BaseHandler struct handles the StartTLS extended operation. If you want to handle other extended operations, define your own Extended() method. Use a switch statement to determine which extended operation to use. For requests you do not handle, simply pass the function arguments on to the BaseHandler's method, which handles StartTLS and unsupported requests.

func (h *MyHandler) Extended(conn *ldapserver.Conn, msg *ldapserver.Message, req *ldapserver.ExtendedRequest) {
	switch req.Name {
	case ldapserver.OIDPasswordModify:
		log.Println("Modify password")
		// Put your password modify code here
	default:
		h.BaseHandler.Extended(conn, msg, req)
	}
}

Returning results

To return a result, create a Result struct with the desired ResultCode. For error results, you should also set the DiagnosticMessage field so that the user knows what sort of error occurred. Pass the result along with the appropriate response type code to the connection's SendResult method.

The Bind and Extended requests have their own response types, BindResponse and ExtendedResponse, that include a Result struct. These, as well as the SearchResultEntry and SearchResultReference structs, can also be passed to Conn.SendResult().

The library defines the result codes in RFC4511 with a Result prefix, e.g. ResultNoSuchObject. These constants have an AsResult() method that returns a pointer to a Result struct with the given DiagnosticMessage field that can be passed directly to Conn.SendResult().

Operation cancellation

To support cancellation of an operation, the following method is recommended. See test/main.go for an example.

Add a map and an accompanying mutex to your handler's struct.

type MyHandler struct {
    ...
    abandonment      map[ldapserver.MessageID]bool // Don't forget to initialize the map!
    abandonmentMutex sync.Mutex
}

At the beginning of an cancelable method, put a flag in the abandonment map to indicate that the operation can be canceled.

h.abandonment[msg.MessageID] = false // i.e. not cancelled but may be
// Remove the flag when done
defer func() {
    t.abandonmentLock.Lock()
    delete(t.abandonment, msg.MessageID)
    t.abandonmentLock.Unlock()
}()

Wherever in the method you want to be able to cancel (e.g. at the beginning/end of a loop), put in the following logic:

...
if t.abandonment[msg.MessageID] {
    log.Println("Abandoning operation")
    return
}
...

Then define your Abandon method like this:

func (t *TestHandler) Abandon(conn *ldapserver.Conn, msg *ldapserver.Message, messageID ldapserver.MessageID) {
    t.abandonmentLock.Lock()
    // Set the flag only if the messageID is in the map
    if _, exists := t.abandonment[messageID]; exists {
        t.abandonment[messageID] = true
    }
    t.abandonmentLock.Unlock()
}

Authentication

The Conn object passed to each request method has an Authentication field with type any, for storing implementation-defined authentication info. See test/main.go for an example.

Feature support

  • TLS support
  • Strict protocol validation
  • OID validation
  • DN parsing support
  • Full concurrency ability
  • Comprehensive message parsing tests
  • Filter stringification
  • Abandon request
  • Add request (concurrent)
  • Bind request
  • Compare request (concurrent)
  • Delete request (concurrent)
  • Extended requests
  • Modify request (concurrent)
  • ModifyDN request (concurrent)
  • Search request (concurrent)
  • StartTLS request
  • Unbind request
  • Intermediate response
  • Unsolicited notifications
  • Notice of disconnection

Goals

  • Full conformance to the relevant specifications, e.g. RFC 4511.
  • Support for all builtin operations and common extended operations
  • Comprehensive encoding/decoding tests
  • Strict client data validity checking
  • Ease of use

Contributions and bug reports are welcome!

ldapserver's People

Contributors

merlinz01 avatar

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.