1
0
forked from wrenn/wrenn
## What's new

Compliance, audit, and account lifecycle improvements — admin actions are now fully auditable, user data is properly anonymized on deletion, and OAuth signup flow gives users control over their profile.

### Audit

- Added audit logging for all admin actions (user activate/deactivate, team BYOC toggle, team delete, template delete, build create/cancel)
- Added admin audit page with infinite scroll and hierarchical filters
- Fixed audit log team assignment — admin/host actions now correctly land under PlatformTeamID
- Anonymize audit logs on user hard-delete (actor name, IDs, emails stripped)
- Deduplicated audit logger internals (665 → 374 lines, no behavior change)

### Authentication

- Separated GitHub OAuth login/signup flows — login no longer auto-creates accounts
- Added name confirmation dialog for new GitHub signups

### Account Lifecycle

- Email notification sent when account is permanently deleted after grace period
- Audit log anonymization tied to user purge (per-user transactional)

### UX

- Removed accent gradient bars from admin host dialogs (border + shadow only)
- Frontend renders deleted users as styled badge in audit log view

### Others

- Version bump
- Bug fixes

Reviewed-on: wrenn/wrenn#36
This commit is contained in:
2026-04-21 10:11:49 +00:00
parent 23dca7d9ff
commit 52ad21c339
25 changed files with 1200 additions and 443 deletions

View File

@ -13,6 +13,8 @@ import (
"github.com/go-chi/chi/v5"
"git.omukk.dev/wrenn/wrenn/internal/layout"
"git.omukk.dev/wrenn/wrenn/pkg/audit"
"git.omukk.dev/wrenn/wrenn/pkg/auth"
"git.omukk.dev/wrenn/wrenn/pkg/db"
"git.omukk.dev/wrenn/wrenn/pkg/id"
"git.omukk.dev/wrenn/wrenn/pkg/lifecycle"
@ -22,13 +24,14 @@ import (
)
type buildHandler struct {
svc *service.BuildService
db *db.Queries
pool *lifecycle.HostClientPool
svc *service.BuildService
db *db.Queries
pool *lifecycle.HostClientPool
audit *audit.AuditLogger
}
func newBuildHandler(svc *service.BuildService, db *db.Queries, pool *lifecycle.HostClientPool) *buildHandler {
return &buildHandler{svc: svc, db: db, pool: pool}
func newBuildHandler(svc *service.BuildService, db *db.Queries, pool *lifecycle.HostClientPool, al *audit.AuditLogger) *buildHandler {
return &buildHandler{svc: svc, db: db, pool: pool, audit: al}
}
type createBuildRequest struct {
@ -187,6 +190,8 @@ func (h *buildHandler) Create(w http.ResponseWriter, r *http.Request) {
return
}
ac := auth.MustFromContext(r.Context())
h.audit.LogBuildCreate(r.Context(), ac, build.ID, req.Name)
writeJSON(w, http.StatusCreated, buildToResponse(build))
}
@ -305,6 +310,8 @@ func (h *buildHandler) DeleteTemplate(w http.ResponseWriter, r *http.Request) {
return
}
ac := auth.MustFromContext(r.Context())
h.audit.LogTemplateDelete(r.Context(), ac, name)
w.WriteHeader(http.StatusNoContent)
}
@ -323,5 +330,7 @@ func (h *buildHandler) Cancel(w http.ResponseWriter, r *http.Request) {
return
}
ac := auth.MustFromContext(r.Context())
h.audit.LogBuildCancel(r.Context(), ac, buildID)
w.WriteHeader(http.StatusNoContent)
}