e05213f9dd
EC keys are smaller and take less time to generate. Our integration tests were super flakey because generating an RSA key would take up to 10 seconds *gasp*. The main token verifier that we care about is Kubernetes, which supports P256, so hopefully it won't be that much of an issue that our default signing key type is EC. The OIDC spec seems kinda squirmy when it comes to using non-RSA signing algorithms... Signed-off-by: Andrew Keesler <akeesler@vmware.com>
96 lines
3.0 KiB
Go
96 lines
3.0 KiB
Go
// 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).
|
|
}
|