Fix SEC-09: hide admin nav links from non-admin users

- Add IsAdmin bool to PageData (embedded in all page view structs)
- Remove redundant IsAdmin from DashboardData
- Add isAdmin() helper to derive admin status from request claims
- Set IsAdmin in all page-level handlers that populate PageData
- Wrap admin-only nav links in base.html with {{if .IsAdmin}}
- Add tests: non-admin dashboard/profile hide admin links,
  admin dashboard shows them

Security: navigation links to /accounts, /audit, /policies,
and /pgcreds are now only rendered for admin users. Server-side
authorization (requireAdminRole middleware) was already in place;
this change removes the information leak of showing links that
return 403 to non-admin users.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 00:44:30 -07:00
parent 586d4e3355
commit d7d7ba21d9
8 changed files with 142 additions and 14 deletions

View File

@@ -569,6 +569,13 @@ func (u *UIServer) clientIP(r *http.Request) string {
return middleware.ClientIP(r, proxyIP)
}
// isAdmin reports whether the authenticated user holds the "admin" role.
// Returns false if claims are absent.
func isAdmin(r *http.Request) bool {
claims := claimsFromContext(r.Context())
return claims != nil && claims.HasRole("admin")
}
// 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".
@@ -594,6 +601,10 @@ type PageData struct {
// 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
// IsAdmin is true when the logged-in user holds the "admin" role.
// Used by the base template to conditionally render admin-only navigation
// links (SEC-09: non-admin users must not see links they cannot access).
IsAdmin bool
}
// LoginData is the view model for the login page.
@@ -609,7 +620,6 @@ type LoginData struct {
// DashboardData is the view model for the dashboard page.
type DashboardData struct {
PageData
IsAdmin bool
RecentEvents []*db.AuditEventView
TotalAccounts int
ActiveAccounts int