mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-04-15 07:06:45 +00:00
Merge branch 'main' into initial_ldap
This commit is contained in:
@@ -208,6 +208,8 @@ func TestCLILoginOIDC(t *testing.T) {
|
||||
require.NoErrorf(t, json.Unmarshal(cmd2Output, &credOutput2),
|
||||
"command returned something other than an ExecCredential:\n%s", string(cmd2Output))
|
||||
require.Equal(t, credOutput, credOutput2)
|
||||
// the logs contain only the ExecCredential. There are 2 elements because the last one is "".
|
||||
require.Len(t, strings.Split(string(cmd2Output), "\n"), 2)
|
||||
|
||||
// Overwrite the cache entry to remove the access and ID tokens.
|
||||
t.Logf("overwriting cache to remove valid ID token")
|
||||
@@ -237,6 +239,26 @@ func TestCLILoginOIDC(t *testing.T) {
|
||||
require.NoErrorf(t, json.Unmarshal(cmd3Output, &credOutput3),
|
||||
"command returned something other than an ExecCredential:\n%s", string(cmd2Output))
|
||||
require.NotEqual(t, credOutput2.Status.Token, credOutput3.Status.Token)
|
||||
// the logs contain only the ExecCredential. There are 2 elements because the last one is "".
|
||||
require.Len(t, strings.Split(string(cmd3Output), "\n"), 2)
|
||||
|
||||
t.Logf("starting fourth CLI subprocess to test debug logging")
|
||||
err = os.Setenv("PINNIPED_DEBUG", "true")
|
||||
require.NoError(t, err)
|
||||
command := oidcLoginCommand(ctx, t, pinnipedExe, sessionCachePath)
|
||||
cmd4CombinedOutput, err := command.CombinedOutput()
|
||||
cmd4StringOutput := string(cmd4CombinedOutput)
|
||||
require.NoError(t, err, cmd4StringOutput)
|
||||
|
||||
// the logs contain only the 4 debug lines plus the ExecCredential. There are 6 elements because the last one is "".
|
||||
require.Len(t, strings.Split(cmd4StringOutput, "\n"), 6)
|
||||
require.Contains(t, cmd4StringOutput, "Performing OIDC login")
|
||||
require.Contains(t, cmd4StringOutput, "Found unexpired cached token")
|
||||
require.Contains(t, cmd4StringOutput, "No concierge configured, skipping token credential exchange")
|
||||
require.Contains(t, cmd4StringOutput, "caching cluster credential for future use.")
|
||||
require.Contains(t, cmd4StringOutput, credOutput3.Status.Token)
|
||||
err = os.Unsetenv("PINNIPED_DEBUG")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func runPinnipedLoginOIDC(
|
||||
@@ -271,6 +293,7 @@ func runPinnipedLoginOIDC(
|
||||
// Start a background goroutine to read stderr from the CLI and parse out the login URL.
|
||||
loginURLChan := make(chan string)
|
||||
spawnTestGoroutine(t, func() (err error) {
|
||||
t.Helper()
|
||||
defer func() {
|
||||
closeErr := stderr.Close()
|
||||
if closeErr == nil || errors.Is(closeErr, os.ErrClosed) {
|
||||
@@ -282,16 +305,18 @@ func runPinnipedLoginOIDC(
|
||||
}()
|
||||
|
||||
reader := bufio.NewReader(library.NewLoggerReader(t, "stderr", stderr))
|
||||
line, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read login URL line from stderr: %w", err)
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(reader)
|
||||
const prompt = "Please log in: "
|
||||
if !strings.HasPrefix(line, prompt) {
|
||||
return fmt.Errorf("expected %q to have prefix %q", line, prompt)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, prompt) {
|
||||
loginURLChan <- strings.TrimPrefix(line, prompt)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
loginURLChan <- strings.TrimPrefix(line, prompt)
|
||||
return readAndExpectEmpty(reader)
|
||||
|
||||
return fmt.Errorf("expected stderr to contain %s", prompt)
|
||||
})
|
||||
|
||||
// Start a background goroutine to read stdout from the CLI and parse out an ExecCredential.
|
||||
|
||||
@@ -6,7 +6,11 @@ package integration
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
@@ -28,6 +32,8 @@ import (
|
||||
"golang.org/x/net/http2"
|
||||
authenticationv1 "k8s.io/api/authentication/v1"
|
||||
authorizationv1 "k8s.io/api/authorization/v1"
|
||||
certificatesv1 "k8s.io/api/certificates/v1"
|
||||
certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -42,6 +48,8 @@ import (
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/transport"
|
||||
"k8s.io/client-go/util/cert"
|
||||
"k8s.io/client-go/util/certificate/csr"
|
||||
"k8s.io/client-go/util/keyutil"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
@@ -607,16 +615,26 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl
|
||||
Create(ctx, &identityv1alpha1.WhoAmIRequest{}, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedExtra := make(map[string]authenticationv1.ExtraValue, len(whoAmIAdmin.Status.KubernetesUserInfo.User.Extra))
|
||||
for k, v := range whoAmIAdmin.Status.KubernetesUserInfo.User.Extra {
|
||||
// The WhoAmI API is lossy:
|
||||
// - It drops UID
|
||||
// - It lowercases all extra keys
|
||||
// the admin user on EKS has both a UID set and an extra key with uppercase characters
|
||||
// Thus we fallback to the CSR API to grab the UID and Extra to handle this scenario
|
||||
uid, extra := getUIDAndExtraViaCSR(ctx, t, whoAmIAdmin.Status.KubernetesUserInfo.User.UID,
|
||||
newImpersonationProxyClientWithCredentials(t,
|
||||
clusterAdminCredentials, impersonationProxyURL, impersonationProxyCACertPEM, nil).
|
||||
Kubernetes,
|
||||
)
|
||||
|
||||
expectedExtra := make(map[string]authenticationv1.ExtraValue, len(extra))
|
||||
for k, v := range extra {
|
||||
expectedExtra[k] = authenticationv1.ExtraValue(v)
|
||||
}
|
||||
expectedOriginalUserInfo := authenticationv1.UserInfo{
|
||||
Username: whoAmIAdmin.Status.KubernetesUserInfo.User.Username,
|
||||
// The WhoAmI API is lossy so this will fail when the admin user actually does have a UID
|
||||
UID: whoAmIAdmin.Status.KubernetesUserInfo.User.UID,
|
||||
Groups: whoAmIAdmin.Status.KubernetesUserInfo.User.Groups,
|
||||
Extra: expectedExtra,
|
||||
UID: uid,
|
||||
Groups: whoAmIAdmin.Status.KubernetesUserInfo.User.Groups,
|
||||
Extra: expectedExtra,
|
||||
}
|
||||
expectedOriginalUserInfoJSON, err := json.Marshal(expectedOriginalUserInfo)
|
||||
require.NoError(t, err)
|
||||
@@ -780,32 +798,148 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl
|
||||
whoAmI,
|
||||
)
|
||||
|
||||
// Test using a service account token. Authenticating as Service Accounts through the impersonation
|
||||
// proxy is not supported, so it should fail.
|
||||
// Test using a service account token.
|
||||
namespaceName := createTestNamespace(t, adminClient)
|
||||
_, saToken, _ := createServiceAccountToken(ctx, t, adminClient, namespaceName)
|
||||
saName, saToken, _ := createServiceAccountToken(ctx, t, adminClient, namespaceName)
|
||||
impersonationProxyServiceAccountPinnipedConciergeClient := newImpersonationProxyClientWithCredentials(t,
|
||||
&loginv1alpha1.ClusterCredential{Token: saToken},
|
||||
impersonationProxyURL, impersonationProxyCACertPEM, nil).PinnipedConcierge
|
||||
_, err = impersonationProxyServiceAccountPinnipedConciergeClient.IdentityV1alpha1().WhoAmIRequests().
|
||||
whoAmI, err = impersonationProxyServiceAccountPinnipedConciergeClient.IdentityV1alpha1().WhoAmIRequests().
|
||||
Create(ctx, &identityv1alpha1.WhoAmIRequest{}, metav1.CreateOptions{})
|
||||
require.EqualError(t, err, "Internal error occurred: unimplemented functionality - unable to act as current user")
|
||||
require.True(t, k8serrors.IsInternalError(err), err)
|
||||
require.Equal(t, &k8serrors.StatusError{
|
||||
ErrStatus: metav1.Status{
|
||||
Status: metav1.StatusFailure,
|
||||
Code: http.StatusInternalServerError,
|
||||
Reason: metav1.StatusReasonInternalError,
|
||||
Details: &metav1.StatusDetails{
|
||||
Causes: []metav1.StatusCause{
|
||||
{
|
||||
Message: "unimplemented functionality - unable to act as current user",
|
||||
},
|
||||
require.NoError(t, err)
|
||||
require.Equal(t,
|
||||
expectedWhoAmIRequestResponse(
|
||||
serviceaccount.MakeUsername(namespaceName, saName),
|
||||
[]string{"system:serviceaccounts", "system:serviceaccounts:" + namespaceName, "system:authenticated"},
|
||||
nil,
|
||||
),
|
||||
whoAmI,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("WhoAmIRequests and SA token request", func(t *testing.T) {
|
||||
namespaceName := createTestNamespace(t, adminClient)
|
||||
kubeClient := adminClient.CoreV1()
|
||||
saName, _, saUID := createServiceAccountToken(ctx, t, adminClient, namespaceName)
|
||||
|
||||
_, tokenRequestProbeErr := kubeClient.ServiceAccounts(namespaceName).CreateToken(ctx, saName, &authenticationv1.TokenRequest{}, metav1.CreateOptions{})
|
||||
if k8serrors.IsNotFound(tokenRequestProbeErr) && tokenRequestProbeErr.Error() == "the server could not find the requested resource" {
|
||||
return // stop test early since the token request API is not enabled on this cluster - other errors are caught below
|
||||
}
|
||||
|
||||
pod, err := kubeClient.Pods(namespaceName).Create(ctx, &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "test-impersonation-proxy-",
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "ignored-but-required",
|
||||
Image: "does-not-matter",
|
||||
},
|
||||
},
|
||||
Message: "Internal error occurred: unimplemented functionality - unable to act as current user",
|
||||
ServiceAccountName: saName,
|
||||
},
|
||||
}, err)
|
||||
}, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
tokenRequestBadAudience, err := kubeClient.ServiceAccounts(namespaceName).CreateToken(ctx, saName, &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
Audiences: []string{"should-fail-because-wrong-audience"}, // anything that is not an API server audience
|
||||
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
||||
Kind: "Pod",
|
||||
APIVersion: "",
|
||||
Name: pod.Name,
|
||||
UID: pod.UID,
|
||||
},
|
||||
},
|
||||
}, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
impersonationProxySABadAudPinnipedConciergeClient := newImpersonationProxyClientWithCredentials(t,
|
||||
&loginv1alpha1.ClusterCredential{Token: tokenRequestBadAudience.Status.Token},
|
||||
impersonationProxyURL, impersonationProxyCACertPEM, nil).PinnipedConcierge
|
||||
|
||||
_, badAudErr := impersonationProxySABadAudPinnipedConciergeClient.IdentityV1alpha1().WhoAmIRequests().
|
||||
Create(ctx, &identityv1alpha1.WhoAmIRequest{}, metav1.CreateOptions{})
|
||||
require.True(t, k8serrors.IsUnauthorized(badAudErr), library.Sdump(badAudErr))
|
||||
|
||||
tokenRequest, err := kubeClient.ServiceAccounts(namespaceName).CreateToken(ctx, saName, &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
Audiences: []string{},
|
||||
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
||||
Kind: "Pod",
|
||||
APIVersion: "",
|
||||
Name: pod.Name,
|
||||
UID: pod.UID,
|
||||
},
|
||||
},
|
||||
}, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
impersonationProxySAClient := newImpersonationProxyClientWithCredentials(t,
|
||||
&loginv1alpha1.ClusterCredential{Token: tokenRequest.Status.Token},
|
||||
impersonationProxyURL, impersonationProxyCACertPEM, nil)
|
||||
|
||||
whoAmITokenReq, err := impersonationProxySAClient.PinnipedConcierge.IdentityV1alpha1().WhoAmIRequests().
|
||||
Create(ctx, &identityv1alpha1.WhoAmIRequest{}, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// new service account tokens include the pod info in the extra fields
|
||||
require.Equal(t,
|
||||
expectedWhoAmIRequestResponse(
|
||||
serviceaccount.MakeUsername(namespaceName, saName),
|
||||
[]string{"system:serviceaccounts", "system:serviceaccounts:" + namespaceName, "system:authenticated"},
|
||||
map[string]identityv1alpha1.ExtraValue{
|
||||
"authentication.kubernetes.io/pod-name": {pod.Name},
|
||||
"authentication.kubernetes.io/pod-uid": {string(pod.UID)},
|
||||
},
|
||||
),
|
||||
whoAmITokenReq,
|
||||
)
|
||||
|
||||
// allow the test SA to create CSRs
|
||||
library.CreateTestClusterRoleBinding(t,
|
||||
rbacv1.Subject{Kind: rbacv1.ServiceAccountKind, Name: saName, Namespace: namespaceName},
|
||||
rbacv1.RoleRef{Kind: "ClusterRole", APIGroup: rbacv1.GroupName, Name: "system:node-bootstrapper"},
|
||||
)
|
||||
library.WaitForUserToHaveAccess(t, serviceaccount.MakeUsername(namespaceName, saName), []string{}, &authorizationv1.ResourceAttributes{
|
||||
Verb: "create", Group: certificatesv1.GroupName, Version: "*", Resource: "certificatesigningrequests",
|
||||
})
|
||||
|
||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
require.NoError(t, err)
|
||||
|
||||
csrPEM, err := cert.MakeCSR(privateKey, &pkix.Name{
|
||||
CommonName: "panda-man",
|
||||
Organization: []string{"living-the-dream", "need-more-sleep"},
|
||||
}, nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
csrName, _, err := csr.RequestCertificate(
|
||||
impersonationProxySAClient.Kubernetes,
|
||||
csrPEM,
|
||||
"",
|
||||
certificatesv1.KubeAPIServerClientSignerName,
|
||||
[]certificatesv1.KeyUsage{certificatesv1.UsageClientAuth},
|
||||
privateKey,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
saCSR, err := impersonationProxySAClient.Kubernetes.CertificatesV1beta1().CertificateSigningRequests().Get(ctx, csrName, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = adminClient.CertificatesV1beta1().CertificateSigningRequests().Delete(ctx, csrName, metav1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// make sure the user info that the CSR captured matches the SA, including the UID
|
||||
require.Equal(t, serviceaccount.MakeUsername(namespaceName, saName), saCSR.Spec.Username)
|
||||
require.Equal(t, string(saUID), saCSR.Spec.UID)
|
||||
require.Equal(t, []string{"system:serviceaccounts", "system:serviceaccounts:" + namespaceName, "system:authenticated"}, saCSR.Spec.Groups)
|
||||
require.Equal(t, map[string]certificatesv1beta1.ExtraValue{
|
||||
"authentication.kubernetes.io/pod-name": {pod.Name},
|
||||
"authentication.kubernetes.io/pod-uid": {string(pod.UID)},
|
||||
}, saCSR.Spec.Extra)
|
||||
})
|
||||
|
||||
t.Run("kubectl as a client", func(t *testing.T) {
|
||||
@@ -1581,17 +1715,18 @@ func getCredForConfig(t *testing.T, config *rest.Config) *loginv1alpha1.ClusterC
|
||||
if tlsConfig != nil && tlsConfig.GetClientCertificate != nil {
|
||||
cert, err := tlsConfig.GetClientCertificate(nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, cert.Certificate, 1)
|
||||
if len(cert.Certificate) > 0 {
|
||||
require.Len(t, cert.Certificate, 1)
|
||||
publicKey := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: cert.Certificate[0],
|
||||
})
|
||||
out.ClientCertificateData = string(publicKey)
|
||||
|
||||
publicKey := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: cert.Certificate[0],
|
||||
})
|
||||
out.ClientCertificateData = string(publicKey)
|
||||
|
||||
privateKey, err := keyutil.MarshalPrivateKeyToPEM(cert.PrivateKey)
|
||||
require.NoError(t, err)
|
||||
out.ClientKeyData = string(privateKey)
|
||||
privateKey, err := keyutil.MarshalPrivateKeyToPEM(cert.PrivateKey)
|
||||
require.NoError(t, err)
|
||||
out.ClientKeyData = string(privateKey)
|
||||
}
|
||||
}
|
||||
|
||||
if *out == (loginv1alpha1.ClusterCredential{}) {
|
||||
@@ -1600,3 +1735,39 @@ func getCredForConfig(t *testing.T, config *rest.Config) *loginv1alpha1.ClusterC
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func getUIDAndExtraViaCSR(ctx context.Context, t *testing.T, uid string, client kubernetes.Interface) (string, map[string]certificatesv1beta1.ExtraValue) {
|
||||
t.Helper()
|
||||
|
||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
require.NoError(t, err)
|
||||
|
||||
csrPEM, err := cert.MakeCSR(privateKey, &pkix.Name{
|
||||
CommonName: "panda-man",
|
||||
Organization: []string{"living-the-dream", "need-more-sleep"},
|
||||
}, nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
csrName, _, err := csr.RequestCertificate(
|
||||
client,
|
||||
csrPEM,
|
||||
"",
|
||||
certificatesv1.KubeAPIServerClientSignerName,
|
||||
[]certificatesv1.KeyUsage{certificatesv1.UsageClientAuth},
|
||||
privateKey,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
csReq, err := client.CertificatesV1beta1().CertificateSigningRequests().Get(ctx, csrName, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.CertificatesV1beta1().CertificateSigningRequests().Delete(ctx, csrName, metav1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
outUID := uid // in the future this may not be empty on some clusters
|
||||
if len(outUID) == 0 {
|
||||
outUID = csReq.Spec.UID
|
||||
}
|
||||
|
||||
return outUID, csReq.Spec.Extra
|
||||
}
|
||||
|
||||
@@ -337,6 +337,6 @@ status:
|
||||
pinnipedExe,
|
||||
kubeconfigPath,
|
||||
env.SupervisorUpstreamOIDC.Username,
|
||||
expectedGroupsPlusUnauthenticated,
|
||||
expectedGroupsPlusAuthenticated,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -48,10 +48,15 @@ func TestStorageGarbageCollection(t *testing.T) {
|
||||
// in the same namespace just to get the controller to respond faster.
|
||||
// This is just a performance optimization to make this test pass faster because otherwise
|
||||
// this test has to wait ~3 minutes for the controller's next full-resync.
|
||||
stopCh := make(chan bool, 1) // It is important that this channel be buffered.
|
||||
go updateSecretEveryTwoSeconds(t, stopCh, secrets, secretNotYetExpired)
|
||||
stopCh := make(chan struct{})
|
||||
errCh := make(chan error)
|
||||
go updateSecretEveryTwoSeconds(stopCh, errCh, secrets, secretNotYetExpired)
|
||||
t.Cleanup(func() {
|
||||
stopCh <- true
|
||||
close(stopCh)
|
||||
|
||||
if updateErr := <-errCh; updateErr != nil {
|
||||
panic(updateErr)
|
||||
}
|
||||
})
|
||||
|
||||
// Wait long enough for the next periodic sweep of the GC controller for the secrets to be deleted, which
|
||||
@@ -69,10 +74,15 @@ func TestStorageGarbageCollection(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func updateSecretEveryTwoSeconds(t *testing.T, stopCh chan bool, secrets corev1client.SecretInterface, secret *v1.Secret) {
|
||||
func updateSecretEveryTwoSeconds(stopCh chan struct{}, errCh chan error, secrets corev1client.SecretInterface, secret *v1.Secret) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
var updateErr error
|
||||
defer func() {
|
||||
errCh <- updateErr
|
||||
}()
|
||||
|
||||
i := 0
|
||||
for {
|
||||
select {
|
||||
@@ -87,9 +97,25 @@ func updateSecretEveryTwoSeconds(t *testing.T, stopCh chan bool, secrets corev1c
|
||||
|
||||
i++
|
||||
secret.Data["foo"] = []byte(fmt.Sprintf("bar-%d", i))
|
||||
var updateErr error
|
||||
secret, updateErr = secrets.Update(ctx, secret, metav1.UpdateOptions{})
|
||||
require.NoError(t, updateErr)
|
||||
|
||||
switch {
|
||||
case updateErr == nil:
|
||||
// continue to next update
|
||||
|
||||
case k8serrors.IsConflict(updateErr), k8serrors.IsNotFound(updateErr):
|
||||
select {
|
||||
case _, ok := <-stopCh:
|
||||
if !ok { // stopCh is closed meaning that test is already finished so these errors are expected
|
||||
updateErr = nil
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
return // even if the error is expected, we must stop
|
||||
default:
|
||||
return // unexpected error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,10 +34,43 @@ func TestSupervisorUpstreamOIDCDiscovery(t *testing.T) {
|
||||
Message: `secret "does-not-exist" not found`,
|
||||
},
|
||||
{
|
||||
Type: "OIDCDiscoverySucceeded",
|
||||
Status: v1alpha1.ConditionFalse,
|
||||
Reason: "Unreachable",
|
||||
Message: `failed to perform OIDC discovery against "https://127.0.0.1:444444/issuer"`,
|
||||
Type: "OIDCDiscoverySucceeded",
|
||||
Status: v1alpha1.ConditionFalse,
|
||||
Reason: "Unreachable",
|
||||
Message: `failed to perform OIDC discovery against "https://127.0.0.1:444444/issuer":
|
||||
Get "https://127.0.0.1:444444/issuer/.well-known/openid-configuration": dial tcp: address 444444: in [truncated 10 chars]`,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("invalid issuer with trailing slash", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
spec := v1alpha1.OIDCIdentityProviderSpec{
|
||||
Issuer: env.SupervisorUpstreamOIDC.Issuer + "/",
|
||||
TLS: &v1alpha1.TLSSpec{
|
||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)),
|
||||
},
|
||||
AuthorizationConfig: v1alpha1.OIDCAuthorizationConfig{
|
||||
AdditionalScopes: []string{"email", "profile"},
|
||||
},
|
||||
Client: v1alpha1.OIDCClient{
|
||||
SecretName: library.CreateClientCredsSecret(t, "test-client-id", "test-client-secret").Name,
|
||||
},
|
||||
}
|
||||
upstream := library.CreateTestOIDCIdentityProvider(t, spec, v1alpha1.PhaseError)
|
||||
expectUpstreamConditions(t, upstream, []v1alpha1.Condition{
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Status: v1alpha1.ConditionTrue,
|
||||
Reason: "Success",
|
||||
Message: "loaded client credentials",
|
||||
},
|
||||
{
|
||||
Type: "OIDCDiscoverySucceeded",
|
||||
Status: v1alpha1.ConditionFalse,
|
||||
Reason: "Unreachable",
|
||||
Message: `failed to perform OIDC discovery against "` + env.SupervisorUpstreamOIDC.Issuer + `/":
|
||||
oidc: issuer did not match the issuer returned by provider, expected "` + env.SupervisorUpstreamOIDC.Issuer + `/" got "` + env.SupervisorUpstreamOIDC.Issuer + `"`,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user