directory

package
v0.42.0 Latest Latest
Warning

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

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

README

Directory facts and SCIM groups

The directory maps its source data into Writ's user and membership objects. Users need an authentication ID. With SCIM, user externalId supplies that identity; the SCIM user's server-issued id remains its provisioning key. Inactive users are absent from the authorization lookup. Administration can still inspect inactive users.

Each group membership contains the group's current display name and SCIM resource ID. An optional group externalId is exposed as an additional attribute. Group names need not be unique. group:ops matches members of any current group named ops; group{id:...} follows one resource across renames. Renaming a group removes its old name immediately. Deleting and recreating a group under the same name makes name-based rules follow the replacement; ID-based rules do not follow it.

There are no historical group-name bindings. epithet directory groups list shows current names and resource IDs; epithet directory groups audit retains provisioning history and historical binding events. The bind command is removed. Existing administrator group grants also match current names.

Static YAML supplies structured memberships without requiring group IDs:

users:
  - id: oidc-subject
    userName: alice
    name: [alice, alice@example.com]
    attributes:
      department: engineering
      role: [developer, oncall]
    groups:
      - name: [operators]
        attributes:
          environment: production

Omitting name uses userName as the selectable name when present. An explicit empty name collection supplies no selectable names. userName remains certificate presentation, with ID fallback. Additional attributes must be strings, collections of strings, or null, and cannot replace the defined name or id fields.

See the fact API contract for the HTTP shape and Writ specification for matching rules.

Database upgrade

The application creates new databases directly at schema version 2, without an aliases table. It opens version 2 databases normally and rejects other schema versions without upgrading them. PRAGMA user_version identifies the schema the binary understands; the application does not manage migrations.

For the current single deployment, upgrades use disposable external tooling that is not checked into this repository. The version 1 to 2 conversion checks the source version, drops aliases, and advances the version in one transaction. Resources, memberships, resource versions, directory revision identity, and audit history remain intact, including historical binding events.

Stop the directory service and back up its database, including WAL state if present, before running the external tool. Then start the new binary against the converted database. Retain the backup for rollback; an older binary cannot open schema version 2. Review policies and administrator grants that depended on historical names, recompile Writ policies for IL schema 3, and update static group entries to objects.

A supported migration framework, potentially Goose, is deferred in YATL gt3vjk35. No framework or automatic upgrade path is introduced by this change.

Documentation

Overview

Package directory owns managed identities, administration, and the user and group facts supplied to authorization.

Index

Constants

View Source
const DefaultAuditLimit = 100
View Source
const MaxAuditLimit = 1000

Variables

View Source
var (
	ErrInvalid  = errors.New("invalid directory resource")
	ErrNotFound = errors.New("directory resource not found")
	ErrConflict = errors.New("directory uniqueness conflict")
	ErrVersion  = errors.New("directory revision changed")
)

Functions

This section is empty.

Types

type AuditEvent

type AuditEvent struct {
	Sequence   AuditSequence
	Revision   uint64
	Time       time.Time
	Actor      string
	Action     string
	ID         string
	Alias      string
	PreviousID string
}

type AuditSequence

type AuditSequence uint64

AuditSequence identifies one audit event, independently of directory revisions: several events can belong to the same mutation. Zero starts at the beginning.

type Directory

type Directory interface {
	LookupUser(context.Context, string) (*User, Revision, error)
}

Directory resolves users within one configured identity provider. LookupUser returns the user and revision from one coherent snapshot. Missing users return nil with a nonempty revision; storage failure is an error, never a missing user.

type Group

type Group struct {
	Metadata
	ExternalID  string
	DisplayName string
	MemberIDs   []string // Stored user IDs, not provider ExternalIDs.
}

Group owns direct user memberships. DisplayName changes affect name selectors; the stable resource ID continues to select the same group.

type GroupSnapshot added in v0.42.0

type GroupSnapshot struct {
	Revision uint64
	Groups   []GroupSummary
}

GroupSnapshot contains current groups from one coherent revision.

type GroupSummary added in v0.42.0

type GroupSummary struct {
	ID          string
	DisplayName string
	ExternalID  string
}

type ManagedUser

type ManagedUser struct {
	Metadata
	ExternalID   string
	UserName     string
	Active       bool
	UserType     string
	Department   string
	Organization string
}

