2021-01-07 22:58:09 +00:00
|
|
|
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
2020-09-16 14:19:51 +00:00
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
2020-09-14 15:41:36 +00:00
|
|
|
|
|
|
|
package webhookcachefiller
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/base64"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
"k8s.io/client-go/tools/clientcmd"
|
|
|
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
|
|
|
|
2021-02-16 19:00:08 +00:00
|
|
|
auth1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
|
|
|
|
pinnipedfake "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/fake"
|
|
|
|
pinnipedinformers "go.pinniped.dev/generated/latest/client/concierge/informers/externalversions"
|
2020-10-30 19:02:21 +00:00
|
|
|
"go.pinniped.dev/internal/controller/authenticator/authncache"
|
2020-09-18 19:56:24 +00:00
|
|
|
"go.pinniped.dev/internal/controllerlib"
|
|
|
|
"go.pinniped.dev/internal/testutil"
|
|
|
|
"go.pinniped.dev/internal/testutil/testlogger"
|
2020-09-14 15:41:36 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestController(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
syncKey controllerlib.Key
|
2020-10-30 19:02:21 +00:00
|
|
|
webhooks []runtime.Object
|
2020-09-14 15:41:36 +00:00
|
|
|
wantErr string
|
|
|
|
wantLogs []string
|
|
|
|
wantCacheEntries int
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "not found",
|
2021-02-09 18:59:32 +00:00
|
|
|
syncKey: controllerlib.Key{Name: "test-name"},
|
2020-09-14 15:41:36 +00:00
|
|
|
wantLogs: []string{
|
2020-10-30 16:39:26 +00:00
|
|
|
`webhookcachefiller-controller "level"=0 "msg"="Sync() found that the WebhookAuthenticator does not exist yet or was deleted"`,
|
2020-09-14 15:41:36 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "invalid webhook",
|
2021-02-09 18:59:32 +00:00
|
|
|
syncKey: controllerlib.Key{Name: "test-name"},
|
2020-10-30 19:02:21 +00:00
|
|
|
webhooks: []runtime.Object{
|
2020-10-30 16:39:26 +00:00
|
|
|
&auth1alpha1.WebhookAuthenticator{
|
2020-09-14 15:41:36 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2021-02-09 18:59:32 +00:00
|
|
|
Name: "test-name",
|
2020-09-14 15:41:36 +00:00
|
|
|
},
|
2020-10-30 16:39:26 +00:00
|
|
|
Spec: auth1alpha1.WebhookAuthenticatorSpec{
|
2020-09-14 15:41:36 +00:00
|
|
|
Endpoint: "invalid url",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
wantErr: `failed to build webhook config: parse "http://invalid url": invalid character " " in host name`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "valid webhook",
|
2021-02-09 18:59:32 +00:00
|
|
|
syncKey: controllerlib.Key{Name: "test-name"},
|
2020-10-30 19:02:21 +00:00
|
|
|
webhooks: []runtime.Object{
|
2020-10-30 16:39:26 +00:00
|
|
|
&auth1alpha1.WebhookAuthenticator{
|
2020-09-14 15:41:36 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2021-02-09 18:59:32 +00:00
|
|
|
Name: "test-name",
|
2020-09-14 15:41:36 +00:00
|
|
|
},
|
2020-10-30 16:39:26 +00:00
|
|
|
Spec: auth1alpha1.WebhookAuthenticatorSpec{
|
2020-09-14 15:41:36 +00:00
|
|
|
Endpoint: "https://example.com",
|
2020-10-30 16:03:25 +00:00
|
|
|
TLS: &auth1alpha1.TLSSpec{CertificateAuthorityData: ""},
|
2020-09-14 15:41:36 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
wantLogs: []string{
|
2021-02-09 18:59:32 +00:00
|
|
|
`webhookcachefiller-controller "level"=0 "msg"="added new webhook authenticator" "endpoint"="https://example.com" "webhook"={"name":"test-name"}`,
|
2020-09-14 15:41:36 +00:00
|
|
|
},
|
|
|
|
wantCacheEntries: 1,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
tt := tt
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2020-10-30 19:02:21 +00:00
|
|
|
fakeClient := pinnipedfake.NewSimpleClientset(tt.webhooks...)
|
2020-09-14 15:41:36 +00:00
|
|
|
informers := pinnipedinformers.NewSharedInformerFactory(fakeClient, 0)
|
2021-02-09 20:51:35 +00:00
|
|
|
cache := authncache.New()
|
2020-09-14 15:41:36 +00:00
|
|
|
testLog := testlogger.New(t)
|
|
|
|
|
2020-10-30 16:39:26 +00:00
|
|
|
controller := New(cache, informers.Authentication().V1alpha1().WebhookAuthenticators(), testLog)
|
2020-09-14 15:41:36 +00:00
|
|
|
|
2021-03-05 01:25:43 +00:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
2020-09-14 15:41:36 +00:00
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
informers.Start(ctx.Done())
|
|
|
|
controllerlib.TestRunSynchronously(t, controller)
|
|
|
|
|
|
|
|
syncCtx := controllerlib.Context{Context: ctx, Key: tt.syncKey}
|
|
|
|
|
|
|
|
if err := controllerlib.TestSync(t, controller, syncCtx); tt.wantErr != "" {
|
|
|
|
require.EqualError(t, err, tt.wantErr)
|
|
|
|
} else {
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
require.Equal(t, tt.wantLogs, testLog.Lines())
|
|
|
|
require.Equal(t, tt.wantCacheEntries, len(cache.Keys()))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNewWebhookAuthenticator(t *testing.T) {
|
|
|
|
t.Run("temp file failure", func(t *testing.T) {
|
|
|
|
brokenTempFile := func(_ string, _ string) (*os.File, error) { return nil, fmt.Errorf("some temp file error") }
|
|
|
|
res, err := newWebhookAuthenticator(nil, brokenTempFile, clientcmd.WriteToFile)
|
|
|
|
require.Nil(t, res)
|
|
|
|
require.EqualError(t, err, "unable to create temporary file: some temp file error")
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("marshal failure", func(t *testing.T) {
|
|
|
|
marshalError := func(_ clientcmdapi.Config, _ string) error { return fmt.Errorf("some marshal error") }
|
2020-10-30 16:39:26 +00:00
|
|
|
res, err := newWebhookAuthenticator(&auth1alpha1.WebhookAuthenticatorSpec{}, ioutil.TempFile, marshalError)
|
2020-09-14 15:41:36 +00:00
|
|
|
require.Nil(t, res)
|
|
|
|
require.EqualError(t, err, "unable to marshal kubeconfig: some marshal error")
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("invalid base64", func(t *testing.T) {
|
2020-10-30 16:39:26 +00:00
|
|
|
res, err := newWebhookAuthenticator(&auth1alpha1.WebhookAuthenticatorSpec{
|
2020-09-14 15:41:36 +00:00
|
|
|
Endpoint: "https://example.com",
|
2020-10-30 16:03:25 +00:00
|
|
|
TLS: &auth1alpha1.TLSSpec{CertificateAuthorityData: "invalid-base64"},
|
2020-09-14 15:41:36 +00:00
|
|
|
}, ioutil.TempFile, clientcmd.WriteToFile)
|
|
|
|
require.Nil(t, res)
|
|
|
|
require.EqualError(t, err, "invalid TLS configuration: illegal base64 data at input byte 7")
|
|
|
|
})
|
|
|
|
|
2021-04-28 17:49:42 +00:00
|
|
|
t.Run("invalid pem data", func(t *testing.T) {
|
|
|
|
res, err := newWebhookAuthenticator(&auth1alpha1.WebhookAuthenticatorSpec{
|
|
|
|
Endpoint: "https://example.com",
|
|
|
|
TLS: &auth1alpha1.TLSSpec{CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte("bad data"))},
|
|
|
|
}, ioutil.TempFile, clientcmd.WriteToFile)
|
|
|
|
require.Nil(t, res)
|
|
|
|
require.EqualError(t, err, "invalid TLS configuration: certificateAuthorityData is not valid PEM")
|
|
|
|
})
|
|
|
|
|
2020-09-14 15:41:36 +00:00
|
|
|
t.Run("valid config with no TLS spec", func(t *testing.T) {
|
2020-10-30 16:39:26 +00:00
|
|
|
res, err := newWebhookAuthenticator(&auth1alpha1.WebhookAuthenticatorSpec{
|
2020-09-14 15:41:36 +00:00
|
|
|
Endpoint: "https://example.com",
|
|
|
|
}, ioutil.TempFile, clientcmd.WriteToFile)
|
|
|
|
require.NotNil(t, res)
|
|
|
|
require.NoError(t, err)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("success", func(t *testing.T) {
|
|
|
|
caBundle, url := testutil.TLSTestServer(t, func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
body, err := ioutil.ReadAll(r.Body)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Contains(t, string(body), "test-token")
|
|
|
|
_, err = w.Write([]byte(`{}`))
|
|
|
|
require.NoError(t, err)
|
|
|
|
})
|
2020-10-30 16:39:26 +00:00
|
|
|
spec := &auth1alpha1.WebhookAuthenticatorSpec{
|
2020-09-14 15:41:36 +00:00
|
|
|
Endpoint: url,
|
2020-10-30 16:03:25 +00:00
|
|
|
TLS: &auth1alpha1.TLSSpec{
|
2020-09-15 18:54:19 +00:00
|
|
|
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(caBundle)),
|
2020-09-14 15:41:36 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
res, err := newWebhookAuthenticator(spec, ioutil.TempFile, clientcmd.WriteToFile)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.NotNil(t, res)
|
|
|
|
|
|
|
|
resp, authenticated, err := res.AuthenticateToken(context.Background(), "test-token")
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Nil(t, resp)
|
|
|
|
require.False(t, authenticated)
|
|
|
|
})
|
|
|
|
}
|