auth-bundle-1 Phase 8 follow-up: classify issuer/target audit rows + auditor end-to-end tests + gofmt drift

Self-audit caught five real gaps in 3ef45e2; this commit closes them.

# Phase 8 — issuer/target audit rows now classified as 'config'

The Phase 8 prompt explicitly required existing config-mutation
calls (issuer config, target config, etc.) to write
event_category=config. The 3ef45e2 commit only migrated the auth
service callers; the 6 issuer/target call-sites
(internal/service/issuer.go: create/update/delete_issuer +
internal/service/target.go: create/update/delete_target) still
defaulted to cert_lifecycle. They now pass through
RecordEventWithCategory(..., domain.EventCategoryConfig, ...) so
auditors filtering /v1/audit?category=config see the slice the
migration's docstring promised.

# Auditor exit-criterion test

Phase 8's exit criteria pin 'a user with the auditor role can list /
export audit events but gets 403 on every other endpoint.' Bundle 1
unit invariants (auditor permission set, rbacGate behaviour) were
in place but no end-to-end test walked the full set of admin perms
with an auditor actor. internal/api/router/rbac_gate_integration_test.go
gains TestRBACGate_AuditorRole_403sOnAdminRoutes (table-driven across
all 5 admin perms — cert.bulk_revoke / crl.admin / scep.admin /
est.admin / ca.hierarchy.manage) plus TestRBACGate_AuditorRole_PassesAuditReadGate
(positive case for audit.read).

# gofmt drift

3ef45e2 left two cosmetic struct-field-alignment diffs in
internal/cli/auth.go and internal/api/handler/audit_handler_test.go
that gofmt -l flagged. CI's gofmt step would have failed; gofmt -w
applied; gofmt -l now clean across the repo.

# CHANGELOG path-prefix

CHANGELOG.md v2.1.0 used '/v1/auth/bootstrap' shorthand in the
operator-facing flow examples. The actual route is
'/api/v1/auth/bootstrap'; an operator copy-pasting the curl would
404. All five hits replaced.

Verifications: gofmt clean, go vet ./internal/service/
./internal/api/router/ clean, go test -short -count=1 green across
internal/service + internal/api/router, including the 6 new
auditor sub-tests (PASS).
This commit is contained in:
shankar0123
2026-05-09 20:23:41 +00:00
parent 3ef45e2ad4
commit af4fa12724
6 changed files with 84 additions and 14 deletions
+3 -3
View File
@@ -191,7 +191,7 @@ func (s *IssuerService) Create(ctx context.Context, iss *domain.Issuer, actor st
}
if s.auditService != nil {
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_issuer", "issuer", iss.ID, nil); auditErr != nil {
if auditErr := s.auditService.RecordEventWithCategory(ctx, actor, domain.ActorTypeUser, "create_issuer", domain.EventCategoryConfig, "issuer", iss.ID, nil); auditErr != nil {
s.logger.Error("failed to record audit event", "error", auditErr)
}
}
@@ -232,7 +232,7 @@ func (s *IssuerService) Update(ctx context.Context, id string, iss *domain.Issue
s.rebuildRegistryQuiet(ctx)
if s.auditService != nil {
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_issuer", "issuer", id, nil); auditErr != nil {
if auditErr := s.auditService.RecordEventWithCategory(ctx, actor, domain.ActorTypeUser, "update_issuer", domain.EventCategoryConfig, "issuer", id, nil); auditErr != nil {
s.logger.Error("failed to record audit event", "error", auditErr)
}
}
@@ -252,7 +252,7 @@ func (s *IssuerService) Delete(ctx context.Context, id string, actor string) err
}
if s.auditService != nil {
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_issuer", "issuer", id, nil); auditErr != nil {
if auditErr := s.auditService.RecordEventWithCategory(ctx, actor, domain.ActorTypeUser, "delete_issuer", domain.EventCategoryConfig, "issuer", id, nil); auditErr != nil {
s.logger.Error("failed to record audit event", "error", auditErr)
}
}
+3 -3
View File
@@ -153,7 +153,7 @@ func (s *TargetService) Create(ctx context.Context, target *domain.DeploymentTar
}
if s.auditService != nil {
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_target", "target", target.ID, nil); auditErr != nil {
if auditErr := s.auditService.RecordEventWithCategory(ctx, actor, domain.ActorTypeUser, "create_target", domain.EventCategoryConfig, "target", target.ID, nil); auditErr != nil {
s.logger.Error("failed to record audit event", "error", auditErr)
}
}
@@ -191,7 +191,7 @@ func (s *TargetService) Update(ctx context.Context, id string, target *domain.De
}
if s.auditService != nil {
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_target", "target", id, nil); auditErr != nil {
if auditErr := s.auditService.RecordEventWithCategory(ctx, actor, domain.ActorTypeUser, "update_target", domain.EventCategoryConfig, "target", id, nil); auditErr != nil {
s.logger.Error("failed to record audit event", "error", auditErr)
}
}
@@ -206,7 +206,7 @@ func (s *TargetService) Delete(ctx context.Context, id string, actor string) err
}
if s.auditService != nil {
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_target", "target", id, nil); auditErr != nil {
if auditErr := s.auditService.RecordEventWithCategory(ctx, actor, domain.ActorTypeUser, "delete_target", domain.EventCategoryConfig, "target", id, nil); auditErr != nil {
s.logger.Error("failed to record audit event", "error", auditErr)
}
}