Implement the OIDCClientSecretRequest API

This commit is a WIP commit because it doesn't include many tests
for the new feature.

Co-authored-by: Ryan Richard <richardry@vmware.com>
Co-authored-by: Benjamin A. Petersen <ben@benjaminapetersen.me>
This commit is contained in:
Ryan Richard
2022-08-26 10:57:45 -07:00
parent 7c247e9000
commit 1c296e5c4c
26 changed files with 676 additions and 91 deletions

View File

@@ -1404,7 +1404,7 @@ func TestSupervisorLogin_Browser(t *testing.T) {
require.Equal(t, http.StatusForbidden, status)
require.Equal(t,
`{"error":"access_denied","error_description":"The resource owner or authorization server denied the request. `+
`missing the 'pinniped:request-audience' scope"}`,
`Missing the 'pinniped:request-audience' scope."}`,
body)
},
},

View File

@@ -361,6 +361,53 @@ func TestOIDCClientStaticValidation_Parallel(t *testing.T) {
},
wantErr: `OIDCClient.config.supervisor.pinniped.dev "zone" is invalid: [metadata.name: Invalid value: "zone": metadata.name in body should match '^client\.oauth\.pinniped\.dev-', spec.allowedGrantTypes[0]: Unsupported value: "the": supported values: "authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:token-exchange", spec.allowedRedirectURIs[0]: Invalid value: "of": spec.allowedRedirectURIs[0] in body should match '^https://.+|^http://(127\.0\.0\.1|\[::1\])(:\d+)?/', spec.allowedScopes[0]: Unsupported value: "enders": supported values: "openid", "offline_access", "username", "groups", "pinniped:request-audience"]`,
},
{
name: "just the prefix is not valid",
client: &supervisorconfigv1alpha1.OIDCClient{
ObjectMeta: metav1.ObjectMeta{
Name: "client.oauth.pinniped.dev-",
},
Spec: supervisorconfigv1alpha1.OIDCClientSpec{
AllowedRedirectURIs: []supervisorconfigv1alpha1.RedirectURI{
"https://example.com",
"http://127.0.0.1/yoyo",
},
AllowedGrantTypes: []supervisorconfigv1alpha1.GrantType{
"authorization_code",
"refresh_token",
"urn:ietf:params:oauth:grant-type:token-exchange",
},
AllowedScopes: []supervisorconfigv1alpha1.Scope{
"openid",
"offline_access",
"username",
"groups",
"pinniped:request-audience",
},
},
},
fixWant: func(t *testing.T, err error, want string) string {
require.Error(t, err)
prefix := `OIDCClient.config.supervisor.pinniped.dev "client.oauth.pinniped.dev-" is invalid: metadata.name: Invalid value: "client.oauth.pinniped.dev-": `
suffix := ` 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])?)*')`
oldErrContains := `a DNS-1123 subdomain` // Kube 1.19 and before used this error text
newErrContains := `a lowercase RFC 1123 subdomain` // Newer versions of Kube use this error text
gotErr := err.Error()
switch {
case strings.Contains(gotErr, oldErrContains):
return prefix + oldErrContains + suffix
case strings.Contains(gotErr, newErrContains):
return prefix + newErrContains + suffix
default:
require.Failf(t, "the error message did not contain %q or %q. actual message was: %s",
oldErrContains, newErrContains, gotErr)
return ""
}
},
wantErr: `this will be replaced by fixWant()`,
},
{
name: "everything valid",
client: &supervisorconfigv1alpha1.OIDCClient{
@@ -606,7 +653,7 @@ func TestOIDCClientControllerValidations_Parallel(t *testing.T) {
if tt.secret != nil {
// Force the Secret's name to match the client created above.
tt.secret.Name = oidcclientsecretstorage.New(nil, nil).GetName(client.UID)
tt.secret.Name = oidcclientsecretstorage.New(nil).GetName(client.UID)
secret, err := secrets.Create(ctx, tt.secret, metav1.CreateOptions{})
require.NoError(t, err)
t.Cleanup(func() {

View File

@@ -5,6 +5,7 @@ package integration
import (
"context"
"encoding/hex"
"testing"
"time"
@@ -12,6 +13,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"go.pinniped.dev/generated/latest/apis/supervisor/clientsecret/v1alpha1"
supervisorconfigv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1"
"go.pinniped.dev/test/testlib"
)
@@ -19,27 +21,61 @@ func TestOIDCClientSecretRequest_HappyPath_Parallel(t *testing.T) {
env := testlib.IntegrationEnv(t)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
t.Cleanup(cancel)
client := testlib.NewSupervisorClientset(t)
oidcClient, err := client.ConfigV1alpha1().OIDCClients(env.SupervisorNamespace).Create(ctx,
&supervisorconfigv1alpha1.OIDCClient{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "client.oauth.pinniped.dev-",
},
Spec: supervisorconfigv1alpha1.OIDCClientSpec{
AllowedRedirectURIs: []supervisorconfigv1alpha1.RedirectURI{
"https://example.com",
"http://127.0.0.1/yoyo",
},
AllowedGrantTypes: []supervisorconfigv1alpha1.GrantType{
"authorization_code",
"refresh_token",
"urn:ietf:params:oauth:grant-type:token-exchange",
},
AllowedScopes: []supervisorconfigv1alpha1.Scope{
"openid",
"offline_access",
"username",
"groups",
"pinniped:request-audience",
},
},
},
metav1.CreateOptions{},
)
require.NoError(t, err)
t.Cleanup(func() {
deleteErr := client.ConfigV1alpha1().OIDCClients(env.SupervisorNamespace).Delete(ctx, oidcClient.Name, metav1.DeleteOptions{})
require.NoError(t, deleteErr)
})
response, err := client.ClientsecretV1alpha1().OIDCClientSecretRequests(env.SupervisorNamespace).Create(ctx,
&v1alpha1.OIDCClientSecretRequest{
ObjectMeta: metav1.ObjectMeta{
Name: oidcClient.Name,
},
Spec: v1alpha1.OIDCClientSecretRequestSpec{
GenerateNewSecret: true,
},
}, metav1.CreateOptions{})
require.NoError(t, err)
// the hardcoded values from the nonfunctional request
require.Equal(t, response.Status.TotalClientSecrets, 20)
require.Equal(t, response.Status.GeneratedSecret, "not-a-real-secret")
require.Equal(t, response.Status.TotalClientSecrets, 1)
require.Len(t, response.Status.GeneratedSecret, hex.EncodedLen(32))
}
func TestOIDCClientSecretRequest_Unauthenticated_Parallel(t *testing.T) {
env := testlib.IntegrationEnv(t)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
t.Cleanup(cancel)
client := testlib.NewAnonymousSupervisorClientset(t)

View File

@@ -441,7 +441,7 @@ func createOIDCClientSecret(t *testing.T, forOIDCClient *configv1alpha1.OIDCClie
created, err := kubeClient.CoreV1().Secrets(env.SupervisorNamespace).Create(ctx, &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: oidcclientsecretstorage.New(nil, nil).GetName(forOIDCClient.UID), // use the required name
Name: oidcclientsecretstorage.New(nil).GetName(forOIDCClient.UID), // use the required name
Labels: map[string]string{"storage.pinniped.dev/type": "oidc-client-secret", "pinniped.dev/test": ""},
Annotations: map[string]string{"pinniped.dev/testName": t.Name()},
},