Merge pull request #1908 from vmware-tanzu/ben/github/identity-provider/federation-domain-resolved-github

Allow GitHub IDP type in FederationDomains
This commit is contained in:
Ben Petersen
2024-04-29 16:07:51 -04:00
committed by GitHub
37 changed files with 948 additions and 127 deletions

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
@@ -15,6 +15,7 @@ const (
IDPTypeOIDC IDPType = "oidc"
IDPTypeLDAP IDPType = "ldap"
IDPTypeActiveDirectory IDPType = "activedirectory"
IDPTypeGitHub IDPType = "github"
IDPFlowCLIPassword IDPFlow = "cli_password"
IDPFlowBrowserAuthcode IDPFlow = "browser_authcode"

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package cmd
@@ -143,7 +143,18 @@ func kubeconfigCommand(deps kubeconfigDeps) *cobra.Command {
f.BoolVar(&flags.oidc.debugSessionCache, "oidc-debug-session-cache", false, "Print debug logs related to the OpenID Connect session cache")
f.StringVar(&flags.oidc.requestAudience, "oidc-request-audience", "", "Request a token with an alternate audience using RFC8693 token exchange")
f.StringVar(&flags.oidc.upstreamIDPName, "upstream-identity-provider-name", "", "The name of the upstream identity provider used during login with a Supervisor")
f.StringVar(&flags.oidc.upstreamIDPType, "upstream-identity-provider-type", "", fmt.Sprintf("The type of the upstream identity provider used during login with a Supervisor (e.g. '%s', '%s', '%s')", idpdiscoveryv1alpha1.IDPTypeOIDC, idpdiscoveryv1alpha1.IDPTypeLDAP, idpdiscoveryv1alpha1.IDPTypeActiveDirectory))
f.StringVar(
&flags.oidc.upstreamIDPType,
"upstream-identity-provider-type",
"",
fmt.Sprintf(
"The type of the upstream identity provider used during login with a Supervisor (e.g. '%s', '%s', '%s', '%s')",
idpdiscoveryv1alpha1.IDPTypeOIDC,
idpdiscoveryv1alpha1.IDPTypeLDAP,
idpdiscoveryv1alpha1.IDPTypeActiveDirectory,
idpdiscoveryv1alpha1.IDPTypeGitHub,
),
)
f.StringVar(&flags.oidc.upstreamIDPFlow, "upstream-identity-provider-flow", "", fmt.Sprintf("The type of client flow to use with the upstream identity provider during login with a Supervisor (e.g. '%s', '%s')", idpdiscoveryv1alpha1.IDPFlowCLIPassword, idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode))
f.StringVar(&flags.kubeconfigPath, "kubeconfig", os.Getenv("KUBECONFIG"), "Path to kubeconfig file")
f.StringVar(&flags.kubeconfigContextOverride, "kubeconfig-context", "", "Kubeconfig context name (default: current active context)")

View File

@@ -156,7 +156,7 @@ func TestGetKubeconfig(t *testing.T) {
--timeout duration Timeout for autodiscovery and validation (default 10m0s)
--upstream-identity-provider-flow string The type of client flow to use with the upstream identity provider during login with a Supervisor (e.g. 'cli_password', 'browser_authcode')
--upstream-identity-provider-name string The name of the upstream identity provider used during login with a Supervisor
--upstream-identity-provider-type string The type of the upstream identity provider used during login with a Supervisor (e.g. 'oidc', 'ldap', 'activedirectory')
--upstream-identity-provider-type string The type of the upstream identity provider used during login with a Supervisor (e.g. 'oidc', 'ldap', 'activedirectory', 'github')
`)
},
},
@@ -908,7 +908,8 @@ func TestGetKubeconfig(t *testing.T) {
idpsDiscoveryResponse: here.Docf(`{
"pinniped_identity_providers": [
{"name": "some-ldap-idp", "type": "ldap"},
{"name": "some-oidc-idp", "type": "oidc"}
{"name": "some-oidc-idp", "type": "oidc"},
{"name": "some-github-idp", "type": "github"}
]
}`),
wantLogs: func(issuerCABundle string, issuerURL string) []string {
@@ -927,7 +928,7 @@ func TestGetKubeconfig(t *testing.T) {
wantStderr: func(issuerCABundle string, issuerURL string) testutil.RequireErrorStringFunc {
return testutil.WantExactErrorString(`Error: multiple Supervisor upstream identity providers were found, ` +
`so the --upstream-identity-provider-name/--upstream-identity-provider-type flags must be specified. ` +
`Found these upstreams: [{"name":"some-ldap-idp","type":"ldap"},{"name":"some-oidc-idp","type":"oidc"}]` + "\n")
`Found these upstreams: [{"name":"some-ldap-idp","type":"ldap"},{"name":"some-oidc-idp","type":"oidc"},{"name":"some-github-idp","type":"github"}]` + "\n")
},
},
{

View File

@@ -304,7 +304,7 @@ func flowOptions(
}
switch requestedIDPType {
case idpdiscoveryv1alpha1.IDPTypeOIDC:
case idpdiscoveryv1alpha1.IDPTypeOIDC, idpdiscoveryv1alpha1.IDPTypeGitHub:
switch requestedFlow {
case idpdiscoveryv1alpha1.IDPFlowCLIPassword:
return useCLIFlow, nil
@@ -337,6 +337,7 @@ func flowOptions(
idpdiscoveryv1alpha1.IDPTypeOIDC.String(),
idpdiscoveryv1alpha1.IDPTypeLDAP.String(),
idpdiscoveryv1alpha1.IDPTypeActiveDirectory.String(),
idpdiscoveryv1alpha1.IDPTypeGitHub.String(),
}, ", "),
)
}

View File

@@ -161,7 +161,7 @@ func TestLoginOIDCCommand(t *testing.T) {
},
wantError: true,
wantStderr: here.Doc(`
Error: --upstream-identity-provider-type value not recognized: invalid (supported values: oidc, ldap, activedirectory)
Error: --upstream-identity-provider-type value not recognized: invalid (supported values: oidc, ldap, activedirectory, github)
`),
},
{
@@ -173,7 +173,7 @@ func TestLoginOIDCCommand(t *testing.T) {
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "browser_authcode"},
wantError: true,
wantStderr: here.Doc(`
Error: --upstream-identity-provider-type value not recognized: invalid (supported values: oidc, ldap, activedirectory)
Error: --upstream-identity-provider-type value not recognized: invalid (supported values: oidc, ldap, activedirectory, github)
`),
},
{

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
@@ -15,6 +15,7 @@ const (
IDPTypeOIDC IDPType = "oidc"
IDPTypeLDAP IDPType = "ldap"
IDPTypeActiveDirectory IDPType = "activedirectory"
IDPTypeGitHub IDPType = "github"
IDPFlowCLIPassword IDPFlow = "cli_password"
IDPFlowBrowserAuthcode IDPFlow = "browser_authcode"

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
@@ -15,6 +15,7 @@ const (
IDPTypeOIDC IDPType = "oidc"
IDPTypeLDAP IDPType = "ldap"
IDPTypeActiveDirectory IDPType = "activedirectory"
IDPTypeGitHub IDPType = "github"
IDPFlowCLIPassword IDPFlow = "cli_password"
IDPFlowBrowserAuthcode IDPFlow = "browser_authcode"

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
@@ -15,6 +15,7 @@ const (
IDPTypeOIDC IDPType = "oidc"
IDPTypeLDAP IDPType = "ldap"
IDPTypeActiveDirectory IDPType = "activedirectory"
IDPTypeGitHub IDPType = "github"
IDPFlowCLIPassword IDPFlow = "cli_password"
IDPFlowBrowserAuthcode IDPFlow = "browser_authcode"

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
@@ -15,6 +15,7 @@ const (
IDPTypeOIDC IDPType = "oidc"
IDPTypeLDAP IDPType = "ldap"
IDPTypeActiveDirectory IDPType = "activedirectory"
IDPTypeGitHub IDPType = "github"
IDPFlowCLIPassword IDPFlow = "cli_password"
IDPFlowBrowserAuthcode IDPFlow = "browser_authcode"

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
@@ -15,6 +15,7 @@ const (
IDPTypeOIDC IDPType = "oidc"
IDPTypeLDAP IDPType = "ldap"
IDPTypeActiveDirectory IDPType = "activedirectory"
IDPTypeGitHub IDPType = "github"
IDPFlowCLIPassword IDPFlow = "cli_password"
IDPFlowBrowserAuthcode IDPFlow = "browser_authcode"

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
@@ -15,6 +15,7 @@ const (
IDPTypeOIDC IDPType = "oidc"
IDPTypeLDAP IDPType = "ldap"
IDPTypeActiveDirectory IDPType = "activedirectory"
IDPTypeGitHub IDPType = "github"
IDPFlowCLIPassword IDPFlow = "cli_password"
IDPFlowBrowserAuthcode IDPFlow = "browser_authcode"

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
@@ -15,6 +15,7 @@ const (
IDPTypeOIDC IDPType = "oidc"
IDPTypeLDAP IDPType = "ldap"
IDPTypeActiveDirectory IDPType = "activedirectory"
IDPTypeGitHub IDPType = "github"
IDPFlowCLIPassword IDPFlow = "cli_password"
IDPFlowBrowserAuthcode IDPFlow = "browser_authcode"

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
@@ -15,6 +15,7 @@ const (
IDPTypeOIDC IDPType = "oidc"
IDPTypeLDAP IDPType = "ldap"
IDPTypeActiveDirectory IDPType = "activedirectory"
IDPTypeGitHub IDPType = "github"
IDPFlowCLIPassword IDPFlow = "cli_password"
IDPFlowBrowserAuthcode IDPFlow = "browser_authcode"

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
@@ -15,6 +15,7 @@ const (
IDPTypeOIDC IDPType = "oidc"
IDPTypeLDAP IDPType = "ldap"
IDPTypeActiveDirectory IDPType = "activedirectory"
IDPTypeGitHub IDPType = "github"
IDPFlowCLIPassword IDPFlow = "cli_password"
IDPFlowBrowserAuthcode IDPFlow = "browser_authcode"

View File

@@ -1,4 +1,4 @@
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
@@ -15,6 +15,7 @@ const (
IDPTypeOIDC IDPType = "oidc"
IDPTypeLDAP IDPType = "ldap"
IDPTypeActiveDirectory IDPType = "activedirectory"
IDPTypeGitHub IDPType = "github"
IDPFlowCLIPassword IDPFlow = "cli_password"
IDPFlowBrowserAuthcode IDPFlow = "browser_authcode"

View File

@@ -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)

View File

@@ -42,6 +42,7 @@ func TestFederationDomainWatcherControllerInformerFilters(t *testing.T) {
oidcIdentityProviderInformer := pinnipedinformers.NewSharedInformerFactoryWithOptions(nil, 0).IDP().V1alpha1().OIDCIdentityProviders()
ldapIdentityProviderInformer := pinnipedinformers.NewSharedInformerFactoryWithOptions(nil, 0).IDP().V1alpha1().LDAPIdentityProviders()
adIdentityProviderInformer := pinnipedinformers.NewSharedInformerFactoryWithOptions(nil, 0).IDP().V1alpha1().ActiveDirectoryIdentityProviders()
githubIdentityProviderInformer := pinnipedinformers.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 {
@@ -100,6 +108,7 @@ func TestFederationDomainWatcherControllerInformerFilters(t *testing.T) {
oidcIdentityProviderInformer,
ldapIdentityProviderInformer,
adIdentityProviderInformer,
githubIdentityProviderInformer,
withInformer.WithInformer, // make it possible to observe the behavior of the Filters
)
@@ -163,6 +172,14 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
},
}
gitHubIdentityProvider := &idpv1alpha1.GitHubIdentityProvider{
ObjectMeta: metav1.ObjectMeta{
Name: "some-github-idp",
Namespace: namespace,
UID: "some-github-idp",
},
}
federationDomain1 := &configv1alpha1.FederationDomain{
ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: namespace, Generation: 123},
Spec: configv1alpha1.FederationDomainSpec{Issuer: "https://issuer1.com"},
@@ -487,7 +504,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),
}
}
@@ -604,6 +621,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",
@@ -948,6 +989,7 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
oidcIdentityProvider,
ldapIdentityProvider,
adIdentityProvider,
gitHubIdentityProvider,
},
wantFDIssuers: []*federationdomainproviders.FederationDomainIssuer{},
wantStatusUpdates: []*configv1alpha1.FederationDomain{
@@ -956,7 +998,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),
}),
),
@@ -994,6 +1036,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",
},
},
},
},
},
@@ -1013,7 +1063,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),
}),
@@ -1026,6 +1078,7 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
oidcIdentityProvider,
ldapIdentityProvider,
adIdentityProvider,
gitHubIdentityProvider,
&configv1alpha1.FederationDomain{
ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: namespace, Generation: 123},
Spec: configv1alpha1.FederationDomainSpec{
@@ -1055,6 +1108,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,
},
},
},
},
},
@@ -1077,6 +1138,11 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
UID: adIdentityProvider.UID,
Transforms: idtransform.NewTransformationPipeline(),
},
{
DisplayName: "can-find-me-four",
UID: gitHubIdentityProvider.UID,
Transforms: idtransform.NewTransformationPipeline(),
},
}),
},
wantStatusUpdates: []*configv1alpha1.FederationDomain{
@@ -1095,6 +1161,7 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
oidcIdentityProvider,
ldapIdentityProvider,
adIdentityProvider,
gitHubIdentityProvider,
&configv1alpha1.FederationDomain{
ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: namespace, Generation: 123},
Spec: configv1alpha1.FederationDomainSpec{
@@ -1148,6 +1215,14 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
Name: adIdentityProvider.Name,
},
},
{
DisplayName: "duplicate2",
ObjectRef: corev1.TypedLocalObjectReference{
APIGroup: ptr.To(apiGroupSupervisor),
Kind: "GitHubIdentityProvider",
Name: gitHubIdentityProvider.Name,
},
},
},
},
},
@@ -1174,6 +1249,7 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
oidcIdentityProvider,
ldapIdentityProvider,
adIdentityProvider,
gitHubIdentityProvider,
&configv1alpha1.FederationDomain{
ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: namespace, Generation: 123},
Spec: configv1alpha1.FederationDomainSpec{
@@ -1211,6 +1287,14 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
Name: adIdentityProvider.Name,
},
},
{
DisplayName: "name5",
ObjectRef: corev1.TypedLocalObjectReference{
APIGroup: ptr.To(apiGroupSupervisor), // correct
Kind: "GitHubIdentityProvider",
Name: gitHubIdentityProvider.Name,
},
},
},
},
},
@@ -1244,6 +1328,7 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
oidcIdentityProvider,
ldapIdentityProvider,
adIdentityProvider,
gitHubIdentityProvider,
&configv1alpha1.FederationDomain{
ObjectMeta: metav1.ObjectMeta{Name: "config1", Namespace: namespace, Generation: 123},
Spec: configv1alpha1.FederationDomainSpec{
@@ -2024,6 +2109,7 @@ func TestTestFederationDomainWatcherControllerSync(t *testing.T) {
pinnipedInformers.IDP().V1alpha1().OIDCIdentityProviders(),
pinnipedInformers.IDP().V1alpha1().LDAPIdentityProviders(),
pinnipedInformers.IDP().V1alpha1().ActiveDirectoryIdentityProviders(),
pinnipedInformers.IDP().V1alpha1().GitHubIdentityProviders(),
controllerlib.WithInformer,
)

View File

@@ -238,7 +238,7 @@ func validateOrganizationsPolicy(organizationsSpec *v1alpha1.GitHubOrganizations
}
func (c *gitHubWatcherController) validateUpstreamAndUpdateConditions(ctx controllerlib.Context, upstream *v1alpha1.GitHubIdentityProvider) (
*upstreamgithub.ProviderConfig, // If validated, returns the config
*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)
@@ -291,22 +291,24 @@ func (c *gitHubWatcherController) validateUpstreamAndUpdateConditions(ctx contro
return nil, k8sutilerrors.NewAggregate(applicationErrors)
}
providerConfig := &upstreamgithub.ProviderConfig{
Name: upstream.Name,
ResourceUID: upstream.UID,
Host: hostURL,
GroupNameAttribute: groupNameAttribute,
UsernameAttribute: usernameAttribute,
OAuth2Config: &oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
provider := upstreamgithub.New(
upstreamgithub.ProviderConfig{
Name: upstream.Name,
ResourceUID: upstream.UID,
Host: hostURL,
GroupNameAttribute: groupNameAttribute,
UsernameAttribute: usernameAttribute,
OAuth2Config: &oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
},
AllowedOrganizations: upstream.Spec.AllowAuthentication.Organizations.Allowed,
OrganizationLoginPolicy: policy,
AuthorizationURL: fmt.Sprintf("%s/login/oauth/authorize", hostURL),
HttpClient: httpClient,
},
AllowedOrganizations: upstream.Spec.AllowAuthentication.Organizations.Allowed,
OrganizationLoginPolicy: policy,
AuthorizationURL: fmt.Sprintf("%s/login/oauth/authorize", hostURL),
HttpClient: httpClient,
}
return providerConfig, k8sutilerrors.NewAggregate(applicationErrors)
)
return provider, k8sutilerrors.NewAggregate(applicationErrors)
}
func validateHost(gitHubAPIConfig v1alpha1.GitHubAPIConfig) (*metav1.Condition, *endpointaddr.HostPort) {

View File

@@ -1717,7 +1717,9 @@ func TestController(t *testing.T) {
cache := dynamicupstreamprovider.NewDynamicUpstreamIDPProvider()
cache.SetGitHubIdentityProviders([]upstreamprovider.UpstreamGithubIdentityProviderI{
&upstreamgithub.ProviderConfig{Name: "initial-entry-to-remove"},
upstreamgithub.New(
upstreamgithub.ProviderConfig{Name: "initial-entry-to-remove"},
),
})
var log bytes.Buffer
@@ -1757,12 +1759,11 @@ func TestController(t *testing.T) {
require.Equal(t, len(tt.wantResultingCache), len(actualIDPList))
for i := 0; i < len(tt.wantResultingCache); i++ {
// Do not expect any particular order in the cache
var actualIDP *upstreamgithub.ProviderConfig
var actualIDP *upstreamgithub.Provider
for _, possibleIDP := range actualIDPList {
if possibleIDP.GetName() == tt.wantResultingCache[i].Name {
// For this check, we know that the actual IDPs are going to have type upstreamgithub.ProviderConfig
var ok bool
actualIDP, ok = possibleIDP.(*upstreamgithub.ProviderConfig)
actualIDP, ok = possibleIDP.(*upstreamgithub.Provider)
require.True(t, ok)
break
}
@@ -1785,7 +1786,7 @@ func TestController(t *testing.T) {
require.NoError(t, err)
compareTLSClientConfigWithinHttpClients(t, phttp.Default(certPool), actualIDP.GetHttpClient())
require.Equal(t, tt.wantResultingCache[i].OAuth2Config, actualIDP.OAuth2Config)
require.Equal(t, tt.wantResultingCache[i].OAuth2Config, actualIDP.GetOAuth2Config())
}
// Verify the status conditions as reported in Kubernetes

View File

@@ -39,6 +39,7 @@ func TestIDPDiscovery(t *testing.T) {
"pinniped_identity_providers": [
{"name": "a-some-ldap-idp", "type": "ldap", "flows": ["cli_password", "browser_authcode"]},
{"name": "a-some-oidc-idp", "type": "oidc", "flows": ["browser_authcode"]},
{"name": "g-some-github-idp", "type": "github", "flows": ["browser_authcode"]},
{"name": "x-some-ldap-idp", "type": "ldap", "flows": ["cli_password", "browser_authcode"]},
{"name": "x-some-oidc-idp", "type": "oidc", "flows": ["browser_authcode"]},
{"name": "y-some-ad-idp", "type": "activedirectory", "flows": ["cli_password", "browser_authcode"]},
@@ -49,6 +50,7 @@ func TestIDPDiscovery(t *testing.T) {
}`),
wantSecondResponseBodyJSON: here.Doc(`{
"pinniped_identity_providers": [
{"name": "g-some-github-idp", "type": "github", "flows": ["browser_authcode"]},
{"name": "some-other-ad-idp-1", "type": "activedirectory", "flows": ["cli_password", "browser_authcode"]},
{"name": "some-other-ad-idp-2", "type": "activedirectory", "flows": ["cli_password", "browser_authcode"]},
{"name": "some-other-ldap-idp-1", "type": "ldap", "flows": ["cli_password", "browser_authcode"]},
@@ -77,6 +79,7 @@ func TestIDPDiscovery(t *testing.T) {
WithOIDC(oidctestutil.NewTestUpstreamOIDCIdentityProviderBuilder().WithName("a-some-oidc-idp").Build()).
WithLDAP(oidctestutil.NewTestUpstreamLDAPIdentityProviderBuilder().WithName("z-some-ldap-idp").Build()).
WithLDAP(oidctestutil.NewTestUpstreamLDAPIdentityProviderBuilder().WithName("x-some-ldap-idp").Build()).
WithGitHub(oidctestutil.NewTestUpstreamGitHubIdentityProviderBuilder().WithName("g-some-github-idp").Build()).
WithActiveDirectory(oidctestutil.NewTestUpstreamLDAPIdentityProviderBuilder().WithName("z-some-ad-idp").Build()).
WithActiveDirectory(oidctestutil.NewTestUpstreamLDAPIdentityProviderBuilder().WithName("y-some-ad-idp").Build()).
BuildFederationDomainIdentityProvidersListerFinder()

View File

@@ -11,6 +11,7 @@ import (
"go.pinniped.dev/internal/federationdomain/idplister"
"go.pinniped.dev/internal/federationdomain/resolvedprovider"
"go.pinniped.dev/internal/federationdomain/resolvedprovider/resolvedgithub"
"go.pinniped.dev/internal/federationdomain/resolvedprovider/resolvedldap"
"go.pinniped.dev/internal/federationdomain/resolvedprovider/resolvedoidc"
"go.pinniped.dev/internal/idtransform"
@@ -144,6 +145,7 @@ func (u *FederationDomainIdentityProvidersListerFinder) GetIdentityProviders() [
cachedOIDCProviders := u.wrappedLister.GetOIDCIdentityProviders()
cachedLDAPProviders := u.wrappedLister.GetLDAPIdentityProviders()
cachedADProviders := u.wrappedLister.GetActiveDirectoryIdentityProviders()
cachedGitHubProviders := u.wrappedLister.GetGitHubIdentityProviders()
providers := []resolvedprovider.FederationDomainResolvedIdentityProvider{}
// Every configured identityProvider on the FederationDomain uses an objetRef to an underlying IDP CR that might
// be available as a provider in the wrapped cache. For each configured identityProvider/displayName...
@@ -184,6 +186,16 @@ func (u *FederationDomainIdentityProvidersListerFinder) GetIdentityProviders() [
})
}
}
for _, p := range cachedGitHubProviders {
if idp.UID == p.GetResourceUID() {
providers = append(providers, &resolvedgithub.FederationDomainResolvedGitHubIdentityProvider{
DisplayName: idp.DisplayName,
Provider: p,
SessionProviderType: psession.ProviderTypeGitHub,
Transforms: idp.Transforms,
})
}
}
}
return providers
}

View File

@@ -10,6 +10,7 @@ import (
"go.pinniped.dev/internal/federationdomain/idplister"
"go.pinniped.dev/internal/federationdomain/resolvedprovider"
"go.pinniped.dev/internal/federationdomain/resolvedprovider/resolvedgithub"
"go.pinniped.dev/internal/federationdomain/resolvedprovider/resolvedldap"
"go.pinniped.dev/internal/federationdomain/resolvedprovider/resolvedoidc"
"go.pinniped.dev/internal/testutil/oidctestutil"
@@ -52,6 +53,14 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
WithName("my-ad-idp2").
WithResourceUID("my-ad-uid-idp2").
Build()
myDefaultGitHubIDP := oidctestutil.NewTestUpstreamGitHubIdentityProviderBuilder().
WithName("my-default-github-idp").
WithResourceUID("my-default-github-uid-idp").
Build()
myGitHubIDP1 := oidctestutil.NewTestUpstreamGitHubIdentityProviderBuilder().
WithName("my-github-idp1").
WithResourceUID("my-github-uid-idp1").
Build()
// FederationDomainIssuers
fakeIssuerURL := "https://www.fakeissuerurl.com"
@@ -77,13 +86,20 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
})
require.NoError(t, err)
fdIssuerWithOIDCAndLDAPAndADIDPs, err := NewFederationDomainIssuer(fakeIssuerURL, []*FederationDomainIdentityProvider{
fdIssuerWithDefaultGitHubIDP, err := NewFederationDomainIssuerWithDefaultIDP(fakeIssuerURL, &FederationDomainIdentityProvider{
DisplayName: "my-default-github-idp",
UID: "my-default-github-uid-idp",
})
require.NoError(t, err)
fdIssuerWithOIDCAndLDAPAndADAndGitHubIDPs, err := NewFederationDomainIssuer(fakeIssuerURL, []*FederationDomainIdentityProvider{
{DisplayName: "my-oidc-idp1", UID: "my-oidc-uid-idp1"},
{DisplayName: "my-oidc-idp2", UID: "my-oidc-uid-idp2"},
{DisplayName: "my-ldap-idp1", UID: "my-ldap-uid-idp1"},
{DisplayName: "my-ldap-idp2", UID: "my-ldap-uid-idp2"},
{DisplayName: "my-ad-idp1", UID: "my-ad-uid-idp1"},
{DisplayName: "my-ad-idp2", UID: "my-ad-uid-idp2"},
{DisplayName: "my-github-idp1", UID: "my-github-uid-idp1"},
})
require.NoError(t, err)
@@ -99,6 +115,7 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
{DisplayName: "my-ldap-idp4", UID: "my-ldap-uid-idp4"},
{DisplayName: "my-ad-idp2", UID: "my-ad-uid-idp2"},
{DisplayName: "my-ad-idp3", UID: "my-ad-uid-idp3"},
{DisplayName: "my-github-idp1", UID: "my-github-uid-idp1"},
})
require.NoError(t, err)
@@ -133,6 +150,11 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
Provider: myADIDP1,
SessionProviderType: "activedirectory",
}
myGitHub1Resolved := &resolvedgithub.FederationDomainResolvedGitHubIdentityProvider{
DisplayName: "my-github-idp1",
Provider: myGitHubIDP1,
SessionProviderType: "github",
}
myDefaultOIDCIDPResolved := &resolvedoidc.FederationDomainResolvedOIDCIdentityProvider{
DisplayName: "my-default-oidc-idp",
@@ -144,15 +166,21 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
Provider: myDefaultLDAPIDP,
SessionProviderType: "ldap",
}
myDefaultGitHubIDPResolved := &resolvedgithub.FederationDomainResolvedGitHubIdentityProvider{
DisplayName: "my-default-github-idp",
Provider: myDefaultGitHubIDP,
SessionProviderType: "github",
}
testFindUpstreamIDPByDisplayName := []struct {
name string
wrappedLister idplister.UpstreamIdentityProvidersLister
federationDomainIssuer *FederationDomainIssuer
findIDPByDisplayName string
wantOIDCIDPByDisplayName *resolvedoidc.FederationDomainResolvedOIDCIdentityProvider
wantLDAPIDPByDisplayName *resolvedldap.FederationDomainResolvedLDAPIdentityProvider
wantError string
name string
wrappedLister idplister.UpstreamIdentityProvidersLister
federationDomainIssuer *FederationDomainIssuer
findIDPByDisplayName string
wantOIDCIDPByDisplayName *resolvedoidc.FederationDomainResolvedOIDCIdentityProvider
wantLDAPIDPByDisplayName *resolvedldap.FederationDomainResolvedLDAPIdentityProvider
wantGitHubIDPByDisplayName *resolvedgithub.FederationDomainResolvedGitHubIdentityProvider
wantError string
}{
{
name: "FindUpstreamIDPByDisplayName will find an upstream IdP by display name with one IDP configured",
@@ -182,8 +210,9 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
WithOIDC(myOIDCIDP2).
WithLDAP(myLDAPIDP1).
WithLDAP(myLDAPIDP2).
WithGitHub(myGitHubIDP1).
BuildDynamicUpstreamIDPProvider(),
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADIDPs,
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADAndGitHubIDPs,
wantOIDCIDPByDisplayName: myOIDCIDP1Resolved,
},
{
@@ -195,6 +224,7 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
WithLDAP(myLDAPIDP1).
WithLDAP(myLDAPIDP2).
WithActiveDirectory(myADIDP1).
WithGitHub(myGitHubIDP1).
BuildDynamicUpstreamIDPProvider(),
federationDomainIssuer: fdIssuerWithOIDCIDP1,
wantOIDCIDPByDisplayName: myOIDCIDP1Resolved,
@@ -208,11 +238,13 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
WithLDAP(myLDAPIDP1).
WithLDAP(myLDAPIDP2).
WithActiveDirectory(myADIDP1).
WithGitHub(myGitHubIDP1).
BuildDynamicUpstreamIDPProvider(),
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADIDPs,
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADAndGitHubIDPs,
wantLDAPIDPByDisplayName: myLDAPIDP1Resolved,
},
{
name: "FindUpstreamIDPByDisplayName will find an upstream IDP of type AD (LDAP) by display name",
name: "FindUpstreamIDPByDisplayName will find an upstream IDP of type AD (LDAP) by display name",
findIDPByDisplayName: "my-ad-idp1",
wrappedLister: testidplister.NewUpstreamIDPListerBuilder().
WithOIDC(myOIDCIDP1).
@@ -220,10 +252,25 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
WithLDAP(myLDAPIDP1).
WithLDAP(myLDAPIDP2).
WithActiveDirectory(myADIDP1).
WithGitHub(myGitHubIDP1).
BuildDynamicUpstreamIDPProvider(),
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADIDPs,
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADAndGitHubIDPs,
wantLDAPIDPByDisplayName: myADIDP1Resolved,
},
{
name: "FindUpstreamIDPByDisplayName will find an upstream IDP of type GitHub by display name",
findIDPByDisplayName: "my-github-idp1",
wrappedLister: testidplister.NewUpstreamIDPListerBuilder().
WithOIDC(myOIDCIDP1).
WithOIDC(myOIDCIDP2).
WithLDAP(myLDAPIDP1).
WithLDAP(myLDAPIDP2).
WithActiveDirectory(myADIDP1).
WithGitHub(myGitHubIDP1).
BuildDynamicUpstreamIDPProvider(),
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADAndGitHubIDPs,
wantGitHubIDPByDisplayName: myGitHub1Resolved,
},
{
name: "FindUpstreamIDPByDisplayName will error if IDP by display name is not found - no such display name",
findIDPByDisplayName: "i-cant-find-my-idp",
@@ -233,8 +280,9 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
WithLDAP(myLDAPIDP1).
WithLDAP(myLDAPIDP2).
WithActiveDirectory(myADIDP1).
WithGitHub(myGitHubIDP1).
BuildDynamicUpstreamIDPProvider(),
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADIDPs,
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADAndGitHubIDPs,
wantError: `identity provider not found: "i-cant-find-my-idp"`,
},
{
@@ -266,6 +314,9 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
if tt.wantLDAPIDPByDisplayName != nil {
require.Equal(t, tt.wantLDAPIDPByDisplayName, foundIDP)
}
if tt.wantGitHubIDPByDisplayName != nil {
require.Equal(t, tt.wantGitHubIDPByDisplayName, foundIDP)
}
})
}
@@ -275,6 +326,7 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
federationDomainIssuer *FederationDomainIssuer
wantDefaultOIDCIDP *resolvedoidc.FederationDomainResolvedOIDCIdentityProvider
wantDefaultLDAPIDP *resolvedldap.FederationDomainResolvedLDAPIdentityProvider
wantDefaultGitHubIDP *resolvedgithub.FederationDomainResolvedGitHubIdentityProvider
wantError string
}{
{
@@ -293,6 +345,14 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
federationDomainIssuer: fdIssuerWithDefaultLDAPIDP,
wantDefaultLDAPIDP: myDefaultLDAPIDPResolved,
},
{
name: "FindDefaultIDP resturns a GitHubIdentityProvider if there is a GitHubIdentityProvider defined as the default IDP",
wrappedLister: testidplister.NewUpstreamIDPListerBuilder().
WithGitHub(myDefaultGitHubIDP).
BuildDynamicUpstreamIDPProvider(),
federationDomainIssuer: fdIssuerWithDefaultGitHubIDP,
wantDefaultGitHubIDP: myDefaultGitHubIDPResolved,
},
{
name: "FindDefaultIDP returns an error if there is no default IDP to return",
wrappedLister: testidplister.NewUpstreamIDPListerBuilder().
@@ -342,6 +402,9 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
if tt.wantDefaultLDAPIDP != nil {
require.Equal(t, tt.wantDefaultLDAPIDP, foundIDP)
}
if tt.wantDefaultGitHubIDP != nil {
require.Equal(t, tt.wantDefaultGitHubIDP, foundIDP)
}
})
}
@@ -359,14 +422,16 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
WithLDAP(myLDAPIDP1).
WithLDAP(myLDAPIDP2).
WithActiveDirectory(myADIDP1).
WithGitHub(myGitHubIDP1).
BuildDynamicUpstreamIDPProvider(),
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADIDPs,
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADAndGitHubIDPs,
wantIDPs: []resolvedprovider.FederationDomainResolvedIdentityProvider{
myOIDCIDP1Resolved,
myOIDCIDP2Resolved,
myLDAPIDP1Resolved,
myLDAPIDP2Resolved,
myADIDP1Resolved,
myGitHub1Resolved,
},
},
{
@@ -380,6 +445,7 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
Build()).
WithLDAP(myLDAPIDP1).
WithActiveDirectory(myADIDP1).
WithGitHub(myGitHubIDP1).
BuildDynamicUpstreamIDPProvider(),
federationDomainIssuer: fdIssuerWithLotsOfIDPs,
wantIDPs: []resolvedprovider.FederationDomainResolvedIdentityProvider{
@@ -387,13 +453,14 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
myOIDCIDP2Resolved,
myLDAPIDP1Resolved,
myADIDP1Resolved,
myGitHub1Resolved,
},
},
{
name: "GetIdentityProviders will return empty list if no IDPs are found",
wrappedLister: testidplister.NewUpstreamIDPListerBuilder().
BuildDynamicUpstreamIDPProvider(),
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADIDPs,
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADAndGitHubIDPs,
wantIDPs: []resolvedprovider.FederationDomainResolvedIdentityProvider{},
},
}
@@ -420,7 +487,7 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
name: "IDPCount when there are none to be found",
wrappedLister: testidplister.NewUpstreamIDPListerBuilder().
BuildDynamicUpstreamIDPProvider(),
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADIDPs,
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADAndGitHubIDPs,
wantCount: 0,
},
{
@@ -443,9 +510,14 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
WithName("my-ad-idp-that-isnt-in-fd-issuer").
WithResourceUID("my-ad-idp-that-isnt-in-fd-issuer").
Build()).
WithGitHub(myGitHubIDP1).
WithGitHub(oidctestutil.NewTestUpstreamGitHubIdentityProviderBuilder().
WithName("my-github-idp-that-isnt-in-fd-issuer").
WithResourceUID("my-github-idp-that-isnt-in-fd-issuer").
Build()).
BuildDynamicUpstreamIDPProvider(),
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADIDPs,
wantCount: 5,
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADAndGitHubIDPs,
wantCount: 6,
},
}
@@ -482,6 +554,14 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
federationDomainIssuer: fdIssuerWithDefaultLDAPIDP,
wantHasDefaultIDP: true,
},
{
name: "HasDefaultIDP when there is a GitHub provider set as default",
wrappedLister: testidplister.NewUpstreamIDPListerBuilder().
WithGitHub(myDefaultGitHubIDP).
BuildDynamicUpstreamIDPProvider(),
federationDomainIssuer: fdIssuerWithDefaultGitHubIDP,
wantHasDefaultIDP: true,
},
{
name: "HasDefaultIDP when there is one set even if it cannot be found",
wrappedLister: testidplister.NewUpstreamIDPListerBuilder().
@@ -497,7 +577,7 @@ func TestFederationDomainIdentityProvidersListerFinder(t *testing.T) {
name: "HasDefaultIDP when there is none set",
wrappedLister: testidplister.NewUpstreamIDPListerBuilder().
BuildDynamicUpstreamIDPProvider(),
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADIDPs,
federationDomainIssuer: fdIssuerWithOIDCAndLDAPAndADAndGitHubIDPs,
wantHasDefaultIDP: false,
},
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package idplister
@@ -19,8 +19,13 @@ type UpstreamActiveDirectoryIdentityProviderLister interface {
GetActiveDirectoryIdentityProviders() []upstreamprovider.UpstreamLDAPIdentityProviderI
}
type UpstreamGitHubIdentityProviderLister interface {
GetGitHubIdentityProviders() []upstreamprovider.UpstreamGithubIdentityProviderI
}
type UpstreamIdentityProvidersLister interface {
UpstreamOIDCIdentityProvidersLister
UpstreamLDAPIdentityProvidersLister
UpstreamActiveDirectoryIdentityProviderLister
UpstreamGitHubIdentityProviderLister
}

View File

@@ -0,0 +1,101 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package resolvedgithub
import (
"context"
"errors"
"fmt"
"go.pinniped.dev/generated/latest/apis/supervisor/idpdiscovery/v1alpha1"
"go.pinniped.dev/internal/federationdomain/resolvedprovider"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/idtransform"
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/pkg/oidcclient/nonce"
"go.pinniped.dev/pkg/oidcclient/pkce"
)
// FederationDomainResolvedGitHubIdentityProvider respresents a FederationDomainIdentityProvider which has
// been resolved dynamically based on the currently loaded IDP CRs to include the provider.UpstreamGitHubIdentityProviderI
// and other metadata about the provider.
type FederationDomainResolvedGitHubIdentityProvider struct {
DisplayName string
Provider upstreamprovider.UpstreamGithubIdentityProviderI
SessionProviderType psession.ProviderType
Transforms *idtransform.TransformationPipeline
}
var _ resolvedprovider.FederationDomainResolvedIdentityProvider = (*FederationDomainResolvedGitHubIdentityProvider)(nil)
func (p *FederationDomainResolvedGitHubIdentityProvider) GetDisplayName() string {
return p.DisplayName
}
func (p *FederationDomainResolvedGitHubIdentityProvider) GetProvider() upstreamprovider.UpstreamIdentityProviderI {
return p.Provider
}
func (p *FederationDomainResolvedGitHubIdentityProvider) GetSessionProviderType() psession.ProviderType {
return p.SessionProviderType
}
func (p *FederationDomainResolvedGitHubIdentityProvider) GetIDPDiscoveryType() v1alpha1.IDPType {
return v1alpha1.IDPTypeGitHub
}
func (p *FederationDomainResolvedGitHubIdentityProvider) GetIDPDiscoveryFlows() []v1alpha1.IDPFlow {
return []v1alpha1.IDPFlow{v1alpha1.IDPFlowBrowserAuthcode}
}
func (p *FederationDomainResolvedGitHubIdentityProvider) GetTransforms() *idtransform.TransformationPipeline {
return p.Transforms
}
func (p *FederationDomainResolvedGitHubIdentityProvider) CloneIDPSpecificSessionDataFromSession(session *psession.CustomSessionData) interface{} {
if session.GitHub == nil {
return nil
}
return session.GitHub.Clone()
}
func (p *FederationDomainResolvedGitHubIdentityProvider) ApplyIDPSpecificSessionDataToSession(session *psession.CustomSessionData, idpSpecificSessionData interface{}) {
session.GitHub = idpSpecificSessionData.(*psession.GitHubSessionData)
}
func (p *FederationDomainResolvedGitHubIdentityProvider) UpstreamAuthorizeRedirectURL(
state *resolvedprovider.UpstreamAuthorizeRequestState,
downstreamIssuerURL string,
) (string, error) {
fmt.Printf("GithubResolvedIdentityProvider ~ UpstreamAuthorizeRedirectURL() called with state: %#v, downstreamIssuerURL %s", state, downstreamIssuerURL)
return "", errors.New("function UpstreamAuthorizeRedirectURL not yet implemented for GitHub IDP")
}
func (p *FederationDomainResolvedGitHubIdentityProvider) Login(
_ context.Context,
submittedUsername string,
submittedPassword string,
) (*resolvedprovider.Identity, *resolvedprovider.IdentityLoginExtras, error) {
fmt.Printf("GithubResolvedIdentityProvider ~ Login() called with submittedUserName %s, submittedPassword %s", submittedUsername, submittedPassword)
return nil, nil, errors.New("function Login not yet implemented for GitHub IDP")
}
func (p *FederationDomainResolvedGitHubIdentityProvider) LoginFromCallback(
_ context.Context,
authCode string,
pkce pkce.Code,
nonce nonce.Nonce,
redirectURI string,
) (*resolvedprovider.Identity, *resolvedprovider.IdentityLoginExtras, error) {
fmt.Printf("GithubResolvedIdentityProvider ~ LoginFromCallback() called with authCode: %s, pkce: %#v, nonce: %#v, redirectURI: %s", authCode, pkce, nonce, redirectURI)
return nil, nil, errors.New("function LoginFromCallback not yet implemented for GitHub IDP")
}
func (p *FederationDomainResolvedGitHubIdentityProvider) UpstreamRefresh(
_ context.Context,
identity *resolvedprovider.Identity,
) (refreshedIdentity *resolvedprovider.RefreshedIdentity, err error) {
fmt.Printf("GithubResolvedIdentityProvider ~ UpstreamRefresh() called with identity %#v", identity)
return nil, errors.New("function UpstreamRefresh not yet implemented for GitHub IDP")
}

View File

@@ -0,0 +1,53 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package resolvedgithub
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"go.pinniped.dev/generated/latest/apis/supervisor/idpdiscovery/v1alpha1"
"go.pinniped.dev/internal/idtransform"
"go.pinniped.dev/internal/psession"
"go.pinniped.dev/internal/upstreamgithub"
)
type fakeTransformer struct{}
func (a fakeTransformer) Evaluate(_ context.Context, _ string, _ []string) (*idtransform.TransformationResult, error) {
return &idtransform.TransformationResult{}, nil
}
func (a fakeTransformer) Source() interface{} { return nil }
func TestFederationDomainResolvedGitHubIdentityProvider(t *testing.T) {
fake := fakeTransformer{}
transforms := idtransform.NewTransformationPipeline()
transforms.AppendTransformation(fake)
subject := FederationDomainResolvedGitHubIdentityProvider{
DisplayName: "fake-display-name",
Provider: upstreamgithub.New(upstreamgithub.ProviderConfig{
Name: "fake-provider-config",
ResourceUID: "fake-resource-uid",
}),
SessionProviderType: psession.ProviderTypeGitHub,
Transforms: transforms,
}
require.Equal(t, "fake-display-name", subject.GetDisplayName())
require.Equal(t, upstreamgithub.New(upstreamgithub.ProviderConfig{
Name: "fake-provider-config",
ResourceUID: "fake-resource-uid",
}), subject.GetProvider())
require.Equal(t, psession.ProviderTypeGitHub, subject.GetSessionProviderType())
require.Equal(t, v1alpha1.IDPTypeGitHub, subject.GetIDPDiscoveryType())
require.Equal(t, []v1alpha1.IDPFlow{v1alpha1.IDPFlowBrowserAuthcode}, subject.GetIDPDiscoveryFlows())
require.Equal(t, transforms, subject.GetTransforms())
require.Equal(t, &psession.GitHubSessionData{}, subject.CloneIDPSpecificSessionDataFromSession(&psession.CustomSessionData{
Username: "fake-username",
UpstreamUsername: "fake-upstream-username",
GitHub: &psession.GitHubSessionData{},
}))
}

View File

@@ -76,7 +76,7 @@ func (p *FederationDomainResolvedLDAPIdentityProvider) CloneIDPSpecificSessionDa
return nil
}
return session.ActiveDirectory.Clone()
case psession.ProviderTypeOIDC: // this is just here to avoid a lint error about not handling all cases
case psession.ProviderTypeOIDC, psession.ProviderTypeGitHub: // this is just here to avoid a lint error about not handling all cases
fallthrough
default:
return nil
@@ -151,7 +151,7 @@ func (p *FederationDomainResolvedLDAPIdentityProvider) Login(
UserDN: authenticateResponse.DN,
ExtraRefreshAttributes: authenticateResponse.ExtraRefreshAttributes,
}
case psession.ProviderTypeOIDC: // this is just here to avoid a lint error about not handling all cases
case psession.ProviderTypeOIDC, psession.ProviderTypeGitHub: // this is just here to avoid a lint error about not handling all cases
fallthrough
default:
return nil, nil, ErrUnexpectedUpstreamLDAPError.WithWrap(fmt.Errorf("unexpected provider type %q", p.GetSessionProviderType()))
@@ -205,7 +205,7 @@ func (p *FederationDomainResolvedLDAPIdentityProvider) UpstreamRefresh(
}
dn = sessionData.UserDN
additionalAttributes = sessionData.ExtraRefreshAttributes
case psession.ProviderTypeOIDC: // this is just here to avoid a lint error about not handling all cases
case psession.ProviderTypeOIDC, psession.ProviderTypeGitHub: // this is just here to avoid a lint error about not handling all cases
fallthrough
default:
// This shouldn't really happen.

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package authorizationcode
@@ -370,15 +370,18 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
"ĩŦʀ宍D挟": "q萮左/篣AÚƄŕ~čfVLPC諡}",
"姧骦:駝重EȫʆɵʮGɃ": "囤1+,Ȳ齠@ɍB鳛Nč乿ƔǴę鏶"
}
},
"github": {
}
}
},
"requestedAudience": [
"ň"
"ȝâ融貵捠ʼn0"
],
"grantedAudience": [
"â融貵捠ʼn",
"d鞕ȸ腿tʏƲ%}ſ¯Ɣ 籌Tǘ乚Ȥ2"
"ȸ腿tʏƲ%",
"泡hUɨĪű鹠NƤ鷒絓",
"ěå=瑅ƍ逤ŔfȀ箬+橇肅aā鲴"
]
},
"version": "6"

View File

@@ -74,6 +74,9 @@ type CustomSessionData struct {
// Only used when ProviderType == "activedirectory".
ActiveDirectory *ActiveDirectorySessionData `json:"activedirectory,omitempty"`
// Only used when ProviderType == "github".
GitHub *GitHubSessionData `json:"github,omitempty"`
}
type ProviderType string
@@ -82,6 +85,7 @@ const (
ProviderTypeOIDC ProviderType = "oidc"
ProviderTypeLDAP ProviderType = "ldap"
ProviderTypeActiveDirectory ProviderType = "activedirectory"
ProviderTypeGitHub ProviderType = "github"
)
// OIDCSessionData is the additional data needed by Pinniped when the upstream IDP is an OIDC provider.
@@ -140,6 +144,14 @@ func (s *ActiveDirectorySessionData) Clone() *ActiveDirectorySessionData {
}
}
type GitHubSessionData struct {
}
func (s *GitHubSessionData) Clone() *GitHubSessionData {
dataCopy := *s // this shortcut works because all fields in this type are currently strings (no pointers)
return &dataCopy
}
// NewPinnipedSession returns a new empty session.
func NewPinnipedSession() *PinnipedSession {
return &PinnipedSession{

View File

@@ -177,6 +177,7 @@ func prepareControllers(
pinnipedInformers.IDP().V1alpha1().OIDCIdentityProviders(),
pinnipedInformers.IDP().V1alpha1().LDAPIdentityProviders(),
pinnipedInformers.IDP().V1alpha1().ActiveDirectoryIdentityProviders(),
pinnipedInformers.IDP().V1alpha1().GitHubIdentityProviders(),
controllerlib.WithInformer,
),
singletonWorker,

View File

@@ -1,8 +0,0 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package githubtestutil
type TestUpstreamGithubIdentityProvider struct {
Name string
}

View File

@@ -0,0 +1,163 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package oidctestutil
import (
"net/http"
"k8s.io/apimachinery/pkg/types"
"go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
"go.pinniped.dev/internal/idtransform"
)
type TestUpstreamGitHubIdentityProviderBuilder struct {
name string
clientID string
resourceUID types.UID
displayNameForFederationDomain string
transformsForFederationDomain *idtransform.TransformationPipeline
usernameAttribute v1alpha1.GitHubUsernameAttribute
groupNameAttribute v1alpha1.GitHubGroupNameAttribute
allowedOrganizations []string
organizationLoginPolicy v1alpha1.GitHubAllowedAuthOrganizationsPolicy
authorizationURL string
httpClient *http.Client
}
func (u *TestUpstreamGitHubIdentityProviderBuilder) WithName(value string) *TestUpstreamGitHubIdentityProviderBuilder {
u.name = value
return u
}
func (u *TestUpstreamGitHubIdentityProviderBuilder) WithResourceUID(value types.UID) *TestUpstreamGitHubIdentityProviderBuilder {
u.resourceUID = value
return u
}
func (u *TestUpstreamGitHubIdentityProviderBuilder) WithClientID(value string) *TestUpstreamGitHubIdentityProviderBuilder {
u.clientID = value
return u
}
func (u *TestUpstreamGitHubIdentityProviderBuilder) WithDisplayNameForFederationDomain(value string) *TestUpstreamGitHubIdentityProviderBuilder {
u.displayNameForFederationDomain = value
return u
}
func (u *TestUpstreamGitHubIdentityProviderBuilder) WithUsernameAttribute(value v1alpha1.GitHubUsernameAttribute) *TestUpstreamGitHubIdentityProviderBuilder {
u.usernameAttribute = value
return u
}
func (u *TestUpstreamGitHubIdentityProviderBuilder) WithGroupNameAttribute(value v1alpha1.GitHubGroupNameAttribute) *TestUpstreamGitHubIdentityProviderBuilder {
u.groupNameAttribute = value
return u
}
func (u *TestUpstreamGitHubIdentityProviderBuilder) WithAllowedOrganizations(value []string) *TestUpstreamGitHubIdentityProviderBuilder {
u.allowedOrganizations = value
return u
}
func (u *TestUpstreamGitHubIdentityProviderBuilder) WithOrganizationLoginPolicy(value v1alpha1.GitHubAllowedAuthOrganizationsPolicy) *TestUpstreamGitHubIdentityProviderBuilder {
u.organizationLoginPolicy = value
return u
}
func (u *TestUpstreamGitHubIdentityProviderBuilder) WithAuthorizationURL(value string) *TestUpstreamGitHubIdentityProviderBuilder {
u.authorizationURL = value
return u
}
func (u *TestUpstreamGitHubIdentityProviderBuilder) WithHttpClient(value *http.Client) *TestUpstreamGitHubIdentityProviderBuilder {
u.httpClient = value
return u
}
func (u *TestUpstreamGitHubIdentityProviderBuilder) Build() *TestUpstreamGitHubIdentityProvider {
if u.displayNameForFederationDomain == "" {
// default it to the CR name
u.displayNameForFederationDomain = u.name
}
if u.transformsForFederationDomain == nil {
// default to an empty pipeline
u.transformsForFederationDomain = idtransform.NewTransformationPipeline()
}
return &TestUpstreamGitHubIdentityProvider{
Name: u.name,
ResourceUID: u.resourceUID,
ClientID: u.clientID,
DisplayNameForFederationDomain: u.displayNameForFederationDomain,
TransformsForFederationDomain: u.transformsForFederationDomain,
UsernameAttribute: u.usernameAttribute,
GroupNameAttribute: u.groupNameAttribute,
AllowedOrganizations: u.allowedOrganizations,
OrganizationLoginPolicy: u.organizationLoginPolicy,
AuthorizationURL: u.authorizationURL,
HttpClient: u.httpClient,
}
}
func NewTestUpstreamGitHubIdentityProviderBuilder() *TestUpstreamGitHubIdentityProviderBuilder {
return &TestUpstreamGitHubIdentityProviderBuilder{}
}
type TestUpstreamGitHubIdentityProvider struct {
Name string
ClientID string
ResourceUID types.UID
Host string
DisplayNameForFederationDomain string
TransformsForFederationDomain *idtransform.TransformationPipeline
UsernameAttribute v1alpha1.GitHubUsernameAttribute
GroupNameAttribute v1alpha1.GitHubGroupNameAttribute
AllowedOrganizations []string
OrganizationLoginPolicy v1alpha1.GitHubAllowedAuthOrganizationsPolicy
AuthorizationURL string
HttpClient *http.Client
}
var _ upstreamprovider.UpstreamGithubIdentityProviderI = &TestUpstreamGitHubIdentityProvider{}
func (u *TestUpstreamGitHubIdentityProvider) GetResourceUID() types.UID {
return u.ResourceUID
}
func (u *TestUpstreamGitHubIdentityProvider) GetName() string {
return u.Name
}
func (u *TestUpstreamGitHubIdentityProvider) GetHost() string {
return u.Host
}
func (u *TestUpstreamGitHubIdentityProvider) GetClientID() string {
return u.ClientID
}
func (u *TestUpstreamGitHubIdentityProvider) GetUsernameAttribute() v1alpha1.GitHubUsernameAttribute {
return u.UsernameAttribute
}
func (u *TestUpstreamGitHubIdentityProvider) GetGroupNameAttribute() v1alpha1.GitHubGroupNameAttribute {
return u.GroupNameAttribute
}
func (u *TestUpstreamGitHubIdentityProvider) GetAllowedOrganizations() []string {
return u.AllowedOrganizations
}
func (u *TestUpstreamGitHubIdentityProvider) GetOrganizationLoginPolicy() v1alpha1.GitHubAllowedAuthOrganizationsPolicy {
return u.OrganizationLoginPolicy
}
func (u *TestUpstreamGitHubIdentityProvider) GetAuthorizationURL() string {
return u.AuthorizationURL
}
func (u *TestUpstreamGitHubIdentityProvider) GetHttpClient() *http.Client {
return u.HttpClient
}

View File

@@ -11,6 +11,7 @@ import (
"go.pinniped.dev/internal/federationdomain/dynamicupstreamprovider"
"go.pinniped.dev/internal/federationdomain/resolvedprovider"
"go.pinniped.dev/internal/federationdomain/resolvedprovider/resolvedgithub"
"go.pinniped.dev/internal/federationdomain/resolvedprovider/resolvedldap"
"go.pinniped.dev/internal/federationdomain/resolvedprovider/resolvedoidc"
"go.pinniped.dev/internal/federationdomain/upstreamprovider"
@@ -24,6 +25,7 @@ type TestFederationDomainIdentityProvidersListerFinder struct {
upstreamOIDCIdentityProviders []*oidctestutil.TestUpstreamOIDCIdentityProvider
upstreamLDAPIdentityProviders []*oidctestutil.TestUpstreamLDAPIdentityProvider
upstreamActiveDirectoryIdentityProviders []*oidctestutil.TestUpstreamLDAPIdentityProvider
upstreamGitHubIdentityProviders []*oidctestutil.TestUpstreamGitHubIdentityProvider
defaultIDPDisplayName string
}
@@ -37,7 +39,7 @@ func (t *TestFederationDomainIdentityProvidersListerFinder) IDPCount() int {
func (t *TestFederationDomainIdentityProvidersListerFinder) GetIdentityProviders() []resolvedprovider.FederationDomainResolvedIdentityProvider {
fdIDPs := make([]resolvedprovider.FederationDomainResolvedIdentityProvider,
len(t.upstreamOIDCIdentityProviders)+len(t.upstreamLDAPIdentityProviders)+len(t.upstreamActiveDirectoryIdentityProviders))
len(t.upstreamOIDCIdentityProviders)+len(t.upstreamLDAPIdentityProviders)+len(t.upstreamActiveDirectoryIdentityProviders)+len(t.upstreamGitHubIdentityProviders))
i := 0
for _, testIDP := range t.upstreamOIDCIdentityProviders {
fdIDP := &resolvedoidc.FederationDomainResolvedOIDCIdentityProvider{
@@ -69,6 +71,16 @@ func (t *TestFederationDomainIdentityProvidersListerFinder) GetIdentityProviders
fdIDPs[i] = fdIDP
i++
}
for _, testIDP := range t.upstreamGitHubIdentityProviders {
fdIDP := &resolvedgithub.FederationDomainResolvedGitHubIdentityProvider{
DisplayName: testIDP.DisplayNameForFederationDomain,
Provider: testIDP,
SessionProviderType: psession.ProviderTypeGitHub,
Transforms: testIDP.TransformsForFederationDomain,
}
fdIDPs[i] = fdIDP
i++
}
return fdIDPs
}
@@ -110,6 +122,16 @@ func (t *TestFederationDomainIdentityProvidersListerFinder) FindUpstreamIDPByDis
}, nil
}
}
for _, testIDP := range t.upstreamGitHubIdentityProviders {
if upstreamIDPDisplayName == testIDP.DisplayNameForFederationDomain {
return &resolvedgithub.FederationDomainResolvedGitHubIdentityProvider{
DisplayName: testIDP.DisplayNameForFederationDomain,
Provider: testIDP,
SessionProviderType: psession.ProviderTypeGitHub,
Transforms: testIDP.TransformsForFederationDomain,
}, nil
}
}
return nil, fmt.Errorf("did not find IDP with name %q", upstreamIDPDisplayName)
}
@@ -125,12 +147,17 @@ func (t *TestFederationDomainIdentityProvidersListerFinder) SetActiveDirectoryId
t.upstreamActiveDirectoryIdentityProviders = providers
}
func (t *TestFederationDomainIdentityProvidersListerFinder) SetGitHubIdentityProviders(providers []*oidctestutil.TestUpstreamGitHubIdentityProvider) {
t.upstreamGitHubIdentityProviders = providers
}
// UpstreamIDPListerBuilder can be used to build either a dynamicupstreamprovider.DynamicUpstreamIDPProvider
// or a FederationDomainIdentityProvidersListerFinderI for testing.
type UpstreamIDPListerBuilder struct {
upstreamOIDCIdentityProviders []*oidctestutil.TestUpstreamOIDCIdentityProvider
upstreamLDAPIdentityProviders []*oidctestutil.TestUpstreamLDAPIdentityProvider
upstreamActiveDirectoryIdentityProviders []*oidctestutil.TestUpstreamLDAPIdentityProvider
upstreamGitHubIdentityProviders []*oidctestutil.TestUpstreamGitHubIdentityProvider
defaultIDPDisplayName string
}
@@ -149,6 +176,11 @@ func (b *UpstreamIDPListerBuilder) WithActiveDirectory(upstreamActiveDirectoryId
return b
}
func (b *UpstreamIDPListerBuilder) WithGitHub(upstreamGithubIdentityProviders ...*oidctestutil.TestUpstreamGitHubIdentityProvider) *UpstreamIDPListerBuilder {
b.upstreamGitHubIdentityProviders = append(b.upstreamGitHubIdentityProviders, upstreamGithubIdentityProviders...)
return b
}
func (b *UpstreamIDPListerBuilder) WithDefaultIDPDisplayName(defaultIDPDisplayName string) *UpstreamIDPListerBuilder {
b.defaultIDPDisplayName = defaultIDPDisplayName
return b
@@ -159,6 +191,7 @@ func (b *UpstreamIDPListerBuilder) BuildFederationDomainIdentityProvidersListerF
upstreamOIDCIdentityProviders: b.upstreamOIDCIdentityProviders,
upstreamLDAPIdentityProviders: b.upstreamLDAPIdentityProviders,
upstreamActiveDirectoryIdentityProviders: b.upstreamActiveDirectoryIdentityProviders,
upstreamGitHubIdentityProviders: b.upstreamGitHubIdentityProviders,
defaultIDPDisplayName: b.defaultIDPDisplayName,
}
}
@@ -184,6 +217,12 @@ func (b *UpstreamIDPListerBuilder) BuildDynamicUpstreamIDPProvider() dynamicupst
}
idpProvider.SetActiveDirectoryIdentityProviders(adUpstreams)
githubUpstreams := make([]upstreamprovider.UpstreamGithubIdentityProviderI, len(b.upstreamGitHubIdentityProviders))
for i := range b.upstreamGitHubIdentityProviders {
githubUpstreams[i] = upstreamprovider.UpstreamGithubIdentityProviderI(b.upstreamGitHubIdentityProviders[i])
}
idpProvider.SetGitHubIdentityProviders(githubUpstreams)
return idpProvider
}
@@ -256,6 +295,7 @@ func (b *UpstreamIDPListerBuilder) RequireExactlyZeroCallsToExchangeAuthcodeAndV
for _, upstreamOIDC := range b.upstreamOIDCIdentityProviders {
actualCallCountAcrossAllOIDCUpstreams += upstreamOIDC.ExchangeAuthcodeAndValidateTokensCallCount()
}
require.Equal(t, 0, actualCallCountAcrossAllOIDCUpstreams,
"expected exactly zero calls to ExchangeAuthcodeAndValidateTokens()",
)
@@ -294,6 +334,7 @@ func (b *UpstreamIDPListerBuilder) RequireExactlyOneCallToPerformRefresh(
actualArgs = upstreamAD.PerformRefreshArgs(0)
}
}
// TODO: probably add GitHub loop once we flesh out the structs
require.Equal(t, 1, actualCallCountAcrossAllUpstreams,
"should have been exactly one call to PerformRefresh() by all upstreams",
)
@@ -315,6 +356,7 @@ func (b *UpstreamIDPListerBuilder) RequireExactlyZeroCallsToPerformRefresh(t *te
for _, upstreamActiveDirectory := range b.upstreamActiveDirectoryIdentityProviders {
actualCallCountAcrossAllUpstreams += upstreamActiveDirectory.PerformRefreshCallCount()
}
// TODO: probably add GitHub loop once we flesh out the structs
require.Equal(t, 0, actualCallCountAcrossAllUpstreams,
"expected exactly zero calls to PerformRefresh()",

View File

@@ -28,44 +28,64 @@ type ProviderConfig struct {
HttpClient *http.Client
}
var _ upstreamprovider.UpstreamGithubIdentityProviderI = (*ProviderConfig)(nil)
func (p *ProviderConfig) GetResourceUID() types.UID {
return p.ResourceUID
type Provider struct {
c ProviderConfig
}
func (p *ProviderConfig) GetName() string {
return p.Name
var _ upstreamprovider.UpstreamGithubIdentityProviderI = &Provider{}
// New creates a Provider. The config is not a pointer to ensure that a copy of the config is created,
// making the resulting Provider use an effectively read-only configuration.
func New(config ProviderConfig) *Provider {
return &Provider{c: config}
}
func (p *ProviderConfig) GetClientID() string {
return p.OAuth2Config.ClientID
// GetConfig is a reader for the config. Returns a copy of the config to keep the underlying config read-only.
func (p *Provider) GetConfig() ProviderConfig {
return p.c
}
func (p *ProviderConfig) GetHost() string {
return p.Host
// GetName returns a name for this upstream provider.
func (p *Provider) GetName() string {
return p.c.Name
}
func (p *ProviderConfig) GetUsernameAttribute() v1alpha1.GitHubUsernameAttribute {
return p.UsernameAttribute
func (p *Provider) GetResourceUID() types.UID {
return p.c.ResourceUID
}
func (p *ProviderConfig) GetGroupNameAttribute() v1alpha1.GitHubGroupNameAttribute {
return p.GroupNameAttribute
func (p *Provider) GetClientID() string {
return p.c.OAuth2Config.ClientID
}
func (p *ProviderConfig) GetAllowedOrganizations() []string {
return p.AllowedOrganizations
func (p *Provider) GetOAuth2Config() *oauth2.Config {
return p.c.OAuth2Config
}
func (p *ProviderConfig) GetOrganizationLoginPolicy() v1alpha1.GitHubAllowedAuthOrganizationsPolicy {
return p.OrganizationLoginPolicy
func (p *Provider) GetHost() string {
return p.c.Host
}
func (p *ProviderConfig) GetAuthorizationURL() string {
return p.AuthorizationURL
func (p *Provider) GetUsernameAttribute() v1alpha1.GitHubUsernameAttribute {
return p.c.UsernameAttribute
}
func (p *ProviderConfig) GetHttpClient() *http.Client {
return p.HttpClient
func (p *Provider) GetGroupNameAttribute() v1alpha1.GitHubGroupNameAttribute {
return p.c.GroupNameAttribute
}
func (p *Provider) GetAllowedOrganizations() []string {
return p.c.AllowedOrganizations
}
func (p *Provider) GetOrganizationLoginPolicy() v1alpha1.GitHubAllowedAuthOrganizationsPolicy {
return p.c.OrganizationLoginPolicy
}
func (p *Provider) GetAuthorizationURL() string {
return p.c.AuthorizationURL
}
func (p *Provider) GetHttpClient() *http.Client {
return p.c.HttpClient
}

View File

@@ -2,3 +2,69 @@
// SPDX-License-Identifier: Apache-2.0
package upstreamgithub
import (
"net/http"
"testing"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
"k8s.io/apimachinery/pkg/types"
"go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1"
)
func TestGitHubProvider(t *testing.T) {
subject := New(ProviderConfig{
Name: "foo",
ResourceUID: "resource-uid-12345",
Host: "fake-host",
UsernameAttribute: "fake-username-attribute",
GroupNameAttribute: "fake-group-name-attribute",
OAuth2Config: &oauth2.Config{
ClientID: "fake-client-id",
ClientSecret: "fake-client-secret",
},
AllowedOrganizations: []string{"fake-org", "fake-org2"},
OrganizationLoginPolicy: v1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers,
AuthorizationURL: "https://fake-authorization-url",
HttpClient: &http.Client{
Timeout: 1234509,
},
})
require.Equal(t, ProviderConfig{
Name: "foo",
ResourceUID: "resource-uid-12345",
Host: "fake-host",
UsernameAttribute: "fake-username-attribute",
GroupNameAttribute: "fake-group-name-attribute",
OAuth2Config: &oauth2.Config{
ClientID: "fake-client-id",
ClientSecret: "fake-client-secret",
},
AllowedOrganizations: []string{"fake-org", "fake-org2"},
OrganizationLoginPolicy: v1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers,
AuthorizationURL: "https://fake-authorization-url",
HttpClient: &http.Client{
Timeout: 1234509,
},
}, subject.GetConfig())
require.Equal(t, "foo", subject.GetName())
require.Equal(t, types.UID("resource-uid-12345"), subject.GetResourceUID())
require.Equal(t, "fake-client-id", subject.GetClientID())
require.Equal(t, &oauth2.Config{
ClientID: "fake-client-id",
ClientSecret: "fake-client-secret",
}, subject.GetOAuth2Config())
require.Equal(t, "fake-host", subject.GetHost())
require.Equal(t, v1alpha1.GitHubUsernameAttribute("fake-username-attribute"), subject.GetUsernameAttribute())
require.Equal(t, v1alpha1.GitHubGroupNameAttribute("fake-group-name-attribute"), subject.GetGroupNameAttribute())
require.Equal(t, []string{"fake-org", "fake-org2"}, subject.GetAllowedOrganizations())
require.Equal(t, v1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers, subject.GetOrganizationLoginPolicy())
require.Equal(t, "https://fake-authorization-url", subject.GetAuthorizationURL())
require.Equal(t, &http.Client{
Timeout: 1234509,
}, subject.GetHttpClient())
}

View File

@@ -1,4 +1,4 @@
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package integration
@@ -23,6 +23,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/util/retry"
"k8s.io/utils/ptr"
"k8s.io/utils/strings/slices"
"go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1"
@@ -93,7 +94,7 @@ func TestSupervisorOIDCDiscovery_Disruptive(t *testing.T) {
// Test that there is no default discovery endpoint available when there are no FederationDomains.
requireDiscoveryEndpointsAreNotFound(t, scheme, addr, caBundle, fmt.Sprintf("%s://%s", scheme, addr))
// Define several unique issuer strings. Always use https in the issuer name even when we are accessing the http port.
// Define several unique issuer URLs. Always use https in the issuer URL even when we are accessing the http port.
issuer1 := fmt.Sprintf("https://%s/nested/issuer1", addr)
issuer2 := fmt.Sprintf("https://%s/nested/issuer2", addr)
issuer3 := fmt.Sprintf("https://%s/issuer3", addr)
@@ -102,7 +103,7 @@ func TestSupervisorOIDCDiscovery_Disruptive(t *testing.T) {
issuer6 := fmt.Sprintf("https://%s/issuer6", addr)
badIssuer := fmt.Sprintf("https://%s/badIssuer?cannot-use=queries", addr)
// When FederationDomain are created in sequence they each cause a discovery endpoint to appear only for as long as the FederationDomain exists.
// When FederationDomains are created in sequence they each cause a discovery endpoint to appear only for as long as the FederationDomain exists.
config1, jwks1 := requireCreatingFederationDomainCausesDiscoveryEndpointsToAppear(ctx, t, scheme, addr, caBundle, issuer1, client)
requireDeletingFederationDomainCausesDiscoveryEndpointsToDisappear(t, config1, client, ns, scheme, addr, caBundle, issuer1)
config2, jwks2 := requireCreatingFederationDomainCausesDiscoveryEndpointsToAppear(ctx, t, scheme, addr, caBundle, issuer2, client)
@@ -119,7 +120,7 @@ func TestSupervisorOIDCDiscovery_Disruptive(t *testing.T) {
require.NotEqual(t, jwks3.Keys[0]["x"], jwks4.Keys[0]["x"])
require.NotEqual(t, jwks3.Keys[0]["y"], jwks4.Keys[0]["y"])
// Editing a provider to change the issuer name updates the endpoints that are being served.
// Editing a FederationDomain to change the issuer URL updates the endpoints that are being served.
updatedConfig4 := editFederationDomainIssuerName(t, config4, client, ns, issuer5)
requireDiscoveryEndpointsAreNotFound(t, scheme, addr, caBundle, issuer4)
jwks5 := requireDiscoveryEndpointsAreWorking(t, scheme, addr, caBundle, issuer5, nil)
@@ -130,31 +131,37 @@ func TestSupervisorOIDCDiscovery_Disruptive(t *testing.T) {
requireDeletingFederationDomainCausesDiscoveryEndpointsToDisappear(t, config3, client, ns, scheme, addr, caBundle, issuer3)
requireDeletingFederationDomainCausesDiscoveryEndpointsToDisappear(t, updatedConfig4, client, ns, scheme, addr, caBundle, issuer5)
// When the same issuer is added twice, both issuers are marked as duplicates, and neither provider is serving.
// When the same issuer URL is added to two FederationDomains, both FederationDomains are marked as duplicates, and neither is serving.
config6Duplicate1, _ := requireCreatingFederationDomainCausesDiscoveryEndpointsToAppear(ctx, t, scheme, addr, caBundle, issuer6, client)
config6Duplicate2 := testlib.CreateTestFederationDomain(ctx, t, v1alpha1.FederationDomainSpec{Issuer: issuer6}, v1alpha1.FederationDomainPhaseError)
requireStatus(t, client, ns, config6Duplicate1.Name, v1alpha1.FederationDomainPhaseError, withFalseConditions([]string{"Ready", "IssuerIsUnique"}))
requireStatus(t, client, ns, config6Duplicate2.Name, v1alpha1.FederationDomainPhaseError, withFalseConditions([]string{"Ready", "IssuerIsUnique"}))
requireDiscoveryEndpointsAreNotFound(t, scheme, addr, caBundle, issuer6)
// If we delete the first duplicate issuer, the second duplicate issuer starts serving.
// If we delete the first duplicate FederationDomain, the second duplicate FederationDomain starts serving.
requireDelete(t, client, ns, config6Duplicate1.Name)
requireWellKnownEndpointIsWorking(t, scheme, addr, caBundle, issuer6, nil)
requireStatus(t, client, ns, config6Duplicate2.Name, v1alpha1.FederationDomainPhaseReady, withAllSuccessfulConditions())
// When we finally delete all issuers, the endpoint should be down.
// When we finally delete all FederationDomains, the discovery endpoints should be down.
requireDeletingFederationDomainCausesDiscoveryEndpointsToDisappear(t, config6Duplicate2, client, ns, scheme, addr, caBundle, issuer6)
// "Host" headers can be used to send requests to discovery endpoints when the public address is different from the issuer name.
// "Host" headers can be used to send requests to discovery endpoints when the public address is different from the issuer URL.
issuer7 := "https://some-issuer-host-and-port-that-doesnt-match-public-supervisor-address.com:2684/issuer7"
config7, _ := requireCreatingFederationDomainCausesDiscoveryEndpointsToAppear(ctx, t, scheme, addr, caBundle, issuer7, client)
requireDeletingFederationDomainCausesDiscoveryEndpointsToDisappear(t, config7, client, ns, scheme, addr, caBundle, issuer7)
// When we create a provider with an invalid issuer, the status is set to invalid.
// When we create a FederationDomain with an invalid issuer url, the status is set to invalid.
badConfig := testlib.CreateTestFederationDomain(ctx, t, v1alpha1.FederationDomainSpec{Issuer: badIssuer}, v1alpha1.FederationDomainPhaseError)
requireStatus(t, client, ns, badConfig.Name, v1alpha1.FederationDomainPhaseError, withFalseConditions([]string{"Ready", "IssuerURLValid"}))
requireDiscoveryEndpointsAreNotFound(t, scheme, addr, caBundle, badIssuer)
requireDeletingFederationDomainCausesDiscoveryEndpointsToDisappear(t, badConfig, client, ns, scheme, addr, caBundle, badIssuer)
issuer8 := fmt.Sprintf("https://%s/issuer8multipleIDP", addr)
config8 := requireIDPsListedByIDPDiscoveryEndpoint(t, env, ctx, kubeClient, ns, scheme, addr, caBundle, issuer8)
// requireJWKSEndpointIsWorking() will give us a bit of an idea what to do...
requireDeletingFederationDomainCausesDiscoveryEndpointsToDisappear(t, config8, client, ns, scheme, addr, caBundle, issuer8)
})
}
}
@@ -494,6 +501,7 @@ func requireCreatingFederationDomainCausesDiscoveryEndpointsToAppear(
return newFederationDomain, jwksResult
}
// TODO: consider renaming this since it does not actually check all discovery endpoints (example: idp discovery is not tested).
func requireDiscoveryEndpointsAreWorking(t *testing.T, supervisorScheme, supervisorAddress, supervisorCABundle, issuerName string, dnsOverrides map[string]string) *ExpectedJWKSResponseFormat {
requireWellKnownEndpointIsWorking(t, supervisorScheme, supervisorAddress, supervisorCABundle, issuerName, dnsOverrides)
jwksResult := requireJWKSEndpointIsWorking(t, supervisorScheme, supervisorAddress, supervisorCABundle, issuerName, dnsOverrides)
@@ -738,3 +746,95 @@ func newHTTPClient(t *testing.T, caBundle string, dnsOverrides map[string]string
return c
}
func requireIDPsListedByIDPDiscoveryEndpoint(
t *testing.T,
env *testlib.TestEnv,
ctx context.Context,
kubeClient kubernetes.Interface,
ns, supervisorScheme, supervisorAddress, supervisorCABundle, issuerName string) *v1alpha1.FederationDomain {
// github
gitHubIDPSecretName := "github-idp-secret" //nolint:gosec // this is not a credential
_, err := kubeClient.CoreV1().Secrets(ns).Create(ctx, &corev1.Secret{
Type: "secrets.pinniped.dev/github-client",
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{
Name: gitHubIDPSecretName,
Namespace: ns,
},
StringData: map[string]string{
"clientID": "foo",
"clientSecret": "bar",
},
}, metav1.CreateOptions{})
require.NoError(t, err)
t.Cleanup(func() {
_ = kubeClient.CoreV1().Secrets(ns).Delete(ctx, gitHubIDPSecretName, metav1.DeleteOptions{})
})
ghIDP := testlib.CreateGitHubIdentityProvider(t, idpv1alpha1.GitHubIdentityProviderSpec{
Client: idpv1alpha1.GitHubClientSpec{
SecretName: gitHubIDPSecretName,
},
AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{
Organizations: idpv1alpha1.GitHubOrganizationsSpec{
Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers),
},
},
}, idpv1alpha1.GitHubPhaseReady)
// TODO: add ldap to prove it shows up in the IDP discovery API
// TODO: add oidc to prove it shows up in the IDP discovery API
// TODO: add ad to prove it shows up in the IDP discovery API
federationDomainConfig := testlib.CreateTestFederationDomain(ctx, t, v1alpha1.FederationDomainSpec{
Issuer: issuerName,
IdentityProviders: []v1alpha1.FederationDomainIdentityProvider{{
DisplayName: ghIDP.Name,
ObjectRef: corev1.TypedLocalObjectReference{
APIGroup: ptr.To("idp.supervisor." + env.APIGroupSuffix),
Kind: "GitHubIdentityProvider",
Name: ghIDP.Name,
},
}},
}, v1alpha1.FederationDomainPhaseReady)
requireDiscoveryEndpointsAreWorking(t, supervisorScheme, supervisorAddress, supervisorCABundle, issuerName, nil)
issuer8URL, err := url.Parse(issuerName)
require.NoError(t, err)
wellKnownURL := wellKnownURLForIssuer(supervisorScheme, supervisorAddress, issuer8URL.Path)
_, wellKnownResponseBody := requireSuccessEndpointResponse(t, wellKnownURL, issuerName, supervisorCABundle, nil) //nolint:bodyclose
type WellKnownResponse struct {
Issuer string `json:"issuer"`
AuthorizationEndpoint string `json:"authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
JWKSUri string `json:"jwks_uri"`
DiscoverySupervisor struct {
IdentityProvidersEndpoint string `json:"pinniped_identity_providers_endpoint"`
} `json:"discovery.supervisor.pinniped.dev/v1alpha1"`
}
var wellKnownResponse WellKnownResponse
err = json.Unmarshal([]byte(wellKnownResponseBody), &wellKnownResponse)
require.NoError(t, err)
discoveryIDPEndpoint := wellKnownResponse.DiscoverySupervisor.IdentityProvidersEndpoint
_, discoveryIDPResponseBody := requireSuccessEndpointResponse(t, discoveryIDPEndpoint, issuerName, supervisorCABundle, nil) //nolint:bodyclose
type IdentityProviderListResponse struct {
IdentityProviders []struct {
Name string `json:"name"`
Type string `json:"type"`
} `json:"pinniped_identity_providers"`
}
var identityProviderListResponse IdentityProviderListResponse
err = json.Unmarshal([]byte(discoveryIDPResponseBody), &identityProviderListResponse)
require.NoError(t, err)
allIDPs := []string{ghIDP.Name}
require.Equal(t, len(identityProviderListResponse.IdentityProviders), 1, "all IDPs should be listed by idp discovery endpoint")
for _, provider := range identityProviderListResponse.IdentityProviders {
require.True(t, slices.Contains(allIDPs, provider.Name))
require.Contains(t, allIDPs, provider.Name, fmt.Sprintf("provider name should be listed in IDP discovery: %s", provider.Name))
}
return federationDomainConfig
}

View File

@@ -1,4 +1,4 @@
// Copyright 2023 the Pinniped contributors. All Rights Reserved.
// Copyright 2023-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package integration
@@ -362,7 +362,7 @@ func TestSupervisorFederationDomainStatus_Disruptive(t *testing.T) {
{
Type: "IdentityProvidersObjectRefKindValid", Status: "False", Reason: "KindUnrecognized",
Message: `some kinds specified by .spec.identityProviders[].objectRef.kind are not recognized ` +
`(should be one of "ActiveDirectoryIdentityProvider", "LDAPIdentityProvider", "OIDCIdentityProvider"): "this is the wrong kind"`,
`(should be one of "ActiveDirectoryIdentityProvider", "GitHubIdentityProvider", "LDAPIdentityProvider", "OIDCIdentityProvider"): "this is the wrong kind"`,
},
{
Type: "Ready", Status: "False", Reason: "NotReady",
@@ -493,7 +493,7 @@ func TestSupervisorFederationDomainStatus_Disruptive(t *testing.T) {
{
Type: "IdentityProvidersObjectRefKindValid", Status: "False", Reason: "KindUnrecognized",
Message: `some kinds specified by .spec.identityProviders[].objectRef.kind are not recognized ` +
`(should be one of "ActiveDirectoryIdentityProvider", "LDAPIdentityProvider", "OIDCIdentityProvider"): "this is the wrong kind"`,
`(should be one of "ActiveDirectoryIdentityProvider", "GitHubIdentityProvider", "LDAPIdentityProvider", "OIDCIdentityProvider"): "this is the wrong kind"`,
},
{
Type: "Ready", Status: "False", Reason: "NotReady",

View File

@@ -186,7 +186,7 @@ func CreateTestWebhookAuthenticator(
defer cancel()
webhook, err := webhooks.Create(createContext, &auth1alpha1.WebhookAuthenticator{
ObjectMeta: testObjectMeta(t, "webhook"),
ObjectMeta: TestObjectMeta(t, "webhook"),
Spec: *webhookSpec,
}, metav1.CreateOptions{})
require.NoError(t, err, "could not create test WebhookAuthenticator")
@@ -295,7 +295,7 @@ func CreateTestJWTAuthenticator(
defer cancel()
jwtAuthenticator, err := jwtAuthenticators.Create(createContext, &auth1alpha1.JWTAuthenticator{
ObjectMeta: testObjectMeta(t, "jwt-authenticator"),
ObjectMeta: TestObjectMeta(t, "jwt-authenticator"),
Spec: spec,
}, metav1.CreateOptions{})
require.NoError(t, err, "could not create test JWTAuthenticator")
@@ -369,7 +369,7 @@ func CreateTestFederationDomain(
federationDomainsClient := NewSupervisorClientset(t).ConfigV1alpha1().FederationDomains(testEnv.SupervisorNamespace)
federationDomain, err := federationDomainsClient.Create(createContext, &configv1alpha1.FederationDomain{
ObjectMeta: testObjectMeta(t, "oidc-provider"),
ObjectMeta: TestObjectMeta(t, "oidc-provider"),
Spec: spec,
}, metav1.CreateOptions{})
require.NoError(t, err, "could not create test FederationDomain")
@@ -460,7 +460,7 @@ func CreateTestSecret(t *testing.T, namespace string, baseName string, secretTyp
defer cancel()
created, err := client.CoreV1().Secrets(namespace).Create(ctx, &corev1.Secret{
ObjectMeta: testObjectMeta(t, baseName),
ObjectMeta: TestObjectMeta(t, baseName),
Type: secretType,
StringData: stringData,
}, metav1.CreateOptions{})
@@ -482,7 +482,7 @@ func CreateTestSecretBytes(t *testing.T, namespace string, baseName string, secr
defer cancel()
created, err := client.CoreV1().Secrets(namespace).Create(ctx, &corev1.Secret{
ObjectMeta: testObjectMeta(t, baseName),
ObjectMeta: TestObjectMeta(t, baseName),
Type: secretType,
Data: data,
}, metav1.CreateOptions{})
@@ -587,7 +587,7 @@ func createOIDCClientSecret(t *testing.T, forOIDCClient *configv1alpha1.OIDCClie
func CreateTestOIDCIdentityProvider(t *testing.T, spec idpv1alpha1.OIDCIdentityProviderSpec, expectedPhase idpv1alpha1.OIDCIdentityProviderPhase) *idpv1alpha1.OIDCIdentityProvider {
t.Helper()
return CreateTestOIDCIdentityProviderWithObjectMeta(t, spec, testObjectMeta(t, "upstream-oidc-idp"), expectedPhase)
return CreateTestOIDCIdentityProviderWithObjectMeta(t, spec, TestObjectMeta(t, "upstream-oidc-idp"), expectedPhase)
}
func CreateTestOIDCIdentityProviderWithObjectMeta(t *testing.T, spec idpv1alpha1.OIDCIdentityProviderSpec, objectMeta metav1.ObjectMeta, expectedPhase idpv1alpha1.OIDCIdentityProviderPhase) *idpv1alpha1.OIDCIdentityProvider {
@@ -640,7 +640,7 @@ func CreateTestLDAPIdentityProvider(t *testing.T, spec idpv1alpha1.LDAPIdentityP
// Create the LDAPIdentityProvider using GenerateName to get a random name.
created, err := upstreams.Create(ctx, &idpv1alpha1.LDAPIdentityProvider{
ObjectMeta: testObjectMeta(t, "upstream-ldap-idp"),
ObjectMeta: TestObjectMeta(t, "upstream-ldap-idp"),
Spec: spec,
}, metav1.CreateOptions{})
require.NoError(t, err)
@@ -681,7 +681,7 @@ func CreateTestActiveDirectoryIdentityProvider(t *testing.T, spec idpv1alpha1.Ac
// Create the ActiveDirectoryIdentityProvider using GenerateName to get a random name.
created, err := upstreams.Create(ctx, &idpv1alpha1.ActiveDirectoryIdentityProvider{
ObjectMeta: testObjectMeta(t, "upstream-ad-idp"),
ObjectMeta: TestObjectMeta(t, "upstream-ad-idp"),
Spec: spec,
}, metav1.CreateOptions{})
require.NoError(t, err)
@@ -711,6 +711,41 @@ func CreateTestActiveDirectoryIdentityProvider(t *testing.T, spec idpv1alpha1.Ac
return result
}
func CreateGitHubIdentityProvider(t *testing.T, spec idpv1alpha1.GitHubIdentityProviderSpec, expectedPhase idpv1alpha1.GitHubIdentityProviderPhase) *idpv1alpha1.GitHubIdentityProvider {
t.Helper()
env := IntegrationEnv(t)
client := NewSupervisorClientset(t)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
upstreams := client.IDPV1alpha1().GitHubIdentityProviders(env.SupervisorNamespace)
// Create the GitHubIdentityProvider using GenerateName to get a random name.
created, err := upstreams.Create(ctx, &idpv1alpha1.GitHubIdentityProvider{
ObjectMeta: TestObjectMeta(t, "upstream-github-idp"),
Spec: spec,
}, metav1.CreateOptions{})
require.NoError(t, err)
// Always clean this up after this point.
t.Cleanup(func() {
t.Logf("cleaning up test GitHubIdentityProvider %s/%s", created.Namespace, created.Name)
err := upstreams.Delete(context.Background(), created.Name, metav1.DeleteOptions{})
require.NoError(t, err)
})
t.Logf("created test GitHubIdentityProvider %s", created.Name)
// Wait for the GitHubIdentityProvider to enter the expected phase (or time out).
var result *idpv1alpha1.GitHubIdentityProvider
RequireEventuallyf(t, func(requireEventually *require.Assertions) {
var err error
result, err = upstreams.Get(ctx, created.Name, metav1.GetOptions{})
requireEventually.NoErrorf(err, "error while getting GitHubIdentityProvider %s/%s", created.Namespace, created.Name)
requireEventually.Equal(expectedPhase, result.Status.Phase)
}, 60*time.Second, 1*time.Second, "expected the GitHubIdentityProvider to go into phase %s, GitHubIdentityProvider was: %s", expectedPhase, Sdump(result))
return result
}
func CreateTestClusterRoleBinding(t *testing.T, subject rbacv1.Subject, roleRef rbacv1.RoleRef) *rbacv1.ClusterRoleBinding {
t.Helper()
client := NewKubernetesClientset(t)
@@ -721,7 +756,7 @@ func CreateTestClusterRoleBinding(t *testing.T, subject rbacv1.Subject, roleRef
// Create the ClusterRoleBinding using GenerateName to get a random name.
created, err := clusterRoles.Create(ctx, &rbacv1.ClusterRoleBinding{
ObjectMeta: testObjectMeta(t, "cluster-role"),
ObjectMeta: TestObjectMeta(t, "cluster-role"),
Subjects: []rbacv1.Subject{subject},
RoleRef: roleRef,
}, metav1.CreateOptions{})
@@ -772,7 +807,7 @@ func CreatePod(ctx context.Context, t *testing.T, name, namespace string, spec c
ctx, cancel := context.WithTimeout(ctx, podCreateTimeout+time.Minute)
defer cancel()
created, err := pods.Create(ctx, &corev1.Pod{ObjectMeta: testObjectMeta(t, name), Spec: spec}, metav1.CreateOptions{})
created, err := pods.Create(ctx, &corev1.Pod{ObjectMeta: TestObjectMeta(t, name), Spec: spec}, metav1.CreateOptions{})
require.NoError(t, err)
t.Logf("created test Pod %s", created.Name)
@@ -890,7 +925,7 @@ func WaitForGitHubIdentityProviderStatusConditions(
}, 60*time.Second, 1*time.Second, "wanted conditions for GitHubIdentityProvider %q", gitHubIDPName)
}
func testObjectMeta(t *testing.T, baseName string) metav1.ObjectMeta {
func TestObjectMeta(t *testing.T, baseName string) metav1.ObjectMeta {
return metav1.ObjectMeta{
GenerateName: fmt.Sprintf("test-%s-", baseName),
Labels: map[string]string{"pinniped.dev/test": ""},