authsetup

package
v0.56.0 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 30 Imported by: 0

Documentation

Overview

Package authsetup builds the instance's sign-in providers and keeps the live set of them — the ONE place a provider is constructed, whether the environment/config file or the Admin → Identity providers page asked for it.

Why this package exists

Until v0.43.0 the admin page wrote `auth.<name>.*` settings rows that nothing read: the server built its drivers from FILEX_AUTH_DRIVERS and the config file only, once, at boot (release-candidate sweep, 2026-09-21). The page looked like it changed who could sign in and changed nothing — the worst kind of screen. The owner's decision: the page really manages sign-in, applied without a restart, and it can never lock the instance out.

The rules, in one place

  • One construction path: Build. server.go calls it for every provider the environment lists; Live calls it for every provider the page enabled.
  • The environment wins, visibly. A provider the environment lists is built from the environment only; rows the page may hold under the same name are kept but not used, and the page shows the provider read-only with where it comes from (Live.EnvDefines, FromWords).
  • Page providers are ADDED after the environment's, never in front of them: `local` stays the first password judge wherever the operator put it, so a directory that hangs never sits in front of the administrator's password.
  • Password sign-in (`local`) and the recovery sign-in (the bootstrap administrator's break-glass, FILEX_AUTH_RECOVERY_LOGIN) are the environment's to decide and nobody else's. The page cannot add or remove either — see docs/SSO.md, "Why nothing on this page can lock you out".
  • A page provider that cannot start is left out and says why; it never takes anything else down with it (assemble).

Index

Constants

View Source
const (
	OriginEnvironment = "environment"
	OriginPage        = "page"
	// OriginTenant: a tenant's administrator made it for their own tenant
	// (docs/TENANT-ADMIN.md).
	OriginTenant = "tenant"
)

Where a provider's configuration comes from.

View Source
const (
	// InstancesSetting records that the one-time upgrade below has run.
	InstancesSetting = "auth.instances.upgraded"
	// ReviewSetting asks the operator to review the bindings the upgrade made
	// (every provider bound to every tenant), until they dismiss it.
	ReviewSetting = "auth.instances.review_pending"
)
View Source
const (

	// OwnSSOKey names a tenant's own OIDC (its provider row) among its choices.
	OwnSSOKey = "tenant"
)
View Source
const (

	// SchemaSetting records that the one-time upgrade below has run.
	SchemaSetting = "auth.providers.schema"
)

Settings keys. `auth.<name>.<field>` is the shape the page always wrote; two control keys sit beside the fields.

Variables

View Source
var ErrNoDirectorySync = errors.New("this provider is not running or has no directory to sync")

ErrNoDirectorySync is a provider that is not running or cannot sync.

View Source
var ErrNoSSOForTenant = errors.New("oidc: no such sign-in for this realm")

ErrNoSSOForTenant answers a start whose tenant has no such OIDC, or a realm nobody has. One error for both, as the sign-in form answers one way.

View Source
var ErrOIDCOff = errors.New("OIDC is not configured")

ErrOIDCOff answers an OIDC flow when no OIDC provider is running.

View Source
var ErrPasswordSignInOff = errors.New("password sign-in is off")

ErrPasswordSignInOff answers a password sign-in when no driver takes one.

View Source
var ErrSecretKeyRequired = errors.New("a client secret or bind password is stored sealed, and that needs FILEX_SECRET_KEY")

ErrSecretKeyRequired: a secret was given and there is no FILEX_SECRET_KEY to seal it with. A secret is never stored in the clear.

View Source
var ErrSyncRunning = errors.New("a directory sync is already running")

ErrSyncRunning is a second run asked for while one is going.

View Source
var Managed = []string{"oidc", "ldap", "proxy-header", "windows", "pam"}

Managed lists the providers the admin page can configure, in the order they are added to the chain. `local` and `api-token` are not among them: password sign-in is the environment's to decide (see the package comment) and API tokens are always on.

View Source
var OSProviders = []string{"windows", "pam"}

OSProviders are the managed providers that sign people in with an account of the operating system filex runs on. They differ from the rest in two ways the page and the tests rely on: a first sign-in opens an account only when the operator says so (auto_create defaults to OFF, where every other provider defaults to ON), and switching one on needs a test that PASSED - never a confirmation over a failed one.

View Source
var Schema = map[string][]Field{
	"oidc": {
		{Key: "issuer", Kind: FieldText, Required: true},
		{Key: "client_id", Kind: FieldText, Required: true},
		{Key: "client_secret", Kind: FieldSecret},
		{Key: "redirect_url", Kind: FieldText},
		{Key: "scopes", Kind: FieldText},
		{Key: "role_claim", Kind: FieldText},
		{Key: "admin_group", Kind: FieldText},

		{Key: "auto_create", Kind: FieldBool, Default: "true"},
		{Key: "allowed_groups", Kind: FieldText},

		{Key: "trust_email", Kind: FieldBool, Default: "false"},
	},
	"ldap": {
		{Key: "url", Kind: FieldText, Required: true},
		{Key: "base_dn", Kind: FieldText, Required: true},
		{Key: "bind_dn", Kind: FieldText},
		{Key: "bind_password", Kind: FieldSecret},
		{Key: "user_filter", Kind: FieldText},
		{Key: "email_attr", Kind: FieldText},

		{Key: "email_domains", Kind: FieldText},
		{Key: "start_tls", Kind: FieldBool},
		{Key: "ca_file", Kind: FieldText},

		{Key: "ca_pem", Kind: FieldMultiline},
		{Key: "protocol_login", Kind: FieldBool, Default: "true"},

		{Key: "auto_create", Kind: FieldBool, Default: "true"},
		{Key: "allowed_groups", Kind: FieldText},
		{Key: "group_attr", Kind: FieldText, Default: "memberOf"},

		{Key: "group_filter", Kind: FieldText},
		{Key: "group_base_dn", Kind: FieldText},

		{Key: "sync_interval", Kind: FieldText},
		{Key: "sync_filter", Kind: FieldText},
		{Key: "sync_disable_missing", Kind: FieldBool},
		{Key: "sync_groups", Kind: FieldBool, Default: "true"},
		{Key: "sync_group_filter", Kind: FieldText},

		{Key: "show_refusal_reason", Kind: FieldBool, Default: "false"},
	},

	"pam": {
		{Key: "pamtester_path", Kind: FieldText, Default: "/usr/bin/pamtester"},
		{Key: "service", Kind: FieldText, Default: "filex"},
		{Key: "use_sudo", Kind: FieldBool, Default: "true"},
		{Key: "sudo_path", Kind: FieldText, Default: "/usr/bin/sudo"},
		{Key: "timeout_seconds", Kind: FieldText, Default: "10"},
		{Key: "max_concurrent", Kind: FieldText, Default: "4"},
		{Key: "protocol_login", Kind: FieldBool, Default: "true"},

		{Key: "email_domain", Kind: FieldText},

		{Key: "auto_create", Kind: FieldBool, Default: "false"},
		{Key: "allowed_groups", Kind: FieldText},

		{Key: "show_refusal_reason", Kind: FieldBool, Default: "false"},
	},
	"proxy-header": {
		{Key: "trusted_proxies", Kind: FieldText, Required: true},
		{Key: "header_user", Kind: FieldText},
		{Key: "header_email", Kind: FieldText},
		{Key: "header_name", Kind: FieldText},
		{Key: "header_roles", Kind: FieldText},
		{Key: "admin_role", Kind: FieldText},

		{Key: "auto_provision", Kind: FieldBool},
		{Key: "auto_create", Kind: FieldBool, Default: "true"},

		{Key: "allowed_groups", Kind: FieldText},
	},

	"windows": {

		{Key: "auto_create", Kind: FieldBool, Default: "false"},

		{Key: "allowed_groups", Kind: FieldText},

		{Key: "domain", Kind: FieldText},
		{Key: "protocol_login", Kind: FieldBool, Default: "true"},

		{Key: "show_refusal_reason", Kind: FieldBool, Default: "false"},
	},
}

Schema is every field the page may store per provider. A key outside it is dropped on save: the settings table is not a scratchpad, and an unknown key is either a typo or a client that means something this server does not do.

View Source
var TenantDrivers = []string{"oidc", "ldap"}

TenantDrivers are the kinds a tenant's administrator may add for their own tenant: an OIDC and an LDAP. The operating-system providers and the header proxy are the platform operator's alone.

Functions

func Build

func Build(ctx context.Context, store db.Store, name string, cfg map[string]any) (auth.Driver, error)

Build constructs and initialises ONE sign-in provider from a configuration map (the keys the driver's Init reads).

⚠⚠ The single construction path. The environment/config file (server.go) and the admin page (Live.Reload) both come here, so a provider configured on the page is exactly the driver the same values in the environment would have produced — no second, subtly different path. A caller that needs a retry (the environment's OIDC, whose IdP often boots alongside filex) wraps this call; it does not grow its own construction.

func Canonical

func Canonical(name string) string

Canonical returns the name a provider is known by. The environment has always accepted a few spellings of the header driver; the page and the wire use one.

func DraftConfig

func DraftConfig(s *Stored, draft map[string]any, box *secretbox.Box, opts Options) (map[string]any, error)

DraftConfig is the configuration "Test now" checks: what is stored with the form's unsaved values laid over it. A secret the form left blank, or sent back as "***", is the stored one — opened for the test, never returned.

func DriverConfig

func DriverConfig(s *Stored, box *secretbox.Box, opts Options) (map[string]any, bool, error)

DriverConfig turns stored rows into the map the driver's Init reads — secrets opened, booleans as booleans, defaults filled — plus whether a directory provider (LDAP, Windows) also judges passwords on the file protocols.

⚠ The opened map lives in memory only, for the length of a build or a test. It is never logged and never sent anywhere but the driver.

func EnsureAnchors added in v0.50.0

func EnsureAnchors(ctx context.Context, store db.Store, env []Entry, log *slog.Logger) error

EnsureAnchors makes sure every existing provider has its row, and binds a row it had to create:

  • before the one-time upgrade (InstancesSetting unset): to the platform's own tenant AND to every tenant that exists now (source `upgrade`) - what happened before 0.50, where one LDAP served every tenant - and it asks the operator to review the bindings (ReviewSetting) when there are tenants to review;
  • after it: to the platform's own tenant only (a provider added later serves nobody else until somebody binds it).

A pin (FILEX_LDAP_PROVIDER, FILEX_HEADER_PROVIDER) is an implicit binding to the pinned tenant (source `pin`), whenever it is read. Idempotent: run at every Start and Reload.

func EnvTrustEmail added in v0.50.0

func EnvTrustEmail(ctx context.Context, store trustStore, explicit *bool) (trust, byUpgrade bool)

EnvTrustEmail is the environment OIDC's trust: what FILEX_OIDC_TRUST_EMAIL (or the config file's trust_email) says when it says anything; otherwise the answer the upgrade to 0.50 took once - on when the database had accounts that came through SSO then - and off on a fresh install. byUpgrade reports that the upgrade's answer is the one in force, for the page.

func ForgetTrustUpgradeInstance added in v0.50.0

func ForgetTrustUpgradeInstance(ctx context.Context, store trustStore, slug string)

ForgetTrustUpgradeInstance: an instance's trust was saved by someone, so it is theirs now and the page stops saying the upgrade set it.

func ForgetTrustUpgradeTenant added in v0.50.0

func ForgetTrustUpgradeTenant(ctx context.Context, store trustStore, id int64)

ForgetTrustUpgradeTenant is ForgetTrustUpgradeInstance for a tenant's own OIDC on its row.

func FromWords

func FromWords(from, lang string) string

FromWords says where an environment provider is defined, in the reader's language: config.Auth.DriversFrom is "FILEX_AUTH_DRIVERS", "file:<path>" or "default"; anything else is shown as it is.

The words are the server catalogue's (`server.auth_provider.from_*`), so a language pack's language reads them too — not the English half of a hard-coded en/tr pair.

func IsManaged

func IsManaged(name string) bool

IsManaged reports whether the page may configure a provider.

func IsOS added in v0.50.0

func IsOS(name string) bool

IsOS reports whether a provider is an operating-system sign-in.

func LoadStored

func LoadStored(ctx context.Context, store db.Store) (map[string]*Stored, error)

LoadStored reads every managed provider's rows.

func PrimarySlug added in v0.50.0

func PrimarySlug(slug, driver string) bool

PrimarySlug reports whether a slug names the first instance of a driver, whose configuration lives in the `auth.<driver>.*` settings rows.

func RowConfigJSON added in v0.50.0

func RowConfigJSON(s *Stored) string

RowConfigJSON is the inverse of StoredOfRow: what a row instance stores.

func Save

func Save(ctx context.Context, store db.Store, next *Stored) error

Save writes a provider's next state. A save is a review: it clears the "saved before v0.43.0, never applied" mark.

func SyncKeyPrefix added in v0.52.0

func SyncKeyPrefix(name string) string

SyncKeyPrefix is the prefix of every settings row an instance's directory sync keeps — what removing the instance removes.

func TenantGuard added in v0.50.0

func TenantGuard(insecureAllowed bool) func(map[string]any)

TenantGuard is what a tenant's own instance is handed on top of its configuration (docs/TENANT-ADMIN.md, the owner's decision of 2026-10-01): `guarded` (the connection goes through internal/netguard, and an LDAP must be encrypted) unless the platform operator allowed the tenant insecure and internal-network providers, and never a file on the server (`ca_file`: a tenant pastes its CA as `ca_pem`).

func UpgradeLegacy

func UpgradeLegacy(ctx context.Context, store db.Store, box *secretbox.Box, log *slog.Logger) error

UpgradeLegacy runs once per database: rows the page saved before v0.43.0 were never applied by any server, so turning them on now would switch on, on upgrade, a sign-in configuration an operator may have typed long ago and forgotten. Each such provider is imported SWITCHED OFF and marked "saved before this version, never applied" for the page to show; the operator reviews and enables it.

A secret among those rows is sealed with FILEX_SECRET_KEY. With no key it cannot be sealed, and it is not kept in the clear either: it is cleared (it was never used), the page shows it as not set, and the log says which provider and which field — never the value.

func UpgradeOIDCTrust added in v0.50.0

func UpgradeOIDCTrust(ctx context.Context, store db.Store, log *slog.Logger) error

UpgradeOIDCTrust runs once, at the first start of 0.50 on a database that already had accounts (the mark migration 00079 wrote): trust ON for every OIDC that exists - the page's first one, every row instance (a tenant's own among them), every tenant's own OIDC on its row - recorded so the page can say the upgrade set it; and the environment OIDC's answer (EnvTrustEmail). Called before the environment's providers are built.