ManagedUser contains the identity and attributes used by authorization. Active is explicit at the storage boundary; the HTTP adapter supplies the SCIM default. Group membership is owned by Group, not by the user.

type Metadata

type Metadata struct {
	ID       string
	Version  uint64
	Created  time.Time
	Modified time.Time
}

Metadata is assigned by storage. ID identifies the provisioned user or group; it is distinct from the provider's ExternalID used for authentication. Version changes on mutations, including membership cleanup after user deletion.

func (Metadata) ETag

func (m Metadata) ETag() string

type Revision

type Revision string

Revision identifies an opaque, coherent directory snapshot. Callers may compare revisions for equality but must not interpret their contents.

type Static

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

Static owns directory facts loaded from YAML at startup. Host and realm sections in combined configuration files are accepted but supply no facts. Reloading requires constructing a new directory.

func NewStatic

func NewStatic(paths []string) (*Static, error)

NewStatic concatenates the user records in paths. IDs are required; duplicate IDs or nonempty user names and unknown fields are load errors. An omitted active field defaults to true. The caller retains ownership of the files.

func (*Static) DirectoryRevision

func (s *Static) DirectoryRevision() string

DirectoryRevision identifies the user records independently of ignored host configuration, preserving the existing static directory snapshot identity.

func (*Static) ListUserFacts

func (s *Static) ListUserFacts(_ context.Context) ([]User, Revision, error)

func (*Static) LookupUser

func (s *Static) LookupUser(_ context.Context, id string) (*User, Revision, error)

LookupUser implements Directory.

type Store

type Store interface {
	Directory
	UserLister
	CreateUser(context.Context, ManagedUser) (ManagedUser, error)
	ReplaceUser(ctx context.Context, id string, user ManagedUser, match string) (ManagedUser, error)
	DeleteUser(ctx context.Context, id, match string) error
	GetUser(context.Context, string) (ManagedUser, error)
	ListUsers(ctx context.Context, start, count int) ([]ManagedUser, int, error)
	CreateGroup(context.Context, Group) (Group, error)
	ReplaceGroup(ctx context.Context, id string, group Group, match string) (Group, error)
	DeleteGroup(ctx context.Context, id, match string) error
	GetGroup(context.Context, string) (Group, error)
	ListGroups(ctx context.Context, start, count int) ([]Group, int, error)
	Groups(context.Context) (GroupSnapshot, error)
	// Audit returns at most limit events after the exclusive sequence cursor,
	// in sequence order. Zero limit uses DefaultAuditLimit; limits above
	// MaxAuditLimit are invalid. An empty result means the cursor is caught up.
	Audit(ctx context.Context, after AuditSequence, limit int) ([]AuditEvent, error)
	Close() error
}

Store owns directory persistence and all changes to authorization indexes. Each mutation commits the user or group, memberships, audit and revision together. No database handles or multi-call transaction protocol escape here. Get methods return independent snapshots; each List returns a typed slice and total count from one snapshot (start is one-based). LookupUser resolves identity, memberships and current group facts in a single read. Backend failures stay errors.

Replace/Delete accept an optional resource ETag ("*" requires existence). A stale precondition leaves state unchanged.

type User

type User struct {
	UserName     string
	ID           string
	Active       bool
	Groups       []object.Object
	Names        []string
	Attributes   object.Attributes
	UserType     string
	Department   string
	Organization string
}

func (User) Facts added in v0.42.0

func (u User) Facts() object.User

Facts projects this built-in directory record onto the provider contract. The source schema owns its field mapping; the matcher does not know SCIM.

type UserLister

type UserLister interface {
	ListUserFacts(context.Context) ([]User, Revision, error)
}

UserLister supplies administration with all users, including inactive users, and their policy-visible groups from one snapshot. IDs are authentication identities, not provisioning resource IDs. Results are ordered by userName then ID; users and group slices are independent copies, with groups sorted by name. Authorization consumers need only Directory.LookupUser.

Directories

Path Synopsis
Package scim adapts SCIM provisioning to the directory storage contract.
Package scim adapts SCIM provisioning to the directory storage contract.
Package sqlitestore implements the managed directory using embedded SQLite.
Package sqlitestore implements the managed directory using embedded SQLite.

Jump to

Keyboard shortcuts

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