diff --git a/test/integration/supervisor_github_idp_test.go b/test/integration/supervisor_github_idp_test.go index 3a135dbc1..ed0781668 100644 --- a/test/integration/supervisor_github_idp_test.go +++ b/test/integration/supervisor_github_idp_test.go @@ -328,10 +328,15 @@ func TestGitHubIDPSetsDefaultsWithKubectl_Parallel(t *testing.T) { 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 + env := testlib.IntegrationEnv(t) + supervisorNamespace := env.SupervisorNamespace ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) t.Cleanup(cancel) + badCABundleConfigMap := testlib.CreateTestConfigMap(t, supervisorNamespace, "ca-bundle", map[string]string{ + "ca.crt": "This is not a real CA bundle", + }) + kubernetesClient := testlib.NewKubernetesClientset(t) secretsClient := kubernetesClient.CoreV1().Secrets(supervisorNamespace) gitHubIDPClient := testlib.NewSupervisorClientset(t).IDPV1alpha1().GitHubIdentityProviders(supervisorNamespace) @@ -483,6 +488,382 @@ func TestGitHubIDPPhaseAndConditions_Parallel(t *testing.T) { }, }, }, + { + name: "invalid when spec.githubAPI.tls supplies both certificateAuthorityData and certificateAuthorityDataSource", + 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{ + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityData: "this is not a CA bundle", + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: "does-not-matter", + Key: "also-does-not-matter", + }, + }, + Host: ptr.To("github.com"), + }, + AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{ + Organizations: idpv1alpha1.GitHubOrganizationsSpec{ + Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers), + }, + }, + }, + }, + }, + wantPhase: idpv1alpha1.GitHubPhaseError, + 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: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, + { + 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.ConditionFalse, + Reason: "InvalidTLSConfig", + Message: "spec.githubAPI.tls is invalid: both tls.certificateAuthorityDataSource and tls.certificateAuthorityData provided", + }, + }, + }, + { + name: "invalid when spec.githubAPI.tls.certificateAuthorityDataSource refers to a configmap that does not exist", + 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{ + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: "does-not-exist", + Key: "does-not-matter", + }, + }, + Host: ptr.To("github.com"), + }, + AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{ + Organizations: idpv1alpha1.GitHubOrganizationsSpec{ + Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers), + }, + }, + }, + }, + }, + wantPhase: idpv1alpha1.GitHubPhaseError, + 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: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, + { + 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.ConditionFalse, + Reason: "InvalidTLSConfig", + Message: "spec.githubAPI.tls.certificateAuthorityDataSource is invalid: failed to get configmap \"supervisor/does-not-exist\": configmap \"does-not-exist\" not found", + }, + }, + }, + { + name: "invalid when spec.githubAPI.tls.certificateAuthorityDataSource refers to a secret that does not exist", + 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{ + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "Secret", + Name: "does-not-exist", + Key: "does-not-matter", + }, + }, + Host: ptr.To("github.com"), + }, + AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{ + Organizations: idpv1alpha1.GitHubOrganizationsSpec{ + Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers), + }, + }, + }, + }, + }, + wantPhase: idpv1alpha1.GitHubPhaseError, + 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: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, + { + 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.ConditionFalse, + Reason: "InvalidTLSConfig", + Message: "spec.githubAPI.tls.certificateAuthorityDataSource is invalid: failed to get secret \"supervisor/does-not-exist\": secret \"does-not-exist\" not found", + }, + }, + }, + { + name: "invalid when spec.githubAPI.tls.certificateAuthorityDataSource refers to a configmap that does not have valid PEM bytes", + 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{ + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: badCABundleConfigMap.Name, + Key: "ca.crt", + }, + }, + Host: ptr.To("github.com"), + }, + AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{ + Organizations: idpv1alpha1.GitHubOrganizationsSpec{ + Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers), + }, + }, + }, + }, + }, + wantPhase: idpv1alpha1.GitHubPhaseError, + 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: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, + { + 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.ConditionFalse, + Reason: "InvalidTLSConfig", + Message: fmt.Sprintf("spec.githubAPI.tls.certificateAuthorityDataSource is invalid: key \"ca.crt\" with 28 bytes of data in configmap \"supervisor/%s\" is not a PEM-encoded certificate (PEM certificates must begin with \"-----BEGIN CERTIFICATE-----\")", badCABundleConfigMap.Name), + }, + }, + }, + { + name: "invalid when spec.githubAPI.tls.certificateAuthorityDataSource refers to a key in a configmap that does not exist", + 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{ + TLS: &idpv1alpha1.TLSSpec{ + CertificateAuthorityDataSource: &idpv1alpha1.CertificateAuthorityDataSourceSpec{ + Kind: "ConfigMap", + Name: badCABundleConfigMap.Name, + Key: "key-not-present", + }, + }, + Host: ptr.To("github.com"), + }, + AllowAuthentication: idpv1alpha1.GitHubAllowAuthenticationSpec{ + Organizations: idpv1alpha1.GitHubOrganizationsSpec{ + Policy: ptr.To(idpv1alpha1.GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers), + }, + }, + }, + }, + }, + wantPhase: idpv1alpha1.GitHubPhaseError, + 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: "Unknown", + Reason: "UnableToValidate", + Message: "unable to validate; see other conditions for details", + }, + { + 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.ConditionFalse, + Reason: "InvalidTLSConfig", + Message: fmt.Sprintf("spec.githubAPI.tls.certificateAuthorityDataSource is invalid: key \"key-not-present\" not found in configmap \"supervisor/%s\"", badCABundleConfigMap.Name), + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -582,7 +963,8 @@ func TestGitHubIDPInWrongNamespace_Parallel(t *testing.T) { func TestGitHubIDPSecretInOtherNamespace_Parallel(t *testing.T) { // The GitHubIdentityProvider must be in the same namespace as the controller - supervisorNamespace := testlib.IntegrationEnv(t).SupervisorNamespace + env := testlib.IntegrationEnv(t) + supervisorNamespace := env.SupervisorNamespace ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) t.Cleanup(cancel)