activeldap

package module
v0.0.0-...-8f354dd Latest Latest
Warning

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

Go to latest
Published: Aug 20, 2026 License: BSD-3-Clause Imports: 4 Imported by: 0

README

go-ruby-activeldap/activeldap

activeldap — go-ruby-activeldap

Docs License Go Coverage

A pure-Go (no cgo) reimplementation of the ActiveRecord-style LDAP object-relational mapper of Ruby's ActiveLdap gem — the object↔entry mapping (ldap_mapping), the finder/search query builder, the dirty-tracking record with single- and multi-valued attributes, validations, belongs_to/has_many associations over distinguished names, and LDIF import/export. It mirrors ActiveLdap's semantics without any Ruby runtime.

It is the ORM backend for go-embedded-ruby (rbgo), layered on the Net::LDAP surface provided by go-ruby-ldap exactly as the real activeldap gem builds on net-ldap — but it is a standalone, reusable module, a sibling of go-ruby-sequel and go-ruby-activerecord.

What it is — and isn't. Turning a mapping plus a set of conditions into a distinguished name and an RFC 4515 search filter, tracking which attributes changed, deciding add-vs-modify on save, diffing dirty attributes into modify operations, validating, and serialising to LDIF is fully deterministic and needs no directory server and no interpreter, so it lives here as pure Go. Talking to a directory is the host's job: a Base is bound to a Directory seam — the four Net::LDAP operations ActiveLdap uses (search / add / modify / delete). The host (rbgo) wires that seam to the bound Net::LDAP connection; tests wire it to the in-memory MockDirectory. The mapping, DN, filter, dirty-diff and LDIF logic is what this library owns and tests; the network is the seam.

Features

Faithful port of ActiveLdap's ORM, validated against ActiveLdap 7.x semantics and (differentially) against the net-ldap gem's own filter escaping and Ruby's Base64:

  • ldap_mappingMapping{DNAttribute, Prefix, Classes, Scope} plus attribute aliases (case-insensitive) and single-valued declarations, compiled by NewClass and bound to a Connection.
  • FindersFind(id), FindFirst / FindAll / Search(FindOptions{…}) with per-call Filter / Base / Scope / Attributes / Limit overrides, and Exist(id). Every find is guarded by the mapping's objectClass filter.
  • RFC 4515 filters — a composable builder (Equal, Present, Substring, And / Or / Not, RawFilter) with metacharacter escaping, a Conditions hash-to-AND helper, plus a parser/evaluator so MockDirectory answers the very filters the builder emits.
  • DN handlingParseDN / BuildDN / Normalized / Equal / Parent with RFC 4514 value escaping.
  • Record — case-insensitive attribute access (Get / One / Set / Add / Delete), dirty tracking (Changed / ChangedAttributes / Changes), New/Persisted, computed DN and ID.
  • PersistenceCreate, Save (INSERT for a new record, minimal diff-based :replace/:delete UPDATE for an existing one), UpdateAttributes, Destroy, Reload.
  • ValidationsPresenceOf, RequiredClasses, custom Validate validators, an Errors object with FullMessages, and a ValidationError that blocks a save without touching the directory.
  • AssociationsBelongsTo / HasMany over an attribute foreign key or the entry DN (DNKey), the classic groupOfNames member pattern included.
  • LDIFToLDIF (RFC 2849, safe-string base64), ParseLDIF (line folding, comments, base64), and LoadLDIF import through the connection.

Install

go get github.com/go-ruby-activeldap/activeldap

Quick start (Go)

dir := activeldap.NewMockDirectory() // or a Net::LDAP-backed Directory
conn := activeldap.NewConnection(dir, "dc=example,dc=com")

person := activeldap.NewClass("Person", &activeldap.Mapping{
    DNAttribute: "uid",
    Prefix:      "ou=Users",
    Classes:     []string{"top", "person", "inetOrgPerson"},
    Scope:       activeldap.ScopeSub,
}, conn)

alice, _ := person.Create(map[string][]string{
    "uid": {"alice"}, "cn": {"Alice"}, "sn": {"Adams"},
})
alice.Set("mail", "alice@example.com")
alice.Save() // diff-based modify: only mail is replaced

people, _ := person.FindAll(activeldap.FindOptions{Filter: activeldap.Present("mail")})

The equivalent Ruby (running on rbgo, once the gem is registered) is in examples/activeldap_usage.rb.

Tests & coverage

