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:
@@ -141,6 +141,22 @@ func New(database *db.DB, cfg *config.Config, priv ed25519.PrivateKey, pub ed255
|
||||
return false
|
||||
},
|
||||
"not": func(b bool) bool { return !b },
|
||||
// derefInt64 safely dereferences a *int64, returning 0 for nil.
|
||||
// Used in templates to compare owner IDs without triggering nil panics.
|
||||
"derefInt64": func(p *int64) int64 {
|
||||
if p == nil {
|
||||
return 0
|
||||
}
|
||||
return *p
|
||||
},
|
||||
// isPGCredOwner returns true when actorID and cred are both non-nil
|
||||
// and actorID matches cred.OwnerID. Safe to call with nil arguments.
|
||||
"isPGCredOwner": func(actorID *int64, cred *model.PGCredential) bool {
|
||||
if actorID == nil || cred == nil || cred.OwnerID == nil {
|
||||
return false
|
||||
}
|
||||
return *actorID == *cred.OwnerID
|
||||
},
|
||||
"add": func(a, b int) int { return a + b },
|
||||
"sub": func(a, b int) int { return a - b },
|
||||
"gt": func(a, b int) bool { return a > b },
|
||||
@@ -190,6 +206,7 @@ func New(database *db.DB, cfg *config.Config, priv ed25519.PrivateKey, pub ed255
|
||||
"audit": "templates/audit.html",
|
||||
"audit_detail": "templates/audit_detail.html",
|
||||
"policies": "templates/policies.html",
|
||||
"pgcreds": "templates/pgcreds.html",
|
||||
}
|
||||
tmpls := make(map[string]*template.Template, len(pageFiles))
|
||||
for name, file := range pageFiles {
|
||||
@@ -264,6 +281,10 @@ func (u *UIServer) Register(mux *http.ServeMux) {
|
||||
uiMux.Handle("DELETE /token/{jti}", admin(u.handleRevokeToken))
|
||||
uiMux.Handle("POST /accounts/{id}/token", admin(u.handleIssueSystemToken))
|
||||
uiMux.Handle("PUT /accounts/{id}/pgcreds", admin(u.handleSetPGCreds))
|
||||
uiMux.Handle("POST /accounts/{id}/pgcreds/access", admin(u.handleGrantPGCredAccess))
|
||||
uiMux.Handle("DELETE /accounts/{id}/pgcreds/access/{grantee}", admin(u.handleRevokePGCredAccess))
|
||||
uiMux.Handle("GET /pgcreds", adminGet(u.handlePGCredsList))
|
||||
uiMux.Handle("POST /pgcreds", admin(u.handleCreatePGCreds))
|
||||
uiMux.Handle("GET /audit", adminGet(u.handleAuditPage))
|
||||
uiMux.Handle("GET /audit/rows", adminGet(u.handleAuditRows))
|
||||
uiMux.Handle("GET /audit/{id}", adminGet(u.handleAuditDetail))
|
||||
@@ -478,6 +499,21 @@ func clientIP(r *http.Request) string {
|
||||
return addr
|
||||
}
|
||||
|
||||
// actorName resolves the username of the currently authenticated user from the
|
||||
// request context. Returns an empty string if claims are absent or the account
|
||||
// cannot be found; callers should treat an empty string as "not logged in".
|
||||
func (u *UIServer) actorName(r *http.Request) string {
|
||||
claims := claimsFromContext(r.Context())
|
||||
if claims == nil {
|
||||
return ""
|
||||
}
|
||||
acct, err := u.db.GetAccountByUUID(claims.Subject)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return acct.Username
|
||||
}
|
||||
|
||||
// ---- Page data types ----
|
||||
|
||||
// PageData is embedded in all page-level view structs.
|
||||
@@ -485,6 +521,9 @@ type PageData struct {
|
||||
CSRFToken string
|
||||
Flash string
|
||||
Error string
|
||||
// ActorName is the username of the currently logged-in user, populated by
|
||||
// handlers so the base template can display it in the navigation bar.
|
||||
ActorName string
|
||||
}
|
||||
|
||||
// LoginData is the view model for the login page.
|
||||
@@ -514,7 +553,16 @@ type AccountsData struct {
|
||||
// AccountDetailData is the view model for the account detail page.
|
||||
type AccountDetailData struct {
|
||||
Account *model.Account
|
||||
PGCred *model.PGCredential // nil if none stored or account is not a system account
|
||||
// PGCred is nil if none stored or the account is not a system account.
|
||||
PGCred *model.PGCredential
|
||||
// PGCredGrants lists accounts that have been granted read access to PGCred.
|
||||
// Only populated when the viewing actor is the credential owner.
|
||||
PGCredGrants []*model.PGCredAccessGrant
|
||||
// GrantableAccounts is the list of accounts the owner may grant access to.
|
||||
GrantableAccounts []*model.Account
|
||||
// ActorID is the DB id of the currently logged-in user; used in templates
|
||||
// to decide whether to show the owner-only management controls.
|
||||
ActorID *int64
|
||||
PageData
|
||||
Roles []string
|
||||
AllRoles []string
|
||||
@@ -556,3 +604,21 @@ type PoliciesData struct {
|
||||
Rules []*PolicyRuleView
|
||||
AllActions []string
|
||||
}
|
||||
|
||||
// PGCredsData is the view model for the "My PG Credentials" list page.
|
||||
// It shows all pg_credentials sets accessible to the currently logged-in user:
|
||||
// those they own and those they have been granted access to.
|
||||
// UncredentialedAccounts is the list of system accounts that have no credentials
|
||||
// yet, populated to drive the "New Credentials" create form on the same page.
|
||||
// CredGrants maps credential ID to the list of access grants for that credential;
|
||||
// only populated for credentials the actor owns.
|
||||
// AllAccounts is used to populate the grant-access dropdown for owned credentials.
|
||||
// ActorID is the DB id of the currently logged-in user.
|
||||
type PGCredsData struct {
|
||||
CredGrants map[int64][]*model.PGCredAccessGrant
|
||||
ActorID *int64
|
||||
PageData
|
||||
Creds []*model.PGCredential
|
||||
UncredentialedAccounts []*model.Account
|
||||
AllAccounts []*model.Account
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user