func WithRecoveryLogin

func WithRecoveryLogin(loginDrvs []auth.LoginDriver, enabled bool, store db.Store) ([]auth.LoginDriver, bool)

WithRecoveryLogin appends the recovery sign-in driver when no enabled login driver is `local` and recovery is on, and reports whether it did.

The owner's ruling, 2026-09-14: on an installation that signs in through an identity provider alone, the administrator created at installation must still be able to sign in with a password — for recovery only. Without it an unreachable IdP, an expired client secret or a broken realm locks out the one person who can repair filex's side of it. local.RecoveryLogin accepts that account and no other, so password sign-in stays off for everyone else.

⚠ It is switched off by the environment only (FILEX_AUTH_RECOVERY_LOGIN): the admin page, which can break the identity provider it would recover from, can never switch off the way back.

It goes LAST: a directory driver in the list judges its own accounts first.

func WithSessionAuthenticator

func WithSessionAuthenticator(enabled []auth.Driver, store db.Store) []auth.Driver

WithSessionAuthenticator makes sure something in the chain can turn a filex session back into a user, whichever login drivers are enabled.

⚠⚠ Issue #24. Every sign-in — a password, an LDAP bind, an OIDC callback — ends in the same sessions row and the same `filex_session` cookie, but only the `local` driver's Authenticate reads that row: OIDC's and LDAP's answer "unauthorized" by design. With `FILEX_AUTH_DRIVERS=oidc` the callback minted a session the very next request refused, so nobody could sign in and every open session died on the restart that applied the setting.