GOWORK=off go test -race -coverpkg=$(go list ./... | paste -sd, -) -coverprofile=cover.out ./...
GOWORK=off go tool cover -func=cover.out | tail -1   # total: 100.0%

100% statement coverage including every error branch. CI additionally builds on the six supported 64-bit targets (amd64/arm64 native, riscv64/loong64/ppc64le/s390x under qemu) and for js/wasip1 wasm. The differential oracle (oracle_test.go) checks filter escaping and LDIF base64 against the real net-ldap gem and Ruby's Base64, and skips where they are absent.

License

BSD-3-Clause © the go-ruby-activeldap/activeldap authors.

Documentation

Overview

Package activeldap is a pure-Go (no cgo) reimplementation of the ActiveRecord-style LDAP object-relational mapper of Ruby's ActiveLdap gem: the object↔entry mapping (ldap_mapping), the finder/search query builder, the dirty-tracking record with single- and multi-valued attributes, validations, belongs_to/has_many associations over distinguished names, and LDIF import/export. It mirrors ActiveLdap's semantics faithfully, independent of any Ruby runtime.

What it is — and isn't

The judgement-and-string-building heart of ActiveLdap — turning a mapping plus a set of conditions into a distinguished name and an RFC 4515 search filter, tracking which attributes changed, deciding add-vs-modify on save, diffing dirty attributes into LDAP modify operations, validating, and serialising to LDIF — is fully deterministic and needs no directory server and no Ruby runtime, so it lives here as plain Go.

Talking to an actual directory is the host's job. A Base is bound to a Directory seam — the small four-method surface ActiveLdap uses out of Net::LDAP (search / add / modify / delete). The host (go-embedded-ruby's rbgo) wires that seam to the bound Net::LDAP connection provided by go-ruby-ldap; tests wire it to the in-memory MockDirectory. The mapping, DN, filter, dirty-diff and LDIF logic is what this library owns and tests; the network is the seam.

Mapping

A model is described by a Mapping — the Go form of ActiveLdap's ldap_mapping(dn_attribute:, prefix:, classes:, scope:). NewClass compiles a mapping (plus attribute aliases and single-valued declarations) into a Class; Class.New and the finder methods (Class.Find, Class.Search, Class.Exist, Class.Create) mint and load Base records against a Connection.

Index

Constants

View Source
const DNKey = "dn"

DNKey is the sentinel key meaning "the entry's distinguished name" in an association's foreign_key/primary_key — the ActiveLdap foreign_key: "dn" form.

View Source
const ErrorsBase = "base"

ErrorsBase is the pseudo-attribute under which record-level errors are filed.

Variables

This section is empty.

Functions

func BuildDN

func BuildDN(attr, value, prefix, base string) string

BuildDN composes a full DN from a leaf RDN (attribute=value), an optional prefix (an ActiveLdap "prefix" such as ou=Users, itself a DN), and a base DN. Empty prefix or base segments are skipped. The result is the ActiveLdap dn = "<dn_attribute>=<value>,<prefix>,<base>".

func EscapeFilterValue

func EscapeFilterValue(v string) string

EscapeFilterValue escapes an assertion value for an RFC 4515 filter: the characters * ( ) \ and NUL become \2a \28 \29 \5c \00. Everything else is passed through, so a caller may embed a literal '*' only via Substring.

func ParseDNParent

func ParseDNParent(normalized string) string

ParseDNParent returns the normalized parent DN of a normalized DN string.

Types

type Base

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

Base is a single LDAP entry as an ActiveRecord-style object — the instance side of the ORM, the Go counterpart of an ActiveLdap::Base instance. It holds the current attribute values, tracks which changed since it was loaded/saved (dirty tracking), knows its class (mapping + connection), and answers whether it is new or persisted. Mint one with Class.New or load one with the finder methods.

func (*Base) Add

func (b *Base) Add(name string, values ...string)

Add appends values to an attribute (a multi-valued convenience), preserving existing values.

func (*Base) Association

func (b *Base) Association(name string) ([]*Base, error)

Association resolves a named association from this record, returning every matching target record. An unknown name returns an error.

func (*Base) AssociationOne

func (b *Base) AssociationOne(name string) (*Base, error)

AssociationOne resolves a singular association (Class.BelongsTo) and returns the first matching record, or nil if none.

func (*Base) AttributeNames

func (b *Base) AttributeNames() []string

