Documentation
¶
Overview ¶
Package seed provides declarative database seeding from JSON files. It creates clients and users idempotently — existing entries (matched by name for clients, email for users) are skipped. Activated via the VAULT_SEED_FILE env var (startup) or the "vault seed" CLI command.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ReservedAdminRoles = map[string]bool{ "admin": true, "super_admin": true, }
ReservedAdminRoles are role names the User table is forbidden from granting. Only the AdminUser table (the admins seed array) may hold these, and the auth/login JWT issuer strips them from user.Roles in case a row was written directly by SQL.
This deliberately does NOT mirror rbac.ValidRoles, and that difference is the thing to understand before editing it.
vault42 carries two role vocabularies that overlap by name:
- rbac.ValidRoles, the admin-plane tiers {viewer, operator, super_admin}, which govern auth.admin_users.role and reach only the admin gateway.
- auth.app_roles, the end-user roles a JWT carries. Migration 005 seeds 'user', 'viewer' and 'operator' there as reserved core roles.
So 'viewer' and 'operator' are legitimate names for an ordinary user to hold. Adding them here would strip them from every user JWT that carries them and break whatever a relying party does with them. That is an outage, not a hardening, which is why the obvious-looking symmetry is not applied.
'admin' is listed although rbac defines no such tier. It is harmless and predates the current vocabulary, but it is why this list cannot be read as an inventory of real roles.
The residual risk is ambiguity rather than escalation: a relying party seeing roles ["operator"] cannot tell which vocabulary it came from. In practice it can only be the app role, because an admin tier never reaches a user JWT. Admin authorization runs off auth.admin_users.role through a session token and never off this claim. Resolving the ambiguity means renaming one vocabulary, which is a data migration against deployed installs.
TestReservedAdminRolesDecisionIsRevisitedWhenATierIsAdded holds the relationship, so a new admin tier forces a decision here rather than silently becoming grantable to users.
Functions ¶
func FilterUserRoles ¶
FilterUserRoles removes admin-tier role names from a user's role list. Returns a new slice; never mutates the input. Intended for the JWT issuance path so a manually-poked DB row can never grant admin access.
func Run ¶
Run executes the seed file against the database. Existing entries are skipped (idempotent). Client secrets are generated and printed to stdout.
pepper is the HMAC-pepper applied to seeded user passwords, and must be the same value AuthService verifies logins with. An empty pepper is legal, for deployments that run without VAULT_PEPPER_FILE, but it has to be passed explicitly: a seeded account whose hash was built with a different pepper than login uses can never authenticate, and nothing reports that, because both halves are individually correct.
Client secrets are never peppered. They are full-entropy random tokens where a pepper adds nothing.
func RunAdmins ¶
func RunAdmins(ctx context.Context, sf *File, admins repository.AdminUserRepository, pepper string) error
RunAdmins seeds admin gateway users from the seed file. Existing admins (matched by username) are skipped. This is safe to call without admin entries in the seed file — it simply does nothing.
pepper is the optional HMAC-pepper applied to admin password hashes; empty means no pepper (back-compat). Must match the value used by the admin gateway login flow, otherwise admins cannot authenticate.
Types ¶
type AdminSeed ¶
type AdminSeed struct {
Username string `json:"username"`
Password string `json:"password"`
Role string `json:"role"`
}
AdminSeed defines an admin gateway user to create. Password must be at least 15 characters. Role must be one of the admin tiers rbac.IsValidRole accepts, which are the roles auth.admin_roles holds and nothing else. The end-user role names a JWT can carry are a separate vocabulary and are not valid here even where the two spell a name the same way.
type ClientSeed ¶
type ClientSeed struct {
Name string `json:"name"`
Role string `json:"role"`
Scopes []string `json:"scopes"`
RedirectURIs []string `json:"redirect_uris"`
}
ClientSeed defines a service client to create.
type Deps ¶
type Deps struct {
Users repository.UserRepository
Clients repository.ClientRepository
}
Deps holds the repositories needed for seeding.
The pepper is deliberately NOT a field here. It was one, and cmd/vault's startup path left it unset while the CLI and the admin gateway set it, so the server seeded every user with an unpeppered hash that login could never match. A struct field that is merely absent compiles, and an empty pepper is a legal configuration, so nothing anywhere could tell the omission from the choice. It is a positional parameter of Run now, which makes forgetting it a compile error instead.
type File ¶ added in v1.0.3
type File struct {
Clients []ClientSeed `json:"clients"`
Users []UserSeed `json:"users"`
Admins []AdminSeed `json:"admins,omitempty"`
}
File is the top-level structure of a seed JSON file. See seed.example.json in the repository root for the expected format.
type UserSeed ¶
type UserSeed struct {
Email string `json:"email"`
Password string `json:"password"`
DisplayName string `json:"display_name"`
Locale string `json:"locale"`
EmailVerified *bool `json:"email_verified"`
Roles []string `json:"roles,omitempty"`
}
UserSeed defines a user to create. EmailVerified defaults to true when omitted (dev convenience). Locale defaults to "en". Password must be at least 15 characters (NIST SP 800-63B).
Roles is the JWT "roles" claim baked into the access token at login. The validator REJECTS the admin-tier role names ("admin", "super_admin") here — those are reserved for the AdminUser table (admins seed array) and reachable only through the admin gateway. Other strings are passed through verbatim and become role claims (e.g. "viewer", "operator"). Empty roles default to ["user"] at JWT issuance time.