The validator is not a LoginDriver, so it enables no password sign-in; and when `local` is already in the list it adds nothing, because that driver does the same lookup.

Types

type Bindings added in v0.50.0

type Bindings struct {
	// Main is the platform's own tenant (the supertenant), 0 when the install
	// has none.
	Main int64
	// contains filtered or unexported fields
}

Bindings is which tenant signs in through which instance, as one reload read it.

func ReadBindings added in v0.50.0

func ReadBindings(ctx context.Context, store db.Store) (*Bindings, error)

ReadBindings reads the bindings and the tenants.

func (*Bindings) Bound added in v0.50.0

func (b *Bindings) Bound(instanceID, tenantID int64) bool

Bound reports whether a tenant signs in through an instance. An entry with no row (instance 0: `local`, or a provider built before its row existed) serves everybody, as every provider did before 0.50.

func (*Bindings) Insecure added in v0.50.0

func (b *Bindings) Insecure(tenantID int64) bool

Insecure reports whether a tenant's own providers may reach internal addresses and plain ldap:// (providers.allow_insecure_auth).

func (*Bindings) Tenants added in v0.50.0

func (b *Bindings) Tenants() []int64

Tenants lists every tenant the reload saw.

func (*Bindings) TenantsOf added in v0.50.0

func (b *Bindings) TenantsOf(instanceID int64) []int64