AttributeNames returns the canonical attribute names in assignment order.

func (*Base) Attributes

func (b *Base) Attributes() map[string][]string

Attributes returns a name→values snapshot of the record's attributes, the Go form of ActiveLdap's #attributes hash.

func (*Base) Changed

func (b *Base) Changed() bool

Changed reports whether any attribute differs from the load/save baseline — ActiveLdap's #changed?. A new record is considered changed once it has any attribute beyond its objectClasses.

func (*Base) ChangedAttributes

func (b *Base) ChangedAttributes() []string

ChangedAttributes returns the folded names of attributes whose values differ from the baseline, sorted — the keys of ActiveLdap's #changes.

func (*Base) Changes

func (b *Base) Changes() map[string][2][]string

Changes returns, per changed attribute, the [before, after] value pair — ActiveLdap's #changes.

func (*Base) Class

func (b *Base) Class() *Class

Class returns the record's class.

func (*Base) DN

func (b *Base) DN() string

DN returns the record's distinguished name: its explicit loaded DN if it has one, otherwise the DN computed from the dn_attribute value, prefix and base. It returns "" when a new record has no dn_attribute value yet.

func (*Base) Delete

func (b *Base) Delete(name string)

Delete removes an attribute entirely.

func (*Base) Destroy

func (b *Base) Destroy() error

Destroy deletes the record from the directory — ActiveLdap's #destroy. A new (never-saved) record cannot be destroyed and returns an error. On success the record becomes non-persisted.

func (*Base) Errors

func (b *Base) Errors() *Errors

Errors returns the validation errors from the last Base.Valid call.

func (*Base) Get

func (b *Base) Get(name string) []string

Get returns all values of an attribute (following aliases), or nil.

func (*Base) Has

func (b *Base) Has(name string) bool

Has reports whether the attribute is present.

func (*Base) ID

func (b *Base) ID() string

ID returns the record's dn_attribute value — ActiveLdap's #id.

func (*Base) NewRecord

func (b *Base) NewRecord() bool

