diff --git a/internal/authz/authz.go b/internal/authz/authz.go index e511c00..b16b2dd 100644 --- a/internal/authz/authz.go +++ b/internal/authz/authz.go @@ -225,7 +225,14 @@ func HTTPWithSessions(tokens map[Surface]string, sessions *Sessions, next http.H workerPath := strings.HasPrefix(r.URL.Path, "/v1/federation/") || (r.Method == http.MethodGet && r.URL.Path == "/v1/tasks") || (r.Method == http.MethodPost && r.URL.Path == "/v1/artifacts") || - (r.Method == http.MethodGet && strings.HasPrefix(r.URL.Path, "/v1/artifacts/")) + (r.Method == http.MethodGet && strings.HasPrefix(r.URL.Path, "/v1/artifacts/")) || + // A federated worker renders its own launch instruction from the + // reduced intent, so this read is as necessary to it as the task + // list. Found live on the first burn-in task: the endpoint was + // added for workers, the exemption was not, and every federated + // launch failed with "effective intent: federation: 401 + // Unauthorized: unauthorized surface". + (r.Method == http.MethodGet && strings.HasPrefix(r.URL.Path, "/v1/tasks/") && strings.HasSuffix(r.URL.Path, "/intent")) if federationRegistration || r.URL.Path == HarnessTurnPath || (worker && workerPath) { next.ServeHTTP(w, r) return diff --git a/internal/authz/authz_test.go b/internal/authz/authz_test.go index bd40881..ccebd34 100644 --- a/internal/authz/authz_test.go +++ b/internal/authz/authz_test.go @@ -140,6 +140,8 @@ func TestFederationRequestsUseTheirOwnCredentials(t *testing.T) { {name: "worker task reconciliation reaches worker handler", method: http.MethodGet, path: "/v1/tasks", worker: "workpc-opencode", want: http.StatusNoContent}, {name: "worker artifact read reaches worker handler", method: http.MethodGet, path: "/v1/artifacts/ref", worker: "workpc-opencode", want: http.StatusNoContent}, {name: "worker artifact upload reaches worker handler", method: http.MethodPost, path: "/v1/artifacts", worker: "workpc-opencode", want: http.StatusNoContent}, + {name: "worker intent read reaches worker handler", method: http.MethodGet, path: "/v1/tasks/06G3YR34117MAYT6KEAC9RJHD0/intent", worker: "workpc-opencode", want: http.StatusNoContent}, + {name: "unnamed intent read remains web gated", method: http.MethodGet, path: "/v1/tasks/06G3YR34117MAYT6KEAC9RJHD0/intent", want: http.StatusUnauthorized}, {name: "unnamed worker request remains web gated", method: http.MethodGet, path: "/v1/federation/events", want: http.StatusUnauthorized}, {name: "worker list remains web gated", method: http.MethodGet, path: "/v1/federation/workers", want: http.StatusUnauthorized}, } {