feat(admin): HTTP endpoints for roles + user-roles + workflows + policies#15
Merged
Conversation
…cies Piece 3a of the dynamic-workflow vertical slice (Piece 3 split into three sub-PRs: 3a admin CRUD, 3b request flow, 3c agent wrap retrieval). This PR is pure HTTP over the four tables from Piece 2 (#14). Admin UI (Piece 5) calls these endpoints to manage roles, role assignments, workflow templates, and policy rules. Endpoints added under /api/v1 ----------------------------- POST /roles create GET /roles list (includes system) GET /roles/:id get PUT /roles/:id/permissions replace permission list DELETE /roles/:id 404 missing, 409 system POST /user-roles grant DELETE /user-roles/:id revoke GET /users/:userID/roles list a user's assignments POST /workflows create GET /workflows list GET /workflows/:id get PUT /workflows/:id update (excludes is_default flip) DELETE /workflows/:id 404 missing, 409 system POST /policies create GET /policies list (priority DESC) GET /policies/:id get PUT /policies/:id update DELETE /policies/:id 404 missing, 409 system Decisions worth flagging in review ---------------------------------- - All admin endpoints under the existing admin route group. Real RBAC (RequirePermission middleware) layers on top when the auth design ships; no route reshape needed. - is_system rows are 404-aware (missing) vs 409-aware (system row) on Delete. ErrSystemRow → 409 with a clear message. - TTLs exposed as integer seconds in JSON — Helm-friendly, matches typical REST conventions, no parsing on the client side. - All TTLs validated > 0 at the handler layer (not just at the DB CHECK). Zero TTLs would silently never expire. - is_default flip NOT included in Update. Atomic swap needs a transaction; defer to its own endpoint to avoid the partial unique index admitting zero defaults briefly during the swap. - Admin handler struct holds all four repositories — single constructor call in main; no risk of forgetting to wire one. - Bodies use snake_case JSON tags (matches existing conventions like agent_secret, wrap_id, correlation_id). Tests ----- 13 handler tests using Fiber's app.Test helper: TestRoles_CreateGetUpdateDelete TestRoles_DeleteSystemRoleReturns409 TestRoles_ListReturnsAllIncludingSystem TestRoles_ListAfterManyCreates TestUserRoles_GrantRevoke TestWorkflows_CreateGetUpdateDelete TestWorkflows_RejectsZeroTTL TestWorkflows_DeleteSystemReturns409 TestPolicies_CreateListDelete TestPolicies_DeleteSystemReturns409 TestAdmin_NotFoundReturns404 TestAdmin_InvalidUUIDReturns400 TestAdmin_MalformedJSONBodyReturns400 Verification ------------ Offline: go build / vet / test -race ./... clean. Live: 13 new tests + full suite green with -p 1. curl walkthrough demonstrates: - List seed roles (admin / approver / developer) - Create custom strict-prod workflow - Create matching prod-only policy at priority 500 - List policies in priority DESC order (ours first, seed match-all last) - Grant alice@example.com the approver role scoped to environment=prod - DELETE the seed admin role → HTTP 409 (is_system protection)
The GET after PUT only uses the body; the response status isn't checked. Switching the assignment to _ silences staticcheck SA4006.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Piece 3a of the dynamic-workflow vertical slice (Piece 3 split into three sub-PRs: 3a admin CRUD, 3b request flow, 3c agent wrap retrieval).
This PR is pure HTTP over the four tables from Piece 2. Admin UI (Piece 5) calls these endpoints to manage roles, role assignments, workflow templates, and policy rules.
18 endpoints under
/api/v1Decisions worth flagging
RequirePermissionmiddleware) layers on top when the auth design ships; no route reshape needed.is_systemrows are 404-aware (missing) vs 409-aware (system) on DeleteErrSystemRow→ HTTP 409 with clear message. UI distinguishes "can't" from "doesn't exist".is_defaultflip NOT in UpdateAdminstruct holds all four repositoriesTests
13 handler tests using Fiber's
app.Test:Live e2e walkthrough
What's next (Pieces 3b + 3c)
POST /requests(dev submits new key values with embedded wraps),POST /requests/:id/approve(approver action that resolves policy + createssync_job), reject + cancelGET /agents/:id/wraps/:wrap_idunderAgentAuth— agent's wrap retrieval.WrapService.Retrievealready has single-shot semantics; this is the thin HTTP layer.Test plan
-p 1internal/handlers/admin.go::adminErrand confirmsErrNotFound→ 404,ErrSystemRow→ 409, default → 500bodyToWorkflow/workflowToBodyand confirms TTL second↔Duration conversions are symmetric