Merge branch 'main' into github_identity_provider

This commit is contained in:
Benjamin A. Petersen
2024-05-01 12:15:08 -04:00
700 changed files with 29057 additions and 66052 deletions
@@ -8,16 +8,16 @@ import (
"testing"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authentication/authenticator"
authv1alpha "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"
controllerAuthenticator "go.pinniped.dev/internal/controller/authenticator"
"go.pinniped.dev/internal/controller/authenticator/authncache"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/mocks/mocktokenauthenticatorcloser"
"go.pinniped.dev/internal/testutil/testlogger"
)
@@ -109,8 +109,8 @@ func TestController(t *testing.T) {
initialCache: func(t *testing.T, cache *authncache.Cache) {
cache.Store(testWebhookKey1, nil)
cache.Store(testWebhookKey2, nil)
cache.Store(testJWTAuthenticatorKey1, newClosableCacheValue(t, 0))
cache.Store(testJWTAuthenticatorKey2, newClosableCacheValue(t, 1))
cache.Store(testJWTAuthenticatorKey1, newClosableCacheValue(t, false))
cache.Store(testJWTAuthenticatorKey2, newClosableCacheValue(t, true))
cache.Store(testKeyUnknownType, nil)
},
objects: []runtime.Object{
@@ -174,10 +174,28 @@ func TestController(t *testing.T) {
}
}
func newClosableCacheValue(t *testing.T, wantCloses int) authncache.Value {
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
tac := mocktokenauthenticatorcloser.NewMockTokenAuthenticatorCloser(ctrl)
tac.EXPECT().Close().Times(wantCloses)
return tac
type mockValue struct {
wasClosed bool
}
func (m *mockValue) Close() {
m.wasClosed = true
}
func (m *mockValue) AuthenticateToken(_ context.Context, _ string) (*authenticator.Response, bool, error) {
panic("implement me")
}
var _ authncache.Value = (*mockValue)(nil)
var _ controllerAuthenticator.Closer = (*mockValue)(nil)
func newClosableCacheValue(t *testing.T, wantClose bool) authncache.Value {
t.Helper()
mock := &mockValue{}
t.Cleanup(func() {
require.Equal(t, wantClose, mock.wasClosed)
})
return mock
}
@@ -102,10 +102,17 @@ type tokenAuthenticatorCloser interface {
}
type cachedJWTAuthenticator struct {
tokenAuthenticatorCloser
spec *auth1alpha1.JWTAuthenticatorSpec
authenticator.Token
spec *auth1alpha1.JWTAuthenticatorSpec
cancel context.CancelFunc
}
func (c *cachedJWTAuthenticator) Close() {
c.cancel()
}
var _ tokenAuthenticatorCloser = (*cachedJWTAuthenticator)(nil)
// New instantiates a new controllerlib.Controller which will populate the provided authncache.Cache.
func New(
cache *authncache.Cache,
@@ -162,7 +169,7 @@ func (c *jwtCacheFillerController) Sync(ctx controllerlib.Context) error {
// If this authenticator already exists, then only recreate it if is different from the desired
// authenticator. We don't want to be creating a new authenticator for every resync period.
//
// If we do need to recreate the authenticator, then make sure we close the old one to avoid
// If we do need to recreate the authenticator, then make sure cancel the old one to avoid
// goroutine leaks.
if value := c.cache.Get(cacheKey); value != nil {
jwtAuthenticator := c.extractValueAsJWTAuthenticator(value)
@@ -517,7 +524,8 @@ func (c *jwtCacheFillerController) newCachedJWTAuthenticator(client *http.Client
groupsClaim = defaultGroupsClaim
}
oidcAuthenticator, err := oidc.New(oidc.Options{
cancelCtx, cancel := context.WithCancel(context.Background())
oidcAuthenticator, err := oidc.New(cancelCtx, oidc.Options{
JWTAuthenticator: apiserver.JWTAuthenticator{
Issuer: apiserver.Issuer{
URL: spec.Issuer,
@@ -552,6 +560,7 @@ func (c *jwtCacheFillerController) newCachedJWTAuthenticator(client *http.Client
Reason: reasonInvalidAuthenticator,
Message: msg,
})
cancel()
// resync err, lots of possible issues that may or may not be machine related
return nil, conditions, fmt.Errorf("%s: %w", errText, err)
}
@@ -563,8 +572,9 @@ func (c *jwtCacheFillerController) newCachedJWTAuthenticator(client *http.Client
Message: msg,
})
return &cachedJWTAuthenticator{
tokenAuthenticatorCloser: oidcAuthenticator,
spec: spec,
Token: oidcAuthenticator,
spec: spec,
cancel: cancel,
}, conditions, nil
}
@@ -24,7 +24,6 @@ import (
fositejwt "github.com/ory/fosite/token/jwt"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -40,7 +39,6 @@ import (
"go.pinniped.dev/internal/controller/authenticator/authncache"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/crypto/ptls"
"go.pinniped.dev/internal/mocks/mocktokenauthenticatorcloser"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/testutil"
"go.pinniped.dev/internal/testutil/conciergetestutil"
@@ -1749,11 +1747,12 @@ func TestController(t *testing.T) {
Kind: "JWTAuthenticator",
Name: syncCtx.Key.Name,
}
cachedAuthenticator := cache.Get(expectedCacheKey)
cachedAuthenticator, ok := cache.Get(expectedCacheKey).(tokenAuthenticatorCloser)
require.True(t, ok)
require.NotNil(t, cachedAuthenticator)
// Schedule it to be closed at the end of the test.
t.Cleanup(cachedAuthenticator.(*cachedJWTAuthenticator).Close)
t.Cleanup(cachedAuthenticator.Close)
const (
goodSubject = "some-subject"
@@ -2087,18 +2086,17 @@ func createJWT(
}
func newCacheValue(t *testing.T, spec auth1alpha1.JWTAuthenticatorSpec, wantClose bool) authncache.Value {
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
tokenAuthenticatorCloser := mocktokenauthenticatorcloser.NewMockTokenAuthenticatorCloser(ctrl)
t.Helper()
wasClosed := false
wantCloses := 0
if wantClose {
wantCloses++
}
tokenAuthenticatorCloser.EXPECT().Close().Times(wantCloses)
t.Cleanup(func() {
require.Equal(t, wantClose, wasClosed)
})
return &cachedJWTAuthenticator{
tokenAuthenticatorCloser: tokenAuthenticatorCloser,
spec: &spec,
spec: &spec,
cancel: func() {
wasClosed = true
},
}
}
@@ -1,4 +1,4 @@
// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package impersonatorconfig
@@ -27,6 +27,7 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
corev1informers "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/klog/v2"
@@ -1205,7 +1206,7 @@ func validateCredentialIssuerSpec(spec *v1alpha1.ImpersonationProxySpec) error {
}
// If specified, validate that the LoadBalancerIP is a valid IPv4 or IPv6 address.
if ip := spec.Service.LoadBalancerIP; ip != "" && len(validation.IsValidIP(ip)) > 0 {
if ip := spec.Service.LoadBalancerIP; ip != "" && len(validation.IsValidIP(field.NewPath("spec", "service", "loadBalancerIP"), ip)) > 0 {
return fmt.Errorf("invalid LoadBalancerIP %q", spec.Service.LoadBalancerIP)
}
@@ -121,7 +121,8 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
spec.Run(t, "Sync", func(t *testing.T, when spec.G, it spec.S) {
const (
installedInNamespace = "some-namespace"
installedInNamespace = "some-namespace"
currentSessionStorageVersion = "7" // update this when you update the storage version in the production code
)
var (
@@ -265,7 +266,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
when("there are valid, expired authcode secrets which contain upstream refresh tokens", func() {
it.Before(func() {
activeOIDCAuthcodeSession := &authorizationcode.Session{
Version: "6",
Version: currentSessionStorageVersion,
Active: true,
Request: &fosite.Request{
ID: "request-id-1",
@@ -310,7 +311,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
r.NoError(kubeClient.Tracker().Add(activeOIDCAuthcodeSessionSecret))
inactiveOIDCAuthcodeSession := &authorizationcode.Session{
Version: "6",
Version: currentSessionStorageVersion,
Active: false,
Request: &fosite.Request{
ID: "request-id-2",
@@ -389,7 +390,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
when("there are valid, expired authcode secrets which contain upstream access tokens", func() {
it.Before(func() {
activeOIDCAuthcodeSession := &authorizationcode.Session{
Version: "6",
Version: currentSessionStorageVersion,
Active: true,
Request: &fosite.Request{
ID: "request-id-1",
@@ -434,7 +435,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
r.NoError(kubeClient.Tracker().Add(activeOIDCAuthcodeSessionSecret))
inactiveOIDCAuthcodeSession := &authorizationcode.Session{
Version: "6",
Version: currentSessionStorageVersion,
Active: false,
Request: &fosite.Request{
ID: "request-id-2",
@@ -513,7 +514,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
when("there is an invalid, expired authcode secret", func() {
it.Before(func() {
invalidOIDCAuthcodeSession := &authorizationcode.Session{
Version: "6",
Version: currentSessionStorageVersion,
Active: true,
Request: &fosite.Request{
ID: "", // it is invalid for there to be a missing request ID
@@ -582,7 +583,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
when("there is a valid, expired authcode secret but its upstream name does not match any existing upstream", func() {
it.Before(func() {
wrongProviderNameOIDCAuthcodeSession := &authorizationcode.Session{
Version: "6",
Version: currentSessionStorageVersion,
Active: true,
Request: &fosite.Request{
ID: "request-id-1",
@@ -653,7 +654,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
when("there is a valid, expired authcode secret but its upstream UID does not match any existing upstream", func() {
it.Before(func() {
wrongProviderNameOIDCAuthcodeSession := &authorizationcode.Session{
Version: "6",
Version: currentSessionStorageVersion,
Active: true,
Request: &fosite.Request{
ID: "request-id-1",
@@ -724,7 +725,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
when("there is a valid, recently expired authcode secret but the upstream revocation fails", func() {
it.Before(func() {
activeOIDCAuthcodeSession := &authorizationcode.Session{
Version: "6",
Version: currentSessionStorageVersion,
Active: true,
Request: &fosite.Request{
ID: "request-id-1",
@@ -829,7 +830,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
when("there is a valid, long-since expired authcode secret but the upstream revocation fails", func() {
it.Before(func() {
activeOIDCAuthcodeSession := &authorizationcode.Session{
Version: "6",
Version: currentSessionStorageVersion,
Active: true,
Request: &fosite.Request{
ID: "request-id-1",
@@ -908,7 +909,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
when("there are valid, expired access token secrets which contain upstream refresh tokens", func() {
it.Before(func() {
offlineAccessGrantedOIDCAccessTokenSession := &accesstoken.Session{
Version: "6",
Version: currentSessionStorageVersion,
Request: &fosite.Request{
GrantedScope: fosite.Arguments{"scope1", "scope2", "offline_access"},
ID: "request-id-1",
@@ -953,7 +954,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
r.NoError(kubeClient.Tracker().Add(offlineAccessGrantedOIDCAccessTokenSessionSecret))
offlineAccessNotGrantedOIDCAccessTokenSession := &accesstoken.Session{
Version: "6",
Version: currentSessionStorageVersion,
Request: &fosite.Request{
GrantedScope: fosite.Arguments{"scope1", "scope2"},
ID: "request-id-2",
@@ -1032,7 +1033,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
when("there are valid, expired access token secrets which contain upstream access tokens", func() {
it.Before(func() {
offlineAccessGrantedOIDCAccessTokenSession := &accesstoken.Session{
Version: "6",
Version: currentSessionStorageVersion,
Request: &fosite.Request{
GrantedScope: fosite.Arguments{"scope1", "scope2", "offline_access"},
ID: "request-id-1",
@@ -1077,7 +1078,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
r.NoError(kubeClient.Tracker().Add(offlineAccessGrantedOIDCAccessTokenSessionSecret))
offlineAccessNotGrantedOIDCAccessTokenSession := &accesstoken.Session{
Version: "6",
Version: currentSessionStorageVersion,
Request: &fosite.Request{
GrantedScope: fosite.Arguments{"scope1", "scope2"},
ID: "request-id-2",
@@ -1156,7 +1157,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
when("there are valid, expired refresh secrets which contain upstream refresh tokens", func() {
it.Before(func() {
oidcRefreshSession := &refreshtoken.Session{
Version: "6",
Version: currentSessionStorageVersion,
Request: &fosite.Request{
ID: "request-id-1",
Client: &clientregistry.Client{},
@@ -1233,7 +1234,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
when("there are valid, expired refresh secrets which contain upstream access tokens", func() {
it.Before(func() {
oidcRefreshSession := &refreshtoken.Session{
Version: "6",
Version: currentSessionStorageVersion,
Request: &fosite.Request{
ID: "request-id-1",
Client: &clientregistry.Client{},