diff --git a/go.mod b/go.mod index 13e43bd..27489d7 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.25.3 require ( github.com/oapi-codegen/runtime v1.6.0 - github.com/saltbo/restish/v2 v2.3.1-0.20260813222710-02b196b2436b + github.com/saltbo/restish/v2 v2.3.1-0.20260828035857-92c8f86bdb55 github.com/spf13/cobra v1.10.2 ) diff --git a/go.sum b/go.sum index 9a2ed26..c30a90b 100644 --- a/go.sum +++ b/go.sum @@ -108,10 +108,8 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/saltbo/restish/v2 v2.3.1-0.20260811210222-2daa7d288a21 h1:ZEe8JDr264sZfPhWmvZE6Vmd3UOZfv1PQ2q6aqkDtS8= -github.com/saltbo/restish/v2 v2.3.1-0.20260811210222-2daa7d288a21/go.mod h1:nB2f22CFu5X6R8Az9GDJ5+WTobImIs4l2cgczEUSWQY= -github.com/saltbo/restish/v2 v2.3.1-0.20260813222710-02b196b2436b h1:RZmrowP0ICoITmm+YtpclhzCnZRw+lFRDGCUFqDAz+U= -github.com/saltbo/restish/v2 v2.3.1-0.20260813222710-02b196b2436b/go.mod h1:nB2f22CFu5X6R8Az9GDJ5+WTobImIs4l2cgczEUSWQY= +github.com/saltbo/restish/v2 v2.3.1-0.20260828035857-92c8f86bdb55 h1:66B4rNBVWBvEtMQsT2q/Kdf0ihta/7gfTm7lmBwJLtA= +github.com/saltbo/restish/v2 v2.3.1-0.20260828035857-92c8f86bdb55/go.mod h1:nB2f22CFu5X6R8Az9GDJ5+WTobImIs4l2cgczEUSWQY= github.com/sandrolain/httpcache v1.4.0 h1:Jf4Vx62X2ybvNPSpPvI1kT3xvMdDG1AsApQjOQKO9E0= github.com/sandrolain/httpcache v1.4.0/go.mod h1:kHBuXveitSn39SNPBhdf/ybG272X706HJ2RJqOQ+Em0= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go index 571f6cb..b3dc1ba 100644 --- a/internal/cli/cli_test.go +++ b/internal/cli/cli_test.go @@ -526,6 +526,32 @@ func TestOperationScopeAlternativesPreserveOAuthAlternatives(t *testing.T) { } } +func TestOperationAuthoritySupportsStandardOpenIDKind(t *testing.T) { + operation := restish.OperationInspection{ + ID: "listProjects", + CredentialAlternatives: [][]restish.CredentialRequirementInspection{{{ + ID: "realmrootOidc", Kind: "openid", Needs: []string{"projects:read"}, + }}}, + } + if got := operationCredentialScopeAlternatives(operation); len(got) != 1 || strings.Join(got[0], " ") != "projects:read" { + t.Fatalf("scope alternatives = %#v", got) + } + if !operationCoveredByScopes(operation, []string{"projects:read"}) { + t.Fatal("standard OpenID requirement not covered by matching authority") + } + + config := &restish.Config{APIs: map[string]*restish.APIConfig{"ama": {}}} + inspection := restish.APIInspection{Operations: []restish.OperationInspection{operation}} + binding := agent.CredentialBinding{Reference: "selected-reference", Scopes: []string{"projects:read"}} + if err := bindProfileCredentials(config, catalog.ResourceServer{CommandName: "ama"}, inspection, "default", binding); err != nil { + t.Fatal(err) + } + credential := config.APIs["ama"].Profiles["default"].Credentials["realmrootOidc"] + if credential == nil || credential.Auth == nil || credential.Auth.Type != "dpop" { + t.Fatalf("credential = %#v", credential) + } +} + func TestOperationAuthorityRejectsObsoleteOAuthDPoPKind(t *testing.T) { operation := restish.OperationInspection{CredentialAlternatives: [][]restish.CredentialRequirementInspection{{{ ID: "oauth2", Kind: "oauth2-dpop", Needs: []string{"applications:read"}, diff --git a/internal/cli/operation_authority.go b/internal/cli/operation_authority.go index 97f6c17..c5243ca 100644 --- a/internal/cli/operation_authority.go +++ b/internal/cli/operation_authority.go @@ -294,7 +294,7 @@ func bindProfileCredentials( for _, candidate := range inspection.Operations { for _, alternative := range candidate.CredentialAlternatives { for _, requirement := range alternative { - if requirement.Kind != "oauth2" { + if !standardOAuthCredentialKind(requirement.Kind) { continue } profile.Credentials[requirement.ID] = &restishconfig.CredentialConfig{ @@ -312,7 +312,7 @@ func operationCredentialScopeAlternatives(operation restish.OperationInspection) scopes := make([]string, 0) supported := len(alternative) > 0 for _, requirement := range alternative { - if requirement.Kind != "oauth2" { + if !standardOAuthCredentialKind(requirement.Kind) { supported = false break } @@ -337,6 +337,10 @@ func uniqueOperationScopes(scopes []string) []string { return result } +func standardOAuthCredentialKind(kind string) bool { + return kind == "oauth2" || kind == "openid" +} + func invocationRequiresAuthority(args []string) bool { for _, argument := range args { switch argument { @@ -379,7 +383,7 @@ func operationCoveredByScopes(operation restish.OperationInspection, scopes []st for _, alternative := range operation.CredentialAlternatives { covered := len(alternative) > 0 for _, requirement := range alternative { - if requirement.Kind != "oauth2" { + if !standardOAuthCredentialKind(requirement.Kind) { covered = false break }