2022-01-14 18:49:22 +00:00
// Copyright 2020-2022 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"
2022-08-24 21:45:55 +00:00
"io"
2020-09-14 15:41:36 +00:00
"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 ( )
2022-08-24 21:45:55 +00:00
testLog := testlogger . NewLegacy ( t ) //nolint:staticcheck // old test with lots of log statements
2020-09-14 15:41:36 +00:00
2021-12-10 22:22:36 +00:00
controller := New ( cache , informers . Authentication ( ) . V1alpha1 ( ) . WebhookAuthenticators ( ) , testLog . Logger )
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" ) }
2022-08-24 21:45:55 +00:00
res , err := newWebhookAuthenticator ( & auth1alpha1 . WebhookAuthenticatorSpec { } , os . CreateTemp , 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" } ,
2022-08-24 21:45:55 +00:00
} , os . CreateTemp , clientcmd . WriteToFile )
2020-09-14 15:41:36 +00:00
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" ) ) } ,
2022-08-24 21:45:55 +00:00
} , os . CreateTemp , clientcmd . WriteToFile )
2021-04-28 17:49:42 +00:00
require . Nil ( t , res )
2021-10-20 11:59:24 +00:00
require . EqualError ( t , err , "invalid TLS configuration: certificateAuthorityData is not valid PEM: data does not contain any valid RSA or ECDSA certificates" )
2021-04-28 17:49:42 +00:00
} )
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" ,
2022-08-24 21:45:55 +00:00
} , os . CreateTemp , clientcmd . WriteToFile )
2020-09-14 15:41:36 +00:00
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 ) {
2022-08-24 21:45:55 +00:00
body , err := io . ReadAll ( r . Body )
2020-09-14 15:41:36 +00:00
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
} ,
}
2022-08-24 21:45:55 +00:00
res , err := newWebhookAuthenticator ( spec , os . CreateTemp , clientcmd . WriteToFile )
2020-09-14 15:41:36 +00:00
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 )
} )
}