77 lines
4.0 KiB
Markdown
77 lines
4.0 KiB
Markdown
---
|
|
id: task-09
|
|
title: HTTP API + auth middleware + capability checks + backendapp wiring
|
|
status: pending
|
|
wave: 2
|
|
depends_on: [task-05, task-06, task-07, task-08]
|
|
plan: docs/plans/plugins/plan.md
|
|
---
|
|
|
|
# HTTP API + auth middleware + capability checks + backendapp wiring
|
|
|
|
## Title
|
|
Expose `/api/plugins/*` (gin), the plugin-facing auth middleware + capability
|
|
gating, state/secrets APIs, webhook/UI proxy routes, tool-listing, and wire the
|
|
whole package into backendapp.
|
|
|
|
## Inputs
|
|
- Spec `docs/specs/plugins/requirements/plugins.md` → "API surface" (all endpoints), "Permissions",
|
|
"Security".
|
|
- Routing precedent: `internal/jira/handlers.go` `RegisterRoutes(router, dispatcher,
|
|
svc, log)` → `router.Group("/api/...")`. Wiring precedent:
|
|
`internal/backendapp/services.go` `initJiraService` + `provideServices`, and
|
|
`internal/backendapp/helpers.go` `routeParams` (add a `pluginsSvc *plugins.Service`
|
|
field) + `registerRoutes` (call `plugins.RegisterRoutes(...)`), gated on
|
|
`p.features.Plugins` (task-10 adds the flag).
|
|
|
|
## Acceptance
|
|
1. `internal/plugins/handlers.go` `RegisterRoutes(router, svc, log)`:
|
|
- Management (operator): `POST /api/plugins/register`, `GET /api/plugins`,
|
|
`GET /api/plugins/:id`, `PATCH /api/plugins/:id`, `DELETE /api/plugins/:id`,
|
|
`POST /api/plugins/:id/enable`, `POST /api/plugins/:id/disable`.
|
|
Register returns cleartext creds once. List/Get never expose creds.
|
|
- `GET /api/plugins/tools` → aggregate declared tools across active plugins
|
|
(id, name, display_name, description, input_schema). (Listing only — not wired
|
|
into agent sessions.)
|
|
- `GET /api/plugins/:id/ui/*path` and `POST` → UIProxy (task-08). Public to the
|
|
browser session (operator UI); no plugin api-key required.
|
|
- `POST /api/plugins/:id/webhooks/:key` → WebhookProxy (task-08). External;
|
|
validates plugin active + key declared.
|
|
2. `internal/plugins/middleware.go`: `PluginAuth(svc)` gin middleware — parses
|
|
`Authorization: Bearer <api_key>`, resolves plugin via `svc.AuthenticatePlugin`,
|
|
sets plugin in context. `RequireCapability(kind, resource)` returns
|
|
`403 {"error":"capability '<kind>:<resource>' not declared"}` when the plugin's
|
|
manifest lacks it. Apply to plugin-facing routes:
|
|
- State API: `GET/POST/DELETE /api/plugins/:id/state`, `GET .../state/list`
|
|
(requires `capabilities.state`), backed by task-03 store, always scoped to the
|
|
authenticated plugin id (a plugin cannot touch another's id → 403 if mismatch).
|
|
- Secrets API: `GET /api/plugins/:id/secrets/:ref` (requires `capabilities.secrets`)
|
|
→ resolve via secretadapter.
|
|
- Office write-back used by plugins is OUT OF SCOPE here (api_read/api_write
|
|
office endpoints deferred) — implement state + secrets only; leave a comment
|
|
noting office data endpoints are follow-up.
|
|
3. `internal/backendapp` wiring: `initPluginsService(...)` in services.go calling
|
|
`plugins.Provide(...)`; field on `Services` + `routeParams`; `registerRoutes`
|
|
calls `plugins.RegisterRoutes` and starts health monitor + delivery via the
|
|
returned service (respect `addCleanup`). Gate registration on `features.Plugins`.
|
|
|
|
## Files
|
|
- `apps/backend/internal/plugins/handlers.go` + `handlers_test.go`
|
|
- `apps/backend/internal/plugins/middleware.go` + `middleware_test.go`
|
|
- `apps/backend/internal/plugins/dto.go` (request/response DTOs)
|
|
- edits: `apps/backend/internal/backendapp/services.go`,
|
|
`apps/backend/internal/backendapp/helpers.go`
|
|
|
|
## Verification
|
|
- `go test ./internal/plugins/... ./internal/backendapp/...` from `apps/backend`
|
|
- `make -C apps/backend build` and `make -C apps/backend lint`
|
|
|
|
## Output contract
|
|
Report: full route table, middleware/capability behavior, wiring points touched in
|
|
backendapp, and confirmation the feature gate is respected. This is the last
|
|
backend task — ensure `make -C apps/backend build test lint` pass for the package.
|
|
|
|
## Dependencies
|
|
task-05, task-06, task-07, task-08. (Feature flag field from task-10 — if task-10
|
|
hasn't landed, add the `Plugins bool` field to FeaturesConfig yourself and note it.)
|