TenantsOf lists the tenants bound to an instance, sorted.

type Change

type Change struct {
	Enabled *bool
	Values  map[string]any
}

Change is what a save asks for. Values holds the fields sent; a secret sent as "" or "***" keeps the stored one (the page never has it to send back).

type Directory

type Directory interface {
	VerifyPassword(ctx context.Context, identifier, password string) (*model.User, error)
}

Directory is an external password authority (protocolauth.Directory).

type Entry

type Entry struct {
	// Name is the DRIVER (oidc, ldap, pam, windows, proxy-header, local).
	Name string
	// Slug names the instance (auth_instances.slug): the driver's name for
	// its first instance, so "the ldap" is still addressed as `ldap`.
	Slug string
	// InstanceID is the instance's row (auth_instances.id); 0 for `local`,
	// which is not an instance, and for a provider built before its row
	// existed. An entry with no row serves every tenant (Bindings.Bound).
	InstanceID int64
	// Label is what the sign-in page calls it ("" = the driver's words).
	Label string
	// Owner is the tenant a tenant's own instance belongs to (0 = none).
	Owner  int64
	Origin string
	// From says where an environment provider is defined
	// (FILEX_AUTH_DRIVERS, the config file, the built-in default).
	From string
	// Driver is nil when the provider could not start; Err says why.
	Driver auth.Driver
	Err    string
	// Directory: an LDAP provider that also judges passwords on the file
	// protocols (WebDAV, SFTP, FTPS, S3, NFS).
	Directory bool
	// Display is the configuration shown on the page for an environment
	// provider — secrets left out (Secrets says which are set).
	Display map[string]any
	// Secrets names the secret fields an environment provider has set.
	Secrets []string
	// SetByUpgrade names the fields whose value the upgrade to 0.50 chose
	// for an environment provider (trust_email, UpgradeOIDCTrust), for the
	// page to say so.
	SetByUpgrade []string
	// contains filtered or unexported fields
}

