Documentation
¶
Overview ¶
Package adminapi provides the admin gateway HTTP handlers, middleware, and server-rendered frontend for The Vault's RBAC admin interface.
Index ¶
- func EnsureFirstAdmin(ctx context.Context, admins repository.AdminUserRepository, ...) error
- func GetAdmin(ctx context.Context) *model.AdminUser
- func GetSession(ctx context.Context) *model.AdminSession
- func LocalOnly(killswitch bool, auditRepo repository.AuditRepository) func(http.Handler) http.Handler
- func MaxBody(maxBytes int64) func(http.Handler) http.Handler
- func NewRouter(auth *AuthHandler, api *Handler, opts ...RouterOpts) http.Handler
- func RBACCheck(perm rbac.Permission, auditLog *audit.Logger) func(http.Handler) http.Handler
- func Recovery(next http.Handler) http.Handler
- func RejectProxyHeaders(next http.Handler) http.Handler
- func RequestID(next http.Handler) http.Handler
- func SecurityHeaders(next http.Handler) http.Handler
- func SessionAuth(sessions repository.AdminSessionRepository, ...) func(http.Handler) http.Handler
- func WithAdmin(ctx context.Context, admin *model.AdminUser) context.Context
- func WithSession(ctx context.Context, session *model.AdminSession) context.Context
- type AuthHandler
- func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request)
- func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request)
- func (h *AuthHandler) Status(w http.ResponseWriter, r *http.Request)
- func (h *AuthHandler) TOTPSetup(w http.ResponseWriter, r *http.Request)
- func (h *AuthHandler) TOTPVerify(w http.ResponseWriter, r *http.Request)
- type FrontendHandler
- func (f *FrontendHandler) AdminsPage(w http.ResponseWriter, r *http.Request)
- func (f *FrontendHandler) AuditPage(w http.ResponseWriter, r *http.Request)
- func (f *FrontendHandler) ClientsPage(w http.ResponseWriter, r *http.Request)
- func (f *FrontendHandler) ConfigPage(w http.ResponseWriter, r *http.Request)
- func (f *FrontendHandler) Dashboard(w http.ResponseWriter, r *http.Request)
- func (f *FrontendHandler) KeysPage(w http.ResponseWriter, r *http.Request)
- func (f *FrontendHandler) LoginPage(w http.ResponseWriter, r *http.Request)
- func (f *FrontendHandler) ServeStatic(w http.ResponseWriter, r *http.Request)
- func (f *FrontendHandler) SessionsPage(w http.ResponseWriter, r *http.Request)
- func (f *FrontendHandler) TOTPSetupPage(w http.ResponseWriter, r *http.Request)
- func (f *FrontendHandler) UserDetailPage(w http.ResponseWriter, r *http.Request)
- func (f *FrontendHandler) UsersPage(w http.ResponseWriter, r *http.Request)
- type Handler
- func (h *Handler) ClearPasswordReset(w http.ResponseWriter, r *http.Request)
- func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request)
- func (h *Handler) CreateClient(w http.ResponseWriter, r *http.Request)
- func (h *Handler) CreateRole(w http.ResponseWriter, r *http.Request)
- func (h *Handler) DeleteConfig(w http.ResponseWriter, r *http.Request)
- func (h *Handler) DeleteEmailBranding(w http.ResponseWriter, r *http.Request)
- func (h *Handler) DeleteEmailTemplate(w http.ResponseWriter, r *http.Request)
- func (h *Handler) DeleteRole(w http.ResponseWriter, r *http.Request)
- func (h *Handler) DeleteUser(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetClient(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetConfig(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetEmailBranding(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetEmailTemplate(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetMetrics(w http.ResponseWriter, r *http.Request)
- func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ImportUsers(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListAdmins(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListClients(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListEmailBranding(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListEmailTemplates(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListKeys(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListRoles(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListSessions(w http.ResponseWriter, r *http.Request)
- func (h *Handler) ListUsers(w http.ResponseWriter, r *http.Request)
- func (h *Handler) LockUser(w http.ResponseWriter, r *http.Request)
- func (h *Handler) PreviewEmailTemplate(w http.ResponseWriter, r *http.Request)
- func (h *Handler) PutEmailBranding(w http.ResponseWriter, r *http.Request)
- func (h *Handler) PutEmailTemplate(w http.ResponseWriter, r *http.Request)
- func (h *Handler) QueryAudit(w http.ResponseWriter, r *http.Request)
- func (h *Handler) RequirePasswordReset(w http.ResponseWriter, r *http.Request)
- func (h *Handler) RevokeAdmin(w http.ResponseWriter, r *http.Request)
- func (h *Handler) RevokeAllSessions(w http.ResponseWriter, r *http.Request)
- func (h *Handler) RevokeClient(w http.ResponseWriter, r *http.Request)
- func (h *Handler) RevokeKey(w http.ResponseWriter, r *http.Request)
- func (h *Handler) RotateClientSecret(w http.ResponseWriter, r *http.Request)
- func (h *Handler) RotateKey(w http.ResponseWriter, r *http.Request)
- func (h *Handler) SetAppRoleRepo(r repository.AppRoleRepository)
- func (h *Handler) SetEmailRepos(branding repository.EmailBrandingRepository, ...)
- func (h *Handler) SetErasureService(s *service.ErasureService)
- func (h *Handler) SetIdentityService(s *service.IdentityService)
- func (h *Handler) UnlockUser(w http.ResponseWriter, r *http.Request)
- func (h *Handler) UpdateConfig(w http.ResponseWriter, r *http.Request)
- type LoginRateLimit
- type RouterOpts
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func EnsureFirstAdmin ¶
func EnsureFirstAdmin( ctx context.Context, admins repository.AdminUserRepository, marker repository.AdminConfigRepository, pepper string, ) error
EnsureFirstAdmin creates a super_admin account on first boot if no admins exist. The password is generated randomly and handed to the operator through firstboot.Deliver, which is never the process log. pepper is applied to the password hash (must match the value the gateway runtime uses; empty for no pepper).
Delivery happens before the row is written, and a failed delivery abandons the bootstrap. The other order is unrecoverable: auth.admin_users is non-empty from the moment Create succeeds, so no later boot mints another, and the deployment owns a super_admin whose password nobody holds and which no admin plane can reset.
Bootstrap happens once per deployment, not once per empty table (F-16). The old gate was admins.Count(ctx) == 0, and auth.admin_users can return to empty: AdminUserRepo.Revoke is a hard DELETE rather than a disable, and RevokeAdmin refuses only self-revocation, so two concurrent super_admin sessions revoking each other empty it — as does anything reaching the database as vault_admin. The next restart then minted a second bootstrap super_admin, with migration 016's created_by-NULL carve-out reopening alongside it, which is precisely the window migration 023 argues can never reopen.
marker is auth.admin_config, which vault_admin may write and which survives the admin table being emptied. The residual risk is honest and smaller than what it replaces: reopening the window now requires both emptying auth.admin_users and blanking this row, and vault_app can do neither on its own. Closing it outright wants an INSERT ... WHERE NOT EXISTS in the repository and a refusal to revoke the last super_admin in the handler.
func GetSession ¶
func GetSession(ctx context.Context) *model.AdminSession
GetSession extracts the admin session from context.
func LocalOnly ¶
func LocalOnly(killswitch bool, auditRepo repository.AuditRepository) func(http.Handler) http.Handler
LocalOnly middleware rejects any request where RemoteAddr is not a loopback address. This is layer 4 of the 6-layer local-only enforcement (on top of bind address, hostNetwork, and mTLS).
When killswitch is enabled (default), a non-loopback request triggers a panic that crashes the pod — a hard crash signals a security breach and triggers CrashLoopBackOff for immediate visibility. When disabled (dev mode), it returns 403.
func NewRouter ¶
func NewRouter(auth *AuthHandler, api *Handler, opts ...RouterOpts) http.Handler
NewRouter creates the admin gateway HTTP mux with all routes and middleware.
func RBACCheck ¶
RBACCheck middleware enforces that the authenticated admin has the required permission. A permission denial is written to the append-only audit log as an admin_authz_denied event (ASVS V16.3.2): the decision is enforced regardless, and the record is what makes privilege-boundary probing detectable after the fact. auditLog may be nil, in which case the denial is enforced but not recorded — the wired gateway always supplies one.
func Recovery ¶
Recovery catches panics and returns 500. Killswitch panics are re-panicked to ensure the pod crashes — Recovery must never swallow a security breach signal.
func RejectProxyHeaders ¶
RejectProxyHeaders middleware rejects requests containing proxy relay headers. This is layer 6 of the local-only enforcement — even if a reverse proxy somehow reaches the gateway, requests with these headers are rejected.
func RequestID ¶
RequestID generates a unique request ID and adds it to the response headers. Never trusts client-supplied X-Request-ID — always generates a new one.
func SecurityHeaders ¶
SecurityHeaders adds security headers to all responses.
func SessionAuth ¶
func SessionAuth(sessions repository.AdminSessionRepository, admins repository.AdminUserRepository, auditLog *audit.Logger) func(http.Handler) http.Handler
SessionAuth middleware validates admin session tokens from the Authorization header. It looks up the session by SHA-256 hash, checks expiry and revocation, and loads the admin user into context. Each token or session validity failure (missing or malformed Authorization header, an unknown, revoked or expired session, or a session whose admin no longer exists) is written to the append-only audit log as an admin_session_rejected event naming the reason (ASVS V16.3.2): the rejection is enforced regardless, and the record is what makes session-token replay and bogus-token probing detectable after the fact. auditLog may be nil, in which case the rejection is still enforced but not recorded; the wired gateway always supplies one.
func WithAdmin ¶ added in v0.6.7
WithAdmin attaches an admin user to the context. SessionAuth calls it once a session resolves, and tests that drive handlers directly with a pre-authenticated admin call the same function, so the fixture and the deployment write the same key.
func WithSession ¶ added in v0.6.7
WithSession attaches an admin session to the context. Same role as WithAdmin but for the session value handlers read via GetSession.
Types ¶
type AuthHandler ¶
type AuthHandler struct {
// contains filtered or unexported fields
}
AuthHandler handles admin authentication (login, logout, TOTP setup).
func NewAuthHandler ¶
func NewAuthHandler( admins repository.AdminUserRepository, sessions repository.AdminSessionRepository, auditLog *audit.Logger, masterKey []byte, pepper string, sessionTTL time.Duration, maxFailed int, lockoutDur time.Duration, ) *AuthHandler
NewAuthHandler creates a new admin authentication handler. pepper is the optional HMAC-pepper applied to admin passwords; it must match the user-side service's pepper so hash formats stay compatible. Empty = no pepper.
func (*AuthHandler) Login ¶
func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request)
Login handles POST /admin/auth/login. Authenticates with username + password + optional TOTP code. Anti-enumeration: always runs Argon2id even for non-existent users.
func (*AuthHandler) Logout ¶
func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request)
Logout handles POST /admin/auth/logout.
func (*AuthHandler) Status ¶
func (h *AuthHandler) Status(w http.ResponseWriter, r *http.Request)
Status handles GET /admin/status.
func (*AuthHandler) TOTPSetup ¶
func (h *AuthHandler) TOTPSetup(w http.ResponseWriter, r *http.Request)
TOTPSetup handles POST /admin/admins/me/totp/setup.
func (*AuthHandler) TOTPVerify ¶
func (h *AuthHandler) TOTPVerify(w http.ResponseWriter, r *http.Request)
TOTPVerify handles POST /admin/admins/me/totp/verify.
type FrontendHandler ¶
type FrontendHandler struct {
// contains filtered or unexported fields
}
FrontendHandler serves the server-rendered HTML admin dashboard.
func NewFrontendHandler ¶
func NewFrontendHandler() *FrontendHandler
NewFrontendHandler creates a new frontend handler with parsed templates. Each page template is parsed independently with the layout to avoid {{define "page-content"}} collisions between pages.
func (*FrontendHandler) AdminsPage ¶
func (f *FrontendHandler) AdminsPage(w http.ResponseWriter, r *http.Request)
AdminsPage serves the admin accounts page.
func (*FrontendHandler) AuditPage ¶
func (f *FrontendHandler) AuditPage(w http.ResponseWriter, r *http.Request)
AuditPage serves the audit log page.
func (*FrontendHandler) ClientsPage ¶
func (f *FrontendHandler) ClientsPage(w http.ResponseWriter, r *http.Request)
ClientsPage serves the service clients page.
func (*FrontendHandler) ConfigPage ¶
func (f *FrontendHandler) ConfigPage(w http.ResponseWriter, r *http.Request)
ConfigPage serves the config management page.
func (*FrontendHandler) Dashboard ¶
func (f *FrontendHandler) Dashboard(w http.ResponseWriter, r *http.Request)
Dashboard serves the main dashboard page.
func (*FrontendHandler) KeysPage ¶
func (f *FrontendHandler) KeysPage(w http.ResponseWriter, r *http.Request)
KeysPage serves the key management page.
func (*FrontendHandler) LoginPage ¶
func (f *FrontendHandler) LoginPage(w http.ResponseWriter, r *http.Request)
LoginPage serves the login page.
func (*FrontendHandler) ServeStatic ¶
func (f *FrontendHandler) ServeStatic(w http.ResponseWriter, r *http.Request)
ServeStatic serves embedded static files (CSS, JS).
func (*FrontendHandler) SessionsPage ¶
func (f *FrontendHandler) SessionsPage(w http.ResponseWriter, r *http.Request)
SessionsPage serves the session management page.
func (*FrontendHandler) TOTPSetupPage ¶
func (f *FrontendHandler) TOTPSetupPage(w http.ResponseWriter, r *http.Request)
TOTPSetupPage serves the TOTP enrollment page.
func (*FrontendHandler) UserDetailPage ¶
func (f *FrontendHandler) UserDetailPage(w http.ResponseWriter, r *http.Request)
UserDetailPage serves the user detail page.
func (*FrontendHandler) UsersPage ¶
func (f *FrontendHandler) UsersPage(w http.ResponseWriter, r *http.Request)
UsersPage serves the users management page.
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler handles admin API endpoints.
func NewHandler ¶
func NewHandler( users repository.UserRepository, clients repository.ClientRepository, tokens repository.RefreshTokenRepository, auditRepo repository.AuditRepository, admins repository.AdminUserRepository, sessions repository.AdminSessionRepository, adminConfig repository.AdminConfigRepository, ks *keystore.KeyStore, auditLog *audit.Logger, masterKey []byte, pepper string, ) *Handler
NewHandler creates a new admin API handler. pepper is the optional HMAC-pepper applied to admin password hashes (must match the user-side service for hash-format parity; empty = none).
func (*Handler) ClearPasswordReset ¶ added in v1.0.3
func (h *Handler) ClearPasswordReset(w http.ResponseWriter, r *http.Request)
ClearPasswordReset handles POST /admin/users/{id}/clear-password-reset. It withdraws a forced password reset, returning the account to the ordinary password gate.
It revokes nothing, and the asymmetry with the route above is the whole of the reasoning: imposing the flag says what is already issued is not to be trusted, lifting it says the account is ordinary again. Signing the holder out on the way to telling them so would be a containment action attached to the one verb here that is not one.
Lifting restores the ordinary password gate; it does not open it. An account imported with a hash vault42 cannot parse still has no password that verifies, so a mistaken lift leaves that account shut rather than open.
It goes through SetMustResetPassword rather than ClearMustResetPassword, which clears the same column, because that method also stamps updated_at: a column the web server holds and the admin gateway does not, so calling it from here fails the whole statement with 42501. The two statements exist because the two roles hold different grants, not because the two directions differ.
func (*Handler) CreateAdmin ¶
func (h *Handler) CreateAdmin(w http.ResponseWriter, r *http.Request)
CreateAdmin handles POST /admin/admins.
func (*Handler) CreateClient ¶
func (h *Handler) CreateClient(w http.ResponseWriter, r *http.Request)
CreateClient handles POST /admin/clients.
func (*Handler) CreateRole ¶ added in v0.8.0
func (h *Handler) CreateRole(w http.ResponseWriter, r *http.Request)
CreateRole handles POST /admin/roles — add a custom (non-reserved) role.
func (*Handler) DeleteConfig ¶
func (h *Handler) DeleteConfig(w http.ResponseWriter, r *http.Request)
DeleteConfig handles DELETE /admin/config/{key}.
func (*Handler) DeleteEmailBranding ¶ added in v0.9.0
func (h *Handler) DeleteEmailBranding(w http.ResponseWriter, r *http.Request)
DeleteEmailBranding handles DELETE /admin/email-branding/{app}.
func (*Handler) DeleteEmailTemplate ¶ added in v0.9.0
func (h *Handler) DeleteEmailTemplate(w http.ResponseWriter, r *http.Request)
DeleteEmailTemplate handles DELETE /admin/email-templates/{app}/{name}.
func (*Handler) DeleteRole ¶ added in v0.8.0
func (h *Handler) DeleteRole(w http.ResponseWriter, r *http.Request)
DeleteRole handles DELETE /admin/roles/{name} — remove a non-reserved role.
func (*Handler) DeleteUser ¶ added in v0.8.0
func (h *Handler) DeleteUser(w http.ResponseWriter, r *http.Request)
DeleteUser handles DELETE /admin/users/{id}. It erases the user account (GDPR) with key-recoverable escrow: when a recovery public key is configured the user's email is written to the encrypted, append-only recovery log before the PII is cascade-deleted and the user row is scrubbed and soft-deleted.
func (*Handler) GetClient ¶
func (h *Handler) GetClient(w http.ResponseWriter, r *http.Request)
GetClient handles GET /admin/clients/{id}.
func (*Handler) GetConfig ¶
func (h *Handler) GetConfig(w http.ResponseWriter, r *http.Request)
GetConfig handles GET /admin/config. entries is a key/value object, not a list, so it carries no list envelope; an empty store is an empty object rather than null. Credential-bearing keys (see redactedConfigKeys) are stripped so a viewer-tier reader never receives them.
func (*Handler) GetEmailBranding ¶ added in v0.9.0
func (h *Handler) GetEmailBranding(w http.ResponseWriter, r *http.Request)
GetEmailBranding handles GET /admin/email-branding/{app}.
func (*Handler) GetEmailTemplate ¶ added in v0.9.0
func (h *Handler) GetEmailTemplate(w http.ResponseWriter, r *http.Request)
GetEmailTemplate handles GET /admin/email-templates/{app}/{name}.
func (*Handler) GetMetrics ¶
func (h *Handler) GetMetrics(w http.ResponseWriter, r *http.Request)
GetMetrics handles GET /admin/metrics.
func (*Handler) GetUser ¶
func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request)
GetUser handles GET /admin/users/{id}.
func (*Handler) ImportUsers ¶ added in v0.8.0
func (h *Handler) ImportUsers(w http.ResponseWriter, r *http.Request)
ImportUsers handles POST /admin/users/import — batch-create passwordless, import_pending accounts from a source system (e.g. the legacy platform). Idempotent on email (CreateImported is ON CONFLICT DO NOTHING). Admin-reserved roles are stripped. On first login each imported account is forced through the magic-link reset.
func (*Handler) ListAdmins ¶
func (h *Handler) ListAdmins(w http.ResponseWriter, r *http.Request)
ListAdmins handles GET /admin/admins. Results are paginated via enforced limit/offset query params (default 50, max 100) to bound the response size of an unbounded admin-user set.
func (*Handler) ListClients ¶
func (h *Handler) ListClients(w http.ResponseWriter, r *http.Request)
ListClients handles GET /admin/clients.
func (*Handler) ListEmailBranding ¶ added in v0.9.0
func (h *Handler) ListEmailBranding(w http.ResponseWriter, r *http.Request)
ListEmailBranding handles GET /admin/email-branding.
func (*Handler) ListEmailTemplates ¶ added in v0.9.0
func (h *Handler) ListEmailTemplates(w http.ResponseWriter, r *http.Request)
ListEmailTemplates handles GET /admin/email-templates (optional ?app= filter).
func (*Handler) ListKeys ¶
func (h *Handler) ListKeys(w http.ResponseWriter, r *http.Request)
ListKeys handles GET /admin/keys.
func (*Handler) ListRoles ¶ added in v0.8.0
func (h *Handler) ListRoles(w http.ResponseWriter, r *http.Request)
ListRoles handles GET /admin/roles — the custom roles catalog.
func (*Handler) ListSessions ¶
func (h *Handler) ListSessions(w http.ResponseWriter, r *http.Request)
ListSessions handles GET /admin/sessions.
It lists ADMIN sessions, not user sessions: the live roster of every currently logged-in admin, with each one's source IP and user agent. That is reconnaissance for an attacker holding a lower-tier admin session, which is the stated reason internal/rbac/rbac.go keeps admins:manage at super_admin, so the route is gated on admins:manage rather than the viewer-tier sessions:list it used to take.
Results are paginated via enforced limit/offset query params (default 50, max 100) to bound the response size of an unbounded active-session set.
func (*Handler) ListUsers ¶
func (h *Handler) ListUsers(w http.ResponseWriter, r *http.Request)
ListUsers handles GET /admin/users. Accepts ?q= query param: UUID format → lookup by ID, contains @ → lookup by email.
func (*Handler) LockUser ¶
func (h *Handler) LockUser(w http.ResponseWriter, r *http.Request)
LockUser handles POST /admin/users/{id}/lock.
func (*Handler) PreviewEmailTemplate ¶ added in v0.9.0
func (h *Handler) PreviewEmailTemplate(w http.ResponseWriter, r *http.Request)
PreviewEmailTemplate handles POST /admin/email-templates/preview — render a candidate template against sample data without saving or sending. Always returns 200 with a structured result so the admin UI can show either the rendered output or the validation error.
func (*Handler) PutEmailBranding ¶ added in v0.9.0
func (h *Handler) PutEmailBranding(w http.ResponseWriter, r *http.Request)
PutEmailBranding handles PUT /admin/email-branding/{app} — create or replace.
func (*Handler) PutEmailTemplate ¶ added in v0.9.0
func (h *Handler) PutEmailTemplate(w http.ResponseWriter, r *http.Request)
PutEmailTemplate handles PUT /admin/email-templates/{app}/{name}.
func (*Handler) QueryAudit ¶
func (h *Handler) QueryAudit(w http.ResponseWriter, r *http.Request)
QueryAudit handles GET /admin/audit.
Pagination shares parsePagination with the other admin list endpoints, so one default (50) and one cap (maxListLimit) apply across the whole gateway.
total is the number of entries in the returned window: repository.AuditFilter has no counterpart that counts matches without returning them. The key is fixed here so that adding a true filtered count later changes a value, not the response shape.
func (*Handler) RequirePasswordReset ¶ added in v1.0.3
func (h *Handler) RequirePasswordReset(w http.ResponseWriter, r *http.Request)
RequirePasswordReset handles POST /admin/users/{id}/require-password-reset. It imposes a forced password reset on an existing account and terminates the sessions that account already holds.
The revocation is deliberate and is reported rather than performed quietly. POST /auth/refresh does not consult must_reset_password -- the flag gates the password, and a refresh presents a token instead -- so without it a user who is already signed in keeps rotating their family indefinitely and never meets the reset. The route would then refuse a login nobody was about to attempt while the access it was imposed against continued: a control that reports containment it does not deliver, which is the defect LockUser and RevokeAllSessions were each fixed for. The response and the audit row both carry sessions_revoked so the operator reads the blast radius off the answer.
Best-effort, and after the flag is written, for LockUser's reason: the demand has already committed, and failing the request here would tell the operator no reset was imposed when one was.
func (*Handler) RevokeAdmin ¶
func (h *Handler) RevokeAdmin(w http.ResponseWriter, r *http.Request)
RevokeAdmin handles POST /admin/admins/{id}/revoke.
func (*Handler) RevokeAllSessions ¶
func (h *Handler) RevokeAllSessions(w http.ResponseWriter, r *http.Request)
RevokeAllSessions handles POST /admin/sessions/revoke-all.
It revokes every USER's refresh tokens service-wide. That is the break-glass containment for bulk refresh-token theft, and it is what docs/security.md, docs/api.md, the SessionsRevoke permission's own definition in internal/rbac/rbac.go and the mitigation named in tests/attack/atk_authtok_lock_refresh_test.go all describe.
It used to run UPDATE auth.admin_sessions instead. It touched zero rows in auth.refresh_tokens, so the control four documents lean on did not exist: an operator responding to mass token theft pressed it, was told all_sessions_revoked, and nothing was contained. It also handed an operator-tier admin an availability lever over every super_admin, because revoking admin sessions logs the whole admin plane out and SessionsRevoke sits one tier below the destructive permissions precisely because it was believed to revoke user tokens.
Admin sessions are deliberately left alone. Revoking them is a different action with a different blast radius and belongs behind its own permission at super_admin tier, not smuggled into the user-containment control.
func (*Handler) RevokeClient ¶
func (h *Handler) RevokeClient(w http.ResponseWriter, r *http.Request)
RevokeClient handles POST /admin/clients/{id}/revoke.
func (*Handler) RevokeKey ¶
func (h *Handler) RevokeKey(w http.ResponseWriter, r *http.Request)
RevokeKey handles DELETE /admin/keys/{kid}.
func (*Handler) RotateClientSecret ¶
func (h *Handler) RotateClientSecret(w http.ResponseWriter, r *http.Request)
RotateClientSecret handles POST /admin/clients/{id}/rotate.
func (*Handler) RotateKey ¶
func (h *Handler) RotateKey(w http.ResponseWriter, r *http.Request)
RotateKey handles POST /admin/keys/rotate.
func (*Handler) SetAppRoleRepo ¶ added in v0.8.0
func (h *Handler) SetAppRoleRepo(r repository.AppRoleRepository)
SetAppRoleRepo wires the custom-roles catalog repository, enabling the /admin/roles endpoints. Optional (nil → those handlers return 503).
func (*Handler) SetEmailRepos ¶ added in v0.9.0
func (h *Handler) SetEmailRepos(branding repository.EmailBrandingRepository, templates repository.EmailTemplateRepository, maxTemplateSize int)
SetEmailRepos wires the per-app email branding + template repositories, enabling the /admin/email-branding and /admin/email-templates endpoints. Optional (nil → those handlers return 503). maxTemplateSize caps custom template body size in bytes; <= 0 disables the size check.
func (*Handler) SetErasureService ¶ added in v0.8.0
func (h *Handler) SetErasureService(s *service.ErasureService)
SetErasureService wires the account-erasure service, enabling the DELETE /admin/users/{id} endpoint. Optional (nil → that handler returns 503).
func (*Handler) SetIdentityService ¶ added in v0.9.0
func (h *Handler) SetIdentityService(s *service.IdentityService)
SetIdentityService wires the identity service so account import can persist a migrated marketing-consent record. Optional: without it, import still creates accounts but drops any marketing preference in the payload rather than storing a preference it cannot attach provenance to.
func (*Handler) UnlockUser ¶
func (h *Handler) UnlockUser(w http.ResponseWriter, r *http.Request)
UnlockUser handles POST /admin/users/{id}/unlock.
func (*Handler) UpdateConfig ¶
func (h *Handler) UpdateConfig(w http.ResponseWriter, r *http.Request)
UpdateConfig handles PUT /admin/config/{key}.
type LoginRateLimit ¶
type LoginRateLimit struct {
// contains filtered or unexported fields
}
LoginRateLimit provides per-IP rate limiting for the login endpoint. Allows maxAttempts per window period. Independent of account lockout.
func NewLoginRateLimit ¶
func NewLoginRateLimit(maxAttempts int, window time.Duration) *LoginRateLimit
NewLoginRateLimit creates a login rate limiter.
func (*LoginRateLimit) Wrap ¶
func (rl *LoginRateLimit) Wrap(next http.HandlerFunc) http.HandlerFunc
Wrap wraps a handler with per-IP login rate limiting.
type RouterOpts ¶
type RouterOpts struct {
// DevMode disables LocalOnly and RejectProxyHeaders middleware
// for development behind ingress controllers.
DevMode bool
// Killswitch enables the killswitch panic on non-loopback requests (default: true).
// When enabled, the pod crashes on breach attempts. When disabled, returns 403.
Killswitch bool
// AuditRepo is used by the killswitch to log breach attempts before crashing.
AuditRepo repository.AuditRepository
}
RouterOpts configures the admin gateway router.