mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2025-12-23 06:15:47 +00:00
- WebhookAuthenticator will now detect the proxy setting and skip dialing the connection probe if it should go through a proxy - GitHubIdentityProvider will avoid using tls.Dial altogether by instead making a real request to the GitHub API as its connection probe, because this will respect the proxy settings
747 lines
25 KiB
Go
747 lines
25 KiB
Go
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package integration
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/utils/ptr"
|
|
|
|
idpv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1"
|
|
"go.pinniped.dev/internal/here"
|
|
"go.pinniped.dev/internal/testutil"
|
|
|
|
"go.pinniped.dev/test/testlib"
|
|
)
|
|
|
|
const generateGitHubNamePrefix = "integration-test-github-idp-"
|
|
|
|
func TestGitHubIDPStaticValidationOnCreate_Parallel(t *testing.T) {
|
|
adminClient := testlib.NewKubernetesClientset(t)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
|
t.Cleanup(cancel)
|
|
|
|
namespaceClient := adminClient.CoreV1().Namespaces()
|
|
skipCELTests := !testutil.KubeServerMinorVersionAtLeastInclusive(t, adminClient.Discovery(), 26)
|
|
|
|
ns, err := namespaceClient.Create(ctx, &corev1.Namespace{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
GenerateName: generateGitHubNamePrefix,
|
|
},
|
|
}, metav1.CreateOptions{})
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
require.NoError(t, namespaceClient.Delete(ctx, ns.Name, metav1.DeleteOptions{}))
|
|
})
|
|
|
|
gitHubIDPClient := testlib.NewSupervisorClientset(t).IDPV1alpha1().GitHubIdentityProviders(ns.Name)
|
|
|
|
tests := []struct {
|
|
name string
|
|
usesCELValidation bool
|
|
inputSpec idpv1alpha1.GitHubIdentityProviderSpec
|
|
wantSpec idpv1alpha1.GitHubIdentityProviderSpec
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "all fields set",
|
|
inputSpec: idpv1alpha1.GitHubIdentityProviderSpec{
|
|
GitHubAPI: idpv1alpha1.GitHubAPIConfig{
|
|
Host: ptr.To("some-host.example.com"),
|
|
TLS: &idpv1alpha1.TLSSpec{
|
|
CertificateAuthorityData: func() string {
|
|
return base64.StdEncoding.EncodeToString([]byte("-----BEGIN CERTIFICATE-----\ndata goes here"))
|
|
}(),
|
|
},
|
|
},
|
|
AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{
|
|
Organizations: idpv1alpha1.GitHubOrganizationsSpec{
|
|
Allowed: []string{
|
|
"org1",
|
|
"that-other-org",
|
|
},
|
|
Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyOnlyUsersFromAllowedOrganizations),
|
|
},
|
|
},
|
|
Claims: idpv1alpha1.GitHubClaims{
|
|
Username: ptr.To(idpv1alpha1.GitHubUsernameLoginAndID),
|
|
Groups: ptr.To(idpv1alpha1.GitHubUseTeamSlugForGroupName),
|
|
},
|
|
Client: idpv1alpha1.GitHubClientSpec{
|
|
SecretName: "any-name-goes-here",
|
|
},
|
|
},
|
|
wantSpec: idpv1alpha1.GitHubIdentityProviderSpec{
|
|
GitHubAPI: idpv1alpha1.GitHubAPIConfig{
|
|
Host: ptr.To("some-host.example.com"),
|
|
TLS: &idpv1alpha1.TLSSpec{
|
|
CertificateAuthorityData: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCmRhdGEgZ29lcyBoZXJl",
|
|
},
|
|
},
|
|
AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{
|
|
Organizations: idpv1alpha1.GitHubOrganizationsSpec{
|
|
Allowed: []string{
|
|
"org1",
|
|
"that-other-org",
|
|
},
|
|
Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyOnlyUsersFromAllowedOrganizations),
|
|
},
|
|
},
|
|
Claims: idpv1alpha1.GitHubClaims{
|
|
Username: ptr.To(idpv1alpha1.GitHubUsernameLoginAndID),
|
|
Groups: ptr.To(idpv1alpha1.GitHubUseTeamSlugForGroupName),
|
|
},
|
|
Client: idpv1alpha1.GitHubClientSpec{
|
|
SecretName: "any-name-goes-here",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "minimum fields set - inherit defaults",
|
|
inputSpec: idpv1alpha1.GitHubIdentityProviderSpec{
|
|
AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{
|
|
Organizations: idpv1alpha1.GitHubOrganizationsSpec{
|
|
Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers),
|
|
},
|
|
},
|
|
Client: idpv1alpha1.GitHubClientSpec{
|
|
SecretName: "name-of-a-secret",
|
|
},
|
|
},
|
|
wantSpec: idpv1alpha1.GitHubIdentityProviderSpec{
|
|
GitHubAPI: idpv1alpha1.GitHubAPIConfig{
|
|
Host: ptr.To("github.com"),
|
|
},
|
|
AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{
|
|
Organizations: idpv1alpha1.GitHubOrganizationsSpec{
|
|
Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers),
|
|
},
|
|
},
|
|
Claims: idpv1alpha1.GitHubClaims{
|
|
Username: ptr.To(idpv1alpha1.GitHubUsernameLoginAndID),
|
|
Groups: ptr.To(idpv1alpha1.GitHubUseTeamSlugForGroupName),
|
|
},
|
|
Client: idpv1alpha1.GitHubClientSpec{
|
|
SecretName: "name-of-a-secret",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: fmt.Sprintf(
|
|
"cannot set AllowedOrganizationsPolicy=%s and set AllowedOrganizations",
|
|
string(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers)),
|
|
usesCELValidation: true,
|
|
inputSpec: idpv1alpha1.GitHubIdentityProviderSpec{
|
|
AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{
|
|
Organizations: idpv1alpha1.GitHubOrganizationsSpec{
|
|
Allowed: []string{
|
|
"some-org",
|
|
},
|
|
Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers),
|
|
},
|
|
},
|
|
Client: idpv1alpha1.GitHubClientSpec{
|
|
SecretName: "name-of-a-secret",
|
|
},
|
|
},
|
|
wantErr: "spec.allowAuthentication.organizations.policy must be 'OnlyUsersFromAllowedOrganizations' when spec.allowAuthentication.organizations.allowed has organizations listed",
|
|
},
|
|
{
|
|
name: fmt.Sprintf("spec.allowAuthentication.organizations.policy must be '%s' when spec.allowAuthentication.organizations.allowed is empty (nil)", string(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers)),
|
|
usesCELValidation: true,
|
|
inputSpec: idpv1alpha1.GitHubIdentityProviderSpec{
|
|
AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{
|
|
Organizations: idpv1alpha1.GitHubOrganizationsSpec{
|
|
Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyOnlyUsersFromAllowedOrganizations),
|
|
},
|
|
},
|
|
Client: idpv1alpha1.GitHubClientSpec{
|
|
SecretName: "name-of-a-secret",
|
|
},
|
|
},
|
|
wantErr: "spec.allowAuthentication.organizations.policy must be 'AllGitHubUsers' when spec.allowAuthentication.organizations.allowed is empty",
|
|
},
|
|
{
|
|
name: fmt.Sprintf("spec.allowAuthentication.organizations.policy must be '%s' when spec.allowAuthentication.organizations.allowed is empty", string(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers)),
|
|
usesCELValidation: true,
|
|
inputSpec: idpv1alpha1.GitHubIdentityProviderSpec{
|
|
AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{
|
|
Organizations: idpv1alpha1.GitHubOrganizationsSpec{
|
|
Allowed: []string{},
|
|
Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyOnlyUsersFromAllowedOrganizations),
|
|
},
|
|
},
|
|
Client: idpv1alpha1.GitHubClientSpec{
|
|
SecretName: "name-of-a-secret",
|
|
},
|
|
},
|
|
wantErr: "spec.allowAuthentication.organizations.policy must be 'AllGitHubUsers' when spec.allowAuthentication.organizations.allowed is empty",
|
|
},
|
|
{
|
|
name: "spec.client.secretName in body should be at least 1 chars long",
|
|
inputSpec: idpv1alpha1.GitHubIdentityProviderSpec{},
|
|
wantErr: "spec.client.secretName in body should be at least 1 chars long",
|
|
},
|
|
{
|
|
name: "spec.githubAPI.host in body should be at least 1 chars long",
|
|
inputSpec: idpv1alpha1.GitHubIdentityProviderSpec{
|
|
GitHubAPI: idpv1alpha1.GitHubAPIConfig{
|
|
Host: ptr.To(""),
|
|
},
|
|
AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{
|
|
Organizations: idpv1alpha1.GitHubOrganizationsSpec{
|
|
Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers),
|
|
},
|
|
},
|
|
Client: idpv1alpha1.GitHubClientSpec{
|
|
SecretName: "name-of-a-secret",
|
|
},
|
|
},
|
|
wantErr: "spec.githubAPI.host in body should be at least 1 chars long",
|
|
},
|
|
{
|
|
name: "duplicates not permitted in spec.allowAuthentication.organizations.allowed",
|
|
inputSpec: idpv1alpha1.GitHubIdentityProviderSpec{
|
|
AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{
|
|
Organizations: idpv1alpha1.GitHubOrganizationsSpec{
|
|
Allowed: []string{
|
|
"org1",
|
|
"org1",
|
|
},
|
|
Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyOnlyUsersFromAllowedOrganizations),
|
|
},
|
|
},
|
|
Client: idpv1alpha1.GitHubClientSpec{
|
|
SecretName: "name-of-a-secret",
|
|
},
|
|
},
|
|
wantErr: `spec.allowAuthentication.organizations.allowed[1]: Duplicate value: "org1"`,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
if tt.usesCELValidation && skipCELTests {
|
|
t.Skip("CEL is not available for current K8s version")
|
|
}
|
|
|
|
input := &idpv1alpha1.GitHubIdentityProvider{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
GenerateName: generateGitHubNamePrefix,
|
|
},
|
|
Spec: tt.inputSpec,
|
|
}
|
|
|
|
outputGitHubIDP, err := gitHubIDPClient.Create(ctx, input, metav1.CreateOptions{})
|
|
if tt.wantErr == "" {
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.wantSpec, outputGitHubIDP.Spec)
|
|
} else {
|
|
require.ErrorContains(t, err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGitHubIDPSetsDefaultsWithKubectl_Parallel(t *testing.T) {
|
|
env := testlib.IntegrationEnv(t)
|
|
|
|
adminClient := testlib.NewKubernetesClientset(t)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
|
t.Cleanup(cancel)
|
|
|
|
namespaceClient := adminClient.CoreV1().Namespaces()
|
|
|
|
ns, err := namespaceClient.Create(ctx, &corev1.Namespace{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
GenerateName: generateGitHubNamePrefix,
|
|
},
|
|
}, metav1.CreateOptions{})
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
require.NoError(t, namespaceClient.Delete(ctx, ns.Name, metav1.DeleteOptions{}))
|
|
})
|
|
t.Logf("Created namespace %s", ns.Name)
|
|
|
|
idpName := generateGitHubNamePrefix + testlib.RandHex(t, 16)
|
|
|
|
githubIDPYaml := []byte(here.Doc(fmt.Sprintf(`
|
|
---
|
|
apiVersion: idp.supervisor.%s/v1alpha1
|
|
kind: GitHubIdentityProvider
|
|
metadata:
|
|
name: %s
|
|
namespace: %s
|
|
spec:
|
|
allowAuthentication:
|
|
organizations:
|
|
policy: AllGitHubUsers
|
|
client:
|
|
secretName: any-secret-name`, env.APIGroupSuffix, idpName, ns.Name)))
|
|
|
|
githubIDPYamlFilepath := filepath.Join(t.TempDir(), "github-idp.yaml")
|
|
|
|
require.NoError(t, os.WriteFile(githubIDPYamlFilepath, githubIDPYaml, 0600))
|
|
|
|
stdOut, stdErr := runTestKubectlCommand(t, "create", "-f", githubIDPYamlFilepath)
|
|
|
|
require.Equal(t, fmt.Sprintf("githubidentityprovider.idp.supervisor.%s/%s created\n", env.APIGroupSuffix, idpName), stdOut)
|
|
require.Empty(t, stdErr)
|
|
|
|
gitHubIDPClient := testlib.NewSupervisorClientset(t).IDPV1alpha1().GitHubIdentityProviders(ns.Name)
|
|
|
|
idp, err := gitHubIDPClient.Get(ctx, idpName, metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, idpv1alpha1.GitHubIdentityProviderSpec{
|
|
GitHubAPI: idpv1alpha1.GitHubAPIConfig{
|
|
Host: ptr.To("github.com"),
|
|
},
|
|
Claims: idpv1alpha1.GitHubClaims{
|
|
Username: ptr.To(idpv1alpha1.GitHubUsernameLoginAndID),
|
|
Groups: ptr.To(idpv1alpha1.GitHubUseTeamSlugForGroupName),
|
|
},
|
|
AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{
|
|
Organizations: idpv1alpha1.GitHubOrganizationsSpec{
|
|
Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers),
|
|
},
|
|
},
|
|
Client: idpv1alpha1.GitHubClientSpec{
|
|
SecretName: "any-secret-name",
|
|
},
|
|
}, idp.Spec)
|
|
}
|
|
|
|
func TestGitHubIDPPhaseAndConditions_Parallel(t *testing.T) {
|
|
// These operations must be performed in the Supervisor's namespace so that the controller can find GitHubIdentityProvider
|
|
supervisorNamespace := testlib.IntegrationEnv(t).SupervisorNamespace
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
|
t.Cleanup(cancel)
|
|
|
|
kubernetesClient := testlib.NewKubernetesClientset(t)
|
|
secretsClient := kubernetesClient.CoreV1().Secrets(supervisorNamespace)
|
|
gitHubIDPClient := testlib.NewSupervisorClientset(t).IDPV1alpha1().GitHubIdentityProviders(supervisorNamespace)
|
|
|
|
happySecretName := generateGitHubNamePrefix + testlib.RandHex(t, 16)
|
|
invalidSecretName := generateGitHubNamePrefix + testlib.RandHex(t, 16)
|
|
|
|
tests := []struct {
|
|
name string
|
|
secrets []*corev1.Secret // Secrets will be created first, and the first secret found will be listed as the configured GitHub Client secret
|
|
idps []*idpv1alpha1.GitHubIdentityProvider
|
|
wantPhase idpv1alpha1.GitHubIdentityProviderPhase
|
|
wantConditions []*metav1.Condition
|
|
}{
|
|
{
|
|
name: "Happy Path",
|
|
secrets: []*corev1.Secret{
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: happySecretName,
|
|
},
|
|
Type: "secrets.pinniped.dev/github-client",
|
|
Data: map[string][]byte{
|
|
"clientID": []byte("foo"),
|
|
"clientSecret": []byte("bar"),
|
|
},
|
|
},
|
|
},
|
|
idps: []*idpv1alpha1.GitHubIdentityProvider{
|
|
{
|
|
Spec: idpv1alpha1.GitHubIdentityProviderSpec{
|
|
GitHubAPI: idpv1alpha1.GitHubAPIConfig{
|
|
Host: ptr.To("github.com"),
|
|
},
|
|
AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{
|
|
Organizations: idpv1alpha1.GitHubOrganizationsSpec{
|
|
Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantPhase: idpv1alpha1.GitHubPhaseReady,
|
|
wantConditions: []*metav1.Condition{
|
|
{
|
|
Type: "ClaimsValid",
|
|
Status: metav1.ConditionTrue,
|
|
Reason: "Success",
|
|
Message: "spec.claims are valid",
|
|
},
|
|
{
|
|
Type: "ClientCredentialsSecretValid",
|
|
Status: metav1.ConditionTrue,
|
|
Reason: "Success",
|
|
Message: fmt.Sprintf("clientID and clientSecret have been read from spec.client.SecretName (%q)", happySecretName),
|
|
},
|
|
{
|
|
Type: "GitHubConnectionValid",
|
|
Status: metav1.ConditionTrue,
|
|
Reason: "Success",
|
|
Message: `probed connection to "api.github.com:443" for spec.githubAPI.host ("github.com"): host is reachable and TLS verification succeeds`,
|
|
},
|
|
{
|
|
Type: "HostValid",
|
|
Status: metav1.ConditionTrue,
|
|
Reason: "Success",
|
|
Message: `spec.githubAPI.host ("github.com") is valid`,
|
|
},
|
|
{
|
|
Type: "OrganizationsPolicyValid",
|
|
Status: metav1.ConditionTrue,
|
|
Reason: "Success",
|
|
Message: `spec.allowAuthentication.organizations.policy ("AllGitHubUsers") is valid`,
|
|
},
|
|
{
|
|
Type: "TLSConfigurationValid",
|
|
Status: metav1.ConditionTrue,
|
|
Reason: "Success",
|
|
Message: "spec.githubAPI.tls is valid: no TLS configuration provided: using default root CA bundle from container image",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Invalid Client Secret",
|
|
secrets: []*corev1.Secret{
|
|
{
|
|
Type: "secrets.pinniped.dev/github-client",
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: invalidSecretName,
|
|
},
|
|
},
|
|
},
|
|
idps: []*idpv1alpha1.GitHubIdentityProvider{
|
|
{
|
|
Spec: idpv1alpha1.GitHubIdentityProviderSpec{
|
|
GitHubAPI: idpv1alpha1.GitHubAPIConfig{
|
|
Host: ptr.To("api.github.com"),
|
|
},
|
|
AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{
|
|
Organizations: idpv1alpha1.GitHubOrganizationsSpec{
|
|
Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers),
|
|
},
|
|
},
|
|
Client: idpv1alpha1.GitHubClientSpec{
|
|
SecretName: invalidSecretName,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantPhase: idpv1alpha1.GitHubPhaseError,
|
|
wantConditions: []*metav1.Condition{
|
|
{
|
|
Type: "ClaimsValid",
|
|
Status: metav1.ConditionTrue,
|
|
Reason: "Success",
|
|
Message: "spec.claims are valid",
|
|
},
|
|
{
|
|
Type: "ClientCredentialsSecretValid",
|
|
Status: metav1.ConditionFalse,
|
|
Reason: "SecretNotFound",
|
|
Message: fmt.Sprintf(`missing key "clientID": secret from spec.client.SecretName (%q) must be found in namespace %q with type "secrets.pinniped.dev/github-client" and keys "clientID" and "clientSecret"`,
|
|
invalidSecretName,
|
|
supervisorNamespace),
|
|
},
|
|
{
|
|
Type: "GitHubConnectionValid",
|
|
Status: metav1.ConditionTrue,
|
|
Reason: "Success",
|
|
Message: `probed connection to "api.github.com:443" for spec.githubAPI.host ("api.github.com"): host is reachable and TLS verification succeeds`,
|
|
},
|
|
{
|
|
Type: "HostValid",
|
|
Status: metav1.ConditionTrue,
|
|
Reason: "Success",
|
|
Message: `spec.githubAPI.host ("api.github.com") is valid`,
|
|
},
|
|
{
|
|
Type: "OrganizationsPolicyValid",
|
|
Status: metav1.ConditionTrue,
|
|
Reason: "Success",
|
|
Message: `spec.allowAuthentication.organizations.policy ("AllGitHubUsers") is valid`,
|
|
},
|
|
{
|
|
Type: "TLSConfigurationValid",
|
|
Status: metav1.ConditionTrue,
|
|
Reason: "Success",
|
|
Message: `spec.githubAPI.tls is valid: no TLS configuration provided: using default root CA bundle from container image`,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var secretName string
|
|
for _, secret := range tt.secrets {
|
|
secret.GenerateName = generateGitHubNamePrefix
|
|
|
|
created, err := secretsClient.Create(ctx, secret, metav1.CreateOptions{})
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() {
|
|
err := secretsClient.Delete(ctx, created.Name, metav1.DeleteOptions{})
|
|
require.NoError(t, err)
|
|
})
|
|
if secretName == "" {
|
|
secretName = created.Name
|
|
}
|
|
}
|
|
|
|
for _, idp := range tt.idps {
|
|
idp.Name = ""
|
|
idp.GenerateName = generateGitHubNamePrefix
|
|
idp.Spec.Client.SecretName = secretName
|
|
|
|
created, err := gitHubIDPClient.Create(ctx, idp, metav1.CreateOptions{})
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
err := gitHubIDPClient.Delete(ctx, created.Name, metav1.DeleteOptions{})
|
|
require.NoError(t, err)
|
|
})
|
|
testlib.WaitForGitHubIDPPhase(ctx, t, gitHubIDPClient, created.Name, tt.wantPhase)
|
|
testlib.WaitForGitHubIdentityProviderStatusConditions(ctx, t, gitHubIDPClient, created.Name, tt.wantConditions)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGitHubIDPInWrongNamespace_Parallel(t *testing.T) {
|
|
// The GitHubIdentityProvider must be in the same namespace as the controller
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
|
t.Cleanup(cancel)
|
|
|
|
kubernetesClient := testlib.NewKubernetesClientset(t)
|
|
|
|
namespaceClient := kubernetesClient.CoreV1().Namespaces()
|
|
otherNamespace, err := namespaceClient.Create(ctx, &corev1.Namespace{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
GenerateName: generateGitHubNamePrefix,
|
|
},
|
|
}, metav1.CreateOptions{})
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
require.NoError(t, namespaceClient.Delete(ctx, otherNamespace.Name, metav1.DeleteOptions{}))
|
|
})
|
|
|
|
gitHubIDPClient := testlib.NewSupervisorClientset(t).IDPV1alpha1().GitHubIdentityProviders(otherNamespace.Name)
|
|
|
|
idp := &idpv1alpha1.GitHubIdentityProvider{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
GenerateName: generateGitHubNamePrefix,
|
|
Namespace: otherNamespace.Name,
|
|
},
|
|
Spec: idpv1alpha1.GitHubIdentityProviderSpec{
|
|
GitHubAPI: idpv1alpha1.GitHubAPIConfig{
|
|
Host: ptr.To("github.com"),
|
|
},
|
|
AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{
|
|
Organizations: idpv1alpha1.GitHubOrganizationsSpec{
|
|
Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers),
|
|
},
|
|
},
|
|
Client: idpv1alpha1.GitHubClientSpec{
|
|
SecretName: "does-not-matter",
|
|
},
|
|
},
|
|
}
|
|
|
|
createdIDP, err := gitHubIDPClient.Create(ctx, idp, metav1.CreateOptions{})
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
err := gitHubIDPClient.Delete(ctx, createdIDP.Name, metav1.DeleteOptions{})
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
// We require that there's never an error
|
|
// ... and that the status phase is never anything but Pending
|
|
// ... and that there are no status conditions
|
|
require.Never(t, func() bool {
|
|
idp, err := gitHubIDPClient.Get(ctx, createdIDP.Name, metav1.GetOptions{})
|
|
return err != nil && idp.Status.Phase != idpv1alpha1.GitHubPhasePending && len(idp.Status.Conditions) > 0
|
|
}, 2*time.Minute, 10*time.Second)
|
|
}
|
|
|
|
func TestGitHubIDPSecretInOtherNamespace_Parallel(t *testing.T) {
|
|
// The GitHubIdentityProvider must be in the same namespace as the controller
|
|
supervisorNamespace := testlib.IntegrationEnv(t).SupervisorNamespace
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
|
t.Cleanup(cancel)
|
|
|
|
kubernetesClient := testlib.NewKubernetesClientset(t)
|
|
gitHubIDPClient := testlib.NewSupervisorClientset(t).IDPV1alpha1().GitHubIdentityProviders(supervisorNamespace)
|
|
|
|
namespaceClient := kubernetesClient.CoreV1().Namespaces()
|
|
otherNamespace, err := namespaceClient.Create(ctx, &corev1.Namespace{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
GenerateName: generateGitHubNamePrefix,
|
|
},
|
|
}, metav1.CreateOptions{})
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
require.NoError(t, namespaceClient.Delete(ctx, otherNamespace.Name, metav1.DeleteOptions{}))
|
|
})
|
|
|
|
secretsClient := kubernetesClient.CoreV1().Secrets(otherNamespace.Name)
|
|
|
|
secret := &corev1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
GenerateName: generateGitHubNamePrefix,
|
|
Namespace: otherNamespace.Name,
|
|
},
|
|
Type: "secrets.pinniped.dev/github-client",
|
|
Data: map[string][]byte{
|
|
"clientID": []byte("foo"),
|
|
"clientSecret": []byte("bar"),
|
|
},
|
|
}
|
|
|
|
// This secret will be cleaned up when its namespace is deleted
|
|
createdSecret, err := secretsClient.Create(ctx, secret, metav1.CreateOptions{})
|
|
require.NoError(t, err)
|
|
|
|
idp := &idpv1alpha1.GitHubIdentityProvider{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
GenerateName: generateGitHubNamePrefix,
|
|
Namespace: supervisorNamespace,
|
|
},
|
|
Spec: idpv1alpha1.GitHubIdentityProviderSpec{
|
|
GitHubAPI: idpv1alpha1.GitHubAPIConfig{
|
|
Host: ptr.To("github.com"),
|
|
},
|
|
AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{
|
|
Organizations: idpv1alpha1.GitHubOrganizationsSpec{
|
|
Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers),
|
|
},
|
|
},
|
|
Client: idpv1alpha1.GitHubClientSpec{
|
|
SecretName: createdSecret.Name,
|
|
},
|
|
},
|
|
}
|
|
|
|
created, err := gitHubIDPClient.Create(ctx, idp, metav1.CreateOptions{})
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
err := gitHubIDPClient.Delete(ctx, created.Name, metav1.DeleteOptions{})
|
|
require.NoError(t, err)
|
|
})
|
|
testlib.WaitForGitHubIDPPhase(ctx, t, gitHubIDPClient, created.Name, idpv1alpha1.GitHubPhaseError)
|
|
|
|
testlib.WaitForGitHubIdentityProviderStatusConditions(ctx, t, gitHubIDPClient, created.Name, []*metav1.Condition{
|
|
{
|
|
Type: "ClaimsValid",
|
|
Status: metav1.ConditionTrue,
|
|
Reason: "Success",
|
|
Message: "spec.claims are valid",
|
|
},
|
|
{
|
|
Type: "ClientCredentialsSecretValid",
|
|
Status: metav1.ConditionFalse,
|
|
Reason: "SecretNotFound",
|
|
Message: fmt.Sprintf(`secret %q not found: secret from spec.client.SecretName (%q) must be found in namespace %q with type "secrets.pinniped.dev/github-client" and keys "clientID" and "clientSecret"`,
|
|
idp.Spec.Client.SecretName,
|
|
idp.Spec.Client.SecretName,
|
|
supervisorNamespace),
|
|
},
|
|
{
|
|
Type: "GitHubConnectionValid",
|
|
Status: metav1.ConditionTrue,
|
|
Reason: "Success",
|
|
Message: `probed connection to "api.github.com:443" for spec.githubAPI.host ("github.com"): host is reachable and TLS verification succeeds`,
|
|
},
|
|
{
|
|
Type: "HostValid",
|
|
Status: metav1.ConditionTrue,
|
|
Reason: "Success",
|
|
Message: `spec.githubAPI.host ("github.com") is valid`,
|
|
},
|
|
{
|
|
Type: "OrganizationsPolicyValid",
|
|
Status: metav1.ConditionTrue,
|
|
Reason: "Success",
|
|
Message: `spec.allowAuthentication.organizations.policy ("AllGitHubUsers") is valid`,
|
|
},
|
|
{
|
|
Type: "TLSConfigurationValid",
|
|
Status: metav1.ConditionTrue,
|
|
Reason: "Success",
|
|
Message: "spec.githubAPI.tls is valid: no TLS configuration provided: using default root CA bundle from container image",
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestGitHubIDPTooManyOrganizationsStaticValidationOnCreate_Parallel(t *testing.T) {
|
|
adminClient := testlib.NewKubernetesClientset(t)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
|
t.Cleanup(cancel)
|
|
|
|
namespaceClient := adminClient.CoreV1().Namespaces()
|
|
|
|
ns, err := namespaceClient.Create(ctx, &corev1.Namespace{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
GenerateName: generateGitHubNamePrefix,
|
|
},
|
|
}, metav1.CreateOptions{})
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
require.NoError(t, namespaceClient.Delete(ctx, ns.Name, metav1.DeleteOptions{}))
|
|
})
|
|
|
|
gitHubIDPClient := testlib.NewSupervisorClientset(t).IDPV1alpha1().GitHubIdentityProviders(ns.Name)
|
|
|
|
input := &idpv1alpha1.GitHubIdentityProvider{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
GenerateName: generateGitHubNamePrefix,
|
|
},
|
|
Spec: idpv1alpha1.GitHubIdentityProviderSpec{
|
|
AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{
|
|
Organizations: idpv1alpha1.GitHubOrganizationsSpec{
|
|
Allowed: func() []string {
|
|
orgs := make([]string, 100)
|
|
for i := range 100 {
|
|
orgs[i] = fmt.Sprintf("org-%d", i)
|
|
}
|
|
return orgs
|
|
}(),
|
|
Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyOnlyUsersFromAllowedOrganizations),
|
|
},
|
|
},
|
|
Client: idpv1alpha1.GitHubClientSpec{
|
|
SecretName: "name-of-a-secret",
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err = gitHubIDPClient.Create(ctx, input, metav1.CreateOptions{})
|
|
|
|
wantErr := "spec.allowAuthentication.organizations.allowed: Invalid value: 100: spec.allowAuthentication.organizations.allowed in body should have at most 64 items"
|
|
if testutil.KubeServerMinorVersionAtLeastInclusive(t, adminClient.Discovery(), 24) {
|
|
wantErr = "spec.allowAuthentication.organizations.allowed: Too many: 100: must have at most 64 items"
|
|
}
|
|
|
|
require.ErrorContains(t, err, wantErr)
|
|
}
|