Entry is one provider the instance knows of.

func NewEnvEntry

func NewEnvEntry(name, from string, cfg map[string]any, d auth.Driver, err error, directory bool) Entry

NewEnvEntry records a provider the environment/config file defines. cfg is what Build was given; the page is shown a copy with every secret removed.

func (Entry) Config

func (e Entry) Config() map[string]any

Config is the configuration the environment gave a provider (secrets included; in memory only).

func (Entry) Live

func (e Entry) Live() bool

Live reports whether the provider is running.

type Field

type Field struct {
	Key      string    `json:"key"`
	Kind     FieldKind `json:"kind"`
	Required bool      `json:"required,omitempty"`
	// Default is what an absent value means, said so the page can show it.
	Default string `json:"default,omitempty"`
}

Field is one setting a managed provider takes on the page.

func FieldOf

func FieldOf(name, key string) (Field, bool)

FieldOf returns a provider's field by key.

func TenantFields added in v0.50.0

func TenantFields(driver string) []Field

TenantFields are the fields a tenant's administrator may set on their own OIDC or LDAP: the schema's, without the ones that name something on the server (`ca_file`).

type FieldKind

type FieldKind string

FieldKind is how a page field is stored and handed to the driver.

const (
	FieldText   FieldKind = "text"
	FieldSecret FieldKind = "secret"
	FieldBool   FieldKind = "bool"
	// FieldMultiline is text that keeps its line breaks (a PEM certificate).
	FieldMultiline FieldKind = "multiline"
)

Field kinds.

type Live

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

Live owns the running providers and swaps them when the page saves.

func New

func New(ctx context.Context, opts Options, env []Entry) (*Live, error)

New prepares a Live over the environment's providers (already built by the caller through Build) and installs a first set holding only those, so the password sign-in the environment configured works before anything the page saved has been looked at.

func (*Live) AdminPaths

func (l *Live) AdminPaths(ctx context.Context, without string) []string

AdminPaths lists the ways an administrator can still sign in with the running set, leaving out one provider (the one about to be switched off, named by its slug: the driver's name for its first instance).

  • "password": password sign-in is on (`local`) and at least one enabled administrator has a password;
  • "recovery": the bootstrap administrator's recovery sign-in is on, and that account exists, is enabled and has a password;
  • the slug of every other running identity provider or directory.

⚠ An identity provider counts as a way in although filex cannot prove an administrator's account is on the other side of it: the rule this serves is "never switch off the LAST way in", and a provider that is running is a way in until shown otherwise. The two password paths are checked for real, because an instance whose administrators all came in through SSO has a password form nobody can use.

On a multi-tenant install this is the platform's own tenant's answer: the providers bound to it (AdminPathsIn).

func (*Live) AdminPathsIn added in v0.50.0

func (l *Live) AdminPathsIn(ctx context.Context, tenantID int64, exclude func(Entry) bool) []string

AdminPathsIn is AdminPaths for one tenant (docs/TENANT-ADMIN.md, "the last way in"): the providers bound to it that exclude does not leave out, and the password form for an enabled administrator OF THAT TENANT who has a password. Recovery is the platform's own tenant's only.

func (*Live) CanSync added in v0.52.0

func (l *Live) CanSync(name string) bool

CanSync reports whether a provider is running and can sync its directory.

func (*Live) Current

func (l *Live) Current() *Set

Current is the running set.

func (*Live) Dir

func (l *Live) Dir() Directory

Dir is the directory the file protocols consult.

func (*Live) DirectoryFor added in v0.52.0

func (l *Live) DirectoryFor(email string, tenant int64) (slug, label string, ok bool)

DirectoryFor names the running LDAP instance that owns an address (its email_domains) — whose people arrive by sign-in and sync, so an account made by hand for that address would be a local one the directory could never sign in. ok is false when no directory claims it.

⚠ Only the directories that sign people in to tenant (0: the platform's own; Set.Serves): the account is made there, and another tenant's own directory is no business of the caller's - asked about every instance, a tenant's administrator was told another tenant's directory label.

func (*Live) EnvDefines

func (l *Live) EnvDefines(name string) (Entry, bool)

EnvDefines reports whether the environment lists a provider — in which case it is the effective one, and the page may not change it.

func (*Live) Environment

func (l *Live) Environment() []Entry

Environment is the environment's providers, in the order it listed them.

func (*Live) LastSync added in v0.52.0

func (l *Live) LastSync(ctx context.Context, name string) (*auth.DirectorySyncReport, error)

LastSync returns a provider's last report, or nil when it never ran.

func (*Live) Login

func (l *Live) Login() auth.LoginDriver

Login is the password sign-in the handlers hold. It always asks the running set, so a reload changes what it does without re-wiring anything.

func (*Live) LoginName

func (l *Live) LoginName() string

LoginName names the password chain, for the boot line.

func (*Live) OIDC

func (l *Live) OIDC() auth.OIDCDriver

OIDC is the OIDC flow the handlers hold.

func (*Live) OnSwap

func (l *Live) OnSwap(f func(*Set))

OnSwap registers a callback run with every new set (and once, now).

func (*Live) Options

func (l *Live) Options() Options

Options returns the options Live runs with.

func (*Live) Reload

func (l *Live) Reload(ctx context.Context) error

Reload reads the page's providers and swaps in the set they make.

⚠ A provider that did not change is not rebuilt (an OIDC discovery per save of an unrelated LDAP field would be a network round trip for nothing), and a provider that fails to start is left out with its reason — the environment's providers and every other page provider stay exactly as they were. There is no path through here that drops `local`.

It also brings the providers' rows up to date (EnsureAnchors: a provider saved since the last reload gets its row, bound to the platform's own tenant) and reads which tenant signs in through which (ReadBindings).

func (*Live) RunDirectorySync added in v0.52.0

func (l *Live) RunDirectorySync(ctx context.Context)

RunDirectorySync runs every provider's directory sync on its interval until ctx ends: at each tick, a provider whose last run started an interval ago or more (or never ran) runs. Blocks — start it with go.

func (*Live) SSOChoices added in v0.50.0

func (l *Live) SSOChoices(ctx context.Context, p *model.Provider, tenantID int64, host string) []SSOChoice

SSOChoices lists the OIDC a tenant may sign in through, in order: its own (provider row) first, then the instances bound to it.

func (*Live) SetProviderAlarm added in v0.50.0

func (l *Live) SetProviderAlarm(f func(name, reason string))

SetProviderAlarm registers what happens when an operating-system provider the admin page switched on cannot start: the provider is left out of the running set (every other way in keeps working) and the operator hears about it, once per distinct reason. A failure that happened before the alarm was registered (the boot, which builds providers before notifications exist) is raised now.

The reason is the driver's error text, which never holds a secret (Build's errors are about the configuration and the machine, not the values).

