ldaptest

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 25, 2026 License: BSD-3-Clause Imports: 8 Imported by: 0

Documentation

Overview

Package ldaptest drives an LDAP server over the wire, for the questions a real client cannot express.

⛔ It is NOT a judge, and it is not a client library. A client from the same module as the server can agree with it about a misreading of the protocol and both be wrong -- which is exactly how a substring filter defect went unnoticed for years in the library go-authn/ldap replaces: its own client encoded the filter with the same mistake, so the round trip looked clean. Conformance belongs to OpenLDAP's `ldapsearch`.

What this is for is the questions ldapsearch cannot ask, all of which are about ONE CONNECTION'S STATE:

  • two binds and a search on a single socket, because "a refused bind keeps the previous bind's rights" does not exist across two;
  • a SASL bind with an arbitrary mechanism, which go-ldap/ldap/v3 offers no way to send;
  • a StartTLS upgrade followed by more LDAP on the same connection.

It returns errors rather than taking a *testing.T, so that importing it pulls no test flags into anything, and so it can be used from a fixture that is not a test.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Paged added in v0.2.0

func Paged(size int, cookie string) ldap.Control

Paged builds the RFC 2696 control: a page size, and the cookie from the previous page (empty to start).

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

A Client is one connection to a server.

func Dial

func Dial(addr string, timeout time.Duration) (*Client, error)

Dial opens a connection.

func (*Client) Bind

func (c *Client) Bind(dn, password string) (ldap.Result, error)

Bind sends a simple bind.

The password is a string here and not []byte, because a test writes a literal. A SERVER must take it as bytes -- see ldap.BindRequest.

func (*Client) BindSASL

func (c *Client) BindSASL(mechanism string, credentials []byte) (SASLStep, error)

BindSASL sends one step of a SASL bind. Passing nil credentials sends the field ABSENT, which several mechanisms use as their first message.

func (*Client) Close

func (c *Client) Close() error

Close hangs up.

func (*Client) Conn

func (c *Client) Conn() net.Conn

Conn is the underlying socket, for a caller that wants a deadline on it.

func (*Client) Extended

func (c *Client) Extended(oid string, value []byte) (ldap.Result, error)

Extended sends any extended operation.

func (*Client) Read

func (c *Client) Read() (*ber.Packet, error)

Read reads one message, for a caller asserting that something arrived -- or, with a deadline, that nothing did.

func (*Client) Search

func (c *Client) Search(s Search) (SearchResult, error)

Search sends one and reads to the searchResDone.

func (*Client) SetDeadline

func (c *Client) SetDeadline(t time.Time) error

SetDeadline bounds every exchange, so that a test against a server that stops answering fails rather than hangs.

func (*Client) StartTLS

func (c *Client) StartTLS(cfg *tls.Config) (ldap.Result, error)

StartTLS upgrades the connection (RFC 4511 4.14).

The handshake is done here, which means a caller that wants to assert a REFUSAL gets it from the result rather than from a handshake that never happens: the result is returned before the handshake is attempted, and the handshake only runs on success.

func (*Client) WhoAmI

func (c *Client) WhoAmI() (string, ldap.Result, error)

WhoAmI is RFC 4532: what this connection says the client is.

type SASLStep

type SASLStep struct {
	ldap.Result
	// ServerCreds is serverSaslCreds, and Present says whether the field was
	// there at all -- RFC 4511 4.2.2 gives an absent field and a zero-length
	// one different meanings, and a test that cannot tell them apart cannot
	// check either.
	ServerCreds []byte
	Present     bool
}

A SASLStep is what one step of a SASL bind answered.

type Search struct {
	Base   string
	Scope  ldap.Scope
	Filter string // RFC 4515; empty means "(objectClass=*)"
	// FilterAST is sent instead of Filter when it is set, so that a test can
	// send a filter the RFC 4515 parser would never produce -- which is what
	// this package is for. A nil one is an error rather than a filter that
	// matches everything.
	FilterAST ldap.Filter
	Attrs     []string
	SizeLimit int
	TypesOnly bool
	Controls  []ldap.Control
}

A Search is what to ask for. The zero value is a base-scope search of the root DSE with a present filter, which is what discovery looks like.

type SearchResult

type SearchResult struct {
	ldap.Result
	Entries []*ldap.Entry
	// Controls are the response controls on the searchResDone -- where the
	// paged-results cookie arrives (RFC 2696).
	Controls []ldap.Control
}

A SearchResult is what a search came back with.

func (SearchResult) Cookie added in v0.2.0

func (r SearchResult) Cookie() (cookie string, ok bool)

Cookie is the paged-results cookie, and ok says whether the control was there at all.

⛔ An EMPTY cookie with the control present means "that was the last page" and is how a sequence ends; an ABSENT control means the server did not page at all. A client that cannot tell them apart either stops early or asks forever.

func (SearchResult) DNs

func (r SearchResult) DNs() []string

DNs is every entry's DN, for a test that only cares which entries came.

func (SearchResult) Values

func (r SearchResult) Values() []string

Values is every attribute value, as "name: value", for a test asserting what an entry published.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL