ContainerImage.Pinniped/cmd/pinniped/cmd/kubeconfig_test.go
Ryan Richard 84c3c3aa9c Optionally allow OIDC password grant for CLI-based login experience
- Add `AllowPasswordGrant` boolean field to OIDCIdentityProvider's spec
- The oidc upstream watcher controller copies the value of
  `AllowPasswordGrant` into the configuration of the cached provider
- Add password grant to the UpstreamOIDCIdentityProviderI interface
  which is implemented by the cached provider instance for use in the
  authorization endpoint
- Enhance the IDP discovery endpoint to return the supported "flows"
  for each IDP ("cli_password" and/or "browser_authcode")
- Enhance `pinniped get kubeconfig` to help the user choose the desired
  flow for the selected IDP, and to write the flow into the resulting
  kubeconfg
- Enhance `pinniped login oidc` to have a flow flag to tell it which
  client-side flow it should use for auth (CLI-based or browser-based)
- In the Dex config, allow the resource owner password grant, which Dex
  implements to also return ID tokens, for use in integration tests
- Enhance the authorize endpoint to perform password grant when
  requested by the incoming headers. This commit does not include unit
  tests for the enhancements to the authorize endpoint, which will come
  in the next commit
- Extract some shared helpers from the callback endpoint to share the
  code with the authorize endpoint
- Add new integration tests
2021-08-12 10:45:39 -07:00

2812 lines
111 KiB
Go

// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"bytes"
"encoding/base64"
"fmt"
"io/ioutil"
"net/http"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
kubetesting "k8s.io/client-go/testing"
"k8s.io/client-go/tools/clientcmd"
conciergev1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
conciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
fakeconciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/fake"
"go.pinniped.dev/internal/certauthority"
"go.pinniped.dev/internal/here"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/internal/testutil/testlogger"
)
func TestGetKubeconfig(t *testing.T) {
testOIDCCA, err := certauthority.New("Test CA", 1*time.Hour)
require.NoError(t, err)
tmpdir := testutil.TempDir(t)
testOIDCCABundlePath := filepath.Join(tmpdir, "testca.pem")
require.NoError(t, ioutil.WriteFile(testOIDCCABundlePath, testOIDCCA.Bundle(), 0600))
testConciergeCA, err := certauthority.New("Test Concierge CA", 1*time.Hour)
require.NoError(t, err)
testConciergeCABundlePath := filepath.Join(tmpdir, "testconciergeca.pem")
require.NoError(t, ioutil.WriteFile(testConciergeCABundlePath, testConciergeCA.Bundle(), 0600))
credentialIssuer := func() runtime.Object {
return &configv1alpha1.CredentialIssuer{
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
Status: configv1alpha1.CredentialIssuerStatus{
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
Status: configv1alpha1.SuccessStrategyStatus,
Reason: configv1alpha1.FetchedKeyStrategyReason,
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.TokenCredentialRequestAPIFrontendType,
TokenCredentialRequestAPIInfo: &configv1alpha1.TokenCredentialRequestAPIInfo{
Server: "https://concierge-endpoint.example.com",
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
},
},
}},
},
}
}
jwtAuthenticator := func(issuerCABundle string, issuerURL string) runtime.Object {
return &conciergev1alpha1.JWTAuthenticator{
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
Spec: conciergev1alpha1.JWTAuthenticatorSpec{
Issuer: issuerURL,
Audience: "test-audience",
TLS: &conciergev1alpha1.TLSSpec{
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(issuerCABundle)),
},
},
}
}
happyOIDCDiscoveryResponse := func(issuerURL string) string {
return here.Docf(`{
"issuer": "%s",
"other-key": "other-value",
"discovery.supervisor.pinniped.dev/v1alpha1": {
"pinniped_identity_providers_endpoint": "%s/v1alpha1/pinniped_identity_providers"
},
"another-key": "another-value"
}`, issuerURL, issuerURL)
}
onlyIssuerOIDCDiscoveryResponse := func(issuerURL string) string {
return here.Docf(`{
"issuer": "%s",
"other-key": "other-value"
}`, issuerURL)
}
tests := []struct {
name string
args func(string, string) []string
env map[string]string
getPathToSelfErr error
getClientsetErr error
conciergeObjects func(string, string) []runtime.Object
conciergeReactions []kubetesting.Reactor
oidcDiscoveryResponse func(string) string
oidcDiscoveryStatusCode int
idpsDiscoveryResponse string
idpsDiscoveryStatusCode int
wantLogs func(string, string) []string
wantError bool
wantStdout func(string, string) string
wantStderr func(string, string) string
wantOptionsCount int
wantAPIGroupSuffix string
}{
{
name: "help flag passed",
args: func(issuerCABundle string, issuerURL string) []string { return []string{"--help"} },
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Doc(`
Generate a Pinniped-based kubeconfig for a cluster
Usage:
kubeconfig [flags]
Flags:
--concierge-api-group-suffix string Concierge API group suffix (default "pinniped.dev")
--concierge-authenticator-name string Concierge authenticator name (default: autodiscover)
--concierge-authenticator-type string Concierge authenticator type (e.g., 'webhook', 'jwt') (default: autodiscover)
--concierge-ca-bundle path Path to TLS certificate authority bundle (PEM format, optional, can be repeated) to use when connecting to the Concierge
--concierge-credential-issuer string Concierge CredentialIssuer object to use for autodiscovery (default: autodiscover)
--concierge-endpoint string API base for the Concierge endpoint
--concierge-mode mode Concierge mode of operation (default TokenCredentialRequestAPI)
--concierge-skip-wait Skip waiting for any pending Concierge strategies to become ready (default: false)
--credential-cache string Path to cluster-specific credentials cache
--generated-name-suffix string Suffix to append to generated cluster, context, user kubeconfig entries (default "-pinniped")
-h, --help help for kubeconfig
--kubeconfig string Path to kubeconfig file
--kubeconfig-context string Kubeconfig context name (default: current active context)
--no-concierge Generate a configuration which does not use the Concierge, but sends the credential to the cluster directly
--oidc-ca-bundle path Path to TLS certificate authority bundle (PEM format, optional, can be repeated)
--oidc-client-id string OpenID Connect client ID (default: autodiscover) (default "pinniped-cli")
--oidc-issuer string OpenID Connect issuer URL (default: autodiscover)
--oidc-listen-port uint16 TCP port for localhost listener (authorization code flow only)
--oidc-request-audience string Request a token with an alternate audience using RFC8693 token exchange
--oidc-scopes strings OpenID Connect scopes to request during login (default [offline_access,openid,pinniped:request-audience])
--oidc-session-cache string Path to OpenID Connect session cache file
--oidc-skip-browser During OpenID Connect login, skip opening the browser (just print the URL)
-o, --output string Output file path (default: stdout)
--skip-validation Skip final validation of the kubeconfig (default: false)
--static-token string Instead of doing an OIDC-based login, specify a static token
--static-token-env string Instead of doing an OIDC-based login, read a static token from the environment
--timeout duration Timeout for autodiscovery and validation (default 10m0s)
--upstream-identity-provider-flow string The type of client flow to use with the upstream identity provider during login with a Supervisor (e.g. 'cli_password', 'browser_authcode')
--upstream-identity-provider-name string The name of the upstream identity provider used during login with a Supervisor
--upstream-identity-provider-type string The type of the upstream identity provider used during login with a Supervisor (e.g. 'oidc', 'ldap')
`)
},
},
{
name: "invalid OIDC CA bundle path",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--oidc-ca-bundle", "./does/not/exist",
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: invalid argument "./does/not/exist" for "--oidc-ca-bundle" flag: could not read CA bundle path: open ./does/not/exist: no such file or directory` + "\n"
},
},
{
name: "invalid Concierge CA bundle",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--concierge-ca-bundle", "./does/not/exist",
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: invalid argument "./does/not/exist" for "--concierge-ca-bundle" flag: could not read CA bundle path: open ./does/not/exist: no such file or directory` + "\n"
},
},
{
name: "invalid kubeconfig path",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./does/not/exist",
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: could not load --kubeconfig: stat ./does/not/exist: no such file or directory` + "\n"
},
},
{
name: "invalid kubeconfig context, missing",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--kubeconfig-context", "invalid",
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: could not load --kubeconfig/--kubeconfig-context: no such context "invalid"` + "\n"
},
},
{
name: "invalid kubeconfig context, missing cluster",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--kubeconfig-context", "invalid-context-no-such-cluster",
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: could not load --kubeconfig/--kubeconfig-context: no such cluster "invalid-cluster"` + "\n"
},
},
{
name: "invalid kubeconfig context, missing user",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--kubeconfig-context", "invalid-context-no-such-user",
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: could not load --kubeconfig/--kubeconfig-context: no such user "invalid-user"` + "\n"
},
},
{
name: "clientset creation failure",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
}
},
getClientsetErr: fmt.Errorf("some kube error"),
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: could not configure Kubernetes client: some kube error` + "\n"
},
},
{
name: "no credentialissuers",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: no CredentialIssuers were found` + "\n"
},
},
{
name: "credentialissuer not found",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--concierge-credential-issuer", "does-not-exist",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: credentialissuers.config.concierge.pinniped.dev "does-not-exist" not found` + "\n"
},
},
{
name: "webhook authenticator not found",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--concierge-authenticator-type", "webhook",
"--concierge-authenticator-name", "test-authenticator",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
}
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: webhookauthenticators.authentication.concierge.pinniped.dev "test-authenticator" not found` + "\n"
},
},
{
name: "JWT authenticator not found",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--concierge-authenticator-type", "jwt",
"--concierge-authenticator-name", "test-authenticator",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
}
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: jwtauthenticators.authentication.concierge.pinniped.dev "test-authenticator" not found` + "\n"
},
},
{
name: "invalid authenticator type",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--concierge-authenticator-type", "invalid",
"--concierge-authenticator-name", "test-authenticator",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
}
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: invalid authenticator type "invalid", supported values are "webhook" and "jwt"` + "\n"
},
},
{
name: "fail to autodetect authenticator, listing jwtauthenticators fails",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
}
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
}
},
conciergeReactions: []kubetesting.Reactor{
&kubetesting.SimpleReactor{
Verb: "*",
Resource: "jwtauthenticators",
Reaction: func(kubetesting.Action) (bool, runtime.Object, error) {
return true, nil, fmt.Errorf("some list error")
},
},
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: failed to list JWTAuthenticator objects for autodiscovery: some list error` + "\n"
},
},
{
name: "fail to autodetect authenticator, listing webhookauthenticators fails",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
}
},
conciergeReactions: []kubetesting.Reactor{
&kubetesting.SimpleReactor{
Verb: "*",
Resource: "webhookauthenticators",
Reaction: func(kubetesting.Action) (bool, runtime.Object, error) {
return true, nil, fmt.Errorf("some list error")
},
},
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: failed to list WebhookAuthenticator objects for autodiscovery: some list error` + "\n"
},
},
{
name: "fail to autodetect authenticator, none found",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
}
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: no authenticators were found` + "\n"
},
},
{
name: "fail to autodetect authenticator, multiple found",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
&configv1alpha1.CredentialIssuer{ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"}},
&conciergev1alpha1.JWTAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator-1"}},
&conciergev1alpha1.JWTAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator-2"}},
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator-3"}},
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator-4"}},
}
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="found JWTAuthenticator" "name"="test-authenticator-1"`,
`"level"=0 "msg"="found JWTAuthenticator" "name"="test-authenticator-2"`,
`"level"=0 "msg"="found WebhookAuthenticator" "name"="test-authenticator-3"`,
`"level"=0 "msg"="found WebhookAuthenticator" "name"="test-authenticator-4"`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: multiple authenticators were found, so the --concierge-authenticator-type/--concierge-authenticator-name flags must be specified` + "\n"
},
},
{
name: "autodetect webhook authenticator, bad credential issuer with only failing strategy",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
&configv1alpha1.CredentialIssuer{
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
Status: configv1alpha1.CredentialIssuerStatus{
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
Type: "SomeType",
Status: configv1alpha1.ErrorStrategyStatus,
Reason: "SomeReason",
Message: "Some message",
}},
},
},
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
}
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="found CredentialIssuer strategy" "message"="Some message" "reason"="SomeReason" "status"="Error" "type"="SomeType"`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: could not autodiscover --concierge-mode` + "\n"
},
},
{
name: "autodetect webhook authenticator, bad credential issuer with invalid impersonation CA",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
&configv1alpha1.CredentialIssuer{
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
Status: configv1alpha1.CredentialIssuerStatus{
Strategies: []configv1alpha1.CredentialIssuerStrategy{
{
Type: "SomeBrokenType",
Status: configv1alpha1.ErrorStrategyStatus,
Reason: "SomeFailureReason",
Message: "Some error message",
LastUpdateTime: metav1.Now(),
},
{
Type: "SomeUnknownType",
Status: configv1alpha1.SuccessStrategyStatus,
Reason: "SomeReason",
Message: "Some error message",
LastUpdateTime: metav1.Now(),
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: "SomeUnknownFrontendType",
},
},
{
Type: "SomeType",
Status: configv1alpha1.SuccessStrategyStatus,
Reason: "SomeReason",
Message: "Some message",
LastUpdateTime: metav1.Now(),
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.ImpersonationProxyFrontendType,
ImpersonationProxyInfo: &configv1alpha1.ImpersonationProxyInfo{
Endpoint: "https://impersonation-endpoint",
CertificateAuthorityData: "invalid-base-64",
},
},
},
},
},
},
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
}
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in impersonation proxy mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://impersonation-endpoint"`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: autodiscovered Concierge CA bundle is invalid: illegal base64 data at input byte 7` + "\n"
},
},
{
name: "autodetect webhook authenticator, missing --oidc-issuer",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
}
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered WebhookAuthenticator" "name"="test-authenticator"`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: could not autodiscover --oidc-issuer and none was provided` + "\n"
},
},
{
name: "autodetect JWT authenticator, invalid TLS bundle",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
&configv1alpha1.CredentialIssuer{
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
Status: configv1alpha1.CredentialIssuerStatus{
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
Server: "https://concierge-endpoint",
CertificateAuthorityData: "ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==",
},
Strategies: []configv1alpha1.CredentialIssuerStrategy{{
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
Status: configv1alpha1.SuccessStrategyStatus,
Reason: configv1alpha1.FetchedKeyStrategyReason,
Message: "Successfully fetched key",
LastUpdateTime: metav1.Now(),
// Simulate a previous version of CredentialIssuer that's missing this Frontend field.
Frontend: nil,
}},
},
},
&conciergev1alpha1.JWTAuthenticator{
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
Spec: conciergev1alpha1.JWTAuthenticatorSpec{
Issuer: issuerURL,
Audience: "some-test-audience",
TLS: &conciergev1alpha1.TLSSpec{
CertificateAuthorityData: "invalid-base64",
},
},
},
}
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="some-test-audience"`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: tried to autodiscover --oidc-ca-bundle, but JWTAuthenticator test-authenticator has invalid spec.tls.certificateAuthorityData: illegal base64 data at input byte 7` + "\n"
},
},
{
name: "fail to get self-path",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
}
},
getPathToSelfErr: fmt.Errorf("some OS error"),
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryResponse: onlyIssuerOIDCDiscoveryResponse,
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: could not determine the Pinniped executable path: some OS error` + "\n"
},
},
{
name: "invalid static token flags",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--static-token", "test-token",
"--static-token-env", "TEST_TOKEN",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
}
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered WebhookAuthenticator" "name"="test-authenticator"`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: only one of --static-token and --static-token-env can be specified` + "\n"
},
},
{
name: "invalid API group suffix",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--concierge-api-group-suffix", ".starts.with.dot",
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: invalid API group suffix: a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')` + "\n"
},
},
{
name: "when OIDC discovery document 400s",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryStatusCode: http.StatusBadRequest,
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return "Error: while fetching OIDC discovery data from issuer: 400 Bad Request: {}\n"
},
},
{
name: "when OIDC discovery document lists the wrong issuer",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryResponse: func(issuerURL string) string {
return here.Doc(`{
"issuer": "https://wrong-issuer.com"
}`)
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return fmt.Sprintf(
"Error: while fetching OIDC discovery data from issuer: oidc: issuer did not match the issuer returned by provider, expected \"%s\" got \"https://wrong-issuer.com\"\n",
issuerURL)
},
},
{
name: "when IDP discovery document returns any error",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryStatusCode: http.StatusBadRequest,
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return "Error: unable to fetch IDP discovery data from issuer: unexpected http response status: 400 Bad Request\n"
},
},
{
name: "when IDP discovery document contains multiple IDPs and no name or type flags are given",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap"},
{"name": "some-oidc-idp", "type": "oidc"}
]
}`),
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: multiple Supervisor upstream identity providers were found, ` +
`so the --upstream-identity-provider-name/--upstream-identity-provider-type flags must be specified. ` +
`Found these upstreams: [{"name":"some-ldap-idp","type":"ldap"},{"name":"some-oidc-idp","type":"oidc"}]` + "\n"
},
},
{
name: "when OIDC discovery document is not valid JSON",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryResponse: func(issuerURL string) string {
return "this is not valid JSON"
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return "Error: while fetching OIDC discovery data from issuer: oidc: failed to decode provider discovery object: got Content-Type = application/json, but could not unmarshal as JSON: invalid character 'h' in literal true (expecting 'r')\n"
},
},
{
name: "when IDP discovery document is not valid JSON",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: "this is not valid JSON",
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return "Error: unable to fetch IDP discovery data from issuer: could not parse response JSON: invalid character 'h' in literal true (expecting 'r')\n"
},
},
{
name: "when tls information is missing from jwtauthenticator, errors because OIDC discovery fails",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
&conciergev1alpha1.JWTAuthenticator{
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
Spec: conciergev1alpha1.JWTAuthenticatorSpec{
Issuer: issuerURL,
Audience: "test-audience",
},
},
}
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return fmt.Sprintf("Error: while fetching OIDC discovery data from issuer: Get \"%s/.well-known/openid-configuration\": x509: certificate signed by unknown authority\n", issuerURL)
},
},
{
name: "when the issuer url is bad",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--oidc-issuer", "https%://bad-issuer-url", // this url cannot be parsed
"--oidc-ca-bundle", f.Name(),
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
&conciergev1alpha1.JWTAuthenticator{
ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"},
Spec: conciergev1alpha1.JWTAuthenticatorSpec{
Issuer: issuerURL,
Audience: "test-audience",
},
},
}
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: while fetching OIDC discovery data from issuer: parse "https%://bad-issuer-url/.well-known/openid-configuration": first path segment in URL cannot contain colon` + "\n"
},
},
{
name: "when the IDP discovery url is bad",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryResponse: func(issuerURL string) string {
return here.Docf(`{
"issuer": "%s",
"discovery.supervisor.pinniped.dev/v1alpha1": {
"pinniped_identity_providers_endpoint": "https%%://illegal_url"
}
}`, issuerURL)
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: while forming request to IDP discovery URL: parse "https%://illegal_url": first path segment in URL cannot contain colon` + "\n"
},
},
{
name: "supervisor upstream IDP discovery does not find matching IDP when name and type are both specified",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-name", "does-not-exist-idp",
"--upstream-identity-provider-type", "ldap",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap"},
{"name": "some-other-ldap-idp", "type": "ldap"}
]
}`),
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: no Supervisor upstream identity providers with name "does-not-exist-idp" of type "ldap" were found.` +
` Found these upstreams: [{"name":"some-ldap-idp","type":"ldap"},{"name":"some-other-ldap-idp","type":"ldap"}]` + "\n"
},
},
{
name: "supervisor upstream IDP discovery fails to resolve ambiguity when type is specified but name is not",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-type", "ldap",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap"},
{"name": "some-other-ldap-idp", "type": "ldap"},
{"name": "some-oidc-idp", "type": "oidc"},
{"name": "some-other-oidc-idp", "type": "oidc"}
]
}`),
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: multiple Supervisor upstream identity providers of type "ldap" were found,` +
` so the --upstream-identity-provider-name flag must be specified.` +
` Found these upstreams: [{"name":"some-ldap-idp","type":"ldap"},{"name":"some-other-ldap-idp","type":"ldap"},{"name":"some-oidc-idp","type":"oidc"},{"name":"some-other-oidc-idp","type":"oidc"}]` + "\n"
},
},
{
name: "supervisor upstream IDP discovery fails to resolve ambiguity when name is specified but type is not",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-name", "my-idp",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "my-idp", "type": "ldap"},
{"name": "my-idp", "type": "oidc"},
{"name": "some-other-oidc-idp", "type": "oidc"}
]
}`),
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: multiple Supervisor upstream identity providers with name "my-idp" were found,` +
` so the --upstream-identity-provider-type flag must be specified.` +
` Found these upstreams: [{"name":"my-idp","type":"ldap"},{"name":"my-idp","type":"oidc"},{"name":"some-other-oidc-idp","type":"oidc"}]` + "\n"
},
},
{
name: "supervisor upstream IDP discovery fails to find any matching IDPs when type is specified but name is not",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-type", "ldap",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-oidc-idp", "type": "oidc"},
{"name": "some-other-oidc-idp", "type": "oidc"}
]
}`),
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: no Supervisor upstream identity providers of type "ldap" were found.` +
` Found these upstreams: [{"name":"some-oidc-idp","type":"oidc"},{"name":"some-other-oidc-idp","type":"oidc"}]` + "\n"
},
},
{
name: "supervisor upstream IDP discovery fails to find any matching IDPs when type is specified but name is not and there is only one IDP found",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-type", "ldap",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-oidc-idp", "type": "oidc"}
]
}`),
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: no Supervisor upstream identity providers of type "ldap" were found.` +
` Found these upstreams: [{"name":"some-oidc-idp","type":"oidc"}]` + "\n"
},
},
{
name: "supervisor upstream IDP discovery fails to find any matching IDPs when name is specified but type is not",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-name", "my-nonexistent-idp",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-oidc-idp", "type": "oidc"},
{"name": "some-other-oidc-idp", "type": "oidc"}
]
}`),
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: no Supervisor upstream identity providers with name "my-nonexistent-idp" were found.` +
` Found these upstreams: [{"name":"some-oidc-idp","type":"oidc"},{"name":"some-other-oidc-idp","type":"oidc"}]` + "\n"
},
},
{
name: "supervisor upstream IDP discovery fails to find any matching IDPs when name is specified but type is not and there is only one IDP found",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-name", "my-nonexistent-idp",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-oidc-idp", "type": "oidc"}
]
}`),
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: no Supervisor upstream identity providers with name "my-nonexistent-idp" were found.` +
` Found these upstreams: [{"name":"some-oidc-idp","type":"oidc"}]` + "\n"
},
},
{
name: "supervisor upstream IDP discovery when flow is specified but it does not match any flow returned by discovery",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-flow", "my-nonexistent-flow",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-oidc-idp", "type": "oidc", "flows": ["non-matching-flow-1", "non-matching-flow-2"]}
]
}`),
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: no client flow "my-nonexistent-flow" for Supervisor upstream identity provider "some-oidc-idp" of type "oidc" were found.` +
` Found these flows: [non-matching-flow-1 non-matching-flow-2]` + "\n"
},
},
{
name: "supervisor upstream IDP discovery when no flow is specified and more than one flow is returned by discovery",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-oidc-idp", "type": "oidc", "flows": ["flow1", "flow2"]}
]
}`),
wantError: true,
wantStderr: func(issuerCABundle string, issuerURL string) string {
return `Error: multiple client flows for Supervisor upstream identity provider "some-oidc-idp" of type "oidc" were found, so the --upstream-identity-provider-flow flag must be specified.` +
` Found these flows: [flow1 flow2]` + "\n"
},
},
{
name: "valid static token",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--static-token", "test-token",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
}
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered WebhookAuthenticator" "name"="test-authenticator"`,
}
},
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Doc(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- static
- --enable-concierge
- --concierge-api-group-suffix=pinniped.dev
- --concierge-authenticator-name=test-authenticator
- --concierge-authenticator-type=webhook
- --concierge-endpoint=https://fake-server-url-value
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --token=test-token
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`)
},
},
{
name: "valid static token from env var",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--static-token-env", "TEST_TOKEN",
"--skip-validation",
"--credential-cache", "",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
}
},
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered WebhookAuthenticator" "name"="test-authenticator"`,
}
},
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Doc(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- static
- --enable-concierge
- --concierge-api-group-suffix=pinniped.dev
- --concierge-authenticator-name=test-authenticator
- --concierge-authenticator-type=webhook
- --concierge-endpoint=https://fake-server-url-value
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --credential-cache=
- --token-env=TEST_TOKEN
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`)
},
},
{
name: "autodetect JWT authenticator",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryResponse: onlyIssuerOIDCDiscoveryResponse,
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --enable-concierge
- --concierge-api-group-suffix=pinniped.dev
- --concierge-authenticator-name=test-authenticator
- --concierge-authenticator-type=jwt
- --concierge-endpoint=https://fake-server-url-value
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "autodetect nothing, set a bunch of options",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--concierge-credential-issuer", "test-credential-issuer",
"--concierge-api-group-suffix", "tuna.io",
"--concierge-authenticator-type", "webhook",
"--concierge-authenticator-name", "test-authenticator",
"--concierge-mode", "TokenCredentialRequestAPI",
"--concierge-endpoint", "https://explicit-concierge-endpoint.example.com",
"--concierge-ca-bundle", testConciergeCABundlePath,
"--oidc-issuer", issuerURL,
"--oidc-skip-browser",
"--oidc-skip-listen",
"--oidc-listen-port", "1234",
"--oidc-ca-bundle", f.Name(),
"--oidc-session-cache", "/path/to/cache/dir/sessions.yaml",
"--oidc-debug-session-cache",
"--oidc-request-audience", "test-audience",
"--skip-validation",
"--generated-name-suffix", "-sso",
"--credential-cache", "/path/to/cache/dir/credentials.yaml",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
&conciergev1alpha1.WebhookAuthenticator{ObjectMeta: metav1.ObjectMeta{Name: "test-authenticator"}},
}
},
oidcDiscoveryResponse: onlyIssuerOIDCDiscoveryResponse,
wantLogs: func(issuerCABundle string, issuerURL string) []string { return nil },
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: %s
server: https://explicit-concierge-endpoint.example.com
name: kind-cluster-sso
contexts:
- context:
cluster: kind-cluster-sso
user: kind-user-sso
name: kind-context-sso
current-context: kind-context-sso
kind: Config
preferences: {}
users:
- name: kind-user-sso
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --enable-concierge
- --concierge-api-group-suffix=tuna.io
- --concierge-authenticator-name=test-authenticator
- --concierge-authenticator-type=webhook
- --concierge-endpoint=https://explicit-concierge-endpoint.example.com
- --concierge-ca-bundle-data=%s
- --credential-cache=/path/to/cache/dir/credentials.yaml
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --skip-browser
- --skip-listen
- --listen-port=1234
- --ca-bundle-data=%s
- --session-cache=/path/to/cache/dir/sessions.yaml
- --debug-session-cache
- --request-audience=test-audience
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)),
)
},
wantAPIGroupSuffix: "tuna.io",
},
{
name: "configure impersonation proxy with autodiscovered JWT authenticator",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--concierge-mode", "ImpersonationProxy",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
&configv1alpha1.CredentialIssuer{
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
Status: configv1alpha1.CredentialIssuerStatus{
Strategies: []configv1alpha1.CredentialIssuerStrategy{
// This TokenCredentialRequestAPI strategy would normally be chosen, but
// --concierge-mode=ImpersonationProxy should force it to be skipped.
{
Type: "SomeType",
Status: configv1alpha1.SuccessStrategyStatus,
Reason: "SomeReason",
Message: "Some message",
LastUpdateTime: metav1.Now(),
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.TokenCredentialRequestAPIFrontendType,
TokenCredentialRequestAPIInfo: &configv1alpha1.TokenCredentialRequestAPIInfo{
Server: "https://token-credential-request-api-endpoint.test",
CertificateAuthorityData: "dGVzdC10Y3ItYXBpLWNh",
},
},
},
// The endpoint and CA from this impersonation proxy strategy should be autodiscovered.
{
Type: "SomeOtherType",
Status: configv1alpha1.SuccessStrategyStatus,
Reason: "SomeOtherReason",
Message: "Some other message",
LastUpdateTime: metav1.Now(),
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.ImpersonationProxyFrontendType,
ImpersonationProxyInfo: &configv1alpha1.ImpersonationProxyInfo{
Endpoint: "https://impersonation-proxy-endpoint.test",
CertificateAuthorityData: base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
},
},
},
},
},
},
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryResponse: onlyIssuerOIDCDiscoveryResponse,
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://impersonation-proxy-endpoint.test"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=1`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: %s
server: https://impersonation-proxy-endpoint.test
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --enable-concierge
- --concierge-api-group-suffix=pinniped.dev
- --concierge-authenticator-name=test-authenticator
- --concierge-authenticator-type=jwt
- --concierge-endpoint=https://impersonation-proxy-endpoint.test
- --concierge-ca-bundle-data=%s
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
base64.StdEncoding.EncodeToString(testConciergeCA.Bundle()),
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)),
)
},
},
{
name: "autodetect impersonation proxy with auto-discovered JWT authenticator",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
&configv1alpha1.CredentialIssuer{
ObjectMeta: metav1.ObjectMeta{Name: "test-credential-issuer"},
Status: configv1alpha1.CredentialIssuerStatus{
Strategies: []configv1alpha1.CredentialIssuerStrategy{
{
Type: "SomeType",
Status: configv1alpha1.SuccessStrategyStatus,
Reason: "SomeReason",
Message: "Some message",
LastUpdateTime: metav1.Now(),
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.ImpersonationProxyFrontendType,
ImpersonationProxyInfo: &configv1alpha1.ImpersonationProxyInfo{
Endpoint: "https://impersonation-proxy-endpoint.test",
CertificateAuthorityData: "dGVzdC1jb25jaWVyZ2UtY2E=",
},
},
},
{
Type: "SomeOtherType",
Status: configv1alpha1.SuccessStrategyStatus,
Reason: "SomeOtherReason",
Message: "Some other message",
LastUpdateTime: metav1.Now(),
Frontend: &configv1alpha1.CredentialIssuerFrontend{
Type: configv1alpha1.ImpersonationProxyFrontendType,
ImpersonationProxyInfo: &configv1alpha1.ImpersonationProxyInfo{
Endpoint: "https://some-other-impersonation-endpoint",
CertificateAuthorityData: "dGVzdC1jb25jaWVyZ2UtY2E=",
},
},
},
},
},
},
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryResponse: onlyIssuerOIDCDiscoveryResponse,
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in impersonation proxy mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://impersonation-proxy-endpoint.test"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: dGVzdC1jb25jaWVyZ2UtY2E=
server: https://impersonation-proxy-endpoint.test
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --enable-concierge
- --concierge-api-group-suffix=pinniped.dev
- --concierge-authenticator-name=test-authenticator
- --concierge-authenticator-type=jwt
- --concierge-endpoint=https://impersonation-proxy-endpoint.test
- --concierge-ca-bundle-data=dGVzdC1jb25jaWVyZ2UtY2E=
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "Find LDAP IDP in IDP discovery document, output ldap related flags",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap"}
]
}`),
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --enable-concierge
- --concierge-api-group-suffix=pinniped.dev
- --concierge-authenticator-name=test-authenticator
- --concierge-authenticator-type=jwt
- --concierge-endpoint=https://fake-server-url-value
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "Find OIDC IDP in IDP discovery document, output oidc related flags",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-oidc-idp", "type": "oidc"}
]
}`),
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --enable-concierge
- --concierge-api-group-suffix=pinniped.dev
- --concierge-authenticator-name=test-authenticator
- --concierge-authenticator-type=jwt
- --concierge-endpoint=https://fake-server-url-value
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
- --upstream-identity-provider-name=some-oidc-idp
- --upstream-identity-provider-type=oidc
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "empty IDP list in IDP discovery document",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": []
}`),
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --enable-concierge
- --concierge-api-group-suffix=pinniped.dev
- --concierge-authenticator-name=test-authenticator
- --concierge-authenticator-type=jwt
- --concierge-endpoint=https://fake-server-url-value
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "Supervisor discovery section is not listed in OIDC discovery document",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryResponse: onlyIssuerOIDCDiscoveryResponse,
idpsDiscoveryStatusCode: http.StatusBadRequest, // IDPs endpoint shouldn't be called by this test
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --enable-concierge
- --concierge-api-group-suffix=pinniped.dev
- --concierge-authenticator-name=test-authenticator
- --concierge-authenticator-type=jwt
- --concierge-endpoint=https://fake-server-url-value
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "IDP discovery endpoint is not listed in OIDC discovery document within the Supervisor discovery section",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryResponse: func(issuerURL string) string {
return here.Docf(`{
"issuer": "%s",
"discovery.supervisor.pinniped.dev/v1alpha1": {
"wrong-key": "some-value"
}
}`, issuerURL)
},
idpsDiscoveryStatusCode: http.StatusBadRequest, // IDP discovery endpoint shouldn't be called by this test
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --enable-concierge
- --concierge-api-group-suffix=pinniped.dev
- --concierge-authenticator-name=test-authenticator
- --concierge-authenticator-type=jwt
- --concierge-endpoint=https://fake-server-url-value
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "when all upstream IDP related flags are sent, pass them through without performing IDP discovery",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--upstream-identity-provider-name=some-oidc-idp",
"--upstream-identity-provider-type=oidc",
"--upstream-identity-provider-flow=foobar",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryStatusCode: http.StatusNotFound, // should not get called by the client in this case
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --enable-concierge
- --concierge-api-group-suffix=pinniped.dev
- --concierge-authenticator-name=test-authenticator
- --concierge-authenticator-type=jwt
- --concierge-endpoint=https://fake-server-url-value
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
- --upstream-identity-provider-name=some-oidc-idp
- --upstream-identity-provider-type=oidc
- --upstream-identity-provider-flow=foobar
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "when all upstream IDP related flags are sent, pass them through even when IDP discovery shows a different IDP",
args: func(issuerCABundle string, issuerURL string) []string {
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--upstream-identity-provider-name=some-oidc-idp",
"--upstream-identity-provider-type=oidc",
"--upstream-identity-provider-flow=foobar",
}
},
conciergeObjects: func(issuerCABundle string, issuerURL string) []runtime.Object {
return []runtime.Object{
credentialIssuer(),
jwtAuthenticator(issuerCABundle, issuerURL),
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-other-ldap-idp", "type": "ldap"}
]
}`),
wantLogs: func(issuerCABundle string, issuerURL string) []string {
return []string{
`"level"=0 "msg"="discovered CredentialIssuer" "name"="test-credential-issuer"`,
`"level"=0 "msg"="discovered Concierge operating in TokenCredentialRequest API mode"`,
`"level"=0 "msg"="discovered Concierge endpoint" "endpoint"="https://fake-server-url-value"`,
`"level"=0 "msg"="discovered Concierge certificate authority bundle" "roots"=0`,
`"level"=0 "msg"="discovered JWTAuthenticator" "name"="test-authenticator"`,
fmt.Sprintf(`"level"=0 "msg"="discovered OIDC issuer" "issuer"="%s"`, issuerURL),
`"level"=0 "msg"="discovered OIDC audience" "audience"="test-audience"`,
`"level"=0 "msg"="discovered OIDC CA bundle" "roots"=1`,
}
},
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --enable-concierge
- --concierge-api-group-suffix=pinniped.dev
- --concierge-authenticator-name=test-authenticator
- --concierge-authenticator-type=jwt
- --concierge-endpoint=https://fake-server-url-value
- --concierge-ca-bundle-data=ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --request-audience=test-audience
- --upstream-identity-provider-name=some-oidc-idp
- --upstream-identity-provider-type=oidc
- --upstream-identity-provider-flow=foobar
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "supervisor upstream IDP discovery still works when --no-concierge is used",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap"}
]
}`),
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "supervisor upstream IDP discovery resolves ambiguity when type is specified but name is not",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-type", "ldap",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap"},
{"name": "some-oidc-idp", "type": "oidc"},
{"name": "some-other-oidc-idp", "type": "oidc"}
]
}`),
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "supervisor upstream IDP discovery resolves ambiguity when name is specified but type is not",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-name", "some-ldap-idp",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap"},
{"name": "some-oidc-idp", "type": "oidc"},
{"name": "some-other-oidc-idp", "type": "oidc"}
]
}`),
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "supervisor upstream IDP discovery when both name and type are specified but flow is not and a matching IDP is found",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-name", "some-ldap-idp",
"--upstream-identity-provider-type", "ldap",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap"},
{"name": "some-oidc-idp", "type": "oidc"},
{"name": "some-other-oidc-idp", "type": "oidc"}
]
}`),
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "supervisor upstream IDP discovery when flow is specified and no flows were returned by discovery uses the specified flow",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-flow", "foobar",
"--upstream-identity-provider-type", "ldap",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap"},
{"name": "some-oidc-idp", "type": "oidc"},
{"name": "some-other-oidc-idp", "type": "oidc"}
]
}`),
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
- --upstream-identity-provider-flow=foobar
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "supervisor upstream IDP discovery when flow is specified and it matches a flow returned by discovery uses the specified flow",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-flow", "cli_password",
"--upstream-identity-provider-type", "ldap",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap", "flows": ["some_flow", "cli_password", "some_other_flow"]}
]
}`),
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
- --upstream-identity-provider-flow=cli_password
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
{
name: "supervisor upstream IDP discovery when no flow is specified but there is only one flow returned by discovery uses the discovered flow",
args: func(issuerCABundle string, issuerURL string) []string {
f := testutil.WriteStringToTempFile(t, "testca-*.pem", issuerCABundle)
return []string{
"--kubeconfig", "./testdata/kubeconfig.yaml",
"--skip-validation",
"--no-concierge",
"--oidc-issuer", issuerURL,
"--oidc-ca-bundle", f.Name(),
"--upstream-identity-provider-type", "ldap",
}
},
oidcDiscoveryResponse: happyOIDCDiscoveryResponse,
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap", "flows": ["cli_password"]}
]
}`),
wantStdout: func(issuerCABundle string, issuerURL string) string {
return here.Docf(`
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ZmFrZS1jZXJ0aWZpY2F0ZS1hdXRob3JpdHktZGF0YS12YWx1ZQ==
server: https://fake-server-url-value
name: kind-cluster-pinniped
contexts:
- context:
cluster: kind-cluster-pinniped
user: kind-user-pinniped
name: kind-context-pinniped
current-context: kind-context-pinniped
kind: Config
preferences: {}
users:
- name: kind-user-pinniped
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- login
- oidc
- --issuer=%s
- --client-id=pinniped-cli
- --scopes=offline_access,openid,pinniped:request-audience
- --ca-bundle-data=%s
- --upstream-identity-provider-name=some-ldap-idp
- --upstream-identity-provider-type=ldap
- --upstream-identity-provider-flow=cli_password
command: '.../path/to/pinniped'
env: []
provideClusterInfo: true
`,
issuerURL,
base64.StdEncoding.EncodeToString([]byte(issuerCABundle)))
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
var issuerEndpointPtr *string
issuerCABundle, issuerEndpoint := testutil.TLSTestServer(t, func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("content-type", "application/json")
switch r.URL.Path {
case "/.well-known/openid-configuration":
jsonResponseBody := "{}"
if tt.oidcDiscoveryResponse != nil {
jsonResponseBody = tt.oidcDiscoveryResponse(*issuerEndpointPtr)
}
if tt.oidcDiscoveryStatusCode == 0 {
tt.oidcDiscoveryStatusCode = http.StatusOK
}
w.WriteHeader(tt.oidcDiscoveryStatusCode)
_, err = w.Write([]byte(jsonResponseBody))
require.NoError(t, err)
case "/v1alpha1/pinniped_identity_providers":
jsonResponseBody := tt.idpsDiscoveryResponse
if tt.idpsDiscoveryResponse == "" {
jsonResponseBody = "{}"
}
if tt.idpsDiscoveryStatusCode == 0 {
tt.idpsDiscoveryStatusCode = http.StatusOK
}
w.WriteHeader(tt.idpsDiscoveryStatusCode)
_, err = w.Write([]byte(jsonResponseBody))
require.NoError(t, err)
default:
t.Fatalf("tried to call issuer at a path that wasn't one of the expected discovery endpoints.")
}
})
issuerEndpointPtr = &issuerEndpoint
testLog := testlogger.New(t)
cmd := kubeconfigCommand(kubeconfigDeps{
getPathToSelf: func() (string, error) {
if tt.getPathToSelfErr != nil {
return "", tt.getPathToSelfErr
}
return ".../path/to/pinniped", nil
},
getClientset: func(clientConfig clientcmd.ClientConfig, apiGroupSuffix string) (conciergeclientset.Interface, error) {
if tt.wantAPIGroupSuffix == "" {
require.Equal(t, "pinniped.dev", apiGroupSuffix) // "pinniped.dev" = api group suffix default
} else {
require.Equal(t, tt.wantAPIGroupSuffix, apiGroupSuffix)
}
if tt.getClientsetErr != nil {
return nil, tt.getClientsetErr
}
fake := fakeconciergeclientset.NewSimpleClientset()
if tt.conciergeObjects != nil {
fake = fakeconciergeclientset.NewSimpleClientset(tt.conciergeObjects(issuerCABundle, issuerEndpoint)...)
}
if len(tt.conciergeReactions) > 0 {
fake.ReactionChain = append(tt.conciergeReactions, fake.ReactionChain...)
}
return fake, nil
},
log: testLog,
})
require.NotNil(t, cmd)
var stdout, stderr bytes.Buffer
cmd.SetOut(&stdout)
cmd.SetErr(&stderr)
cmd.SetArgs(tt.args(issuerCABundle, issuerEndpoint))
err := cmd.Execute()
if tt.wantError {
require.Error(t, err)
} else {
require.NoError(t, err)
}
var expectedLogs []string
if tt.wantLogs != nil {
expectedLogs = tt.wantLogs(issuerCABundle, issuerEndpoint)
}
testLog.Expect(expectedLogs)
expectedStdout := ""
if tt.wantStdout != nil {
expectedStdout = tt.wantStdout(issuerCABundle, issuerEndpoint)
}
require.Equal(t, expectedStdout, stdout.String(), "unexpected stdout")
expectedStderr := ""
if tt.wantStderr != nil {
expectedStderr = tt.wantStderr(issuerCABundle, issuerEndpoint)
}
require.Equal(t, expectedStderr, stderr.String(), "unexpected stderr")
})
}
}