func (*Live) Start

func (l *Live) Start(ctx context.Context)

Start imports what an earlier version saved (switched off), builds the page's enabled providers and, when one could not start, keeps re-trying it in the background.

func (*Live) StartSync added in v0.52.0

func (l *Live) StartSync(ctx context.Context, name, trigger string) error

StartSync claims a provider's sync and runs it in the background: the error is that it could not start (ErrNoDirectorySync, ErrSyncRunning); the run's own outcome is its report (LastSync).

func (*Live) SyncInterval added in v0.52.0

func (l *Live) SyncInterval(name string) time.Duration

SyncInterval is a running provider's sync interval (0: none).

func (*Live) SyncNow added in v0.52.0

func (l *Live) SyncNow(ctx context.Context, name, trigger string) (*auth.DirectorySyncReport, error)

SyncNow runs a provider's directory sync and keeps its report. trigger is "manual" or "schedule". The run is detached from ctx's cancellation (a closed browser tab must not stop it half way) but bounded by syncTimeout.

func (*Live) Syncing added in v0.52.0

func (l *Live) Syncing(name string) bool

Syncing reports whether a provider's sync is running now.

type Options

type Options struct {
	Store         db.Store
	Box           *secretbox.Box
	MultiTenant   bool
	RecoveryLogin bool
	PublicURL     string
	// LoginEmailToken is the installation's e-mail token (FILEX_OS_LOGIN_EMAIL_TOKEN):
	// what follows the `@` of an account a provider knows only by login name.
	// Environment only — never a page field: it is part of every such account's
	// address, and changing it later makes a second account of the same person
	// (identity.EmailToken).
	LoginEmailToken string
	// BuildTimeout bounds one page provider's construction (an OIDC
	// discovery that hangs must not hold a save, or the boot, hostage).
	BuildTimeout time.Duration
	// RetryBackoff re-tries, after boot, a page provider that could not
	// start — its IdP may be booting alongside filex.
	RetryBackoff []time.Duration
	Log          *slog.Logger
}

Options is what Live needs from the server.

type SSOChoice added in v0.50.0

type SSOChoice struct {
	// Key names it in /api/auth/oidc/start?instance=: the instance's slug, or
	// OwnSSOKey for the tenant's own OIDC on its provider row.
	Key   string
	Label string
	// contains filtered or unexported fields
}

SSOChoice is one OIDC a tenant may sign in through.

type Set

type Set struct {
	Entries []Entry
	// contains filtered or unexported fields
}

Set is one immutable arrangement of the providers. Live swaps whole sets, so a request never sees half of a reload.

func (*Set) Bindings added in v0.50.0

func (s *Set) Bindings() *Bindings

Bindings is which tenant signs in through which instance, as the reload that made this set read it (nil on a set built before any reload).

