mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-09-06 16:17:08 +00:00
Merge branch 'main' into jtc/add-importas-linter
This commit is contained in:
@@ -4,16 +4,6 @@
|
||||
// Package authenticator contains helper code for dealing with *Authenticator CRDs.
|
||||
package authenticator
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/client-go/util/cert"
|
||||
|
||||
authenticationv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
|
||||
)
|
||||
|
||||
// Closer is a type that can be closed idempotently.
|
||||
//
|
||||
// This type is slightly different from io.Closer, because io.Closer can return an error and is not
|
||||
@@ -21,24 +11,3 @@ import (
|
||||
type Closer interface {
|
||||
Close()
|
||||
}
|
||||
|
||||
// CABundle returns a PEM-encoded CA bundle from the provided spec. If the provided spec is nil, a
|
||||
// nil CA bundle will be returned. If the provided spec contains a CA bundle that is not properly
|
||||
// encoded, an error will be returned.
|
||||
func CABundle(spec *authenticationv1alpha1.TLSSpec) (*x509.CertPool, []byte, error) {
|
||||
if spec == nil || len(spec.CertificateAuthorityData) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
pem, err := base64.StdEncoding.DecodeString(spec.CertificateAuthorityData)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rootCAs, err := cert.NewPoolFromBytes(pem)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("certificateAuthorityData is not valid PEM: %w", err)
|
||||
}
|
||||
|
||||
return rootCAs, pem, nil
|
||||
}
|
||||
|
||||
@@ -246,7 +246,7 @@ func (c *jwtCacheFillerController) extractValueAsJWTAuthenticator(value authncac
|
||||
}
|
||||
|
||||
func (c *jwtCacheFillerController) validateTLS(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []*metav1.Condition, bool) {
|
||||
rootCAs, _, err := pinnipedauthenticator.CABundle(tlsSpec)
|
||||
rootCAs, _, err := pinnipedcontroller.BuildCertPoolAuth(tlsSpec)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("%s: %s", "invalid TLS configuration", err.Error())
|
||||
conditions = append(conditions, &metav1.Condition{
|
||||
@@ -603,7 +603,7 @@ func (c *jwtCacheFillerController) updateStatus(
|
||||
})
|
||||
}
|
||||
|
||||
_ = conditionsutil.MergeConfigConditions(
|
||||
_ = conditionsutil.MergeConditions(
|
||||
conditions,
|
||||
original.Generation,
|
||||
&updated.Status.Conditions,
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
conciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
|
||||
authinformers "go.pinniped.dev/generated/latest/client/concierge/informers/externalversions/authentication/v1alpha1"
|
||||
pinnipedcontroller "go.pinniped.dev/internal/controller"
|
||||
pinnipedauthenticator "go.pinniped.dev/internal/controller/authenticator"
|
||||
"go.pinniped.dev/internal/controller/authenticator/authncache"
|
||||
"go.pinniped.dev/internal/controller/conditionsutil"
|
||||
"go.pinniped.dev/internal/controllerlib"
|
||||
@@ -265,7 +264,7 @@ func (c *webhookCacheFillerController) validateConnection(certPool *x509.CertPoo
|
||||
}
|
||||
|
||||
func (c *webhookCacheFillerController) validateTLSBundle(tlsSpec *authenticationv1alpha1.TLSSpec, conditions []*metav1.Condition) (*x509.CertPool, []byte, []*metav1.Condition, bool) {
|
||||
rootCAs, pemBytes, err := pinnipedauthenticator.CABundle(tlsSpec)
|
||||
rootCAs, pemBytes, err := pinnipedcontroller.BuildCertPoolAuth(tlsSpec)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("%s: %s", "invalid TLS configuration", err.Error())
|
||||
conditions = append(conditions, &metav1.Condition{
|
||||
@@ -360,7 +359,7 @@ func (c *webhookCacheFillerController) updateStatus(
|
||||
})
|
||||
}
|
||||
|
||||
_ = conditionsutil.MergeConfigConditions(
|
||||
_ = conditionsutil.MergeConditions(
|
||||
conditions,
|
||||
original.Generation,
|
||||
&updated.Status.Conditions,
|
||||
|
||||
@@ -12,29 +12,34 @@ import (
|
||||
"go.pinniped.dev/internal/plog"
|
||||
)
|
||||
|
||||
// MergeIDPConditions merges conditions into conditionsToUpdate. If returns true if it merged any error conditions.
|
||||
func MergeIDPConditions(conditions []*metav1.Condition, observedGeneration int64, conditionsToUpdate *[]metav1.Condition, log plog.MinLogger) bool {
|
||||
hadErrorCondition := false
|
||||
// MergeConditions merges conditions into conditionsToUpdate.
|
||||
// Note that LastTransitionTime refers to the time when the status changed,
|
||||
// but ObservedGeneration should be the current generation for all conditions, since Pinniped should always check every condition.
|
||||
// It returns true if any resulting condition has non-true status.
|
||||
func MergeConditions(
|
||||
conditions []*metav1.Condition,
|
||||
observedGeneration int64,
|
||||
conditionsToUpdate *[]metav1.Condition,
|
||||
log plog.MinLogger,
|
||||
lastTransitionTime metav1.Time,
|
||||
) bool {
|
||||
for i := range conditions {
|
||||
cond := conditions[i].DeepCopy()
|
||||
cond.LastTransitionTime = metav1.Now()
|
||||
cond.LastTransitionTime = lastTransitionTime
|
||||
cond.ObservedGeneration = observedGeneration
|
||||
if mergeIDPCondition(conditionsToUpdate, cond) {
|
||||
if mergeCondition(conditionsToUpdate, cond) {
|
||||
log.Info("updated condition", "type", cond.Type, "status", cond.Status, "reason", cond.Reason, "message", cond.Message)
|
||||
}
|
||||
if cond.Status == metav1.ConditionFalse {
|
||||
hadErrorCondition = true
|
||||
}
|
||||
}
|
||||
sort.SliceStable(*conditionsToUpdate, func(i, j int) bool {
|
||||
return (*conditionsToUpdate)[i].Type < (*conditionsToUpdate)[j].Type
|
||||
})
|
||||
return hadErrorCondition
|
||||
return HadErrorCondition(conditions)
|
||||
}
|
||||
|
||||
// mergeIDPCondition merges a new metav1.Condition into a slice of existing conditions. It returns true
|
||||
// mergeCondition merges a new metav1.Condition into a slice of existing conditions. It returns true
|
||||
// if the condition has meaningfully changed.
|
||||
func mergeIDPCondition(existing *[]metav1.Condition, new *metav1.Condition) bool {
|
||||
func mergeCondition(existing *[]metav1.Condition, new *metav1.Condition) bool {
|
||||
// Find any existing condition with a matching type.
|
||||
var old *metav1.Condition
|
||||
for i := range *existing {
|
||||
@@ -62,61 +67,7 @@ func mergeIDPCondition(existing *[]metav1.Condition, new *metav1.Condition) bool
|
||||
return true
|
||||
}
|
||||
|
||||
// Otherwise the entry is already up to date.
|
||||
return false
|
||||
}
|
||||
|
||||
// MergeConfigConditions merges conditions into conditionsToUpdate. It returns true if it merged any error conditions.
|
||||
func MergeConfigConditions(conditions []*metav1.Condition, observedGeneration int64, conditionsToUpdate *[]metav1.Condition, log plog.MinLogger, now metav1.Time) bool {
|
||||
hadErrorCondition := false
|
||||
for i := range conditions {
|
||||
cond := conditions[i].DeepCopy()
|
||||
cond.LastTransitionTime = now
|
||||
cond.ObservedGeneration = observedGeneration
|
||||
if mergeConfigCondition(conditionsToUpdate, cond) {
|
||||
log.Info("updated condition", "type", cond.Type, "status", cond.Status, "reason", cond.Reason, "message", cond.Message)
|
||||
}
|
||||
if cond.Status == metav1.ConditionFalse {
|
||||
hadErrorCondition = true
|
||||
}
|
||||
}
|
||||
sort.SliceStable(*conditionsToUpdate, func(i, j int) bool {
|
||||
return (*conditionsToUpdate)[i].Type < (*conditionsToUpdate)[j].Type
|
||||
})
|
||||
return hadErrorCondition
|
||||
}
|
||||
|
||||
// mergeConfigCondition merges a new metav1.Condition into a slice of existing conditions. It returns true
|
||||
// if the condition has meaningfully changed.
|
||||
func mergeConfigCondition(existing *[]metav1.Condition, new *metav1.Condition) bool {
|
||||
// Find any existing condition with a matching type.
|
||||
var old *metav1.Condition
|
||||
for i := range *existing {
|
||||
if (*existing)[i].Type == new.Type {
|
||||
old = &(*existing)[i]
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no existing condition of this type, append this one and we're done.
|
||||
if old == nil {
|
||||
*existing = append(*existing, *new)
|
||||
return true
|
||||
}
|
||||
|
||||
// Set the LastTransitionTime depending on whether the status has changed.
|
||||
new = new.DeepCopy()
|
||||
if old.Status == new.Status {
|
||||
new.LastTransitionTime = old.LastTransitionTime
|
||||
}
|
||||
|
||||
// If anything has actually changed, update the entry and return true.
|
||||
if !equality.Semantic.DeepEqual(old, new) {
|
||||
*old = *new
|
||||
return true
|
||||
}
|
||||
|
||||
// Otherwise the entry is already up to date.
|
||||
// Otherwise the entry is already up-to-date.
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package conditionsutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"go.pinniped.dev/internal/plog"
|
||||
)
|
||||
|
||||
func TestMergeIDPConditions(t *testing.T) {
|
||||
twoHoursAgo := metav1.Time{Time: time.Now().Add(-2 * time.Hour)}
|
||||
oneHourAgo := metav1.Time{Time: time.Now().Add(-1 * time.Hour)}
|
||||
testTime := metav1.Now()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
newConditions []*metav1.Condition
|
||||
conditionsToUpdate *[]metav1.Condition
|
||||
observedGeneration int64
|
||||
wantResult bool
|
||||
wantLogSnippets []string
|
||||
wantConditions []metav1.Condition
|
||||
}{
|
||||
{
|
||||
name: "Adding a new condition with status=True returns false",
|
||||
newConditions: []*metav1.Condition{
|
||||
{
|
||||
Type: "NewType",
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "new reason",
|
||||
Message: "new message",
|
||||
},
|
||||
},
|
||||
observedGeneration: int64(999),
|
||||
conditionsToUpdate: &[]metav1.Condition{},
|
||||
wantLogSnippets: []string{
|
||||
`"message":"updated condition","type":"NewType","status":"True"`,
|
||||
},
|
||||
wantConditions: []metav1.Condition{
|
||||
{
|
||||
Type: "NewType",
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: int64(999),
|
||||
LastTransitionTime: testTime,
|
||||
Reason: "new reason",
|
||||
Message: "new message",
|
||||
},
|
||||
},
|
||||
wantResult: false,
|
||||
},
|
||||
{
|
||||
name: "Updating a condition status from False to True returns true",
|
||||
newConditions: []*metav1.Condition{
|
||||
{
|
||||
Type: "UnchangedType",
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "unchanged reason",
|
||||
Message: "unchanged message",
|
||||
},
|
||||
{
|
||||
Type: "FalseToTrueType",
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "new reason",
|
||||
Message: "new message",
|
||||
},
|
||||
{
|
||||
Type: "NewType",
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: "new reason",
|
||||
Message: "new message",
|
||||
},
|
||||
},
|
||||
conditionsToUpdate: &[]metav1.Condition{
|
||||
{
|
||||
Type: "UnchangedType",
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: int64(10),
|
||||
LastTransitionTime: twoHoursAgo,
|
||||
Reason: "unchanged reason",
|
||||
Message: "unchanged message",
|
||||
},
|
||||
{
|
||||
Type: "FalseToTrueType",
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: int64(5),
|
||||
LastTransitionTime: oneHourAgo,
|
||||
Reason: "old reason",
|
||||
Message: "old message",
|
||||
},
|
||||
},
|
||||
observedGeneration: int64(100),
|
||||
wantLogSnippets: []string{
|
||||
`"message":"updated condition","type":"UnchangedType","status":"True"`,
|
||||
`"message":"updated condition","type":"NewType","status":"True"`,
|
||||
`"message":"updated condition","type":"FalseToTrueType","status":"False"`,
|
||||
},
|
||||
wantConditions: []metav1.Condition{
|
||||
{
|
||||
Type: "FalseToTrueType",
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: int64(100),
|
||||
LastTransitionTime: testTime,
|
||||
Reason: "new reason",
|
||||
Message: "new message",
|
||||
},
|
||||
{
|
||||
Type: "NewType",
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: int64(100),
|
||||
LastTransitionTime: testTime,
|
||||
Reason: "new reason",
|
||||
Message: "new message",
|
||||
},
|
||||
{
|
||||
Type: "UnchangedType",
|
||||
Status: metav1.ConditionTrue,
|
||||
ObservedGeneration: int64(100),
|
||||
LastTransitionTime: twoHoursAgo,
|
||||
Reason: "unchanged reason",
|
||||
Message: "unchanged message",
|
||||
},
|
||||
},
|
||||
wantResult: true,
|
||||
},
|
||||
{
|
||||
name: "No logs when ObservedGeneration is unchanged",
|
||||
newConditions: []*metav1.Condition{
|
||||
{
|
||||
Type: "UnchangedType",
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "unchanged reason",
|
||||
Message: "unchanged message",
|
||||
},
|
||||
},
|
||||
conditionsToUpdate: &[]metav1.Condition{
|
||||
{
|
||||
Type: "UnchangedType",
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: int64(10),
|
||||
LastTransitionTime: twoHoursAgo,
|
||||
Reason: "unchanged reason",
|
||||
Message: "unchanged message",
|
||||
},
|
||||
},
|
||||
observedGeneration: int64(10),
|
||||
wantConditions: []metav1.Condition{
|
||||
{
|
||||
Type: "UnchangedType",
|
||||
Status: metav1.ConditionFalse,
|
||||
ObservedGeneration: int64(10),
|
||||
LastTransitionTime: twoHoursAgo,
|
||||
Reason: "unchanged reason",
|
||||
Message: "unchanged message",
|
||||
},
|
||||
},
|
||||
wantResult: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var log bytes.Buffer
|
||||
logger := plog.TestLogger(t, &log)
|
||||
|
||||
result := MergeConditions(
|
||||
tt.newConditions,
|
||||
tt.observedGeneration,
|
||||
tt.conditionsToUpdate,
|
||||
logger,
|
||||
testTime,
|
||||
)
|
||||
|
||||
logString := log.String()
|
||||
require.Equal(t, len(tt.wantLogSnippets), strings.Count(logString, "\n"))
|
||||
for _, wantLog := range tt.wantLogSnippets {
|
||||
require.Contains(t, logString, wantLog)
|
||||
}
|
||||
require.Equal(t, tt.wantResult, result)
|
||||
require.Equal(t, tt.wantConditions, *tt.conditionsToUpdate)
|
||||
})
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -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, upstreamprovider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
|
||||
pwdLastSetAttribute: attributeUnchangedSinceLogin(pwdLastSetAttribute),
|
||||
userAccountControlAttribute: validUserAccountControl,
|
||||
userAccountControlComputedAttribute: validComputedUserAccountControl,
|
||||
@@ -368,7 +368,7 @@ func (c *activeDirectoryWatcherController) updateStatus(ctx context.Context, ups
|
||||
log := plog.WithValues("namespace", upstream.Namespace, "name", upstream.Name)
|
||||
updated := upstream.DeepCopy()
|
||||
|
||||
hadErrorCondition := conditionsutil.MergeIDPConditions(conditions, upstream.Generation, &updated.Status.Conditions, log)
|
||||
hadErrorCondition := conditionsutil.MergeConditions(conditions, upstream.Generation, &updated.Status.Conditions, log, metav1.Now())
|
||||
|
||||
updated.Status.Phase = idpv1alpha1.ActiveDirectoryPhaseReady
|
||||
if hadErrorCondition {
|
||||
@@ -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, _ upstreamprovider.RefreshAttributes) error {
|
||||
var validUserAccountControl = func(entry *ldap.Entry, _ upstreamprovider.LDAPRefreshAttributes) error {
|
||||
userAccountControl, err := strconv.Atoi(entry.GetAttributeValue(userAccountControlAttribute))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -459,7 +459,7 @@ var validUserAccountControl = func(entry *ldap.Entry, _ upstreamprovider.Refresh
|
||||
}
|
||||
|
||||
//nolint:gochecknoglobals // this needs to be a global variable so that tests can check pointer equality
|
||||
var validComputedUserAccountControl = func(entry *ldap.Entry, _ upstreamprovider.RefreshAttributes) error {
|
||||
var validComputedUserAccountControl = func(entry *ldap.Entry, _ upstreamprovider.LDAPRefreshAttributes) error {
|
||||
userAccountControl, err := strconv.Atoi(entry.GetAttributeValue(userAccountControlComputedAttribute))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -473,8 +473,8 @@ var validComputedUserAccountControl = func(entry *ldap.Entry, _ upstreamprovider
|
||||
}
|
||||
|
||||
//nolint:gochecknoglobals // this needs to be a global variable so that tests can check pointer equality
|
||||
var attributeUnchangedSinceLogin = func(attribute string) func(*ldap.Entry, upstreamprovider.RefreshAttributes) error {
|
||||
return func(entry *ldap.Entry, storedAttributes upstreamprovider.RefreshAttributes) error {
|
||||
var attributeUnchangedSinceLogin = func(attribute string) func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error {
|
||||
return func(entry *ldap.Entry, storedAttributes upstreamprovider.LDAPRefreshAttributes) error {
|
||||
prevAttributeValue := storedAttributes.AdditionalAttributes[attribute]
|
||||
newValues := entry.GetRawAttributeValues(attribute)
|
||||
|
||||
|
||||
+20
-20
@@ -228,7 +228,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, upstreamprovider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -571,7 +571,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, upstreamprovider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -641,7 +641,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, upstreamprovider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -714,7 +714,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, upstreamprovider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -794,7 +794,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, upstreamprovider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -858,7 +858,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, upstreamprovider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1009,7 +1009,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, upstreamprovider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1159,7 +1159,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, upstreamprovider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1231,7 +1231,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, upstreamprovider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1498,7 +1498,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, upstreamprovider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1558,7 +1558,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, upstreamprovider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1622,7 +1622,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, upstreamprovider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1686,7 +1686,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, upstreamprovider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1898,7 +1898,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, upstreamprovider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -1961,7 +1961,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, upstreamprovider.RefreshAttributes) error{
|
||||
RefreshAttributeChecks: map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{
|
||||
"pwdLastSet": attributeUnchangedSinceLogin("pwdLastSet"),
|
||||
"userAccountControl": validUserAccountControl,
|
||||
"msDS-User-Account-Control-Computed": validComputedUserAccountControl,
|
||||
@@ -2102,8 +2102,8 @@ func TestActiveDirectoryUpstreamWatcherControllerSync(t *testing.T) {
|
||||
|
||||
expectedRefreshAttributeChecks := copyOfExpectedValueForResultingCache.RefreshAttributeChecks
|
||||
actualRefreshAttributeChecks := actualConfig.RefreshAttributeChecks
|
||||
copyOfExpectedValueForResultingCache.RefreshAttributeChecks = map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{}
|
||||
actualConfig.RefreshAttributeChecks = map[string]func(*ldap.Entry, upstreamprovider.RefreshAttributes) error{}
|
||||
copyOfExpectedValueForResultingCache.RefreshAttributeChecks = map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{}
|
||||
actualConfig.RefreshAttributeChecks = map[string]func(*ldap.Entry, upstreamprovider.LDAPRefreshAttributes) error{}
|
||||
require.Equal(t, len(expectedRefreshAttributeChecks), len(actualRefreshAttributeChecks))
|
||||
for k, v := range expectedRefreshAttributeChecks {
|
||||
require.NotNil(t, actualRefreshAttributeChecks[k])
|
||||
@@ -2352,7 +2352,7 @@ func TestValidUserAccountControl(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
tt := test
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := validUserAccountControl(tt.entry, upstreamprovider.RefreshAttributes{})
|
||||
err := validUserAccountControl(tt.entry, upstreamprovider.LDAPRefreshAttributes{})
|
||||
|
||||
if tt.wantErr != "" {
|
||||
require.Error(t, err)
|
||||
@@ -2413,7 +2413,7 @@ func TestValidComputedUserAccountControl(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
tt := test
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := validComputedUserAccountControl(tt.entry, upstreamprovider.RefreshAttributes{})
|
||||
err := validComputedUserAccountControl(tt.entry, upstreamprovider.LDAPRefreshAttributes{})
|
||||
|
||||
if tt.wantErr != "" {
|
||||
require.Error(t, err)
|
||||
@@ -2488,7 +2488,7 @@ func TestAttributeUnchangedSinceLogin(t *testing.T) {
|
||||
tt := test
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
initialValRawEncoded := base64.RawURLEncoding.EncodeToString([]byte(initialVal))
|
||||
err := attributeUnchangedSinceLogin(attributeName)(tt.entry, upstreamprovider.RefreshAttributes{AdditionalAttributes: map[string]string{attributeName: initialValRawEncoded}})
|
||||
err := attributeUnchangedSinceLogin(attributeName)(tt.entry, upstreamprovider.LDAPRefreshAttributes{AdditionalAttributes: map[string]string{attributeName: initialValRawEncoded}})
|
||||
if tt.wantErr != "" {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, tt.wantErr, err.Error())
|
||||
|
||||
@@ -67,6 +67,7 @@ const (
|
||||
kindLDAPIdentityProvider = "LDAPIdentityProvider"
|
||||
kindOIDCIdentityProvider = "OIDCIdentityProvider"
|
||||
kindActiveDirectoryIdentityProvider = "ActiveDirectoryIdentityProvider"
|
||||
kindGitHubIdentityProvider = "GitHubIdentityProvider"
|
||||
|
||||
celTransformerMaxExpressionRuntime = 5 * time.Second
|
||||
)
|
||||
@@ -88,6 +89,7 @@ type federationDomainWatcherController struct {
|
||||
oidcIdentityProviderInformer idpinformers.OIDCIdentityProviderInformer
|
||||
ldapIdentityProviderInformer idpinformers.LDAPIdentityProviderInformer
|
||||
activeDirectoryIdentityProviderInformer idpinformers.ActiveDirectoryIdentityProviderInformer
|
||||
githubIdentityProviderInformer idpinformers.GitHubIdentityProviderInformer
|
||||
|
||||
celTransformer *celtransformer.CELTransformer
|
||||
allowedKinds sets.Set[string]
|
||||
@@ -104,9 +106,10 @@ func NewFederationDomainWatcherController(
|
||||
oidcIdentityProviderInformer idpinformers.OIDCIdentityProviderInformer,
|
||||
ldapIdentityProviderInformer idpinformers.LDAPIdentityProviderInformer,
|
||||
activeDirectoryIdentityProviderInformer idpinformers.ActiveDirectoryIdentityProviderInformer,
|
||||
githubProviderInformer idpinformers.GitHubIdentityProviderInformer,
|
||||
withInformer pinnipedcontroller.WithInformerOptionFunc,
|
||||
) controllerlib.Controller {
|
||||
allowedKinds := sets.New(kindActiveDirectoryIdentityProvider, kindLDAPIdentityProvider, kindOIDCIdentityProvider)
|
||||
allowedKinds := sets.New(kindActiveDirectoryIdentityProvider, kindLDAPIdentityProvider, kindOIDCIdentityProvider, kindGitHubIdentityProvider)
|
||||
return controllerlib.New(
|
||||
controllerlib.Config{
|
||||
Name: controllerName,
|
||||
@@ -119,6 +122,7 @@ func NewFederationDomainWatcherController(
|
||||
oidcIdentityProviderInformer: oidcIdentityProviderInformer,
|
||||
ldapIdentityProviderInformer: ldapIdentityProviderInformer,
|
||||
activeDirectoryIdentityProviderInformer: activeDirectoryIdentityProviderInformer,
|
||||
githubIdentityProviderInformer: githubProviderInformer,
|
||||
allowedKinds: allowedKinds,
|
||||
},
|
||||
},
|
||||
@@ -148,6 +152,13 @@ func NewFederationDomainWatcherController(
|
||||
pinnipedcontroller.MatchAnythingIgnoringUpdatesFilter(pinnipedcontroller.SingletonQueue()),
|
||||
controllerlib.InformerOption{},
|
||||
),
|
||||
withInformer(
|
||||
githubProviderInformer,
|
||||
// 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{},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -264,9 +275,12 @@ func (c *federationDomainWatcherController) makeLegacyFederationDomainIssuer(
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
githubIdentityProviders, err := c.githubIdentityProviderInformer.Lister().List(labels.Everything())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// 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)
|
||||
idpCRsCount := len(oidcIdentityProviders) + len(ldapIdentityProviders) + len(activeDirectoryIdentityProviders) + len(githubIdentityProviders)
|
||||
|
||||
switch {
|
||||
case idpCRsCount == 1:
|
||||
@@ -286,6 +300,10 @@ func (c *federationDomainWatcherController) makeLegacyFederationDomainIssuer(
|
||||
defaultFederationDomainIdentityProvider.DisplayName = activeDirectoryIdentityProviders[0].Name
|
||||
defaultFederationDomainIdentityProvider.UID = activeDirectoryIdentityProviders[0].UID
|
||||
foundIDPName = activeDirectoryIdentityProviders[0].Name
|
||||
case len(githubIdentityProviders) == 1:
|
||||
defaultFederationDomainIdentityProvider.DisplayName = githubIdentityProviders[0].Name
|
||||
defaultFederationDomainIdentityProvider.UID = githubIdentityProviders[0].UID
|
||||
foundIDPName = githubIdentityProviders[0].Name
|
||||
}
|
||||
// Backwards compatibility mode always uses an empty identity transformation pipeline since no
|
||||
// transformations are defined on the FederationDomain.
|
||||
@@ -446,6 +464,8 @@ func (c *federationDomainWatcherController) findIDPsUIDByObjectRef(objectRef cor
|
||||
foundIDP, err = c.activeDirectoryIdentityProviderInformer.Lister().ActiveDirectoryIdentityProviders(namespace).Get(objectRef.Name)
|
||||
case kindOIDCIdentityProvider:
|
||||
foundIDP, err = c.oidcIdentityProviderInformer.Lister().OIDCIdentityProviders(namespace).Get(objectRef.Name)
|
||||
case kindGitHubIdentityProvider:
|
||||
foundIDP, err = c.githubIdentityProviderInformer.Lister().GitHubIdentityProviders(namespace).Get(objectRef.Name)
|
||||
default:
|
||||
// This shouldn't happen because this helper function is not called when the kind is invalid.
|
||||
return "", false, fmt.Errorf("unexpected kind: %s", objectRef.Kind)
|
||||
@@ -813,7 +833,7 @@ func (c *federationDomainWatcherController) updateStatus(
|
||||
})
|
||||
}
|
||||
|
||||
_ = conditionsutil.MergeConfigConditions(conditions,
|
||||
_ = conditionsutil.MergeConditions(conditions,
|
||||
federationDomain.Generation, &updated.Status.Conditions, plog.New().WithName(controllerName), metav1.NewTime(c.clock.Now()))
|
||||
|
||||
if equality.Semantic.DeepEqual(federationDomain, updated) {
|
||||
|
||||
@@ -42,6 +42,7 @@ func TestFederationDomainWatcherControllerInformerFilters(t *testing.T) {
|
||||
oidcIdentityProviderInformer := supervisorinformers.NewSharedInformerFactoryWithOptions(nil, 0).IDP().V1alpha1().OIDCIdentityProviders()
|
||||
ldapIdentityProviderInformer := supervisorinformers.NewSharedInformerFactoryWithOptions(nil, 0).IDP().V1alpha1().LDAPIdentityProviders()
|
||||
adIdentityProviderInformer := supervisorinformers.NewSharedInformerFactoryWithOptions(nil, 0).IDP().V1alpha1().ActiveDirectoryIdentityProviders()
|
||||
githubIdentityProviderInformer := supervisorinformers.NewSharedInformerFactoryWithOptions(nil, 0).IDP().V1alpha1().GitHubIdentityProviders()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -82,6 +83,13 @@ func TestFederationDomainWatcherControllerInformerFilters(t *testing.T) {
|
||||
wantAdd: true,
|
||||
wantUpdate: false,
|
||||
wantDelete: true,
|
||||
}, {
|
||||
name: "any GitHubIdentityProvider adds or deletes, but updates are ignored",
|
||||
obj: &idpv1alpha1.GitHubIdentityProvider{},
|
||||
informer: githubIdentityProviderInformer,
|
||||
wantAdd: true,
|
||||
wantUpdate: false,
|
||||
wantDelete: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
@@ -99,6 +107,7 @@ func TestFederationDomainWatcherControllerInformerFilters(t *testing.T) {
|
||||
oidcIdentityProviderInformer,
|
||||
ldapIdentityProviderInformer,
|
||||
adIdentityProviderInformer,
|
||||
githubIdentityProviderInformer,
|
||||
withInformer.WithInformer, // make it possible to observe the behavior of the Filters
|
||||
)
|
||||
|
||||
@@ -162,6 +171,14 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
gitHubIdentityProvider := &idpv1alpha1.GitHubIdentityProvider{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "some-github-idp",
|
||||
Namespace: namespace,
|
||||
UID: "some-github-idp",
|
||||
},
|
||||
}
|
||||
|
||||
federationDomain1 := &supervisorconfigv1alpha1.FederationDomain{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: namespace, Generation: 123},
|
||||
Spec: supervisorconfigv1alpha1.FederationDomainSpec{Issuer: "https://issuer1.com"},
|
||||
@@ -486,7 +503,7 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
||||
LastTransitionTime: time,
|
||||
Reason: "KindUnrecognized",
|
||||
Message: fmt.Sprintf(`some kinds specified by .spec.identityProviders[].objectRef.kind are `+
|
||||
`not recognized (should be one of "ActiveDirectoryIdentityProvider", "LDAPIdentityProvider", "OIDCIdentityProvider"): %s`, badKinds),
|
||||
`not recognized (should be one of "ActiveDirectoryIdentityProvider", "GitHubIdentityProvider", "LDAPIdentityProvider", "OIDCIdentityProvider"): %s`, badKinds),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -603,6 +620,30 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "legacy config: when no identity provider is specified on federation domains, but exactly one GitHub identity " +
|
||||
"provider resource exists on cluster, the controller will set a default IDP on each federation domain " +
|
||||
"matching the only identity provider found",
|
||||
inputObjects: []runtime.Object{
|
||||
federationDomain1,
|
||||
federationDomain2,
|
||||
gitHubIdentityProvider,
|
||||
},
|
||||
wantFDIssuers: []*federationdomainproviders.FederationDomainIssuer{
|
||||
federationDomainIssuerWithDefaultIDP(t, federationDomain1.Spec.Issuer, gitHubIdentityProvider.ObjectMeta),
|
||||
federationDomainIssuerWithDefaultIDP(t, federationDomain2.Spec.Issuer, gitHubIdentityProvider.ObjectMeta),
|
||||
},
|
||||
wantStatusUpdates: []*configv1alpha1.FederationDomain{
|
||||
expectedFederationDomainStatusUpdate(federationDomain1,
|
||||
configv1alpha1.FederationDomainPhaseReady,
|
||||
allHappyConditionsLegacyConfigurationSuccess(federationDomain1.Spec.Issuer, gitHubIdentityProvider.Name, frozenMetav1Now, 123),
|
||||
),
|
||||
expectedFederationDomainStatusUpdate(federationDomain2,
|
||||
configv1alpha1.FederationDomainPhaseReady,
|
||||
allHappyConditionsLegacyConfigurationSuccess(federationDomain2.Spec.Issuer, gitHubIdentityProvider.Name, frozenMetav1Now, 123),
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when there are two valid FederationDomains, but one is already up to date, the sync loop only updates " +
|
||||
"the out-of-date FederationDomain",
|
||||
@@ -947,6 +988,7 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
||||
oidcIdentityProvider,
|
||||
ldapIdentityProvider,
|
||||
adIdentityProvider,
|
||||
gitHubIdentityProvider,
|
||||
},
|
||||
wantFDIssuers: []*federationdomainproviders.FederationDomainIssuer{},
|
||||
wantStatusUpdates: []*supervisorconfigv1alpha1.FederationDomain{
|
||||
@@ -955,7 +997,7 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
||||
conditionstestutil.Replace(
|
||||
allHappyConditionsLegacyConfigurationSuccess(federationDomain1.Spec.Issuer, "", frozenMetav1Now, 123),
|
||||
[]metav1.Condition{
|
||||
sadIdentityProvidersFoundConditionIdentityProviderNotSpecified(3, frozenMetav1Now, 123),
|
||||
sadIdentityProvidersFoundConditionIdentityProviderNotSpecified(4, frozenMetav1Now, 123),
|
||||
sadReadyCondition(frozenMetav1Now, 123),
|
||||
}),
|
||||
),
|
||||
@@ -993,6 +1035,14 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
||||
Name: "cant-find-me-still-name",
|
||||
},
|
||||
},
|
||||
{
|
||||
DisplayName: "cant-find-me-again",
|
||||
ObjectRef: corev1.TypedLocalObjectReference{
|
||||
APIGroup: ptr.To(apiGroupSupervisor),
|
||||
Kind: "GitHubIdentityProvider",
|
||||
Name: "cant-find-me-again-name",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1012,7 +1062,9 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
||||
|
||||
cannot find resource specified by .spec.identityProviders[1].objectRef (with name "cant-find-me-either-name")
|
||||
|
||||
cannot find resource specified by .spec.identityProviders[2].objectRef (with name "cant-find-me-still-name")`,
|
||||
cannot find resource specified by .spec.identityProviders[2].objectRef (with name "cant-find-me-still-name")
|
||||
|
||||
cannot find resource specified by .spec.identityProviders[3].objectRef (with name "cant-find-me-again-name")`,
|
||||
), frozenMetav1Now, 123),
|
||||
sadReadyCondition(frozenMetav1Now, 123),
|
||||
}),
|
||||
@@ -1025,6 +1077,7 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
||||
oidcIdentityProvider,
|
||||
ldapIdentityProvider,
|
||||
adIdentityProvider,
|
||||
gitHubIdentityProvider,
|
||||
&supervisorconfigv1alpha1.FederationDomain{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: namespace, Generation: 123},
|
||||
Spec: supervisorconfigv1alpha1.FederationDomainSpec{
|
||||
@@ -1054,6 +1107,14 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
||||
Name: adIdentityProvider.Name,
|
||||
},
|
||||
},
|
||||
{
|
||||
DisplayName: "can-find-me-four",
|
||||
ObjectRef: corev1.TypedLocalObjectReference{
|
||||
APIGroup: ptr.To(apiGroupSupervisor),
|
||||
Kind: "GitHubIdentityProvider",
|
||||
Name: gitHubIdentityProvider.Name,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1076,6 +1137,11 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
||||
UID: adIdentityProvider.UID,
|
||||
Transforms: idtransform.NewTransformationPipeline(),
|
||||
},
|
||||
{
|
||||
DisplayName: "can-find-me-four",
|
||||
UID: gitHubIdentityProvider.UID,
|
||||
Transforms: idtransform.NewTransformationPipeline(),
|
||||
},
|
||||
}),
|
||||
},
|
||||
wantStatusUpdates: []*supervisorconfigv1alpha1.FederationDomain{
|
||||
@@ -1094,6 +1160,7 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
||||
oidcIdentityProvider,
|
||||
ldapIdentityProvider,
|
||||
adIdentityProvider,
|
||||
gitHubIdentityProvider,
|
||||
&supervisorconfigv1alpha1.FederationDomain{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: namespace, Generation: 123},
|
||||
Spec: supervisorconfigv1alpha1.FederationDomainSpec{
|
||||
@@ -1147,6 +1214,14 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
||||
Name: adIdentityProvider.Name,
|
||||
},
|
||||
},
|
||||
{
|
||||
DisplayName: "duplicate2",
|
||||
ObjectRef: corev1.TypedLocalObjectReference{
|
||||
APIGroup: ptr.To(apiGroupSupervisor),
|
||||
Kind: "GitHubIdentityProvider",
|
||||
Name: gitHubIdentityProvider.Name,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1173,6 +1248,7 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
||||
oidcIdentityProvider,
|
||||
ldapIdentityProvider,
|
||||
adIdentityProvider,
|
||||
gitHubIdentityProvider,
|
||||
&supervisorconfigv1alpha1.FederationDomain{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: namespace, Generation: 123},
|
||||
Spec: supervisorconfigv1alpha1.FederationDomainSpec{
|
||||
@@ -1210,6 +1286,14 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
||||
Name: adIdentityProvider.Name,
|
||||
},
|
||||
},
|
||||
{
|
||||
DisplayName: "name5",
|
||||
ObjectRef: corev1.TypedLocalObjectReference{
|
||||
APIGroup: ptr.To(apiGroupSupervisor), // correct
|
||||
Kind: "GitHubIdentityProvider",
|
||||
Name: gitHubIdentityProvider.Name,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1243,6 +1327,7 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
||||
oidcIdentityProvider,
|
||||
ldapIdentityProvider,
|
||||
adIdentityProvider,
|
||||
gitHubIdentityProvider,
|
||||
&supervisorconfigv1alpha1.FederationDomain{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: namespace, Generation: 123},
|
||||
Spec: supervisorconfigv1alpha1.FederationDomainSpec{
|
||||
@@ -2022,6 +2107,7 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
|
||||
pinnipedInformers.IDP().V1alpha1().OIDCIdentityProviders(),
|
||||
pinnipedInformers.IDP().V1alpha1().LDAPIdentityProviders(),
|
||||
pinnipedInformers.IDP().V1alpha1().ActiveDirectoryIdentityProviders(),
|
||||
pinnipedInformers.IDP().V1alpha1().GitHubIdentityProviders(),
|
||||
controllerlib.WithInformer,
|
||||
)
|
||||
|
||||
|
||||
+511
@@ -0,0 +1,511 @@
|
||||
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package githubupstreamwatcher implements a controller which watches GitHubIdentityProviders.
|
||||
package githubupstreamwatcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
k8sapierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
errorsutil "k8s.io/apimachinery/pkg/util/errors"
|
||||
k8sutilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
corev1informers "k8s.io/client-go/informers/core/v1"
|
||||
"k8s.io/utils/clock"
|
||||
|
||||
"go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1"
|
||||
supervisorclientset "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned"
|
||||
idpinformers "go.pinniped.dev/generated/latest/client/supervisor/informers/externalversions/idp/v1alpha1"
|
||||
pinnipedcontroller "go.pinniped.dev/internal/controller"
|
||||
"go.pinniped.dev/internal/controller/conditionsutil"
|
||||
"go.pinniped.dev/internal/controller/supervisorconfig/upstreamwatchers"
|
||||
"go.pinniped.dev/internal/controllerlib"
|
||||
"go.pinniped.dev/internal/crypto/ptls"
|
||||
"go.pinniped.dev/internal/endpointaddr"
|
||||
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
|
||||
"go.pinniped.dev/internal/net/phttp"
|
||||
"go.pinniped.dev/internal/plog"
|
||||
"go.pinniped.dev/internal/setutil"
|
||||
"go.pinniped.dev/internal/upstreamgithub"
|
||||
)
|
||||
|
||||
const (
|
||||
controllerName = "github-upstream-observer"
|
||||
|
||||
// Constants related to the client credentials Secret.
|
||||
gitHubClientSecretType corev1.SecretType = "secrets.pinniped.dev/github-client"
|
||||
clientIDDataKey, clientSecretDataKey string = "clientID", "clientSecret"
|
||||
|
||||
countExpectedConditions = 6
|
||||
|
||||
HostValid string = "HostValid"
|
||||
TLSConfigurationValid string = "TLSConfigurationValid"
|
||||
OrganizationsPolicyValid string = "OrganizationsPolicyValid"
|
||||
ClientCredentialsSecretValid string = "ClientCredentialsSecretValid" //nolint:gosec // this is not a credential
|
||||
GitHubConnectionValid string = "GitHubConnectionValid"
|
||||
ClaimsValid string = "ClaimsValid"
|
||||
|
||||
defaultHost = "github.com"
|
||||
defaultApiBaseURL = "https://api.github.com"
|
||||
)
|
||||
|
||||
// UpstreamGitHubIdentityProviderICache is a thread safe cache that holds a list of validated upstream GitHub IDP configurations.
|
||||
type UpstreamGitHubIdentityProviderICache interface {
|
||||
SetGitHubIdentityProviders([]upstreamprovider.UpstreamGithubIdentityProviderI)
|
||||
}
|
||||
|
||||
type gitHubWatcherController struct {
|
||||
namespace string
|
||||
cache UpstreamGitHubIdentityProviderICache
|
||||
log plog.Logger
|
||||
client supervisorclientset.Interface
|
||||
gitHubIdentityProviderInformer idpinformers.GitHubIdentityProviderInformer
|
||||
secretInformer corev1informers.SecretInformer
|
||||
clock clock.Clock
|
||||
dialFunc func(network, addr string, config *tls.Config) (*tls.Conn, error)
|
||||
}
|
||||
|
||||
// New instantiates a new controllerlib.Controller which will populate the provided UpstreamGitHubIdentityProviderICache.
|
||||
func New(
|
||||
namespace string,
|
||||
idpCache UpstreamGitHubIdentityProviderICache,
|
||||
client supervisorclientset.Interface,
|
||||
gitHubIdentityProviderInformer idpinformers.GitHubIdentityProviderInformer,
|
||||
secretInformer corev1informers.SecretInformer,
|
||||
log plog.Logger,
|
||||
withInformer pinnipedcontroller.WithInformerOptionFunc,
|
||||
clock clock.Clock,
|
||||
dialFunc func(network, addr string, config *tls.Config) (*tls.Conn, error),
|
||||
) controllerlib.Controller {
|
||||
c := gitHubWatcherController{
|
||||
namespace: namespace,
|
||||
cache: idpCache,
|
||||
client: client,
|
||||
log: log.WithName(controllerName),
|
||||
gitHubIdentityProviderInformer: gitHubIdentityProviderInformer,
|
||||
secretInformer: secretInformer,
|
||||
clock: clock,
|
||||
dialFunc: dialFunc,
|
||||
}
|
||||
|
||||
return controllerlib.New(
|
||||
controllerlib.Config{Name: controllerName, Syncer: &c},
|
||||
withInformer(
|
||||
gitHubIdentityProviderInformer,
|
||||
pinnipedcontroller.SimpleFilter(func(obj metav1.Object) bool {
|
||||
gitHubIDP, ok := obj.(*v1alpha1.GitHubIdentityProvider)
|
||||
return ok && gitHubIDP.Namespace == namespace
|
||||
}, pinnipedcontroller.SingletonQueue()),
|
||||
controllerlib.InformerOption{},
|
||||
),
|
||||
withInformer(
|
||||
secretInformer,
|
||||
pinnipedcontroller.MatchAnySecretOfTypeFilter(gitHubClientSecretType, pinnipedcontroller.SingletonQueue(), namespace),
|
||||
controllerlib.InformerOption{},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Sync implements controllerlib.Syncer.
|
||||
func (c *gitHubWatcherController) Sync(ctx controllerlib.Context) error {
|
||||
actualUpstreams, err := c.gitHubIdentityProviderInformer.Lister().List(labels.Everything())
|
||||
if err != nil { // untested
|
||||
return fmt.Errorf("failed to list GitHubIdentityProviders: %w", err)
|
||||
}
|
||||
|
||||
// Sort them by name just so that the logs output is consistent
|
||||
slices.SortStableFunc(actualUpstreams, func(a, b *v1alpha1.GitHubIdentityProvider) int {
|
||||
return strings.Compare(a.Name, b.Name)
|
||||
})
|
||||
|
||||
var applicationErrors []error
|
||||
validatedUpstreams := make([]upstreamprovider.UpstreamGithubIdentityProviderI, 0, len(actualUpstreams))
|
||||
for _, upstream := range actualUpstreams {
|
||||
validatedUpstream, applicationErr := c.validateUpstreamAndUpdateConditions(ctx, upstream)
|
||||
if applicationErr != nil {
|
||||
applicationErrors = append(applicationErrors, applicationErr)
|
||||
} else if validatedUpstream != nil {
|
||||
validatedUpstreams = append(validatedUpstreams, validatedUpstream)
|
||||
}
|
||||
// Else:
|
||||
// If both validatedUpstream and applicationErr are nil, this must be because the upstream had configuration errors.
|
||||
// This controller should take no action until the user has reconfigured the upstream.
|
||||
}
|
||||
c.cache.SetGitHubIdentityProviders(validatedUpstreams)
|
||||
|
||||
// If we have recoverable application errors, let's do a requeue and capture all the applicationErrors too
|
||||
if len(applicationErrors) > 0 {
|
||||
applicationErrors = append([]error{controllerlib.ErrSyntheticRequeue}, applicationErrors...)
|
||||
}
|
||||
|
||||
return errorsutil.NewAggregate(applicationErrors)
|
||||
}
|
||||
|
||||
func (c *gitHubWatcherController) validateClientSecret(secretName string) (*metav1.Condition, string, string, error) {
|
||||
secret, unableToRetrieveSecretErr := c.secretInformer.Lister().Secrets(c.namespace).Get(secretName)
|
||||
|
||||
// This error requires user interaction, so ignore it.
|
||||
if k8sapierrors.IsNotFound(unableToRetrieveSecretErr) {
|
||||
unableToRetrieveSecretErr = nil
|
||||
}
|
||||
|
||||
buildFalseCondition := func(prefix string) (*metav1.Condition, string, string, error) {
|
||||
return &metav1.Condition{
|
||||
Type: ClientCredentialsSecretValid,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: upstreamwatchers.ReasonNotFound,
|
||||
Message: fmt.Sprintf("%s: secret from spec.client.SecretName (%q) must be found in namespace %q with type %q and keys %q and %q",
|
||||
prefix,
|
||||
secretName,
|
||||
c.namespace,
|
||||
gitHubClientSecretType,
|
||||
clientIDDataKey,
|
||||
clientSecretDataKey),
|
||||
}, "", "", unableToRetrieveSecretErr
|
||||
}
|
||||
|
||||
if unableToRetrieveSecretErr != nil || secret == nil {
|
||||
return buildFalseCondition(fmt.Sprintf("secret %q not found", secretName))
|
||||
}
|
||||
|
||||
if secret.Type != gitHubClientSecretType {
|
||||
return buildFalseCondition(fmt.Sprintf("wrong secret type %q", secret.Type))
|
||||
}
|
||||
|
||||
clientID := string(secret.Data[clientIDDataKey])
|
||||
if len(clientID) < 1 {
|
||||
return buildFalseCondition(fmt.Sprintf("missing key %q", clientIDDataKey))
|
||||
}
|
||||
|
||||
clientSecret := string(secret.Data[clientSecretDataKey])
|
||||
if len(clientSecret) < 1 {
|
||||
return buildFalseCondition(fmt.Sprintf("missing key %q", clientSecretDataKey))
|
||||
}
|
||||
|
||||
if len(secret.Data) != 2 {
|
||||
return buildFalseCondition("extra keys found")
|
||||
}
|
||||
|
||||
return &metav1.Condition{
|
||||
Type: ClientCredentialsSecretValid,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: upstreamwatchers.ReasonSuccess,
|
||||
Message: fmt.Sprintf("clientID and clientSecret have been read from spec.client.SecretName (%q)", secretName),
|
||||
}, clientID, clientSecret, nil
|
||||
}
|
||||
|
||||
func validateOrganizationsPolicy(organizationsSpec *v1alpha1.GitHubOrganizationsSpec) *metav1.Condition {
|
||||
var policy v1alpha1.GitHubAllowedAuthOrganizationsPolicy
|
||||
if organizationsSpec.Policy != nil {
|
||||
policy = *organizationsSpec.Policy
|
||||
}
|
||||
|
||||
// Should not happen due to CRD defaulting, enum validation, and CEL validation (for recent versions of K8s only!)
|
||||
// That is why the message here is very minimal
|
||||
if (policy == v1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers && len(organizationsSpec.Allowed) == 0) ||
|
||||
(policy == v1alpha1.GitHubAllowedAuthOrganizationsPolicyOnlyUsersFromAllowedOrganizations && len(organizationsSpec.Allowed) > 0) {
|
||||
return &metav1.Condition{
|
||||
Type: OrganizationsPolicyValid,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: upstreamwatchers.ReasonSuccess,
|
||||
Message: fmt.Sprintf("spec.allowAuthentication.organizations.policy (%q) is valid", policy),
|
||||
}
|
||||
}
|
||||
|
||||
if len(organizationsSpec.Allowed) > 0 {
|
||||
return &metav1.Condition{
|
||||
Type: OrganizationsPolicyValid,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "Invalid",
|
||||
Message: "spec.allowAuthentication.organizations.policy must be 'OnlyUsersFromAllowedOrganizations' when spec.allowAuthentication.organizations.allowed has organizations listed",
|
||||
}
|
||||
}
|
||||
|
||||
return &metav1.Condition{
|
||||
Type: OrganizationsPolicyValid,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "Invalid",
|
||||
Message: "spec.allowAuthentication.organizations.policy must be 'AllGitHubUsers' when spec.allowAuthentication.organizations.allowed is empty",
|
||||
}
|
||||
}
|
||||
|
||||
func (c *gitHubWatcherController) validateUpstreamAndUpdateConditions(ctx controllerlib.Context, upstream *v1alpha1.GitHubIdentityProvider) (
|
||||
*upstreamgithub.Provider, // If validated, returns the config
|
||||
error, // This error will only refer to programmatic errors such as inability to perform a Dial or dereference a pointer, not configuration errors
|
||||
) {
|
||||
conditions := make([]*metav1.Condition, 0)
|
||||
applicationErrors := make([]error, 0)
|
||||
|
||||
clientSecretCondition, clientID, clientSecret, clientSecretErr := c.validateClientSecret(upstream.Spec.Client.SecretName)
|
||||
conditions = append(conditions, clientSecretCondition)
|
||||
if clientSecretErr != nil { // untested
|
||||
applicationErrors = append(applicationErrors, clientSecretErr)
|
||||
}
|
||||
|
||||
// Should there be some sort of catch-all condition to capture this?
|
||||
// This does not actually prevent a GitHub IDP from being added to the cache.
|
||||
// CRD defaulting and validation should eliminate the possibility of an error here.
|
||||
userAndGroupCondition, groupNameAttribute, usernameAttribute := validateUserAndGroupAttributes(upstream)
|
||||
conditions = append(conditions, userAndGroupCondition)
|
||||
|
||||
organizationPolicyCondition := validateOrganizationsPolicy(&upstream.Spec.AllowAuthentication.Organizations)
|
||||
conditions = append(conditions, organizationPolicyCondition)
|
||||
|
||||
hostCondition, hostPort := validateHost(upstream.Spec.GitHubAPI)
|
||||
conditions = append(conditions, hostCondition)
|
||||
|
||||
tlsConfigCondition, certPool := c.validateTLSConfiguration(upstream.Spec.GitHubAPI.TLS)
|
||||
conditions = append(conditions, tlsConfigCondition)
|
||||
|
||||
githubConnectionCondition, hostURL, httpClient, githubConnectionErr := c.validateGitHubConnection(
|
||||
hostPort,
|
||||
certPool,
|
||||
hostCondition.Status == metav1.ConditionTrue && tlsConfigCondition.Status == metav1.ConditionTrue,
|
||||
)
|
||||
if githubConnectionErr != nil {
|
||||
applicationErrors = append(applicationErrors, githubConnectionErr)
|
||||
}
|
||||
conditions = append(conditions, githubConnectionCondition)
|
||||
|
||||
// The critical pattern to maintain is that every run of the sync loop will populate the exact number of the exact
|
||||
// same set of conditions. Conditions depending on other conditions should get Status: metav1.ConditionUnknown, or
|
||||
// Status: metav1.ConditionFalse, never be omitted.
|
||||
if len(conditions) != countExpectedConditions { // untested since all code paths return the same number of conditions
|
||||
applicationErrors = append(applicationErrors, fmt.Errorf("expected %d conditions but found %d conditions", countExpectedConditions, len(conditions)))
|
||||
return nil, k8sutilerrors.NewAggregate(applicationErrors)
|
||||
}
|
||||
hadErrorCondition, updateStatusErr := c.updateStatus(ctx.Context, upstream, conditions)
|
||||
if updateStatusErr != nil {
|
||||
applicationErrors = append(applicationErrors, updateStatusErr)
|
||||
}
|
||||
// Any error condition means we will not add the IDP to the cache, so just return nil here
|
||||
if hadErrorCondition {
|
||||
return nil, k8sutilerrors.NewAggregate(applicationErrors)
|
||||
}
|
||||
|
||||
provider := upstreamgithub.New(
|
||||
upstreamgithub.ProviderConfig{
|
||||
Name: upstream.Name,
|
||||
ResourceUID: upstream.UID,
|
||||
APIBaseURL: apiBaseUrl(*upstream.Spec.GitHubAPI.Host, hostURL),
|
||||
GroupNameAttribute: groupNameAttribute,
|
||||
UsernameAttribute: usernameAttribute,
|
||||
OAuth2Config: &oauth2.Config{
|
||||
ClientID: clientID,
|
||||
ClientSecret: clientSecret,
|
||||
// See https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: fmt.Sprintf("%s/login/oauth/authorize", hostURL),
|
||||
DeviceAuthURL: "", // we do not use device code flow
|
||||
TokenURL: fmt.Sprintf("%s/login/oauth/access_token", hostURL),
|
||||
AuthStyle: oauth2.AuthStyleInParams,
|
||||
},
|
||||
RedirectURL: "", // this will be different for each FederationDomain, so we do not set it here
|
||||
Scopes: []string{"read:user", "read:org"},
|
||||
},
|
||||
AllowedOrganizations: setutil.NewCaseInsensitiveSet(upstream.Spec.AllowAuthentication.Organizations.Allowed...),
|
||||
HttpClient: httpClient,
|
||||
},
|
||||
)
|
||||
return provider, k8sutilerrors.NewAggregate(applicationErrors)
|
||||
}
|
||||
|
||||
func apiBaseUrl(upstreamSpecHost string, hostURL string) string {
|
||||
if upstreamSpecHost != defaultHost {
|
||||
return fmt.Sprintf("%s/api/v3", hostURL)
|
||||
}
|
||||
return defaultApiBaseURL
|
||||
}
|
||||
|
||||
func validateHost(gitHubAPIConfig v1alpha1.GitHubAPIConfig) (*metav1.Condition, *endpointaddr.HostPort) {
|
||||
buildInvalidHost := func(host, reason string) *metav1.Condition {
|
||||
return &metav1.Condition{
|
||||
Type: HostValid,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "InvalidHost",
|
||||
Message: fmt.Sprintf("spec.githubAPI.host (%q) is not valid: %s", host, reason),
|
||||
}
|
||||
}
|
||||
|
||||
// Should not happen due to CRD defaulting
|
||||
if gitHubAPIConfig.Host == nil || len(*gitHubAPIConfig.Host) < 1 {
|
||||
return buildInvalidHost("", "must not be empty"), nil
|
||||
}
|
||||
|
||||
host := *gitHubAPIConfig.Host
|
||||
hostPort, addressParseErr := endpointaddr.Parse(host, 443)
|
||||
if addressParseErr != nil {
|
||||
// addressParseErr is not recoverable. It requires user interaction, so do not return the error.
|
||||
return buildInvalidHost(host, addressParseErr.Error()), nil
|
||||
}
|
||||
|
||||
return &metav1.Condition{
|
||||
Type: HostValid,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: upstreamwatchers.ReasonSuccess,
|
||||
Message: fmt.Sprintf("spec.githubAPI.host (%q) is valid", host),
|
||||
}, &hostPort
|
||||
}
|
||||
|
||||
func (c *gitHubWatcherController) validateTLSConfiguration(tlsSpec *v1alpha1.TLSSpec) (*metav1.Condition, *x509.CertPool) {
|
||||
certPool, _, buildCertPoolErr := pinnipedcontroller.BuildCertPoolIDP(tlsSpec)
|
||||
if buildCertPoolErr != nil {
|
||||
// buildCertPoolErr is not recoverable with a resync.
|
||||
// It requires user interaction, so do not return the error.
|
||||
return &metav1.Condition{
|
||||
Type: TLSConfigurationValid,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "InvalidTLSConfig",
|
||||
Message: fmt.Sprintf("spec.githubAPI.tls.certificateAuthorityData is not valid: %s", buildCertPoolErr),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &metav1.Condition{
|
||||
Type: TLSConfigurationValid,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: upstreamwatchers.ReasonSuccess,
|
||||
Message: "spec.githubAPI.tls.certificateAuthorityData is valid",
|
||||
}, certPool
|
||||
}
|
||||
|
||||
func (c *gitHubWatcherController) validateGitHubConnection(
|
||||
hostPort *endpointaddr.HostPort,
|
||||
certPool *x509.CertPool,
|
||||
validSoFar bool,
|
||||
) (*metav1.Condition, string, *http.Client, error) {
|
||||
if !validSoFar {
|
||||
return &metav1.Condition{
|
||||
Type: GitHubConnectionValid,
|
||||
Status: metav1.ConditionUnknown,
|
||||
Reason: "UnableToValidate",
|
||||
Message: "unable to validate; see other conditions for details",
|
||||
}, "", nil, nil
|
||||
}
|
||||
|
||||
conn, tlsDialErr := c.dialFunc("tcp", hostPort.Endpoint(), ptls.Default(certPool))
|
||||
if tlsDialErr != nil {
|
||||
return &metav1.Condition{
|
||||
Type: GitHubConnectionValid,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "UnableToDialServer",
|
||||
Message: fmt.Sprintf("cannot dial server spec.githubAPI.host (%q): %s", hostPort.Endpoint(), buildDialErrorMessage(tlsDialErr)),
|
||||
}, "", nil, tlsDialErr
|
||||
}
|
||||
|
||||
return &metav1.Condition{
|
||||
Type: GitHubConnectionValid,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: upstreamwatchers.ReasonSuccess,
|
||||
Message: fmt.Sprintf("spec.githubAPI.host (%q) is reachable and TLS verification succeeds", hostPort.Endpoint()),
|
||||
}, fmt.Sprintf("https://%s", hostPort.Endpoint()), phttp.Default(certPool), conn.Close()
|
||||
}
|
||||
|
||||
// buildDialErrorMessage standardizes DNS error messages that appear differently on different platforms, so that tests and log grepping is uniform.
|
||||
func buildDialErrorMessage(tlsDialErr error) string {
|
||||
reason := tlsDialErr.Error()
|
||||
|
||||
var opError *net.OpError
|
||||
var dnsError *net.DNSError
|
||||
if errors.As(tlsDialErr, &opError) && errors.As(tlsDialErr, &dnsError) {
|
||||
dnsError.Server = ""
|
||||
opError.Err = dnsError
|
||||
return opError.Error()
|
||||
}
|
||||
|
||||
return reason
|
||||
}
|
||||
|
||||
func validateUserAndGroupAttributes(upstream *v1alpha1.GitHubIdentityProvider) (*metav1.Condition, v1alpha1.GitHubGroupNameAttribute, v1alpha1.GitHubUsernameAttribute) {
|
||||
buildInvalidCondition := func(message string) *metav1.Condition {
|
||||
return &metav1.Condition{
|
||||
Type: ClaimsValid,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: "Invalid",
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
var usernameAttribute v1alpha1.GitHubUsernameAttribute
|
||||
if upstream.Spec.Claims.Username == nil {
|
||||
return buildInvalidCondition("spec.claims.username is required"), "", ""
|
||||
} else {
|
||||
usernameAttribute = *upstream.Spec.Claims.Username
|
||||
}
|
||||
|
||||
var groupNameAttribute v1alpha1.GitHubGroupNameAttribute
|
||||
if upstream.Spec.Claims.Groups == nil {
|
||||
return buildInvalidCondition("spec.claims.groups is required"), "", ""
|
||||
} else {
|
||||
groupNameAttribute = *upstream.Spec.Claims.Groups
|
||||
}
|
||||
|
||||
switch usernameAttribute {
|
||||
case v1alpha1.GitHubUsernameLoginAndID:
|
||||
case v1alpha1.GitHubUsernameLogin:
|
||||
case v1alpha1.GitHubUsernameID:
|
||||
default:
|
||||
// Should not happen due to CRD enum validation
|
||||
return buildInvalidCondition(fmt.Sprintf("spec.claims.username (%q) is not valid", usernameAttribute)), "", ""
|
||||
}
|
||||
|
||||
switch groupNameAttribute {
|
||||
case v1alpha1.GitHubUseTeamNameForGroupName:
|
||||
case v1alpha1.GitHubUseTeamSlugForGroupName:
|
||||
default:
|
||||
// Should not happen due to CRD enum validation
|
||||
return buildInvalidCondition(fmt.Sprintf("spec.claims.groups (%q) is not valid", groupNameAttribute)), "", ""
|
||||
}
|
||||
|
||||
return &metav1.Condition{
|
||||
Type: ClaimsValid,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: upstreamwatchers.ReasonSuccess,
|
||||
Message: "spec.claims are valid",
|
||||
}, groupNameAttribute, usernameAttribute
|
||||
}
|
||||
|
||||
func (c *gitHubWatcherController) updateStatus(
|
||||
ctx context.Context,
|
||||
upstream *v1alpha1.GitHubIdentityProvider,
|
||||
conditions []*metav1.Condition) (bool, error) {
|
||||
log := c.log.WithValues("namespace", upstream.Namespace, "name", upstream.Name)
|
||||
updated := upstream.DeepCopy()
|
||||
|
||||
hadErrorCondition := conditionsutil.MergeConditions(
|
||||
conditions,
|
||||
upstream.Generation,
|
||||
&updated.Status.Conditions,
|
||||
log,
|
||||
metav1.NewTime(c.clock.Now()),
|
||||
)
|
||||
|
||||
updated.Status.Phase = v1alpha1.GitHubPhaseReady
|
||||
if hadErrorCondition {
|
||||
updated.Status.Phase = v1alpha1.GitHubPhaseError
|
||||
}
|
||||
|
||||
if equality.Semantic.DeepEqual(upstream, updated) {
|
||||
return hadErrorCondition, nil
|
||||
}
|
||||
|
||||
log.Info("updating GitHubIdentityProvider status", "phase", updated.Status.Phase)
|
||||
|
||||
_, updateStatusError := c.client.
|
||||
IDPV1alpha1().
|
||||
GitHubIdentityProviders(upstream.Namespace).
|
||||
UpdateStatus(ctx, updated, metav1.UpdateOptions{})
|
||||
return hadErrorCondition, updateStatusError
|
||||
}
|
||||
+2423
File diff suppressed because it is too large
Load Diff
@@ -260,7 +260,7 @@ func (c *ldapWatcherController) updateStatus(ctx context.Context, upstream *idpv
|
||||
log := plog.WithValues("namespace", upstream.Namespace, "name", upstream.Name)
|
||||
updated := upstream.DeepCopy()
|
||||
|
||||
hadErrorCondition := conditionsutil.MergeIDPConditions(conditions, upstream.Generation, &updated.Status.Conditions, log)
|
||||
hadErrorCondition := conditionsutil.MergeConditions(conditions, upstream.Generation, &updated.Status.Conditions, log, metav1.Now())
|
||||
|
||||
updated.Status.Phase = idpv1alpha1.LDAPPhaseReady
|
||||
if hadErrorCondition {
|
||||
|
||||
@@ -133,7 +133,7 @@ func (c *oidcClientWatcherController) updateStatus(
|
||||
) error {
|
||||
updated := upstream.DeepCopy()
|
||||
|
||||
hadErrorCondition := conditionsutil.MergeConfigConditions(conditions,
|
||||
hadErrorCondition := conditionsutil.MergeConditions(conditions,
|
||||
upstream.Generation, &updated.Status.Conditions, plog.New(), metav1.Now())
|
||||
|
||||
updated.Status.Phase = supervisorconfigv1alpha1.OIDCClientPhaseReady
|
||||
|
||||
@@ -54,7 +54,7 @@ const (
|
||||
oidcValidatorCacheTTL = 15 * time.Minute
|
||||
|
||||
// Constants related to conditions.
|
||||
typeClientCredentialsValid = "ClientCredentialsValid" //nolint:gosec // this is not a credential
|
||||
typeClientCredentialsSecretValid = "ClientCredentialsSecretValid" //nolint:gosec // this is not a credential
|
||||
typeAdditionalAuthorizeParametersValid = "AdditionalAuthorizeParametersValid"
|
||||
typeOIDCDiscoverySucceeded = "OIDCDiscoverySucceeded"
|
||||
|
||||
@@ -260,7 +260,7 @@ func (c *oidcWatcherController) validateUpstream(ctx controllerlib.Context, upst
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateSecret validates the .spec.client.secretName field and returns the appropriate ClientCredentialsValid condition.
|
||||
// validateSecret validates the .spec.client.secretName field and returns the appropriate ClientCredentialsSecretValid condition.
|
||||
func (c *oidcWatcherController) validateSecret(upstream *idpv1alpha1.OIDCIdentityProvider, result *upstreamoidc.ProviderConfig) *metav1.Condition {
|
||||
secretName := upstream.Spec.Client.SecretName
|
||||
|
||||
@@ -268,7 +268,7 @@ func (c *oidcWatcherController) validateSecret(upstream *idpv1alpha1.OIDCIdentit
|
||||
secret, err := c.secretInformer.Lister().Secrets(upstream.Namespace).Get(secretName)
|
||||
if err != nil {
|
||||
return &metav1.Condition{
|
||||
Type: typeClientCredentialsValid,
|
||||
Type: typeClientCredentialsSecretValid,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: upstreamwatchers.ReasonNotFound,
|
||||
Message: err.Error(),
|
||||
@@ -278,7 +278,7 @@ func (c *oidcWatcherController) validateSecret(upstream *idpv1alpha1.OIDCIdentit
|
||||
// Validate the secret .type field.
|
||||
if secret.Type != oidcClientSecretType {
|
||||
return &metav1.Condition{
|
||||
Type: typeClientCredentialsValid,
|
||||
Type: typeClientCredentialsSecretValid,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: upstreamwatchers.ReasonWrongType,
|
||||
Message: fmt.Sprintf("referenced Secret %q has wrong type %q (should be %q)", secretName, secret.Type, oidcClientSecretType),
|
||||
@@ -290,7 +290,7 @@ func (c *oidcWatcherController) validateSecret(upstream *idpv1alpha1.OIDCIdentit
|
||||
clientSecret := secret.Data[clientSecretDataKey]
|
||||
if len(clientID) == 0 || len(clientSecret) == 0 {
|
||||
return &metav1.Condition{
|
||||
Type: typeClientCredentialsValid,
|
||||
Type: typeClientCredentialsSecretValid,
|
||||
Status: metav1.ConditionFalse,
|
||||
Reason: upstreamwatchers.ReasonMissingKeys,
|
||||
Message: fmt.Sprintf("referenced Secret %q is missing required keys %q", secretName, []string{clientIDDataKey, clientSecretDataKey}),
|
||||
@@ -301,7 +301,7 @@ func (c *oidcWatcherController) validateSecret(upstream *idpv1alpha1.OIDCIdentit
|
||||
result.Config.ClientID = string(clientID)
|
||||
result.Config.ClientSecret = string(clientSecret)
|
||||
return &metav1.Condition{
|
||||
Type: typeClientCredentialsValid,
|
||||
Type: typeClientCredentialsSecretValid,
|
||||
Status: metav1.ConditionTrue,
|
||||
Reason: upstreamwatchers.ReasonSuccess,
|
||||
Message: "loaded client credentials",
|
||||
@@ -412,7 +412,7 @@ func (c *oidcWatcherController) updateStatus(ctx context.Context, upstream *idpv
|
||||
log := c.log.WithValues("namespace", upstream.Namespace, "name", upstream.Name)
|
||||
updated := upstream.DeepCopy()
|
||||
|
||||
hadErrorCondition := conditionsutil.MergeIDPConditions(conditions, upstream.Generation, &updated.Status.Conditions, log)
|
||||
hadErrorCondition := conditionsutil.MergeConditions(conditions, upstream.Generation, &updated.Status.Conditions, log, metav1.Now())
|
||||
|
||||
updated.Status.Phase = idpv1alpha1.PhaseReady
|
||||
if hadErrorCondition {
|
||||
|
||||
+59
-59
@@ -174,10 +174,10 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
inputSecrets: []runtime.Object{},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="secret \"test-client-secret\" not found" "reason"="SecretNotFound" "status"="False" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="secret \"test-client-secret\" not found" "reason"="SecretNotFound" "status"="False" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="discovered issuer configuration" "reason"="Success" "status"="True" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="secret \"test-client-secret\" not found" "name"="test-name" "namespace"="test-namespace" "reason"="SecretNotFound" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="secret \"test-client-secret\" not found" "name"="test-name" "namespace"="test-namespace" "reason"="SecretNotFound" "type"="ClientCredentialsSecretValid"`,
|
||||
},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{},
|
||||
wantResultingUpstreams: []idpv1alpha1.OIDCIdentityProvider{{
|
||||
@@ -187,7 +187,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "False",
|
||||
LastTransitionTime: now,
|
||||
Reason: "SecretNotFound",
|
||||
@@ -221,10 +221,10 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="referenced Secret \"test-client-secret\" has wrong type \"some-other-type\" (should be \"secrets.pinniped.dev/oidc-client\")" "reason"="SecretWrongType" "status"="False" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="referenced Secret \"test-client-secret\" has wrong type \"some-other-type\" (should be \"secrets.pinniped.dev/oidc-client\")" "reason"="SecretWrongType" "status"="False" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="discovered issuer configuration" "reason"="Success" "status"="True" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="referenced Secret \"test-client-secret\" has wrong type \"some-other-type\" (should be \"secrets.pinniped.dev/oidc-client\")" "name"="test-name" "namespace"="test-namespace" "reason"="SecretWrongType" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="referenced Secret \"test-client-secret\" has wrong type \"some-other-type\" (should be \"secrets.pinniped.dev/oidc-client\")" "name"="test-name" "namespace"="test-namespace" "reason"="SecretWrongType" "type"="ClientCredentialsSecretValid"`,
|
||||
},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{},
|
||||
wantResultingUpstreams: []idpv1alpha1.OIDCIdentityProvider{{
|
||||
@@ -234,7 +234,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "False",
|
||||
LastTransitionTime: now,
|
||||
Reason: "SecretWrongType",
|
||||
@@ -267,10 +267,10 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="referenced Secret \"test-client-secret\" is missing required keys [\"clientID\" \"clientSecret\"]" "reason"="SecretMissingKeys" "status"="False" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="referenced Secret \"test-client-secret\" is missing required keys [\"clientID\" \"clientSecret\"]" "reason"="SecretMissingKeys" "status"="False" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="discovered issuer configuration" "reason"="Success" "status"="True" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="referenced Secret \"test-client-secret\" is missing required keys [\"clientID\" \"clientSecret\"]" "name"="test-name" "namespace"="test-namespace" "reason"="SecretMissingKeys" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="referenced Secret \"test-client-secret\" is missing required keys [\"clientID\" \"clientSecret\"]" "name"="test-name" "namespace"="test-namespace" "reason"="SecretMissingKeys" "type"="ClientCredentialsSecretValid"`,
|
||||
},
|
||||
wantResultingCache: []*oidctestutil.TestUpstreamOIDCIdentityProvider{},
|
||||
wantResultingUpstreams: []idpv1alpha1.OIDCIdentityProvider{{
|
||||
@@ -280,7 +280,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "False",
|
||||
LastTransitionTime: now,
|
||||
Reason: "SecretMissingKeys",
|
||||
@@ -316,7 +316,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="spec.certificateAuthorityData is invalid: illegal base64 data at input byte 7" "reason"="InvalidTLSConfig" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="spec.certificateAuthorityData is invalid: illegal base64 data at input byte 7" "name"="test-name" "namespace"="test-namespace" "reason"="InvalidTLSConfig" "type"="OIDCDiscoverySucceeded"`,
|
||||
@@ -329,7 +329,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
@@ -365,7 +365,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="spec.certificateAuthorityData is invalid: no certificates found" "reason"="InvalidTLSConfig" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="spec.certificateAuthorityData is invalid: no certificates found" "name"="test-name" "namespace"="test-namespace" "reason"="InvalidTLSConfig" "type"="OIDCDiscoverySucceeded"`,
|
||||
@@ -378,7 +378,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
@@ -411,7 +411,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="failed to parse issuer URL: parse \"%invalid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\": invalid URL escape \"%in\"" "reason"="Unreachable" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="failed to parse issuer URL: parse \"%invalid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\": invalid URL escape \"%in\"" "name"="test-name" "namespace"="test-namespace" "reason"="Unreachable" "type"="OIDCDiscoverySucceeded"`,
|
||||
@@ -424,7 +424,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
@@ -457,7 +457,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="issuer URL '` + strings.Replace(testIssuerURL, "https", "http", 1) + `' must have \"https\" scheme, not \"http\"" "reason"="Unreachable" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="issuer URL '` + strings.Replace(testIssuerURL, "https", "http", 1) + `' must have \"https\" scheme, not \"http\"" "name"="test-name" "namespace"="test-namespace" "reason"="Unreachable" "type"="OIDCDiscoverySucceeded"`,
|
||||
@@ -470,7 +470,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
@@ -503,7 +503,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="issuer URL '` + testIssuerURL + "?sub=foo" + `' cannot contain query or fragment component" "reason"="Unreachable" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="issuer URL '` + testIssuerURL + "?sub=foo" + `' cannot contain query or fragment component" "name"="test-name" "namespace"="test-namespace" "reason"="Unreachable" "type"="OIDCDiscoverySucceeded"`,
|
||||
@@ -516,7 +516,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
@@ -549,7 +549,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="issuer URL '` + testIssuerURL + "#fragment" + `' cannot contain query or fragment component" "reason"="Unreachable" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="issuer URL '` + testIssuerURL + "#fragment" + `' cannot contain query or fragment component" "name"="test-name" "namespace"="test-namespace" "reason"="Unreachable" "type"="OIDCDiscoverySucceeded"`,
|
||||
@@ -562,7 +562,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
@@ -597,7 +597,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "msg"="failed to perform OIDC discovery" "error"="Get \"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee/.well-known/openid-configuration\": tls: failed to verify certificate: x509: certificate signed by unknown authority" "issuer"="` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" "name"="test-name" "namespace"="test-namespace"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="failed to perform OIDC discovery against \"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\":\nGet \"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee/.well-known/openid-configuration\": tls: failed to verify certificate: x509: certificate signed by unknown authority" "reason"="Unreachable" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="failed to perform OIDC discovery against \"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\":\nGet \"` + testIssuerURL + `/valid-url-that-is-really-really-long-nanananananananannanananan-batman-nanananananananananananananana-batman-lalalalalalalalalal-batman-weeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee/.well-known/openid-configuration\": tls: failed to verify certificate: x509: certificate signed by unknown authority" "name"="test-name" "namespace"="test-namespace" "reason"="Unreachable" "type"="OIDCDiscoverySucceeded"`,
|
||||
@@ -610,7 +610,7 @@ func TestOIDCUpstreamWatcherControllerSync(t *testing.T) {
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
@@ -645,7 +645,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="failed to parse authorization endpoint URL: parse \"%\": invalid URL escape \"%\"" "reason"="InvalidResponse" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="failed to parse authorization endpoint URL: parse \"%\": invalid URL escape \"%\"" "name"="test-name" "namespace"="test-namespace" "reason"="InvalidResponse" "type"="OIDCDiscoverySucceeded"`,
|
||||
@@ -658,7 +658,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
@@ -692,7 +692,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="failed to parse revocation endpoint URL: parse \"%\": invalid URL escape \"%\"" "reason"="InvalidResponse" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="failed to parse revocation endpoint URL: parse \"%\": invalid URL escape \"%\"" "name"="test-name" "namespace"="test-namespace" "reason"="InvalidResponse" "type"="OIDCDiscoverySucceeded"`,
|
||||
@@ -705,7 +705,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
@@ -739,7 +739,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="authorization endpoint URL 'http://example.com/authorize' must have \"https\" scheme, not \"http\"" "reason"="InvalidResponse" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="authorization endpoint URL 'http://example.com/authorize' must have \"https\" scheme, not \"http\"" "name"="test-name" "namespace"="test-namespace" "reason"="InvalidResponse" "type"="OIDCDiscoverySucceeded"`,
|
||||
@@ -752,7 +752,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
@@ -786,7 +786,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="revocation endpoint URL 'http://example.com/revoke' must have \"https\" scheme, not \"http\"" "reason"="InvalidResponse" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="revocation endpoint URL 'http://example.com/revoke' must have \"https\" scheme, not \"http\"" "name"="test-name" "namespace"="test-namespace" "reason"="InvalidResponse" "type"="OIDCDiscoverySucceeded"`,
|
||||
@@ -799,7 +799,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
@@ -833,7 +833,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="token endpoint URL 'http://example.com/token' must have \"https\" scheme, not \"http\"" "reason"="InvalidResponse" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="token endpoint URL 'http://example.com/token' must have \"https\" scheme, not \"http\"" "name"="test-name" "namespace"="test-namespace" "reason"="InvalidResponse" "type"="OIDCDiscoverySucceeded"`,
|
||||
@@ -846,7 +846,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
@@ -880,7 +880,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="token endpoint URL '' must have \"https\" scheme, not \"\"" "reason"="InvalidResponse" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="token endpoint URL '' must have \"https\" scheme, not \"\"" "name"="test-name" "namespace"="test-namespace" "reason"="InvalidResponse" "type"="OIDCDiscoverySucceeded"`,
|
||||
@@ -893,7 +893,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
@@ -927,7 +927,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="authorization endpoint URL '' must have \"https\" scheme, not \"\"" "reason"="InvalidResponse" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="authorization endpoint URL '' must have \"https\" scheme, not \"\"" "name"="test-name" "namespace"="test-namespace" "reason"="InvalidResponse" "type"="OIDCDiscoverySucceeded"`,
|
||||
@@ -940,7 +940,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
@@ -974,7 +974,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Status: idpv1alpha1.OIDCIdentityProviderStatus{
|
||||
Phase: "Error",
|
||||
Conditions: []metav1.Condition{
|
||||
{Type: "ClientCredentialsValid", Status: "False", LastTransitionTime: earlier, Reason: "SomeError1", Message: "some previous error 1"},
|
||||
{Type: "ClientCredentialsSecretValid", Status: "False", LastTransitionTime: earlier, Reason: "SomeError1", Message: "some previous error 1"},
|
||||
{Type: "OIDCDiscoverySucceeded", Status: "False", LastTransitionTime: earlier, Reason: "SomeError2", Message: "some previous error 2"},
|
||||
},
|
||||
},
|
||||
@@ -985,7 +985,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Data: testValidSecretData,
|
||||
}},
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="discovered issuer configuration" "reason"="Success" "status"="True" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
},
|
||||
@@ -1010,7 +1010,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Phase: "Ready",
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{Type: "ClientCredentialsValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "loaded client credentials"},
|
||||
{Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "loaded client credentials"},
|
||||
{Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "discovered issuer configuration"},
|
||||
},
|
||||
},
|
||||
@@ -1030,7 +1030,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Phase: "Ready",
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidConditionEarlier,
|
||||
{Type: "ClientCredentialsValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials"},
|
||||
{Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials"},
|
||||
{Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration"},
|
||||
},
|
||||
},
|
||||
@@ -1041,7 +1041,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Data: testValidSecretData,
|
||||
}},
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="discovered issuer configuration" "reason"="Success" "status"="True" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
},
|
||||
@@ -1066,7 +1066,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Phase: "Ready",
|
||||
Conditions: []metav1.Condition{
|
||||
{Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234},
|
||||
{Type: "ClientCredentialsValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234},
|
||||
{Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234},
|
||||
{Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234},
|
||||
},
|
||||
},
|
||||
@@ -1086,7 +1086,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Phase: "Ready",
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidConditionEarlier,
|
||||
{Type: "ClientCredentialsValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials"},
|
||||
{Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials"},
|
||||
{Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration"},
|
||||
},
|
||||
},
|
||||
@@ -1097,7 +1097,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Data: testValidSecretData,
|
||||
}},
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="discovered issuer configuration" "reason"="Success" "status"="True" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
},
|
||||
@@ -1122,7 +1122,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Phase: "Ready",
|
||||
Conditions: []metav1.Condition{
|
||||
{Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234},
|
||||
{Type: "ClientCredentialsValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234},
|
||||
{Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234},
|
||||
{Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234},
|
||||
},
|
||||
},
|
||||
@@ -1145,7 +1145,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Phase: "Ready",
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidConditionEarlier,
|
||||
{Type: "ClientCredentialsValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials"},
|
||||
{Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials"},
|
||||
{Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration"},
|
||||
},
|
||||
},
|
||||
@@ -1156,7 +1156,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Data: testValidSecretData,
|
||||
}},
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="discovered issuer configuration" "reason"="Success" "status"="True" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
},
|
||||
@@ -1181,7 +1181,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Phase: "Ready",
|
||||
Conditions: []metav1.Condition{
|
||||
{Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234},
|
||||
{Type: "ClientCredentialsValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234},
|
||||
{Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234},
|
||||
{Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234},
|
||||
},
|
||||
},
|
||||
@@ -1212,7 +1212,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Phase: "Ready",
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidConditionEarlier,
|
||||
{Type: "ClientCredentialsValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials"},
|
||||
{Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials"},
|
||||
{Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration"},
|
||||
},
|
||||
},
|
||||
@@ -1223,7 +1223,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Data: testValidSecretData,
|
||||
}},
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="discovered issuer configuration" "reason"="Success" "status"="True" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
},
|
||||
@@ -1250,7 +1250,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Phase: "Ready",
|
||||
Conditions: []metav1.Condition{
|
||||
{Type: "AdditionalAuthorizeParametersValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "additionalAuthorizeParameters parameter names are allowed", ObservedGeneration: 1234},
|
||||
{Type: "ClientCredentialsValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234},
|
||||
{Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234},
|
||||
{Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: earlier, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234},
|
||||
},
|
||||
},
|
||||
@@ -1287,7 +1287,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
}},
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="discovered issuer configuration" "reason"="Success" "status"="True" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="the following additionalAuthorizeParameters are not allowed: response_type,scope,client_id,state,nonce,code_challenge,code_challenge_method,redirect_uri,hd" "reason"="DisallowedParameterName" "status"="False" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="the following additionalAuthorizeParameters are not allowed: response_type,scope,client_id,state,nonce,code_challenge,code_challenge_method,redirect_uri,hd" "name"="test-name" "namespace"="test-namespace" "reason"="DisallowedParameterName" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
@@ -1301,7 +1301,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
{Type: "AdditionalAuthorizeParametersValid", Status: "False", LastTransitionTime: now, Reason: "DisallowedParameterName",
|
||||
Message: "the following additionalAuthorizeParameters are not allowed: " +
|
||||
"response_type,scope,client_id,state,nonce,code_challenge,code_challenge_method,redirect_uri,hd", ObservedGeneration: 1234},
|
||||
{Type: "ClientCredentialsValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234},
|
||||
{Type: "ClientCredentialsSecretValid", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "loaded client credentials", ObservedGeneration: 1234},
|
||||
{Type: "OIDCDiscoverySucceeded", Status: "True", LastTransitionTime: now, Reason: "Success", Message: "discovered issuer configuration", ObservedGeneration: 1234},
|
||||
},
|
||||
},
|
||||
@@ -1325,7 +1325,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "msg"="failed to perform OIDC discovery" "error"="oidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/ends-with-slash\" got \"` + testIssuerURL + `/ends-with-slash/\"" "issuer"="` + testIssuerURL + `/ends-with-slash" "name"="test-name" "namespace"="test-namespace"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="failed to perform OIDC discovery against \"` + testIssuerURL + `/ends-with-slash\":\noidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/ends-with-slash\" got \"` + testIssuerURL + `/ends-with-slash/\"" "reason"="Unreachable" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="failed to perform OIDC discovery against \"` + testIssuerURL + `/ends-with-slash\":\noidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/ends-with-slash\" got \"` + testIssuerURL + `/ends-with-slash/\"" "name"="test-name" "namespace"="test-namespace" "reason"="Unreachable" "type"="OIDCDiscoverySucceeded"`,
|
||||
@@ -1338,7 +1338,7 @@ Get "` + testIssuerURL + `/valid-url-that-is-really-really-long-nananananananana
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
@@ -1374,7 +1374,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + testIs
|
||||
wantErr: controllerlib.ErrSyntheticRequeue.Error(),
|
||||
wantLogs: []string{
|
||||
`oidc-upstream-observer "msg"="failed to perform OIDC discovery" "error"="oidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/\" got \"` + testIssuerURL + `\"" "issuer"="` + testIssuerURL + `/" "name"="test-name" "namespace"="test-namespace"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="loaded client credentials" "reason"="Success" "status"="True" "type"="ClientCredentialsSecretValid"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="failed to perform OIDC discovery against \"` + testIssuerURL + `/\":\noidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/\" got \"` + testIssuerURL + `\"" "reason"="Unreachable" "status"="False" "type"="OIDCDiscoverySucceeded"`,
|
||||
`oidc-upstream-observer "level"=0 "msg"="updated condition" "name"="test-name" "namespace"="test-namespace" "message"="additionalAuthorizeParameters parameter names are allowed" "reason"="Success" "status"="True" "type"="AdditionalAuthorizeParametersValid"`,
|
||||
`oidc-upstream-observer "msg"="found failing condition" "error"="OIDCIdentityProvider has a failing condition" "message"="failed to perform OIDC discovery against \"` + testIssuerURL + `/\":\noidc: issuer did not match the issuer returned by provider, expected \"` + testIssuerURL + `/\" got \"` + testIssuerURL + `\"" "name"="test-name" "namespace"="test-namespace" "reason"="Unreachable" "type"="OIDCDiscoverySucceeded"`,
|
||||
@@ -1387,7 +1387,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + testIs
|
||||
Conditions: []metav1.Condition{
|
||||
happyAdditionalAuthorizeParametersValidCondition,
|
||||
{
|
||||
Type: "ClientCredentialsValid",
|
||||
Type: "ClientCredentialsSecretValid",
|
||||
Status: "True",
|
||||
LastTransitionTime: now,
|
||||
Reason: "Success",
|
||||
@@ -1448,7 +1448,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + testIs
|
||||
require.Equal(t, len(tt.wantResultingCache), len(actualIDPList))
|
||||
for i := range actualIDPList {
|
||||
actualIDP := actualIDPList[i].(*upstreamoidc.ProviderConfig)
|
||||
require.Equal(t, tt.wantResultingCache[i].GetName(), actualIDP.GetName())
|
||||
require.Equal(t, tt.wantResultingCache[i].GetResourceName(), actualIDP.GetResourceName())
|
||||
require.Equal(t, tt.wantResultingCache[i].GetClientID(), actualIDP.GetClientID())
|
||||
require.Equal(t, tt.wantResultingCache[i].GetAuthorizationURL().String(), actualIDP.GetAuthorizationURL().String())
|
||||
require.Equal(t, tt.wantResultingCache[i].GetUsernameClaim(), actualIDP.GetUsernameClaim())
|
||||
|
||||
@@ -246,7 +246,7 @@ func (c *garbageCollectorController) tryRevokeUpstreamOIDCToken(ctx context.Cont
|
||||
// Try to find the provider that was originally used to create the stored session.
|
||||
var foundOIDCIdentityProviderI upstreamprovider.UpstreamOIDCIdentityProviderI
|
||||
for _, p := range c.idpCache.GetOIDCIdentityProviders() {
|
||||
if p.GetName() == customSessionData.ProviderName && p.GetResourceUID() == customSessionData.ProviderUID {
|
||||
if p.GetResourceName() == customSessionData.ProviderName && p.GetResourceUID() == customSessionData.ProviderUID {
|
||||
foundOIDCIdentityProviderI = p
|
||||
break
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
|
||||
spec.Run(t, "Sync", func(t *testing.T, when spec.G, it spec.S) {
|
||||
const (
|
||||
installedInNamespace = "some-namespace"
|
||||
currentSessionStorageVersion = "7" // update this when you update the storage version in the production code
|
||||
currentSessionStorageVersion = "8" // update this when you update the storage version in the production code
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -4,9 +4,17 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/util/cert"
|
||||
|
||||
authv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
|
||||
idpv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1"
|
||||
"go.pinniped.dev/internal/controllerlib"
|
||||
)
|
||||
|
||||
@@ -43,12 +51,16 @@ func SimpleFilter(match func(metav1.Object) bool, parentFunc controllerlib.Paren
|
||||
}
|
||||
}
|
||||
|
||||
func MatchAnySecretOfTypeFilter(secretType corev1.SecretType, parentFunc controllerlib.ParentFunc) controllerlib.Filter {
|
||||
func MatchAnySecretOfTypeFilter(secretType corev1.SecretType, parentFunc controllerlib.ParentFunc, namespaces ...string) controllerlib.Filter {
|
||||
isSecretOfType := func(obj metav1.Object) bool {
|
||||
secret, ok := obj.(*corev1.Secret)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
// Only match on namespace if namespaces are provided
|
||||
if len(namespaces) > 0 && !slices.Contains(namespaces, secret.Namespace) {
|
||||
return false
|
||||
}
|
||||
return secret.Type == secretType
|
||||
}
|
||||
return SimpleFilter(isSecretOfType, parentFunc)
|
||||
@@ -87,3 +99,43 @@ type WithInformerOptionFunc func(
|
||||
|
||||
// Same signature as controllerlib.WithInitialEvent().
|
||||
type WithInitialEventOptionFunc func(key controllerlib.Key) controllerlib.Option
|
||||
|
||||
// BuildCertPoolAuth returns a PEM-encoded CA bundle from the provided spec. If the provided spec is nil, a
|
||||
// nil CA bundle will be returned. If the provided spec contains a CA bundle that is not properly
|
||||
// encoded, an error will be returned.
|
||||
func BuildCertPoolAuth(spec *authv1alpha1.TLSSpec) (*x509.CertPool, []byte, error) {
|
||||
if spec == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
return buildCertPool(spec.CertificateAuthorityData)
|
||||
}
|
||||
|
||||
// BuildCertPoolIDP returns a PEM-encoded CA bundle from the provided spec. If the provided spec is nil, a
|
||||
// nil CA bundle will be returned. If the provided spec contains a CA bundle that is not properly
|
||||
// encoded, an error will be returned.
|
||||
func BuildCertPoolIDP(spec *idpv1alpha1.TLSSpec) (*x509.CertPool, []byte, error) {
|
||||
if spec == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
return buildCertPool(spec.CertificateAuthorityData)
|
||||
}
|
||||
|
||||
func buildCertPool(certificateAuthorityData string) (*x509.CertPool, []byte, error) {
|
||||
if len(certificateAuthorityData) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
pem, err := base64.StdEncoding.DecodeString(certificateAuthorityData)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rootCAs, err := cert.NewPoolFromBytes(pem)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("certificateAuthorityData is not valid PEM: %w", err)
|
||||
}
|
||||
|
||||
return rootCAs, pem, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user