Documentation
¶
Overview ¶
Package directory turns what an admin TYPES into what Wardyn STORES.
Every "who" field in the product is a claim value, rarely the thing a human knows (an Entra group's `groups` claim carries its object GUID, not its name). Hand-typing those is a silent-mismatch landmine: a plausible-looking wrong string binds nothing and fails open.
So: Entry.ClaimValue IS the contract — the exact string the claim will carry, and what a UI inserts on selection; Entry.DisplayName is what a UI renders. Deliberately different fields, since for groups they are deliberately different strings.
Provider-abstracted on purpose: one interface, Entra ships first, Okta/Google are later connectors behind the same contract. The whole capability is opt-in — unconfigured, every field stays free text.
Index ¶
Constants ¶
const ( // MaxResults is ONE GLOBAL cap across every kind in a search, not a // per-kind quota: in KindAny the three per-kind result sets concatenate in // anyKindOrder and the first MaxResults survive, so the outcome is // deterministic ("App Roles first, then groups, then users"). Every // search re-trims to this cap on the way out, since $top is a request to // the upstream, not a guarantee from it. MaxResults = 20 // MinQueryLen is the shortest query a connector will send upstream: below // it a search returns empty without a round trip, since one or two // characters match most of a directory. The connector-side backstop for // the same floor the HTTP layer enforces. MinQueryLen = 2 )
Variables ¶
var ErrUnconfigured = errors.New("directory: no provider configured")
ErrUnconfigured means no directory provider is configured. The HTTP layer answers it with a distinct 503 code, the UI's ABSENT-MODE signal: the combobox degrades to a plain free-text input. Deliberately a sentinel, not a ProviderError — nothing upstream failed, the feature is simply off.
Functions ¶
This section is empty.
Types ¶
type Directory ¶
type Directory interface {
// Search returns at most MaxResults entries matching q, in the fixed
// anyKindOrder when kind is KindAny. A query shorter than MinQueryLen
// returns (nil, nil) — empty is not an error.
Search(ctx context.Context, q string, kind Kind) ([]Entry, error)
}
Directory searches a configured identity provider. Implementations are expected to be safe for concurrent use.
func NewEntra ¶
func NewEntra(cfg EntraConfig) (Directory, error)
NewEntra builds the Microsoft Graph connector. It returns ErrUnconfigured when any credential is absent (the "feature is off" answer, not a failure) and performs no network I/O: a tenant reachable at boot but not at first search would fail either way.
type EntraConfig ¶
EntraConfig carries the client-credentials app registration. This package reads NO environment: the boot slice resolves the credentials and passes them in, so the fail-closed refusal for a PUBLIC OIDC client (no secret, cannot do client credentials) lives at boot, where it can refuse startup, not here where it could only 503.
type Entry ¶
type Entry struct {
// DisplayName is the human-readable label — a user's display name, a
// group's name, an App Role's display name. Never store this.
DisplayName string `json:"display_name"`
// ClaimValue is the exact string the claim carries and the field must
// hold: email (user), object GUID (group), manifest value (App Role).
ClaimValue string `json:"claim_value"`
// Kind is which of the three sources produced this entry.
Kind Kind `json:"kind"`
// Detail is a secondary, presentation-only disambiguator, never stored.
Detail string `json:"detail,omitempty"`
}
Entry is one suggestion. DisplayName is rendered; ClaimValue is stored.
type Kind ¶
type Kind string
Kind selects which class of directory object a search covers. KindAny is IN the v1 contract, not a convenience: the People-step Value field is one kind-LESS input that accepts an App Role, a group, or an email.
type ProviderError ¶
ProviderError wraps a failure talking to the upstream directory. Op names the call and Status carries the upstream HTTP status when there was one (0 otherwise), so the HTTP layer can decide what to tell the admin.
func (*ProviderError) Error ¶
func (e *ProviderError) Error() string
func (*ProviderError) Unwrap ¶
func (e *ProviderError) Unwrap() error