func (*Set) Entry

func (s *Set) Entry(name string) (Entry, bool)

Entry returns a provider by its slug: the driver's name for the first instance of a driver, so `ldap` is still "the LDAP".

func (*Set) For added in v0.50.0

func (s *Set) For(tenantID int64) *View

For is a tenant's view. On a single-tenant install every tenant's view is the whole set. 0 is the platform's own tenant.

func (*Set) HasDirectory

func (s *Set) HasDirectory() bool

HasDirectory reports whether a directory judges passwords on the file protocols.

func (*Set) MultiTenant added in v0.50.0

func (s *Set) MultiTenant() bool

MultiTenant reports whether the set arranges providers per tenant.

func (*Set) Names

func (s *Set) Names() []string

Names are the sign-in methods the login page offers: every provider the environment lists (as before) and every page provider that is running.

func (*Set) PasswordLogin

func (s *Set) PasswordLogin() bool

PasswordLogin reports whether the password form is answered at all.

func (*Set) Recovery

func (s *Set) Recovery() bool

Recovery reports whether the bootstrap administrator's recovery sign-in is on.

func (*Set) Serves added in v0.52.0

func (s *Set) Serves(e Entry, tenantID int64) bool

Serves reports whether an entry signs people in to a tenant (0: the platform's own): always on an install with one tenant; on a multi-tenant one when it is bound to that tenant and, a tenant's own instance, it is that tenant's - the rule buildView arranges a tenant's sign-in by.

type Stored

type Stored struct {
	Name    string
	Enabled bool
	// Legacy: saved before v0.43.0 and never applied — review and enable.
	Legacy bool
	// Values are the stored field values; a secret stays SEALED here.
	Values map[string]string
	// Exists: at least one row is stored for this provider.
	Exists bool
}

Stored is what the settings table holds for one managed provider.

func Merge

func Merge(cur *Stored, ch Change, box *secretbox.Box) (*Stored, []string, error)

Merge applies a change to what is stored, without writing anything, and returns the next stored state plus the names of the fields that changed (never their values — this list goes into the audit row).

func StoredOfRow added in v0.50.0

func StoredOfRow(a *model.AuthInstance) (*Stored, error)

StoredOfRow reads a row instance's configuration (config_json: field → value, secrets sealed) as the Stored the page's primary instance reads from the settings rows, so DriverConfig builds both the same way.

type TrustUpgrade added in v0.50.0

type TrustUpgrade struct {
	// Instances are page and row instances, by slug (`oidc` is the page's
	// first OIDC).
	Instances []string `json:"instances,omitempty"`
	// Tenants are tenants whose own OIDC on their row got trust.
	Tenants []int64 `json:"tenants,omitempty"`
}

TrustUpgrade is what the upgrade switched on: which providers' trust is still the value it set (until someone saves it).

func ReadTrustUpgrade added in v0.50.0

func ReadTrustUpgrade(ctx context.Context, store trustStore) TrustUpgrade

ReadTrustUpgrade is what the upgrade switched on, as still recorded.

func (TrustUpgrade) Instance added in v0.50.0

func (t TrustUpgrade) Instance(slug string) bool

Instance reports whether an instance's trust is the upgrade's.

func (TrustUpgrade) Tenant added in v0.50.0

func (t TrustUpgrade) Tenant(id int64) bool

Tenant reports whether a tenant's own OIDC's trust is the upgrade's.

type View added in v0.50.0

type View struct {
	Tenant int64

	// SSO are the running OIDC instances bound to the tenant, in the set's
	// order (the environment's first).
	SSO []Entry
	// contains filtered or unexported fields
}

View is one tenant's arrangement of the providers bound to it: what a sign-in for that tenant is judged by.

func (*View) Names added in v0.50.0

func (v *View) Names() []string

Names are the providers the tenant signs in through.

func (*View) PasswordLogin added in v0.50.0

func (v *View) PasswordLogin() bool

PasswordLogin reports whether a password sign-in for the tenant is answered at all.

func (*View) Recovery added in v0.50.0

func (v *View) Recovery() bool

Recovery reports whether the tenant has the recovery sign-in (the platform's own tenant only).

Jump to

Keyboard shortcuts

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