directory

package
v0.8.2 Latest Latest
Warning

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

Go to latest
Published: Sep 30, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

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

View Source
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

View Source
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

type EntraConfig struct {
	TenantID     string
	ClientID     string
	ClientSecret string
}

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.

const (
	KindUser    Kind = "user"
	KindGroup   Kind = "group"
	KindAppRole Kind = "approle"
	KindAny     Kind = "any"
)

func (Kind) Valid

func (k Kind) Valid() bool

Valid reports whether k is one of the four contract kinds.

type ProviderError

type ProviderError struct {
	Provider string // "entra"
	Op       string
	Status   int
	Err      error
}

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

Jump to

Keyboard shortcuts

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