mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-04-15 07:06:45 +00:00
Merge branch 'main' into label_every_resource
This commit is contained in:
@@ -13,7 +13,6 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"go.pinniped.dev/generated/1.19/apis/config/v1alpha1"
|
||||
@@ -63,14 +62,14 @@ func TestSupervisorOIDCDiscovery(t *testing.T) {
|
||||
badIssuer := fmt.Sprintf("http://%s/badIssuer?cannot-use=queries", env.SupervisorAddress)
|
||||
|
||||
// When OIDCProviderConfig are created in sequence they each cause a discovery endpoint to appear only for as long as the OIDCProviderConfig exists.
|
||||
config1 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer1, "from-integration-test1")
|
||||
config1 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(ctx, t, issuer1, client)
|
||||
requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config1, client, ns, issuer1)
|
||||
config2 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer2, "from-integration-test2")
|
||||
config2 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(ctx, t, issuer2, client)
|
||||
requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config2, client, ns, issuer2)
|
||||
|
||||
// When multiple OIDCProviderConfigs exist at the same time they each serve a unique discovery endpoint.
|
||||
config3 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer3, "from-integration-test3")
|
||||
config4 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer4, "from-integration-test4")
|
||||
config3 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(ctx, t, issuer3, client)
|
||||
config4 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(ctx, t, issuer4, client)
|
||||
requireWellKnownEndpointIsWorking(t, issuer3) // discovery for issuer3 is still working after issuer4 started working
|
||||
|
||||
// When they are deleted they stop serving discovery endpoints.
|
||||
@@ -78,8 +77,8 @@ func TestSupervisorOIDCDiscovery(t *testing.T) {
|
||||
requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config4, client, ns, issuer4)
|
||||
|
||||
// When the same issuer is added twice, both issuers are marked as duplicates, and neither provider is serving.
|
||||
config5Duplicate1 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t, client, ns, issuer5, "from-integration-test5")
|
||||
config5Duplicate2 := createOIDCProviderConfig(t, "from-integration-test5-duplicate", client, ns, issuer5)
|
||||
config5Duplicate1 := requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(ctx, t, issuer5, client)
|
||||
config5Duplicate2 := library.CreateTestOIDCProvider(ctx, t, issuer5)
|
||||
requireStatus(t, client, ns, config5Duplicate1.Name, v1alpha1.DuplicateOIDCProviderStatus)
|
||||
requireStatus(t, client, ns, config5Duplicate2.Name, v1alpha1.DuplicateOIDCProviderStatus)
|
||||
requireDiscoveryEndpointIsNotFound(t, issuer5)
|
||||
@@ -93,7 +92,7 @@ func TestSupervisorOIDCDiscovery(t *testing.T) {
|
||||
requireDeletingOIDCProviderConfigCausesWellKnownEndpointToDisappear(t, config5Duplicate2, client, ns, issuer5)
|
||||
|
||||
// When we create a provider with an invalid issuer, the status is set to invalid.
|
||||
badConfig := createOIDCProviderConfig(t, "from-integration-test6", client, ns, badIssuer)
|
||||
badConfig := library.CreateTestOIDCProvider(ctx, t, badIssuer)
|
||||
requireStatus(t, client, ns, badConfig.Name, v1alpha1.InvalidOIDCProviderStatus)
|
||||
requireDiscoveryEndpointIsNotFound(t, badIssuer)
|
||||
}
|
||||
@@ -122,11 +121,16 @@ func requireDiscoveryEndpointIsNotFound(t *testing.T, issuerName string) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(t *testing.T, client pinnipedclientset.Interface, ns string, issuerName string, oidcProviderConfigName string) *v1alpha1.OIDCProviderConfig {
|
||||
func requireCreatingOIDCProviderConfigCausesWellKnownEndpointToAppear(
|
||||
ctx context.Context,
|
||||
t *testing.T,
|
||||
issuerName string,
|
||||
client pinnipedclientset.Interface,
|
||||
) *v1alpha1.OIDCProviderConfig {
|
||||
t.Helper()
|
||||
newOIDCProviderConfig := createOIDCProviderConfig(t, oidcProviderConfigName, client, ns, issuerName)
|
||||
newOIDCProviderConfig := library.CreateTestOIDCProvider(ctx, t, issuerName)
|
||||
requireWellKnownEndpointIsWorking(t, issuerName)
|
||||
requireStatus(t, client, ns, oidcProviderConfigName, v1alpha1.SuccessOIDCProviderStatus)
|
||||
requireStatus(t, client, newOIDCProviderConfig.Namespace, newOIDCProviderConfig.Name, v1alpha1.SuccessOIDCProviderStatus)
|
||||
return newOIDCProviderConfig
|
||||
}
|
||||
|
||||
@@ -192,43 +196,6 @@ func requireWellKnownEndpointIsWorking(t *testing.T, issuerName string) {
|
||||
require.JSONEq(t, expectedJSON, string(responseBody))
|
||||
}
|
||||
|
||||
func createOIDCProviderConfig(t *testing.T, oidcProviderConfigName string, client pinnipedclientset.Interface, ns string, issuerName string) *v1alpha1.OIDCProviderConfig {
|
||||
t.Helper()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
newOIDCProviderConfig := v1alpha1.OIDCProviderConfig{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "OIDCProviderConfig",
|
||||
APIVersion: v1alpha1.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: oidcProviderConfigName,
|
||||
Namespace: ns,
|
||||
},
|
||||
Spec: v1alpha1.OIDCProviderConfigSpec{
|
||||
Issuer: issuerName,
|
||||
},
|
||||
}
|
||||
createdOIDCProviderConfig, err := client.ConfigV1alpha1().OIDCProviderConfigs(ns).Create(ctx, &newOIDCProviderConfig, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// When this test has finished, be sure to clean up the new OIDCProviderConfig.
|
||||
t.Cleanup(func() {
|
||||
cleanupCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
err = client.ConfigV1alpha1().OIDCProviderConfigs(ns).Delete(cleanupCtx, newOIDCProviderConfig.Name, metav1.DeleteOptions{})
|
||||
notFound := k8serrors.IsNotFound(err)
|
||||
// It's okay if it is not found, because it might have been deleted by another part of this test.
|
||||
if !notFound {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
|
||||
return createdOIDCProviderConfig
|
||||
}
|
||||
|
||||
func requireDelete(t *testing.T, client pinnipedclientset.Interface, ns, name string) {
|
||||
t.Helper()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
|
||||
95
test/integration/supervisor_keys_test.go
Normal file
95
test/integration/supervisor_keys_test.go
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
configv1alpha1 "go.pinniped.dev/generated/1.19/apis/config/v1alpha1"
|
||||
"go.pinniped.dev/test/library"
|
||||
)
|
||||
|
||||
func TestSupervisorOIDCKeys(t *testing.T) {
|
||||
env := library.IntegrationEnv(t)
|
||||
kubeClient := library.NewClientset(t)
|
||||
pinnipedClient := library.NewPinnipedClientset(t)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
// Create our OPC under test.
|
||||
opc := library.CreateTestOIDCProvider(ctx, t, "")
|
||||
|
||||
// Ensure a secret is created with the OPC's JWKS.
|
||||
var updatedOPC *configv1alpha1.OIDCProviderConfig
|
||||
var err error
|
||||
assert.Eventually(t, func() bool {
|
||||
updatedOPC, err = pinnipedClient.
|
||||
ConfigV1alpha1().
|
||||
OIDCProviderConfigs(env.SupervisorNamespace).
|
||||
Get(ctx, opc.Name, metav1.GetOptions{})
|
||||
return err == nil && updatedOPC.Status.JWKSSecret.Name != ""
|
||||
}, time.Second*10, time.Millisecond*500)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, updatedOPC.Status.JWKSSecret.Name)
|
||||
|
||||
// Ensure the secret actually exists.
|
||||
secret, err := kubeClient.
|
||||
CoreV1().
|
||||
Secrets(env.SupervisorNamespace).
|
||||
Get(ctx, updatedOPC.Status.JWKSSecret.Name, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Ensure the secret has an active key.
|
||||
jwkData, ok := secret.Data["activeJWK"]
|
||||
require.True(t, ok, "secret is missing active jwk")
|
||||
|
||||
// Ensure the secret's active key is valid.
|
||||
var activeJWK jose.JSONWebKey
|
||||
require.NoError(t, json.Unmarshal(jwkData, &activeJWK))
|
||||
require.True(t, activeJWK.Valid(), "active jwk is invalid")
|
||||
require.False(t, activeJWK.IsPublic(), "active jwk is public")
|
||||
|
||||
// Ensure the secret has a JWKS.
|
||||
jwksData, ok := secret.Data["jwks"]
|
||||
require.True(t, ok, "secret is missing jwks")
|
||||
|
||||
// Ensure the secret's JWKS is valid, public, and contains the singing key.
|
||||
var jwks jose.JSONWebKeySet
|
||||
require.NoError(t, json.Unmarshal(jwksData, &jwks))
|
||||
foundActiveJWK := false
|
||||
for _, jwk := range jwks.Keys {
|
||||
require.Truef(t, jwk.Valid(), "jwk %s is invalid", jwk.KeyID)
|
||||
require.Truef(t, jwk.IsPublic(), "jwk %s is not public", jwk.KeyID)
|
||||
if jwk.KeyID == activeJWK.KeyID {
|
||||
foundActiveJWK = true
|
||||
}
|
||||
}
|
||||
require.True(t, foundActiveJWK, "could not find active JWK in JWKS: %s", jwks)
|
||||
|
||||
// Ensure upon deleting the secret, it is eventually brought back.
|
||||
err = kubeClient.
|
||||
CoreV1().
|
||||
Secrets(env.SupervisorNamespace).
|
||||
Delete(ctx, updatedOPC.Status.JWKSSecret.Name, metav1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
assert.Eventually(t, func() bool {
|
||||
secret, err = kubeClient.
|
||||
CoreV1().
|
||||
Secrets(env.SupervisorNamespace).
|
||||
Get(ctx, updatedOPC.Status.JWKSSecret.Name, metav1.GetOptions{})
|
||||
return err == nil
|
||||
}, time.Second*10, time.Millisecond*500)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Upon deleting the OPC, the secret is deleted (we test this behavior in our uninstall tests).
|
||||
}
|
||||
@@ -5,6 +5,10 @@ package library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -12,12 +16,14 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
aggregatorclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
|
||||
|
||||
configv1alpha1 "go.pinniped.dev/generated/1.19/apis/config/v1alpha1"
|
||||
idpv1alpha1 "go.pinniped.dev/generated/1.19/apis/idp/v1alpha1"
|
||||
pinnipedclientset "go.pinniped.dev/generated/1.19/client/clientset/versioned"
|
||||
|
||||
@@ -152,3 +158,60 @@ func CreateTestWebhookIDP(ctx context.Context, t *testing.T) corev1.TypedLocalOb
|
||||
Name: idp.Name,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTestOIDCProvider creates and returns a test OIDCProviderConfig in
|
||||
// $PINNIPED_TEST_SUPERVISOR_NAMESPACE, which will be automatically deleted at the end of the
|
||||
// current test's lifetime. It generates a random, valid, issuer for the OIDCProviderConfig.
|
||||
//
|
||||
// If the provided issuer is not the empty string, then it will be used for the
|
||||
// OIDCProviderConfig.Spec.Issuer field. Else, a random issuer will be generated.
|
||||
func CreateTestOIDCProvider(ctx context.Context, t *testing.T, issuer string) *configv1alpha1.OIDCProviderConfig {
|
||||
t.Helper()
|
||||
testEnv := IntegrationEnv(t)
|
||||
|
||||
createContext, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if issuer == "" {
|
||||
var err error
|
||||
issuer, err = randomIssuer()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
opcs := NewPinnipedClientset(t).ConfigV1alpha1().OIDCProviderConfigs(testEnv.SupervisorNamespace)
|
||||
opc, err := opcs.Create(createContext, &configv1alpha1.OIDCProviderConfig{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "test-oidc-provider-",
|
||||
Labels: map[string]string{"pinniped.dev/test": ""},
|
||||
Annotations: map[string]string{"pinniped.dev/testName": t.Name()},
|
||||
},
|
||||
Spec: configv1alpha1.OIDCProviderConfigSpec{
|
||||
Issuer: issuer,
|
||||
},
|
||||
}, metav1.CreateOptions{})
|
||||
require.NoError(t, err, "could not create test OIDCProviderConfig")
|
||||
t.Logf("created test OIDCProviderConfig %s/%s", opc.Namespace, opc.Name)
|
||||
|
||||
t.Cleanup(func() {
|
||||
t.Helper()
|
||||
t.Logf("cleaning up test OIDCProviderConfig %s/%s", opc.Namespace, opc.Name)
|
||||
deleteCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
err := opcs.Delete(deleteCtx, opc.Name, metav1.DeleteOptions{})
|
||||
notFound := k8serrors.IsNotFound(err)
|
||||
// It's okay if it is not found, because it might have been deleted by another part of this test.
|
||||
if !notFound {
|
||||
require.NoErrorf(t, err, "could not cleanup test OIDCProviderConfig %s/%s", opc.Namespace, opc.Name)
|
||||
}
|
||||
})
|
||||
|
||||
return opc
|
||||
}
|
||||
|
||||
func randomIssuer() (string, error) {
|
||||
var buf [8]byte
|
||||
if _, err := io.ReadFull(rand.Reader, buf[:]); err != nil {
|
||||
return "", fmt.Errorf("could not generate random state: %w", err)
|
||||
}
|
||||
return fmt.Sprintf("http://test-issuer-%s.pinniped.dev", hex.EncodeToString(buf[:])), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user