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

# Conflicts:
#	internal/ui/ui_test.go
This commit is contained in:
2026-03-13 01:06:50 -07:00
8 changed files with 142 additions and 14 deletions

View File

@@ -631,3 +631,121 @@ func TestLoginLockedAccountShowsInvalidCredentials(t *testing.T) {
t.Error("wrong password response does not contain 'invalid credentials'")
}
}
// ---- SEC-09: admin nav link visibility tests ----
// issueUserSession creates a human account with the "user" role (non-admin),
// issues a JWT, tracks it, and returns the raw token string.
func issueUserSession(t *testing.T, u *UIServer) string {
t.Helper()
acct, err := u.db.CreateAccount("regular-user", model.AccountTypeHuman, "")
if err != nil {
t.Fatalf("CreateAccount: %v", err)
}
if err := u.db.SetRoles(acct.ID, []string{"user"}, nil); err != nil {
t.Fatalf("SetRoles: %v", err)
}
tok, claims, err := token.IssueToken(u.privKey, testIssuer, acct.UUID, []string{"user"}, time.Hour)
if err != nil {
t.Fatalf("IssueToken: %v", err)
}
if err := u.db.TrackToken(claims.JTI, acct.ID, claims.IssuedAt, claims.ExpiresAt); err != nil {
t.Fatalf("TrackToken: %v", err)
}
return tok
}
// TestNonAdminDashboardHidesAdminNavLinks verifies that a non-admin user's
// dashboard does not contain links to admin-only pages (SEC-09).
func TestNonAdminDashboardHidesAdminNavLinks(t *testing.T) {
u := newTestUIServer(t)
mux := http.NewServeMux()
u.Register(mux)
userToken := issueUserSession(t, u)
req := authenticatedGET(t, userToken, "/dashboard")
rr := httptest.NewRecorder()
mux.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status = %d, want 200; body: %s", rr.Code, rr.Body.String())
}
body := rr.Body.String()
for _, adminPath := range []string{
`href="/accounts"`,
`href="/audit"`,
`href="/policies"`,
`href="/pgcreds"`,
} {
if strings.Contains(body, adminPath) {
t.Errorf("non-admin dashboard contains admin link %s — SEC-09 violation", adminPath)
}
}
// Dashboard link should still be present.
if !strings.Contains(body, `href="/dashboard"`) {
t.Error("dashboard link missing from non-admin nav")
}
}
// TestAdminDashboardShowsAdminNavLinks verifies that an admin user's
// dashboard contains all admin navigation links.
func TestAdminDashboardShowsAdminNavLinks(t *testing.T) {
u := newTestUIServer(t)
mux := http.NewServeMux()
u.Register(mux)
adminToken, _, _ := issueAdminSession(t, u)
req := authenticatedGET(t, adminToken, "/dashboard")
rr := httptest.NewRecorder()
mux.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status = %d, want 200; body: %s", rr.Code, rr.Body.String())
}
body := rr.Body.String()
for _, adminPath := range []string{
`href="/accounts"`,
`href="/audit"`,
`href="/policies"`,
`href="/pgcreds"`,
} {
if !strings.Contains(body, adminPath) {
t.Errorf("admin dashboard missing admin link %s", adminPath)
}
}
}
// TestNonAdminProfileHidesAdminNavLinks verifies that the profile page
// also hides admin nav links for non-admin users (SEC-09).
func TestNonAdminProfileHidesAdminNavLinks(t *testing.T) {
u := newTestUIServer(t)
mux := http.NewServeMux()
u.Register(mux)
userToken := issueUserSession(t, u)
req := authenticatedGET(t, userToken, "/profile")
rr := httptest.NewRecorder()
mux.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status = %d, want 200; body: %s", rr.Code, rr.Body.String())
}
body := rr.Body.String()
for _, adminPath := range []string{
`href="/accounts"`,
`href="/audit"`,
`href="/policies"`,
`href="/pgcreds"`,
} {
if strings.Contains(body, adminPath) {
t.Errorf("non-admin profile page contains admin link %s — SEC-09 violation", adminPath)
}
}
}