NewRecord reports whether the record has not yet been saved (ActiveLdap's new_entry? / !persisted?).

func (*Base) One

func (b *Base) One(name string) string

One returns the first value of an attribute, or "" — the natural accessor for a single-valued attribute.

func (*Base) Persisted

func (b *Base) Persisted() bool

Persisted reports whether the record exists in the directory.

func (*Base) Reload

func (b *Base) Reload() error

Reload re-reads the record from the directory, discarding unsaved changes and resetting dirty tracking — ActiveLdap's #reload. It errors if the entry no longer exists.

func (*Base) Save

func (b *Base) Save() error

Save persists the record: an INSERT (Directory.Add) for a new record, or a diff-based UPDATE (Directory.Modify of only the changed attributes) for an existing one — ActiveLdap's #save. It validates first and returns a ValidationError without touching the directory when invalid. On success the record becomes persisted and its dirty baseline is reset.

func (*Base) SaveOK

func (b *Base) SaveOK() bool

SaveOK is the boolean-returning save (ActiveLdap's #save returning true/false): it reports success and leaves the reason in Base.Errors / the returned error discarded.

func (*Base) Set

func (b *Base) Set(name string, values ...string)

Set assigns an attribute's values, replacing any existing ones, and records the change for dirty tracking. Passing no values clears the attribute.

func (*Base) SetID

func (b *Base) SetID(id string)

SetID assigns the dn_attribute value.

func (*Base) ToLDIF

func (b *Base) ToLDIF() string

ToLDIF renders the record as an RFC 2849 LDIF entry — ActiveLdap's #to_ldif. The dn: line comes first, then every attribute value on its own "name: value" line, in canonical-name order with the objectClass values first (as ActiveLdap emits them). A value needing it (leading space/colon/<, a non-ASCII or control byte) is base64-encoded on a "name:: b64" line.

func (*Base) ToS

func (b *Base) ToS() string

ToS renders a short description used in errors and inspection.

func (*Base) UpdateAttributes

func (b *Base) UpdateAttributes(attrs map[string][]string) error

UpdateAttributes assigns the given attributes and saves — ActiveLdap's #update_attributes. It returns the save error (a ValidationError on invalid input, leaving the in-memory changes in place as ActiveLdap does).

func (*Base) Valid

func (b *Base) Valid() bool

Valid runs all validations and returns whether the record is error-free — ActiveLdap's #valid?. It clears prior errors first, then runs the structural checks (dn_attribute presence, mapped objectClasses present) followed by every registered custom validator, so Base.Errors reflects only the latest run.

type Class

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

Class is a compiled model — a Mapping bound to a Connection, the Go counterpart of an ActiveLdap::Base subclass. Its methods (Class.New, Class.Find, Class.Search, Class.Exist, Class.Create) are the class-side ORM surface. Associations and validators are registered on it before use.

func NewClass

func NewClass(name string, m *Mapping, conn *Connection) *Class

NewClass compiles a Mapping and binds it to a Connection, yielding a Class. The class name is used in error messages and to_s. It panics if the mapping omits its required DNAttribute, mirroring ActiveLdap raising on an incomplete ldap_mapping.

func (*Class) BaseDN

func (c *Class) BaseDN() string

BaseDN returns the DN of this class's container: the mapping prefix joined to the connection base — the parent under which every entry's DN is formed.

func (*Class) BelongsTo

func (c *Class) BelongsTo(name string, target *Class, foreignKey, primaryKey string)

BelongsTo registers a belongs_to association: this record's foreignKey values reference the target's primaryKey. Resolving it yields the target record(s) whose primaryKey matches — the "parent" of this record. Use DNKey for a DN-valued reference (e.g. member: "dn").

func (*Class) Connection

func (c *Class) Connection() *Connection

Connection returns the class's bound connection.

func (*Class) Create

func (c *Class) Create(attrs map[string][]string) (*Base, error)

Create mints a record, assigns the given attributes, and saves it — the Go form of Model.create. The record is returned whether or not it saved; check the error (a ValidationError on invalid input) and Base.Persisted.

func (*Class) DNForID

func (c *Class) DNForID(id string) string

DNForID builds the full DN for a record whose dn_attribute value is id: "<dn_attribute>=<id>,<prefix>,<base>".

func (*Class) Exist

func (c *Class) Exist(id string) (bool, error)

Exist reports whether a record with the given dn_attribute value exists — ActiveLdap's exist?.

func (*Class) Find

func (c *Class) Find(id string) (*Base, error)

Find loads the single record whose dn_attribute equals id — find(id). It searches at ScopeBase on the record's computed DN, raising EntryNotFoundError when absent.

func (*Class) FindAll

func (c *Class) FindAll(opts FindOptions) ([]*Base, error)

FindAll returns all records matching the options (find(:all, ...)).

func (*Class) FindFirst

func (c *Class) FindFirst(opts FindOptions) (*Base, error)

FindFirst returns the first matching record, or nil if none — find(:first, ...).

func (*Class) HasMany

func (c *Class) HasMany(name string, target *Class, foreignKey, primaryKey string)

HasMany registers a has_many association: target records carry a foreignKey referencing this record's primaryKey. Resolving it yields every target that points back — the "children" of this record.

func (*Class) LoadLDIF

func (c *Class) LoadLDIF(ldif string) ([]*Base, error)

LoadLDIF parses an LDIF string and creates a record of this class for each entry, saving them through the connection — the Go form of ActiveLdap's LDIF import. It returns the created records; the first save error aborts and is returned with the records created so far.

func (*Class) Mapping

func (c *Class) Mapping() *Mapping

Mapping returns the class's mapping.

func (*Class) Name

func (c *Class) Name() string

Name returns the class name.

func (*Class) New

func (c *Class) New() *Base

New mints a new, unsaved record of this class with the objectClass values from the mapping already set — the Go form of Model.new. Additional attributes are assigned afterwards with Base.Set.

func (*Class) Search

func (c *Class) Search(opts FindOptions) ([]*Base, error)

Search runs a search and returns every matching record, the Go form of Model.search / find(:all). It applies the class objectClass guard, the given options, and (if set) the limit.

func (*Class) Validate

func (c *Class) Validate(v Validator)

Validate registers a custom validator on the class, run by Base.Valid after the built-in structural checks.

type Connection

type Connection struct {
	// Directory is the seam performing the actual LDAP operations.
	Directory Directory
	// Base is the connection's base DN (the base: of establish_connection),
	// e.g. "dc=example,dc=com".
	Base string
}

Connection is the bound directory a Class operates against — the Directory seam plus the base DN under which the class's prefix is resolved. It is the Go form of the state ActiveLdap::Base.setup_connection / establish_connection installs (host/port/bind live inside the Directory the host wires).

func NewConnection

func NewConnection(dir Directory, base string) *Connection

NewConnection builds a Connection over a Directory and base DN.

type DN

type DN struct{ RDNs []RDN }

DN is a parsed distinguished name — an ordered list of RDNs, most specific first, exactly as ActiveLdap's DN wraps a sequence of components.

func ParseDN

func ParseDN(s string) (DN, bool)

ParseDN parses a distinguished name string into a DN. Unescaped commas separate RDNs and the first unescaped '=' splits each RDN; surrounding whitespace around a component is trimmed. An empty string parses to the empty (root) DN. A component with no '=' makes ParseDN return ok=false.

func (DN) Equal

func (d DN) Equal(o DN) bool

Equal reports whether two DNs denote the same entry, comparing case- and whitespace-insensitively via DN.Normalized.

func (DN) Normalized

func (d DN) Normalized() string

Normalized returns a canonical, case-folded form used for DN equality: each attribute name is lower-cased and each value is lower-cased and whitespace-collapsed. ActiveLdap compares DNs case-insensitively, so two DNs are "equal" when their Normalized strings match.

func (DN) Parent

func (d DN) Parent() DN

Parent returns the DN with its most specific RDN removed — the DN of the entry one level up. The root DN's parent is the root DN.

func (DN) String

func (d DN) String() string

String renders the DN as a comma-joined, RFC 4514-escaped string.

type Directory

type Directory interface {
	Search(req SearchRequest) ([]*Entry, error)
	Add(dn string, attributes map[string][]string) error
	Modify(dn string, ops []ModifyOp) error
	Delete(dn string) error
}

Directory is the seam between this ORM and a real LDAP server: the four Net::LDAP operations ActiveLdap uses. The host (rbgo) wires it to a bound Net::LDAP connection; tests wire it to MockDirectory. Every method returns an error the ORM surfaces as a save/find failure.

type Entry

type Entry struct {
	DN         string
	Attributes map[string][]string
}

Entry is a directory entry as returned by a search — a DN plus its attributes. It mirrors a Net::LDAP::Entry: attribute names are case-insensitive and values are always a list.

func (*Entry) Get

func (e *Entry) Get(name string) []string

Get returns the value list for a (case-insensitively matched) attribute name.

type EntryNotFoundError

type EntryNotFoundError struct {
	Class string
	ID    string
}

EntryNotFoundError is returned by Class.Find when no entry has the requested dn_attribute value — ActiveLdap's EntryNotFound.

func (*EntryNotFoundError) Error

func (e *EntryNotFoundError) Error() string

type Errors

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

Errors collects validation messages per attribute, the Go form of ActiveLdap/ActiveModel's errors object. The special key ErrorsBase holds record-level messages not tied to one attribute.

func (*Errors) Add

func (e *Errors) Add(attr, message string)

Add files a message against an attribute (use ErrorsBase for record-level).

func (*Errors) Count

func (e *Errors) Count() int

Count returns the total number of messages.

func (*Errors) Empty

func (e *Errors) Empty() bool

Empty reports whether there are no errors — the negation of ActiveModel's #any?.

func (*Errors) FullMessages

func (e *Errors) FullMessages() []string

FullMessages returns every message prefixed by its attribute, in insertion order — ActiveModel's #full_messages. Record-level (ErrorsBase) messages are emitted unprefixed.

func (*Errors) On

func (e *Errors) On(attr string) []string

On returns the messages filed against an attribute.

type Filter

type Filter interface {
	String() string
	// contains filtered or unexported methods
}

Filter is an RFC 4515 LDAP search filter, the value ActiveLdap builds from a find/search :filter option (a String is used verbatim; a Hash of conditions is AND-combined) and passes to Net::LDAP. Filters compose with And, Or and Not; leaves are built with Equal, Present and Substring. String renders the parenthesised text.

func And

func And(subs ...Filter) Filter

And builds a conjunction (&...). With a single sub-filter it returns that sub-filter unchanged (no redundant &-wrapper), matching ActiveLdap's filter simplification; with none it returns the "match everything" objectClass=* present filter is left to callers — And of nothing renders as an empty group.

func Conditions

func Conditions(conds map[string][]string) Filter

Conditions builds the Filter for a Hash-style :filter option — a map of attribute→values AND-combined as equalities, the common ActiveLdap find(filter: {uid: "alice"}) form. It returns nil for an empty map.

func Equal

func Equal(attr, value string) Filter

Equal builds an equality filter (attr=value), escaping the value per RFC 4515.

func Not

func Not(sub Filter) Filter

Not builds a negation (!...).

func Or

func Or(subs ...Filter) Filter

Or builds a disjunction (|...). A single sub-filter is returned unchanged.

func ParseFilter

func ParseFilter(s string) (Filter, error)

ParseFilter parses an RFC 4515 filter string into a Filter tree. It handles the operators & | ! and the leaves attr=value, attr=* (present) and substring (attr=a*b*c), unescaping \\XX and \\c in assertion values. It is the inverse of Filter.String for the subset this ORM emits, letting MockDirectory evaluate the very filters the query builder produces. A malformed filter returns an error.

func Present

func Present(attr string) Filter

Present builds a presence filter (attr=*).

func RawFilter

func RawFilter(text string) Filter

RawFilter wraps a filter string the caller already has (the String form of a find/search :filter option), so it composes with the built nodes. It is emitted verbatim; if it is not already parenthesised it is wrapped in parens.

func Substring

func Substring(attr, initial string, anyParts []string, final string) Filter

Substring builds a substring filter (attr=initial*any*...*final). Any of initial, the any-parts, or final may be empty; an all-empty Substring is equivalent to Present.

type FindOptions

type FindOptions struct {
	// Filter is an extra condition AND-combined with the objectClass guard. It
	// may be any [Filter] (including one built from a Hash of conditions via
	// [Conditions]); nil adds no extra condition.
	Filter Filter
	// Base overrides the search base DN (the base: option). Empty uses the
	// class base DN.
	Base string
	// Scope overrides the search scope; nil uses the mapping scope.
	Scope *Scope
	// Attributes limits the returned attributes; nil returns all.
	Attributes []string
	// Limit caps the number of records returned when > 0 (ActiveLdap's :limit).
	Limit int
}

FindOptions are the keyword options of ActiveLdap's find/search:

find(:all, filter: "(mail=*)", base: "ou=People,dc=x", scope: :one, attributes: [...])

A zero FindOptions means "the class defaults": the mapping scope, the class base DN, the objectClass guard as the only filter, and all attributes.

type LDIFRecord

type LDIFRecord struct {
	DN         string
	Attributes map[string][]string
}

LDIFRecord is one entry parsed from an LDIF stream: its DN and attributes.

func ParseLDIF

func ParseLDIF(s string) ([]LDIFRecord, error)

ParseLDIF parses an RFC 2849 LDIF string into records. It handles line folding (a continuation line begins with a single space), comments (# …), blank-line record separators, and both "name: value" and base64 "name:: value" forms. A record missing its dn: line is an error.

type Mapping

type Mapping struct {
	// DNAttribute is the RDN attribute of every entry of this class (the
	// dn_attribute:), e.g. "uid" or "cn". Required.
	DNAttribute string
	// Prefix is the container DN, relative to the connection base, under which
	// entries live (the prefix:), e.g. "ou=Users". May be empty.
	Prefix string
	// Classes are the objectClass values every entry of this model carries
	// (classes:). The first is conventionally the structural class. Required.
	Classes []string
	// Scope is the default search scope for finds (scope:); the zero value
	// [ScopeSub] matches ActiveLdap's default of :sub.
	Scope Scope
	// Aliases maps an alternative attribute name to its canonical name
	// (ActiveLdap attribute aliases), e.g. {"commonName": "cn"}. Case-insensitive.
	Aliases map[string]string
	// SingleValued lists attributes presented as a single scalar rather than a
	// list (schema SINGLE-VALUE), so [Base.One] is the natural accessor.
	SingleValued []string
}

Mapping is the Go form of ActiveLdap's ldap_mapping declaration — how a model class maps to a region of the directory:

ldap_mapping dn_attribute: "uid", prefix: "ou=Users",
             classes: ["top", "person", "inetOrgPerson"], scope: :sub

The zero Mapping is invalid; build one and pass it to NewClass.

type MockDirectory

type MockDirectory struct {

	// Log records every mutating call in order, for assertions (mirrors the
	// Net::LDAP mock's operation log).
	Log []string
	// FailOn, when set, makes the named operation ("add"/"modify"/"delete"/
	// "search") return an error, to exercise the ORM's error branches.
	FailOn map[string]string
	// contains filtered or unexported fields
}

MockDirectory is an in-memory Directory — the ActiveLdap test double and the fallback the rbgo binding uses when no Net::LDAP connection is configured. It stores entries by their normalized DN and answers searches by scope and filter, so the whole ORM can be exercised with no server.

func NewMockDirectory

func NewMockDirectory() *MockDirectory

NewMockDirectory builds an empty MockDirectory.

func (*MockDirectory) Add

func (m *MockDirectory) Add(dn string, attributes map[string][]string) error

Add implements Directory.Add.

func (*MockDirectory) Delete

func (m *MockDirectory) Delete(dn string) error

Delete implements Directory.Delete.

func (*MockDirectory) Modify

func (m *MockDirectory) Modify(dn string, ops []ModifyOp) error

Modify implements Directory.Modify, applying add/replace/delete ops.

func (*MockDirectory) Search

func (m *MockDirectory) Search(req SearchRequest) ([]*Entry, error)

Search implements Directory.Search over the in-memory store.

func (*MockDirectory) Seed

func (m *MockDirectory) Seed(dn string, attrs map[string][]string)

Seed inserts or replaces an entry directly, bypassing the Log — used by tests to populate the directory before exercising the ORM.

type ModOp

type ModOp int

ModOp is the kind of a ModifyOp — the Net::LDAP modify operation symbol.

const (
	// ModAdd adds values to an attribute (:add).
	ModAdd ModOp = iota
	// ModReplace replaces an attribute's values (:replace).
	ModReplace
	// ModDelete deletes an attribute or specific values (:delete).
	ModDelete
)

func (ModOp) String

func (m ModOp) String() string

String renders the op as ActiveLdap/Net::LDAP's symbol name.

type ModifyOp

type ModifyOp struct {
	Op        ModOp
	Attribute string
	Values    []string
}

ModifyOp is one element of a Net::LDAP modify operations list — [op, attribute, values].

type RDN

type RDN struct {
	Attribute string
	Value     string
}

RDN is a single relative distinguished name component — one attribute=value pair such as cn=Alice. Multi-valued RDNs (cn=Alice+uid=alice) are not part of the small model ActiveLdap's dn_attribute mapping produces, so an RDN holds exactly one pair.

func (RDN) String

func (r RDN) String() string

String renders the RDN as attribute=value with the value DN-escaped per RFC 4514 (leading/trailing space, and any of ,+"\<>; escaped).

type Scope

type Scope int

Scope is an LDAP search scope, the ActiveLdap :scope option and the Net::LDAP scope constant it maps to.

const (
	// ScopeBase searches only the base entry itself (Net::LDAP::SearchScope_BaseObject).
	ScopeBase Scope = iota
	// ScopeOne searches the base's immediate children (…SingleLevel).
	ScopeOne
	// ScopeSub searches the base and its whole subtree (…WholeSubtree).
	ScopeSub
)

func ParseScope

func ParseScope(name string) (Scope, bool)

ParseScope maps an ActiveLdap scope symbol name to a Scope. It accepts "base"/"baseobject", "one"/"onelevel"/"singlelevel", "sub"/"subtree"/ "wholesubtree"; an unknown name returns ok=false.

func (Scope) String

func (s Scope) String() string

String renders the scope as ActiveLdap's symbol name (:base/:one/:sub).

type SearchRequest

type SearchRequest struct {
	Base       string
	Scope      Scope
	Filter     string
	Attributes []string
}

SearchRequest is the argument to Directory.Search, the four fields ActiveLdap fills from a find/search: the base DN, the scope, the RFC 4515 filter string, and the attributes to return (nil = all).

type ValidationError

type ValidationError struct {
	Record   *Base
	Messages []string
}

ValidationError is the error a save returns when validation fails; its message is the joined full messages.

func (*ValidationError) Error

func (e *ValidationError) Error() string

type Validator

type Validator func(b *Base)

Validator inspects a record and files any problems on its Errors. Register one with Class.Validate; the built-ins (PresenceOf, RequiredClasses and the mapping-derived DN/objectClass checks) are Validators too.

func PresenceOf

func PresenceOf(attrs ...string) Validator

PresenceOf returns a validator asserting each named attribute has at least one non-blank value — ActiveLdap's validates_presence_of.

func RequiredClasses

func RequiredClasses(classes ...string) Validator

RequiredClasses returns a validator asserting the record carries each named objectClass — the check ActiveLdap derives from a mapping's required classes.

Jump to

Keyboard shortcuts

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