UI: pgcreds create button; show logged-in user

* web/templates/pgcreds.html: New Credentials card is now always
  rendered; Add Credentials toggle button reveals the create form
  (hidden by default). Shows a message when all system accounts
  already have credentials. Previously the card was hidden when
  UncredentialedAccounts was empty.
* internal/ui/ui.go: added ActorName string field to PageData;
  added actorName(r) helper resolving username from JWT claims
  via DB lookup, returns empty string if unauthenticated.
* internal/ui/handlers_*.go: all full-page PageData constructors
  now pass ActorName: u.actorName(r).
* web/templates/base.html: nav bar renders actor username as a
  muted label before the Logout button when logged in.
* web/static/style.css: added .nav-actor rule (muted grey, 0.85rem).
This commit is contained in:
2026-03-12 11:38:57 -07:00
parent bbf9f6fe3f
commit b2f2f04646
19 changed files with 1152 additions and 49 deletions

View File

@@ -87,18 +87,26 @@ type SystemToken struct {
// PGCredential holds Postgres connection details for a system account.
// The password is encrypted at rest; PGPassword is only populated after
// decryption and must never be logged or included in API responses.
//
// OwnerID identifies the account permitted to update, delete, and manage
// access grants for this credential set. A nil OwnerID means the credential
// pre-dates ownership tracking; for backwards compatibility, nil is treated as
// unowned (only admins can manage it via the UI).
type PGCredential struct {
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
PGHost string `json:"host"`
PGDatabase string `json:"database"`
PGUsername string `json:"username"`
PGPassword string `json:"-"`
PGPasswordEnc []byte `json:"-"`
PGPasswordNonce []byte `json:"-"`
ID int64 `json:"-"`
AccountID int64 `json:"-"`
PGPort int `json:"port"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
OwnerID *int64 `json:"-"`
ServiceAccountUUID string `json:"service_account_uuid,omitempty"`
PGUsername string `json:"username"`
PGPassword string `json:"-"`
ServiceUsername string `json:"service_username,omitempty"`
PGDatabase string `json:"database"`
PGHost string `json:"host"`
PGPasswordEnc []byte `json:"-"`
PGPasswordNonce []byte `json:"-"`
ID int64 `json:"-"`
AccountID int64 `json:"-"`
PGPort int `json:"port"`
}
// AuditEvent represents a single entry in the append-only audit log.
@@ -141,6 +149,26 @@ const (
EventPolicyDeny = "policy_deny"
)
// PGCredAccessGrant records that a specific account has been granted read
// access to a pg_credentials set. Only the credential owner can manage
// grants; grantees can view connection metadata but never the plaintext
// password, and they cannot update or delete the credential set.
type PGCredAccessGrant struct {
GrantedAt time.Time `json:"granted_at"`
GrantedBy *int64 `json:"-"`
GranteeUUID string `json:"grantee_id"`
GranteeName string `json:"grantee_username"`
ID int64 `json:"-"`
CredentialID int64 `json:"-"`
GranteeID int64 `json:"-"`
}
// Audit event type for pg_credential_access changes.
const (
EventPGCredAccessGranted = "pgcred_access_granted" //nolint:gosec // G101: audit event type, not a credential
EventPGCredAccessRevoked = "pgcred_access_revoked" //nolint:gosec // G101: audit event type, not a credential
)
// PolicyRuleRecord is the database representation of a policy rule.
// RuleJSON holds a JSON-encoded policy.RuleBody (all match and effect fields).
// The ID, Priority, and Description are stored as dedicated columns.