mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 17:22:07 +00:00
622c19cafe35201c774fbea06a7f4fde7ac21bc4
1 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b09bd0984a |
auth-bundle-2 Phase 9: 11 OIDC + session MCP tools (Phase-5 surface parity)
Closes Phase 9 of cowork/auth-bundle-2-prompt.md. Every Phase-5 HTTP
endpoint now has a matching MCP tool so operators driving certctl
from Claude / VS Code / any MCP client get the same OIDC-provider +
group-mapping + session management capability the GUI + CLI already
expose.
Coverage map (each tool → HTTP endpoint → permission)
=====================================================
certctl_auth_list_oidc_providers GET /v1/auth/oidc/providers auth.oidc.list
certctl_auth_get_oidc_provider GET /v1/auth/oidc/providers (filtered) auth.oidc.list
certctl_auth_create_oidc_provider POST /v1/auth/oidc/providers auth.oidc.create
certctl_auth_update_oidc_provider PUT /v1/auth/oidc/providers/{id} auth.oidc.edit
certctl_auth_delete_oidc_provider DELETE /v1/auth/oidc/providers/{id} auth.oidc.delete
certctl_auth_refresh_oidc_provider POST /v1/auth/oidc/providers/{id}/refresh auth.oidc.edit
certctl_auth_list_group_mappings GET /v1/auth/oidc/group-mappings?provider_id auth.oidc.list
certctl_auth_add_group_mapping POST /v1/auth/oidc/group-mappings auth.oidc.edit
certctl_auth_remove_group_mapping DELETE /v1/auth/oidc/group-mappings/{id} auth.oidc.edit
certctl_auth_list_sessions GET /v1/auth/sessions[?actor_id=&actor_type=] auth.session.list (own) | auth.session.list.all (other)
certctl_auth_revoke_session DELETE /v1/auth/sessions/{id} auth.session.revoke (or own-bypass)
Implementation notes
====================
internal/mcp/tools_auth_bundle2.go (NEW): 11 tools wired through three
focused register functions (registerAuthOIDCProviderTools,
registerAuthGroupMappingTools, registerAuthSessionTools). Every tool
routes through the existing Client (Get/Post/Put/Delete) so permission
gates fire server-side via the Phase-5 rbacGate wrappers — a non-admin
caller's MCP tool invocation gets whatever 403 the underlying HTTP
handler emits, not an MCP-side bypass.
Empty-id guard
--------------
Every path-id tool short-circuits to errorResult(fmt.Errorf("id is required"))
BEFORE the HTTP call. Defense against url.PathEscape("") collapsing a
singular op into the list endpoint (which would silently succeed against
a permissive backend). Same pattern across all 6 path-id tools (get,
update, delete, refresh provider; remove mapping; revoke session).
auth_get_oidc_provider list-then-filter
---------------------------------------
The Phase-5 HTTP API doesn't expose a singular GET /v1/auth/oidc/providers/{id}
endpoint — the GUI's OIDCProviderDetailPage fetches the full list and
filters in-process. The MCP tool mirrors that pattern exactly: GET the
list, JSON-decode the providers envelope, walk the array filtering by
id, return the matching raw JSON object on hit or an explicit "oidc
provider not found: <id>" error on miss. This keeps the MCP surface
in lockstep with the GUI's permission boundary (auth.oidc.list grants
"see any provider", as it does on the GUI) without inventing a new HTTP
endpoint.
internal/mcp/types.go (MODIFIED): 8 new input types matching the
Phase-5 wire shapes (oidcProviderRequest at internal/api/handler/auth_session_oidc.go).
client_secret on Update is optional — empty preserves the existing
ciphertext on the server, providing a value rotates. Mirrors the GUI's
edit-without-rotate UX from web/src/pages/auth/OIDCProviderDetailPage.tsx.
internal/mcp/tools.go (MODIFIED): registerAuthBundle2Tools wired into
RegisterTools alongside the Bundle 1 Phase 11 registerAuthTools.
Test coverage
=============
internal/mcp/tools_auth_bundle2_test.go (NEW), 5 test cases:
* TestAuthBundle2MCP_AllToolsRegister — registerAuthBundle2Tools
doesn't panic; catches duplicate-name regressions before CI.
* TestAuthBundle2MCP_PathsAndMethods — 11 cases (one per tool) +
the admin-other-actor variant of list_sessions; asserts the right
method + path + body + query string fires against the mock API.
* TestAuthBundle2MCP_ForbiddenSurfacesError — every tool's underlying
HTTP path returns a propagated error containing "forbidden" / "403"
when the mock returns 403, exercising the errorResult fence path.
* TestAuthBundle2MCP_GetProviderFiltersListByID — pins the list-then-
filter shape end-to-end with both the hit-and-return (returns the
matching raw JSON object) and miss-returns-error (sentinel string
"oidc provider not found") branches.
* TestAuthBundle2MCP_EmptyIDInputShortCircuits — pins the
strings.TrimSpace empty-id guard at the top of every path-id handler.
* TestAuthBundle2MCP_PromptCoverage — every tool the prompt enumerates
is also present in tools_per_tool_test.go's allHappyPathCases (so
the live-dispatch + 5xx error-path tests cover all 11 tools).
internal/mcp/tools_per_tool_test.go (MODIFIED): 11 new toolCase entries
in allHappyPathCases (live in-memory MCP dispatch + happy-path fence
shape + 5xx error-path fence shape) + a mock-API special case for
GET /api/v1/auth/oidc/providers that returns the right envelope shape
({"providers":[{"id":"op-okta",...}]}) so the get_oidc_provider tool's
in-process filter resolves under the live dispatch.
Verification
============
* gofmt + go vet — clean across internal/mcp/...
* go test -short -count=1 — green across internal/mcp + internal/auth/...
+ internal/api/handler + internal/api/router (13 packages, 0 failures).
* MCP tool count re-derive (CLAUDE.md command):
grep -cE 'mcp\.AddTool\(' internal/mcp/tools*.go
→ tools.go=121, tools_auth.go=12, tools_auth_bundle2.go=11 (new),
tools_est.go=6 — total 150. Matches the live count
TestMCP_RegisterTools_DispatchableToolCount asserts.
* staticcheck deferred — sandbox /tmp at 99% disk, can't install the
binary; all SA*/ST* lints would have run via the staticcheck-CI step
on push. go vet caught the only real issue (an unused context import)
before commit.
Not in this commit (deferred)
=============================
* Break-glass admin MCP tools (4 endpoints from Phase 7.5). The Phase 9
prompt does NOT enumerate break-glass tools; its exit criteria is
"Every API endpoint from Phase 5 has an MCP tool". Phase 5 does not
include the break-glass surface (Phase 7.5 ships those endpoints with
surface-invisibility semantics: 404 when CERTCTL_BREAKGLASS_ENABLED=false,
which complicates LLM tool-discovery UX). If the operator wants
break-glass MCP parity, that's a follow-on bundle.
|