mirror of
https://github.com/juanfont/headscale.git
synced 2026-07-16 04:50:45 +09:00
af46fe8867
Add scope-enforcement coverage and integration tests for the oauth-clients CLI across the test matrix.
59 lines
1.9 KiB
Go
59 lines
1.9 KiB
Go
package apiv2
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/danielgtaylor/huma/v2"
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/juanfont/headscale/hscontrol/scope"
|
|
)
|
|
|
|
// selfEnforcedKeyOps are the authenticated operations that intentionally declare
|
|
// NO static scope because they multiplex on keyType and authorize inside the
|
|
// handler via requireKeyScope (see keys.go). Every other authenticated operation
|
|
// must declare a scope.
|
|
var selfEnforcedKeyOps = map[string]bool{
|
|
"POST /api/v2/tailnet/{tailnet}/keys": true,
|
|
"GET /api/v2/tailnet/{tailnet}/keys": true,
|
|
"GET /api/v2/tailnet/{tailnet}/keys/{keyId}": true,
|
|
"DELETE /api/v2/tailnet/{tailnet}/keys/{keyId}": true,
|
|
}
|
|
|
|
// TestEveryAuthenticatedOperationDeclaresScope is the structural guarantee that no
|
|
// v2 operation ships unprotected: any operation that requires authentication
|
|
// (non-empty Security) must either declare a required scope via requireScope, or
|
|
// be one of the keyType-multiplexed keys operations that self-enforce. A new
|
|
// operation added without scope protection fails this test.
|
|
func TestEveryAuthenticatedOperationDeclaresScope(t *testing.T) {
|
|
api := NewAPI(chi.NewMux(), Backend{})
|
|
|
|
for path, item := range api.OpenAPI().Paths {
|
|
for method, op := range humaOperations(item) {
|
|
if op == nil || len(op.Security) == 0 {
|
|
continue // unregistered method or a public operation
|
|
}
|
|
|
|
key := method + " " + path
|
|
if selfEnforcedKeyOps[key] {
|
|
continue
|
|
}
|
|
|
|
if _, ok := op.Metadata[scopeMetaKey].(scope.Scope); !ok {
|
|
t.Errorf("operation %q is authenticated but declares no required scope; "+
|
|
"wrap it in requireScope, or add it to selfEnforcedKeyOps if it "+
|
|
"authorizes inside the handler", key)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func humaOperations(item *huma.PathItem) map[string]*huma.Operation {
|
|
return map[string]*huma.Operation{
|
|
"GET": item.Get,
|
|
"POST": item.Post,
|
|
"PUT": item.Put,
|
|
"DELETE": item.Delete,
|
|
"PATCH": item.Patch,
|
|
}
|
|
}
|