boardstorage

package
v0.0.4 Latest Latest
Warning

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

Go to latest
Published: Apr 26, 2026 License: GPL-3.0 Imports: 8 Imported by: 0

Documentation

Overview

Package boardstorage hosts the control-plane side of the board-storage feature (see #283/#284/#285): startup SQL, per-user PG role provisioning, vault static-role registration. Runtime data access happens directly between the R worker and PostgreSQL/vault; nothing in this package is on that path.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EnsureBlockyardAdmin

func EnsureBlockyardAdmin(ctx context.Context, d *db.DB) error

EnsureBlockyardAdmin runs the idempotent startup SQL that creates the blockyard_admin role (and grants it ADMIN OPTION on blockr_user) when board storage is enabled. No-op if the role already exists.

Not a migration: the GRANT syntax is PG16-only, and operators may run on PG13/14/15 with board storage disabled. Migrations are dialect-sensitive but not version-sensitive; this sidesteps that by living in Go startup code guarded by the preflight.

func SetRoleLogin

func SetRoleLogin(ctx context.Context, d *db.DB, roleName string, login bool) error

SetRoleLogin flips a per-user role between LOGIN and NOLOGIN. Called from the admin deactivation path: we don't DROP the role (that would fail once boards reference it as owner via the `owner_sub` FK-adjacent mapping), but flipping LOGIN has the same effect of blocking access while preserving ownership references.

Types

type Provisioner

type Provisioner struct {
	DB                  *db.DB
	Vault               *integration.Client
	VaultMount          string        // cfg.Database.VaultMount
	VaultDBConnName     string        // cfg.Database.VaultDBConnection
	VaultRotationPeriod time.Duration // cfg.Database.VaultRotationPeriod

	// MountAccessor is the opaque accessor of the OIDC/JWT auth mount
	// blockyard users log in through. Required for the entity lookup
	// below; resolved once at startup via AuthMountAccessor and
	// passed in at construction time.
	MountAccessor string
}

Provisioner orchestrates the per-user side-effects at OIDC first login: resolve the vault entity ID for the user's sub, create the PG role named `user_<entity-id>`, register it with vault's database secrets engine, persist the role on the users row.

Every step is idempotent, and `users.pg_role` is written last — so a login interrupted between steps replays cleanly next time (CREATE ROLE IF NOT EXISTS, GRANT is a no-op when already granted, vault POST is upsert). The durable signal that provisioning completed is users.pg_role being non-NULL.

Entity-ID-based role naming (#285) replaces the earlier sub- normalization scheme: the templated per-user vault policy resolves `user_{{identity.entity.id}}` from the user's own token context, which only agrees with blockyard's role name if blockyard also uses the entity ID. Bridging two identifier spaces is how we'd get silent mismatches; using vault's own identifier for both sides removes the bridge.

func (*Provisioner) ProvisionUser

func (p *Provisioner) ProvisionUser(ctx context.Context, sub string) error

ProvisionUser runs the first-login flow for `sub`. Fast-path: returns nil immediately if users.pg_role is already populated. Otherwise resolves the vault entity for this sub, derives the PG role name `user_<entity-id>`, executes the provisioning steps, and writes pg_role to the users row.

On any error, leaves users.pg_role NULL so the next login retries from scratch. Partial state (an orphan PG role or a vault static role without a matching users row) is tolerated because every step short-circuits on re-run.

Jump to

Keyboard shortcuts

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