mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-09-19 14:34:27 +00:00
First draft of implementation of multiple IDPs support
This commit is contained in:
+9
-9
@@ -26,7 +26,7 @@ import (
|
||||
"go.pinniped.dev/internal/controller/conditionsutil"
|
||||
"go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers"
|
||||
"go.pinniped.dev/internal/controllerlib"
|
||||
"go.pinniped.dev/internal/oidc/provider"
|
||||
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
|
||||
"go.pinniped.dev/internal/plog"
|
||||
"go.pinniped.dev/internal/upstreamldap"
|
||||
)
|
||||
@@ -225,7 +225,7 @@ func (s *activeDirectoryUpstreamGenericLDAPStatus) Conditions() []metav1.Conditi
|
||||
|
||||
// UpstreamActiveDirectoryIdentityProviderICache is a thread safe cache that holds a list of validated upstream LDAP IDP configurations.
|
||||
type UpstreamActiveDirectoryIdentityProviderICache interface {
|
||||
SetActiveDirectoryIdentityProviders([]provider.UpstreamLDAPIdentityProviderI)
|
||||
SetActiveDirectoryIdentityProviders([]upstreamprovider.UpstreamLDAPIdentityProviderI)
|
||||
}
|
||||
|
||||
type activeDirectoryWatcherController struct {
|
||||
@@ -299,7 +299,7 @@ func (c *activeDirectoryWatcherController) Sync(ctx controllerlib.Context) error
|
||||
}
|
||||
|
||||
requeue := false
|
||||
validatedUpstreams := make([]provider.UpstreamLDAPIdentityProviderI, 0, len(actualUpstreams))
|
||||
validatedUpstreams := make([]upstreamprovider.UpstreamLDAPIdentityProviderI, 0, len(actualUpstreams))
|
||||
for _, upstream := range actualUpstreams {
|
||||
valid, requestedRequeue := c.validateUpstream(ctx.Context, upstream)
|
||||
if valid != nil {
|
||||
@@ -318,7 +318,7 @@ func (c *activeDirectoryWatcherController) Sync(ctx controllerlib.Context) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *activeDirectoryWatcherController) validateUpstream(ctx context.Context, upstream *v1alpha1.ActiveDirectoryIdentityProvider) (p provider.UpstreamLDAPIdentityProviderI, requeue bool) {
|
||||
func (c *activeDirectoryWatcherController) validateUpstream(ctx context.Context, upstream *v1alpha1.ActiveDirectoryIdentityProvider) (p upstreamprovider.UpstreamLDAPIdentityProviderI, requeue bool) {
|
||||
spec := upstream.Spec
|
||||
|
||||
adUpstreamImpl := &activeDirectoryUpstreamGenericLDAPImpl{activeDirectoryIdentityProvider: *upstream}
|
||||
@@ -344,7 +344,7 @@ func (c *activeDirectoryWatcherController) validateUpstream(ctx context.Context,
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){
|
||||
"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID"),
|
||||
},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
|
||||
pwdLastSetAttribute: attributeUnchangedSinceLogin(pwdLastSetAttribute),
|
||||
userAccountControlAttribute: validUserAccountControl,
|
||||
userAccountControlComputedAttribute: validComputedUserAccountControl,
|
||||
@@ -445,7 +445,7 @@ func getDomainFromDistinguishedName(distinguishedName string) (string, error) {
|
||||
}
|
||||
|
||||
//nolint:gochecknoglobals // this needs to be a global variable so that tests can check pointer equality
|
||||
var validUserAccountControl = func(entry *ldap.Entry, _ provider.RefreshAttributes) error {
|
||||
var validUserAccountControl = func(entry *ldap.Entry, _ upstreamprovider.RefreshAttributes) error {
|
||||
userAccountControl, err := strconv.Atoi(entry.GetAttributeValue(userAccountControlAttribute))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -459,7 +459,7 @@ var validUserAccountControl = func(entry *ldap.Entry, _ provider.RefreshAttribut
|
||||
}
|
||||
|
||||
//nolint:gochecknoglobals // this needs to be a global variable so that tests can check pointer equality
|
||||
var validComputedUserAccountControl = func(entry *ldap.Entry, _ provider.RefreshAttributes) error {
|
||||
var validComputedUserAccountControl = func(entry *ldap.Entry, _ upstreamprovider.RefreshAttributes) error {
|
||||
userAccountControl, err := strconv.Atoi(entry.GetAttributeValue(userAccountControlComputedAttribute))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -473,8 +473,8 @@ var validComputedUserAccountControl = func(entry *ldap.Entry, _ provider.Refresh
|
||||
}
|
||||
|
||||
//nolint:gochecknoglobals // this needs to be a global variable so that tests can check pointer equality
|
||||
var attributeUnchangedSinceLogin = func(attribute string) func(*ldap.Entry, provider.RefreshAttributes) error {
|
||||
return func(entry *ldap.Entry, storedAttributes provider.RefreshAttributes) error {
|
||||
var attributeUnchangedSinceLogin = func(attribute string) func(*ldap.Entry, upstreamprovider.RefreshAttributes) error {
|
||||
return func(entry *ldap.Entry, storedAttributes upstreamprovider.RefreshAttributes) error {
|
||||
prevAttributeValue := storedAttributes.AdditionalAttributes[attribute]
|
||||
newValues := entry.GetRawAttributeValues(attribute)
|
||||
|
||||
|
||||
+21
-20
@@ -31,6 +31,7 @@ import (
|
||||
"go.pinniped.dev/internal/endpointaddr"
|
||||
"go.pinniped.dev/internal/mocks/mockldapconn"
|
||||
"go.pinniped.dev/internal/oidc/provider"
|
||||
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
|
||||
"go.pinniped.dev/internal/testutil"
|
||||
"go.pinniped.dev/internal/upstreamldap"
|
||||
)
|
||||
@@ -229,7 +230,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
GroupNameAttribute: testGroupSearchNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -572,7 +573,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
GroupNameAttribute: testGroupSearchNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -642,7 +643,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
GroupNameAttribute: "sAMAccountName",
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -715,7 +716,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
GroupNameAttribute: testGroupSearchNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -795,7 +796,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
GroupNameAttribute: testGroupSearchNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -859,7 +860,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
GroupNameAttribute: testGroupSearchNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1010,7 +1011,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
GroupNameAttribute: testGroupSearchNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1160,7 +1161,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
GroupNameAttribute: testGroupSearchNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1232,7 +1233,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
GroupNameAttribute: testGroupSearchNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1499,7 +1500,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
|
||||
GroupAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"sAMAccountName": groupSAMAccountNameWithDomainSuffix},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1559,7 +1560,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
GroupNameAttribute: testGroupSearchNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1623,7 +1624,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
GroupNameAttribute: testGroupSearchNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1687,7 +1688,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
GroupNameAttribute: testGroupSearchNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1899,7 +1900,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
GroupNameAttribute: testGroupSearchNameAttrName,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1962,7 +1963,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
SkipGroupRefresh: true,
|
||||
},
|
||||
UIDAttributeParsingOverrides: map[string]func(*ldap.Entry) (string, error){"objectGUID": microsoftUUIDFromBinaryAttr("objectGUID")},
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, provider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -2010,7 +2011,7 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
fakeKubeClient := fake.NewSimpleClientset(tt.inputSecrets...)
|
||||
kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0)
|
||||
cache := provider.NewDynamicUpstreamIDPProvider()
|
||||
cache.SetActiveDirectoryIdentityProviders([]provider.UpstreamLDAPIdentityProviderI{
|
||||
cache.SetActiveDirectoryIdentityProviders([]upstreamprovider.UpstreamLDAPIdentityProviderI{
|
||||
upstreamldap.New(upstreamldap.ProviderConfig{Name: "initial-entry"}),
|
||||
})
|
||||
|
||||
@@ -2104,8 +2105,8 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
|
||||
expectedRefreshAttributeChecks := copyOfExpectedValueForResultingCache.RefreshAttributeChecks
|
||||
actualRefreshAttributeChecks := actualConfig.RefreshAttributeChecks
|
||||
copyOfExpectedValueForResultingCache.RefreshAttributeChecks = map[string]func(*ldap.Entry, provider.RefreshAttributes) error{}
|
||||
actualConfig.RefreshAttributeChecks = map[string]func(*ldap.Entry, provider.RefreshAttributes) error{}
|
||||
copyOfExpectedValueForResultingCache.RefreshAttributeChecks = map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{}
|
||||
actualConfig.RefreshAttributeChecks = map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{}
|
||||
require.Equal(t, len(expectedRefreshAttributeChecks), len(actualRefreshAttributeChecks))
|
||||
for k, v := range expectedRefreshAttributeChecks {
|
||||
require.NotNil(t, actualRefreshAttributeChecks[k])
|
||||
@@ -2354,7 +2355,7 @@ func TestValidUserAccountControl(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
tt := test
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := validUserAccountControl(tt.entry, provider.RefreshAttributes{})
|
||||
err := validUserAccountControl(tt.entry, upstreamprovider.RefreshAttributes{})
|
||||
|
||||
if tt.wantErr != "" {
|
||||
require.Error(t, err)
|
||||
@@ -2415,7 +2416,7 @@ func TestValidComputedUserAccountControl(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
tt := test
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := validComputedUserAccountControl(tt.entry, provider.RefreshAttributes{})
|
||||
err := validComputedUserAccountControl(tt.entry, upstreamprovider.RefreshAttributes{})
|
||||
|
||||
if tt.wantErr != "" {
|
||||
require.Error(t, err)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package supervisorconfig
|
||||
@@ -8,9 +8,11 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/client-go/util/retry"
|
||||
"k8s.io/klog/v2"
|
||||
@@ -19,8 +21,11 @@ import (
|
||||
configv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1"
|
||||
pinnipedclientset "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned"
|
||||
configinformers "go.pinniped.dev/generated/latest/client/supervisor/informers/externalversions/config/v1alpha1"
|
||||
idpinformers "go.pinniped.dev/generated/latest/client/supervisor/informers/externalversions/idp/v1alpha1"
|
||||
"go.pinniped.dev/internal/celtransformer"
|
||||
pinnipedcontroller "go.pinniped.dev/internal/controller"
|
||||
"go.pinniped.dev/internal/controllerlib"
|
||||
"go.pinniped.dev/internal/idtransform"
|
||||
"go.pinniped.dev/internal/oidc/provider"
|
||||
"go.pinniped.dev/internal/plog"
|
||||
)
|
||||
@@ -33,10 +38,14 @@ type ProvidersSetter interface {
|
||||
}
|
||||
|
||||
type federationDomainWatcherController struct {
|
||||
providerSetter ProvidersSetter
|
||||
clock clock.Clock
|
||||
client pinnipedclientset.Interface
|
||||
federationDomainInformer configinformers.FederationDomainInformer
|
||||
providerSetter ProvidersSetter
|
||||
clock clock.Clock
|
||||
client pinnipedclientset.Interface
|
||||
|
||||
federationDomainInformer configinformers.FederationDomainInformer
|
||||
oidcIdentityProviderInformer idpinformers.OIDCIdentityProviderInformer
|
||||
ldapIdentityProviderInformer idpinformers.LDAPIdentityProviderInformer
|
||||
activeDirectoryIdentityProviderInformer idpinformers.ActiveDirectoryIdentityProviderInformer
|
||||
}
|
||||
|
||||
// NewFederationDomainWatcherController creates a controllerlib.Controller that watches
|
||||
@@ -46,16 +55,22 @@ func NewFederationDomainWatcherController(
|
||||
clock clock.Clock,
|
||||
client pinnipedclientset.Interface,
|
||||
federationDomainInformer configinformers.FederationDomainInformer,
|
||||
oidcIdentityProviderInformer idpinformers.OIDCIdentityProviderInformer,
|
||||
ldapIdentityProviderInformer idpinformers.LDAPIdentityProviderInformer,
|
||||
activeDirectoryIdentityProviderInformer idpinformers.ActiveDirectoryIdentityProviderInformer,
|
||||
withInformer pinnipedcontroller.WithInformerOptionFunc,
|
||||
) controllerlib.Controller {
|
||||
return controllerlib.New(
|
||||
controllerlib.Config{
|
||||
Name: "FederationDomainWatcherController",
|
||||
Syncer: &federationDomainWatcherController{
|
||||
providerSetter: providerSetter,
|
||||
clock: clock,
|
||||
client: client,
|
||||
federationDomainInformer: federationDomainInformer,
|
||||
providerSetter: providerSetter,
|
||||
clock: clock,
|
||||
client: client,
|
||||
federationDomainInformer: federationDomainInformer,
|
||||
oidcIdentityProviderInformer: oidcIdentityProviderInformer,
|
||||
ldapIdentityProviderInformer: ldapIdentityProviderInformer,
|
||||
activeDirectoryIdentityProviderInformer: activeDirectoryIdentityProviderInformer,
|
||||
},
|
||||
},
|
||||
withInformer(
|
||||
@@ -63,6 +78,27 @@ func NewFederationDomainWatcherController(
|
||||
pinnipedcontroller.MatchAnythingFilter(pinnipedcontroller.SingletonQueue()),
|
||||
controllerlib.InformerOption{},
|
||||
),
|
||||
withInformer(
|
||||
oidcIdentityProviderInformer,
|
||||
// Since this controller only cares about IDP metadata names and UIDs (immutable fields),
|
||||
// we only need to trigger Sync on creates and deletes.
|
||||
pinnipedcontroller.MatchAnythingIgnoringUpdatesFilter(pinnipedcontroller.SingletonQueue()),
|
||||
controllerlib.InformerOption{},
|
||||
),
|
||||
withInformer(
|
||||
ldapIdentityProviderInformer,
|
||||
// Since this controller only cares about IDP metadata names and UIDs (immutable fields),
|
||||
// we only need to trigger Sync on creates and deletes.
|
||||
pinnipedcontroller.MatchAnythingIgnoringUpdatesFilter(pinnipedcontroller.SingletonQueue()),
|
||||
controllerlib.InformerOption{},
|
||||
),
|
||||
withInformer(
|
||||
activeDirectoryIdentityProviderInformer,
|
||||
// Since this controller only cares about IDP metadata names and UIDs (immutable fields),
|
||||
// we only need to trigger Sync on creates and deletes.
|
||||
pinnipedcontroller.MatchAnythingIgnoringUpdatesFilter(pinnipedcontroller.SingletonQueue()),
|
||||
controllerlib.InformerOption{},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -143,8 +179,239 @@ func (c *federationDomainWatcherController) Sync(ctx controllerlib.Context) erro
|
||||
continue
|
||||
}
|
||||
|
||||
federationDomainIssuer, err := provider.NewFederationDomainIssuer(federationDomain.Spec.Issuer) // This validates the Issuer URL.
|
||||
// TODO: Move all this identity provider stuff into helper functions. This is just a sketch of how the code would
|
||||
// work in the sense that this is not doing error handling, is not validating everything that it should, and
|
||||
// is not updating the status of the FederationDomain with anything related to these identity providers.
|
||||
// This code may crash on invalid inputs since it is not handling any errors. However, when given valid inputs,
|
||||
// this correctly implements the multiple IDPs features.
|
||||
// Create the list of IDPs for this FederationDomain.
|
||||
// Don't worry if the IDP CRs themselves is phase=Ready because those which are not ready will not be loaded
|
||||
// into the provider cache, so they cannot actually be used to authenticate.
|
||||
federationDomainIdentityProviders := []*provider.FederationDomainIdentityProvider{}
|
||||
var defaultFederationDomainIdentityProvider *provider.FederationDomainIdentityProvider
|
||||
if len(federationDomain.Spec.IdentityProviders) == 0 {
|
||||
// When the FederationDomain does not list any IDPs, then we might be in backwards compatibility mode.
|
||||
oidcIdentityProviders, _ := c.oidcIdentityProviderInformer.Lister().List(labels.Everything())
|
||||
ldapIdentityProviders, _ := c.ldapIdentityProviderInformer.Lister().List(labels.Everything())
|
||||
activeDirectoryIdentityProviders, _ := c.activeDirectoryIdentityProviderInformer.Lister().List(labels.Everything())
|
||||
// TODO handle err return value for each of the above three lines
|
||||
|
||||
// Check if that there is exactly one IDP defined in the Supervisor namespace of any IDP CRD type.
|
||||
idpCRsCount := len(oidcIdentityProviders) + len(ldapIdentityProviders) + len(activeDirectoryIdentityProviders)
|
||||
if idpCRsCount == 1 {
|
||||
// If so, default that IDP's DisplayName to be the same as its resource Name.
|
||||
defaultFederationDomainIdentityProvider = &provider.FederationDomainIdentityProvider{}
|
||||
switch {
|
||||
case len(oidcIdentityProviders) == 1:
|
||||
defaultFederationDomainIdentityProvider.DisplayName = oidcIdentityProviders[0].Name
|
||||
defaultFederationDomainIdentityProvider.UID = oidcIdentityProviders[0].UID
|
||||
case len(ldapIdentityProviders) == 1:
|
||||
defaultFederationDomainIdentityProvider.DisplayName = ldapIdentityProviders[0].Name
|
||||
defaultFederationDomainIdentityProvider.UID = ldapIdentityProviders[0].UID
|
||||
case len(activeDirectoryIdentityProviders) == 1:
|
||||
defaultFederationDomainIdentityProvider.DisplayName = activeDirectoryIdentityProviders[0].Name
|
||||
defaultFederationDomainIdentityProvider.UID = activeDirectoryIdentityProviders[0].UID
|
||||
}
|
||||
// Backwards compatibility mode always uses an empty identity transformation pipline since no
|
||||
// transformations are defined on the FederationDomain.
|
||||
defaultFederationDomainIdentityProvider.Transforms = idtransform.NewTransformationPipeline()
|
||||
plog.Warning("detected FederationDomain identity provider backwards compatibility mode: using the one existing identity provider for authentication",
|
||||
"federationDomain", federationDomain.Name)
|
||||
} else {
|
||||
// There are no IDP CRs or there is more than one IDP CR. Either way, we are not in the backwards
|
||||
// compatibility mode because there is not exactly one IDP CR in the namespace, despite the fact that no
|
||||
// IDPs are listed on the FederationDomain. Create a FederationDomain which has no IDPs and therefore
|
||||
// cannot actually be used to log in, but still serves endpoints.
|
||||
// TODO: Write something into the FederationDomain's status to explain what's happening and how to fix it.
|
||||
plog.Warning("FederationDomain has no identity providers listed and there is not exactly one identity provider defined in the namespace: authentication disabled",
|
||||
"federationDomain", federationDomain.Name,
|
||||
"namespace", federationDomain.Namespace,
|
||||
"identityProvidersCustomResourcesCount", idpCRsCount,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// If there is an explicit list of IDPs on the FederationDomain, then process the list.
|
||||
celTransformer, _ := celtransformer.NewCELTransformer(time.Second) // TODO: what is a good duration limit here?
|
||||
// TODO: handle err
|
||||
for _, idp := range federationDomain.Spec.IdentityProviders {
|
||||
var idpResourceUID types.UID
|
||||
var idpResourceName string
|
||||
// TODO: Validate that all displayNames are unique within this FederationDomain's spec's list of identity providers.
|
||||
// TODO: Validate that idp.ObjectRef.APIGroup is the expected APIGroup for IDP CRs "idp.supervisor.pinniped.dev"
|
||||
// Validate that each objectRef resolves to an existing IDP. It does not matter if the IDP itself
|
||||
// is phase=Ready, because it will not be loaded into the cache if not ready. For each objectRef
|
||||
// that does not resolve, put an error on the FederationDomain status.
|
||||
switch idp.ObjectRef.Kind {
|
||||
case "LDAPIdentityProvider":
|
||||
ldapIDP, _ := c.ldapIdentityProviderInformer.Lister().LDAPIdentityProviders(federationDomain.Namespace).Get(idp.ObjectRef.Name)
|
||||
// TODO: handle notfound err and also unexpected errors
|
||||
idpResourceName = ldapIDP.Name
|
||||
idpResourceUID = ldapIDP.UID
|
||||
case "ActiveDirectoryIdentityProvider":
|
||||
adIDP, _ := c.activeDirectoryIdentityProviderInformer.Lister().ActiveDirectoryIdentityProviders(federationDomain.Namespace).Get(idp.ObjectRef.Name)
|
||||
// TODO: handle notfound err and also unexpected errors
|
||||
idpResourceName = adIDP.Name
|
||||
idpResourceUID = adIDP.UID
|
||||
case "OIDCIdentityProvider":
|
||||
oidcIDP, _ := c.oidcIdentityProviderInformer.Lister().OIDCIdentityProviders(federationDomain.Namespace).Get(idp.ObjectRef.Name)
|
||||
// TODO: handle notfound err and also unexpected errors
|
||||
idpResourceName = oidcIDP.Name
|
||||
idpResourceUID = oidcIDP.UID
|
||||
default:
|
||||
// TODO: handle bad user input
|
||||
}
|
||||
plog.Debug("resolved identity provider object reference",
|
||||
"kind", idp.ObjectRef.Kind,
|
||||
"name", idp.ObjectRef.Name,
|
||||
"foundResourceName", idpResourceName,
|
||||
"foundResourceUID", idpResourceUID,
|
||||
)
|
||||
|
||||
// Prepare the transformations.
|
||||
pipeline := idtransform.NewTransformationPipeline()
|
||||
consts := &celtransformer.TransformationConstants{
|
||||
StringConstants: map[string]string{},
|
||||
StringListConstants: map[string][]string{},
|
||||
}
|
||||
// Read all the declared constants.
|
||||
for _, c := range idp.Transforms.Constants {
|
||||
switch c.Type {
|
||||
case "string":
|
||||
consts.StringConstants[c.Name] = c.StringValue
|
||||
case "stringList":
|
||||
consts.StringListConstants[c.Name] = c.StringListValue
|
||||
default:
|
||||
// TODO: this shouldn't really happen since the CRD validates it, but handle it as an error
|
||||
}
|
||||
}
|
||||
// Compile all the expressions and add them to the pipeline.
|
||||
for idx, e := range idp.Transforms.Expressions {
|
||||
var rawTransform celtransformer.CELTransformation
|
||||
switch e.Type {
|
||||
case "username/v1":
|
||||
rawTransform = &celtransformer.UsernameTransformation{Expression: e.Expression}
|
||||
case "groups/v1":
|
||||
rawTransform = &celtransformer.GroupsTransformation{Expression: e.Expression}
|
||||
case "policy/v1":
|
||||
rawTransform = &celtransformer.AllowAuthenticationPolicy{
|
||||
Expression: e.Expression,
|
||||
RejectedAuthenticationMessage: e.Message,
|
||||
}
|
||||
default:
|
||||
// TODO: this shouldn't really happen since the CRD validates it, but handle it as an error
|
||||
}
|
||||
compiledTransform, err := celTransformer.CompileTransformation(rawTransform, consts)
|
||||
if err != nil {
|
||||
// TODO: handle compile err
|
||||
plog.Error("error compiling identity transformation", err,
|
||||
"federationDomain", federationDomain.Name,
|
||||
"idpDisplayName", idp.DisplayName,
|
||||
"transformationIndex", idx,
|
||||
"transformationType", e.Type,
|
||||
"transformationExpression", e.Expression,
|
||||
)
|
||||
}
|
||||
pipeline.AppendTransformation(compiledTransform)
|
||||
plog.Debug("successfully compiled identity transformation expression",
|
||||
"type", e.Type,
|
||||
"expr", e.Expression,
|
||||
"policyMessage", e.Message,
|
||||
)
|
||||
}
|
||||
// Run all the provided transform examples. If any fail, put errors on the FederationDomain status.
|
||||
for idx, e := range idp.Transforms.Examples {
|
||||
// TODO: use a real context param below
|
||||
result, _ := pipeline.Evaluate(context.TODO(), e.Username, e.Groups)
|
||||
// TODO: handle err
|
||||
resultWasAuthRejected := !result.AuthenticationAllowed
|
||||
if e.Expects.Rejected && !resultWasAuthRejected {
|
||||
// TODO: handle this failed example
|
||||
plog.Warning("FederationDomain identity provider transformations example failed: expected authentication to be rejected but it was not",
|
||||
"federationDomain", federationDomain.Name,
|
||||
"idpDisplayName", idp.DisplayName,
|
||||
"exampleIndex", idx,
|
||||
"expectedRejected", e.Expects.Rejected,
|
||||
"actualRejectedResult", resultWasAuthRejected,
|
||||
"expectedMessage", e.Expects.Message,
|
||||
"actualMessageResult", result.RejectedAuthenticationMessage,
|
||||
)
|
||||
} else if !e.Expects.Rejected && resultWasAuthRejected {
|
||||
// TODO: handle this failed example
|
||||
plog.Warning("FederationDomain identity provider transformations example failed: expected authentication not to be rejected but it was rejected",
|
||||
"federationDomain", federationDomain.Name,
|
||||
"idpDisplayName", idp.DisplayName,
|
||||
"exampleIndex", idx,
|
||||
"expectedRejected", e.Expects.Rejected,
|
||||
"actualRejectedResult", resultWasAuthRejected,
|
||||
"expectedMessage", e.Expects.Message,
|
||||
"actualMessageResult", result.RejectedAuthenticationMessage,
|
||||
)
|
||||
} else if e.Expects.Rejected && resultWasAuthRejected && e.Expects.Message != result.RejectedAuthenticationMessage {
|
||||
// TODO: when expected message is blank, then treat it like it expects the default message
|
||||
// TODO: handle this failed example
|
||||
plog.Warning("FederationDomain identity provider transformations example failed: expected a different authentication rejection message",
|
||||
"federationDomain", federationDomain.Name,
|
||||
"idpDisplayName", idp.DisplayName,
|
||||
"exampleIndex", idx,
|
||||
"expectedRejected", e.Expects.Rejected,
|
||||
"actualRejectedResult", resultWasAuthRejected,
|
||||
"expectedMessage", e.Expects.Message,
|
||||
"actualMessageResult", result.RejectedAuthenticationMessage,
|
||||
)
|
||||
} else if result.AuthenticationAllowed {
|
||||
// In the case where the user expected the auth to be allowed and it was allowed, then compare
|
||||
// the expected username and group names to the actual username and group names.
|
||||
// TODO: when both of these fail, put both errors onto the status (not just the first one)
|
||||
if e.Expects.Username != result.Username {
|
||||
// TODO: handle this failed example
|
||||
plog.Warning("FederationDomain identity provider transformations example failed: expected a different transformed username",
|
||||
"federationDomain", federationDomain.Name,
|
||||
"idpDisplayName", idp.DisplayName,
|
||||
"exampleIndex", idx,
|
||||
"expectedUsername", e.Expects.Username,
|
||||
"actualUsernameResult", result.Username,
|
||||
)
|
||||
}
|
||||
if !stringSlicesEqual(e.Expects.Groups, result.Groups) {
|
||||
// TODO: Do we need to make this insensitive to ordering, or should the transformations evaluator be changed to always return sorted group names at the end of the pipeline?
|
||||
// TODO: What happens if the user did not write any group expectation? Treat it like expecting any empty list of groups?
|
||||
// TODO: handle this failed example
|
||||
plog.Warning("FederationDomain identity provider transformations example failed: expected a different transformed groups list",
|
||||
"federationDomain", federationDomain.Name,
|
||||
"idpDisplayName", idp.DisplayName,
|
||||
"exampleIndex", idx,
|
||||
"expectedGroups", e.Expects.Groups,
|
||||
"actualGroupsResult", result.Groups,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// For each valid IDP (unique displayName, valid objectRef + valid transforms), add it to the list.
|
||||
federationDomainIdentityProviders = append(federationDomainIdentityProviders, &provider.FederationDomainIdentityProvider{
|
||||
DisplayName: idp.DisplayName,
|
||||
UID: idpResourceUID,
|
||||
Transforms: pipeline,
|
||||
})
|
||||
plog.Debug("loaded FederationDomain identity provider",
|
||||
"federationDomain", federationDomain.Name,
|
||||
"identityProviderDisplayName", idp.DisplayName,
|
||||
"identityProviderResourceUID", idpResourceUID,
|
||||
)
|
||||
}
|
||||
|
||||
// Now that we have the list of IDPs for this FederationDomain, create the issuer.
|
||||
var federationDomainIssuer *provider.FederationDomainIssuer
|
||||
err = nil
|
||||
if defaultFederationDomainIdentityProvider != nil {
|
||||
// This is the constructor for the backwards compatibility mode.
|
||||
federationDomainIssuer, err = provider.NewFederationDomainIssuerWithDefaultIDP(federationDomain.Spec.Issuer, defaultFederationDomainIdentityProvider)
|
||||
} else {
|
||||
// This is the constructor for any other case, including when there is an empty list of IDPs.
|
||||
federationDomainIssuer, err = provider.NewFederationDomainIssuer(federationDomain.Spec.Issuer, federationDomainIdentityProviders)
|
||||
}
|
||||
if err != nil {
|
||||
// Note that the FederationDomainIssuer constructors validate the Issuer URL.
|
||||
if err := c.updateStatus(
|
||||
ctx.Context,
|
||||
federationDomain.Namespace,
|
||||
@@ -176,6 +443,18 @@ func (c *federationDomainWatcherController) Sync(ctx controllerlib.Context) erro
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func stringSlicesEqual(a []string, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i, itemFromA := range a {
|
||||
if b[i] != itemFromA {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *federationDomainWatcherController) updateStatus(
|
||||
ctx context.Context,
|
||||
namespace, name string,
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"go.pinniped.dev/internal/controller/conditionsutil"
|
||||
"go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers"
|
||||
"go.pinniped.dev/internal/controllerlib"
|
||||
"go.pinniped.dev/internal/oidc/provider"
|
||||
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
|
||||
"go.pinniped.dev/internal/plog"
|
||||
"go.pinniped.dev/internal/upstreamldap"
|
||||
)
|
||||
@@ -133,7 +133,7 @@ func (s *ldapUpstreamGenericLDAPStatus) Conditions() []metav1.Condition {
|
||||
|
||||
// UpstreamLDAPIdentityProviderICache is a thread safe cache that holds a list of validated upstream LDAP IDP configurations.
|
||||
type UpstreamLDAPIdentityProviderICache interface {
|
||||
SetLDAPIdentityProviders([]provider.UpstreamLDAPIdentityProviderI)
|
||||
SetLDAPIdentityProviders([]upstreamprovider.UpstreamLDAPIdentityProviderI)
|
||||
}
|
||||
|
||||
type ldapWatcherController struct {
|
||||
@@ -207,7 +207,7 @@ func (c *ldapWatcherController) Sync(ctx controllerlib.Context) error {
|
||||
}
|
||||
|
||||
requeue := false
|
||||
validatedUpstreams := make([]provider.UpstreamLDAPIdentityProviderI, 0, len(actualUpstreams))
|
||||
validatedUpstreams := make([]upstreamprovider.UpstreamLDAPIdentityProviderI, 0, len(actualUpstreams))
|
||||
for _, upstream := range actualUpstreams {
|
||||
valid, requestedRequeue := c.validateUpstream(ctx.Context, upstream)
|
||||
if valid != nil {
|
||||
@@ -226,7 +226,7 @@ func (c *ldapWatcherController) Sync(ctx controllerlib.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ldapWatcherController) validateUpstream(ctx context.Context, upstream *v1alpha1.LDAPIdentityProvider) (p provider.UpstreamLDAPIdentityProviderI, requeue bool) {
|
||||
func (c *ldapWatcherController) validateUpstream(ctx context.Context, upstream *v1alpha1.LDAPIdentityProvider) (p upstreamprovider.UpstreamLDAPIdentityProviderI, requeue bool) {
|
||||
spec := upstream.Spec
|
||||
|
||||
config := &upstreamldap.ProviderConfig{
|
||||
|
||||
+2
-1
@@ -30,6 +30,7 @@ import (
|
||||
"go.pinniped.dev/internal/endpointaddr"
|
||||
"go.pinniped.dev/internal/mocks/mockldapconn"
|
||||
"go.pinniped.dev/internal/oidc/provider"
|
||||
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
|
||||
"go.pinniped.dev/internal/testutil"
|
||||
"go.pinniped.dev/internal/upstreamldap"
|
||||
)
|
||||
@@ -1139,7 +1140,7 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) {
|
||||
fakeKubeClient := fake.NewSimpleClientset(tt.inputSecrets...)
|
||||
kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0)
|
||||
cache := provider.NewDynamicUpstreamIDPProvider()
|
||||
cache.SetLDAPIdentityProviders([]provider.UpstreamLDAPIdentityProviderI{
|
||||
cache.SetLDAPIdentityProviders([]upstreamprovider.UpstreamLDAPIdentityProviderI{
|
||||
upstreamldap.New(upstreamldap.ProviderConfig{Name: "initial-entry"}),
|
||||
})
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import (
|
||||
"go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers"
|
||||
"go.pinniped.dev/internal/controllerlib"
|
||||
"go.pinniped.dev/internal/net/phttp"
|
||||
"go.pinniped.dev/internal/oidc/provider"
|
||||
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
|
||||
"go.pinniped.dev/internal/plog"
|
||||
"go.pinniped.dev/internal/upstreamoidc"
|
||||
)
|
||||
@@ -91,7 +91,7 @@ var (
|
||||
|
||||
// UpstreamOIDCIdentityProviderICache is a thread safe cache that holds a list of validated upstream OIDC IDP configurations.
|
||||
type UpstreamOIDCIdentityProviderICache interface {
|
||||
SetOIDCIdentityProviders([]provider.UpstreamOIDCIdentityProviderI)
|
||||
SetOIDCIdentityProviders([]upstreamprovider.UpstreamOIDCIdentityProviderI)
|
||||
}
|
||||
|
||||
// lruValidatorCache caches the *oidc.Provider associated with a particular issuer/TLS configuration.
|
||||
@@ -175,13 +175,13 @@ func (c *oidcWatcherController) Sync(ctx controllerlib.Context) error {
|
||||
}
|
||||
|
||||
requeue := false
|
||||
validatedUpstreams := make([]provider.UpstreamOIDCIdentityProviderI, 0, len(actualUpstreams))
|
||||
validatedUpstreams := make([]upstreamprovider.UpstreamOIDCIdentityProviderI, 0, len(actualUpstreams))
|
||||
for _, upstream := range actualUpstreams {
|
||||
valid := c.validateUpstream(ctx, upstream)
|
||||
if valid == nil {
|
||||
requeue = true
|
||||
} else {
|
||||
validatedUpstreams = append(validatedUpstreams, provider.UpstreamOIDCIdentityProviderI(valid))
|
||||
validatedUpstreams = append(validatedUpstreams, upstreamprovider.UpstreamOIDCIdentityProviderI(valid))
|
||||
}
|
||||
}
|
||||
c.cache.SetOIDCIdentityProviders(validatedUpstreams)
|
||||
|
||||
+3
-2
@@ -29,6 +29,7 @@ import (
|
||||
"go.pinniped.dev/internal/certauthority"
|
||||
"go.pinniped.dev/internal/controllerlib"
|
||||
"go.pinniped.dev/internal/oidc/provider"
|
||||
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
|
||||
"go.pinniped.dev/internal/plog"
|
||||
"go.pinniped.dev/internal/testutil"
|
||||
"go.pinniped.dev/internal/testutil/oidctestutil"
|
||||
@@ -81,7 +82,7 @@ func TestOIDCUpstreamWatcherControllerFilterSecret(t *testing.T) {
|
||||
fakeKubeClient := fake.NewSimpleClientset()
|
||||
kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0)
|
||||
cache := provider.NewDynamicUpstreamIDPProvider()
|
||||
cache.SetOIDCIdentityProviders([]provider.UpstreamOIDCIdentityProviderI{
|
||||
cache.SetOIDCIdentityProviders([]upstreamprovider.UpstreamOIDCIdentityProviderI{
|
||||
&upstreamoidc.ProviderConfig{Name: "initial-entry"},
|
||||
})
|
||||
secretInformer := kubeInformers.Core().V1().Secrets()
|
||||
@@ -1416,7 +1417,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + testIs
|
||||
kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0)
|
||||
testLog := testlogger.NewLegacy(t) //nolint:staticcheck // old test with lots of log statements
|
||||
cache := provider.NewDynamicUpstreamIDPProvider()
|
||||
cache.SetOIDCIdentityProviders([]provider.UpstreamOIDCIdentityProviderI{
|
||||
cache.SetOIDCIdentityProviders([]upstreamprovider.UpstreamOIDCIdentityProviderI{
|
||||
&upstreamoidc.ProviderConfig{Name: "initial-entry"},
|
||||
})
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
|
||||
"go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1"
|
||||
"go.pinniped.dev/internal/constable"
|
||||
"go.pinniped.dev/internal/oidc/provider"
|
||||
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
|
||||
"go.pinniped.dev/internal/plog"
|
||||
"go.pinniped.dev/internal/upstreamldap"
|
||||
)
|
||||
@@ -365,7 +365,7 @@ func validateAndSetLDAPServerConnectivityAndSearchBase(
|
||||
return ldapConnectionValidCondition, searchBaseFoundCondition
|
||||
}
|
||||
|
||||
func EvaluateConditions(conditions GradatedConditions, config *upstreamldap.ProviderConfig) (provider.UpstreamLDAPIdentityProviderI, bool) {
|
||||
func EvaluateConditions(conditions GradatedConditions, config *upstreamldap.ProviderConfig) (upstreamprovider.UpstreamLDAPIdentityProviderI, bool) {
|
||||
for _, gradatedCondition := range conditions.gradatedConditions {
|
||||
if gradatedCondition.condition.Status != metav1.ConditionTrue && gradatedCondition.isFatal {
|
||||
// Invalid provider, so do not load it into the cache.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package supervisorstorage
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"go.pinniped.dev/internal/fositestorage/pkce"
|
||||
"go.pinniped.dev/internal/fositestorage/refreshtoken"
|
||||
"go.pinniped.dev/internal/oidc/provider"
|
||||
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
|
||||
"go.pinniped.dev/internal/plog"
|
||||
"go.pinniped.dev/internal/psession"
|
||||
)
|
||||
@@ -43,7 +44,7 @@ type garbageCollectorController struct {
|
||||
|
||||
// UpstreamOIDCIdentityProviderICache is a thread safe cache that holds a list of validated upstream OIDC IDP configurations.
|
||||
type UpstreamOIDCIdentityProviderICache interface {
|
||||
GetOIDCIdentityProviders() []provider.UpstreamOIDCIdentityProviderI
|
||||
GetOIDCIdentityProviders() []upstreamprovider.UpstreamOIDCIdentityProviderI
|
||||
}
|
||||
|
||||
func GarbageCollectorController(
|
||||
@@ -244,7 +245,7 @@ func (c *garbageCollectorController) tryRevokeUpstreamOIDCToken(ctx context.Cont
|
||||
}
|
||||
|
||||
// Try to find the provider that was originally used to create the stored session.
|
||||
var foundOIDCIdentityProviderI provider.UpstreamOIDCIdentityProviderI
|
||||
var foundOIDCIdentityProviderI upstreamprovider.UpstreamOIDCIdentityProviderI
|
||||
for _, p := range c.idpCache.GetOIDCIdentityProviders() {
|
||||
if p.GetName() == customSessionData.ProviderName && p.GetResourceUID() == customSessionData.ProviderUID {
|
||||
foundOIDCIdentityProviderI = p
|
||||
@@ -260,7 +261,7 @@ func (c *garbageCollectorController) tryRevokeUpstreamOIDCToken(ctx context.Cont
|
||||
upstreamAccessToken := customSessionData.OIDC.UpstreamAccessToken
|
||||
|
||||
if upstreamRefreshToken != "" {
|
||||
err := foundOIDCIdentityProviderI.RevokeToken(ctx, upstreamRefreshToken, provider.RefreshTokenType)
|
||||
err := foundOIDCIdentityProviderI.RevokeToken(ctx, upstreamRefreshToken, upstreamprovider.RefreshTokenType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -268,7 +269,7 @@ func (c *garbageCollectorController) tryRevokeUpstreamOIDCToken(ctx context.Cont
|
||||
}
|
||||
|
||||
if upstreamAccessToken != "" {
|
||||
err := foundOIDCIdentityProviderI.RevokeToken(ctx, upstreamAccessToken, provider.AccessTokenType)
|
||||
err := foundOIDCIdentityProviderI.RevokeToken(ctx, upstreamAccessToken, upstreamprovider.AccessTokenType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package supervisorstorage
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"go.pinniped.dev/internal/fositestorage/refreshtoken"
|
||||
"go.pinniped.dev/internal/oidc/clientregistry"
|
||||
"go.pinniped.dev/internal/oidc/provider"
|
||||
"go.pinniped.dev/internal/oidc/provider/upstreamprovider"
|
||||
"go.pinniped.dev/internal/psession"
|
||||
"go.pinniped.dev/internal/testutil"
|
||||
"go.pinniped.dev/internal/testutil/oidctestutil"
|
||||
@@ -369,7 +370,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
&oidctestutil.RevokeTokenArgs{
|
||||
Ctx: syncContext.Context,
|
||||
Token: "fake-upstream-refresh-token",
|
||||
TokenType: provider.RefreshTokenType,
|
||||
TokenType: upstreamprovider.RefreshTokenType,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -493,7 +494,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
&oidctestutil.RevokeTokenArgs{
|
||||
Ctx: syncContext.Context,
|
||||
Token: "fake-upstream-access-token",
|
||||
TokenType: provider.AccessTokenType,
|
||||
TokenType: upstreamprovider.AccessTokenType,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -785,7 +786,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
&oidctestutil.RevokeTokenArgs{
|
||||
Ctx: syncContext.Context,
|
||||
Token: "fake-upstream-refresh-token",
|
||||
TokenType: provider.RefreshTokenType,
|
||||
TokenType: upstreamprovider.RefreshTokenType,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -810,7 +811,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
&oidctestutil.RevokeTokenArgs{
|
||||
Ctx: syncContext.Context,
|
||||
Token: "fake-upstream-refresh-token",
|
||||
TokenType: provider.RefreshTokenType,
|
||||
TokenType: upstreamprovider.RefreshTokenType,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -889,7 +890,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
&oidctestutil.RevokeTokenArgs{
|
||||
Ctx: syncContext.Context,
|
||||
Token: "fake-upstream-refresh-token",
|
||||
TokenType: provider.RefreshTokenType,
|
||||
TokenType: upstreamprovider.RefreshTokenType,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1012,7 +1013,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
&oidctestutil.RevokeTokenArgs{
|
||||
Ctx: syncContext.Context,
|
||||
Token: "fake-upstream-refresh-token",
|
||||
TokenType: provider.RefreshTokenType,
|
||||
TokenType: upstreamprovider.RefreshTokenType,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1136,7 +1137,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
&oidctestutil.RevokeTokenArgs{
|
||||
Ctx: syncContext.Context,
|
||||
Token: "fake-upstream-access-token",
|
||||
TokenType: provider.AccessTokenType,
|
||||
TokenType: upstreamprovider.AccessTokenType,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1214,7 +1215,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
&oidctestutil.RevokeTokenArgs{
|
||||
Ctx: syncContext.Context,
|
||||
Token: "fake-upstream-refresh-token",
|
||||
TokenType: provider.RefreshTokenType,
|
||||
TokenType: upstreamprovider.RefreshTokenType,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1291,7 +1292,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
&oidctestutil.RevokeTokenArgs{
|
||||
Ctx: syncContext.Context,
|
||||
Token: "fake-upstream-access-token",
|
||||
TokenType: provider.AccessTokenType,
|
||||
TokenType: upstreamprovider.AccessTokenType,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package controller
|
||||
@@ -18,6 +18,16 @@ func NameAndNamespaceExactMatchFilterFactory(name, namespace string) controllerl
|
||||
}, nil)
|
||||
}
|
||||
|
||||
// MatchAnythingIgnoringUpdatesFilter returns a controllerlib.Filter that allows all objects but ignores updates.
|
||||
func MatchAnythingIgnoringUpdatesFilter(parentFunc controllerlib.ParentFunc) controllerlib.Filter {
|
||||
return controllerlib.FilterFuncs{
|
||||
AddFunc: func(object metav1.Object) bool { return true },
|
||||
UpdateFunc: func(oldObj, newObj metav1.Object) bool { return false },
|
||||
DeleteFunc: func(object metav1.Object) bool { return true },
|
||||
ParentFunc: parentFunc,
|
||||
}
|
||||
}
|
||||
|
||||
// MatchAnythingFilter returns a controllerlib.Filter that allows all objects.
|
||||
func MatchAnythingFilter(parentFunc controllerlib.ParentFunc) controllerlib.Filter {
|
||||
return SimpleFilter(func(object metav1.Object) bool { return true }, parentFunc)
|
||||
|
||||
Reference in New Issue
Block a user