oauth2-proxy/pkg/validation/providers_test.go

210 lines
6.6 KiB
Go

package validation
import (
"fmt"
"os"
"path/filepath"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Providers", func() {
type validateProvidersTableInput struct {
options *options.Options
errStrings []string
}
validProvider := options.Provider{
ID: "ProviderID",
ClientID: "ClientID",
ClientSecret: "ClientSecret",
}
validOIDCSigningAlgorithmsProvider := options.Provider{
ID: "ProviderIDOIDCSigningAlgorithms",
ClientID: "ClientID",
ClientSecret: "ClientSecret",
OIDCConfig: options.OIDCOptions{
EnabledSigningAlgs: []string{"RS256", "EdDSA"},
},
}
invalidOIDCSigningAlgorithmsProvider := options.Provider{
ID: "ProviderIDInvalidOIDCSigningAlgorithms",
ClientID: "ClientID",
ClientSecret: "ClientSecret",
OIDCConfig: options.OIDCOptions{
EnabledSigningAlgs: []string{"RS256", "invalid"},
},
}
invalidOIDCSigningAlgorithmCaseProvider := options.Provider{
ID: "ProviderIDInvalidOIDCSigningAlgorithmCase",
ClientID: "ClientID",
ClientSecret: "ClientSecret",
OIDCConfig: options.OIDCOptions{
EnabledSigningAlgs: []string{"rs256"},
},
}
validLoginGovProvider := options.Provider{
Type: "login.gov",
ID: "ProviderIDLoginGov",
ClientID: "ClientID",
ClientSecret: "ClientSecret",
}
missingIDProvider := options.Provider{
ClientID: "ClientID",
ClientSecret: "ClientSecret",
}
missingProvider := "at least one provider has to be defined"
emptyIDMsg := "provider has empty id: ids are required for all providers"
duplicateProviderIDMsg := "multiple providers found with id ProviderID: provider ids must be unique"
skipButtonAndMultipleProvidersMsg := "SkipProviderButton and multiple providers are mutually exclusive"
invalidOIDCSigningAlgorithmMsg := "provider ProviderIDInvalidOIDCSigningAlgorithms has invalid EnabledSigningAlgs entry \"invalid\""
invalidOIDCSigningAlgorithmCaseMsg := "provider ProviderIDInvalidOIDCSigningAlgorithmCase has invalid EnabledSigningAlgs entry \"rs256\""
DescribeTable("validateProviders",
func(o *validateProvidersTableInput) {
Expect(validateProviders(o.options)).To(ConsistOf(o.errStrings))
},
Entry("with no providers", &validateProvidersTableInput{
options: &options.Options{},
errStrings: []string{missingProvider},
}),
Entry("with valid providers", &validateProvidersTableInput{
options: &options.Options{
Providers: options.Providers{
validProvider,
validLoginGovProvider,
},
},
errStrings: []string{},
}),
Entry("with an empty providerID", &validateProvidersTableInput{
options: &options.Options{
Providers: options.Providers{
missingIDProvider,
},
},
errStrings: []string{emptyIDMsg},
}),
Entry("with same providerID", &validateProvidersTableInput{
options: &options.Options{
Providers: options.Providers{
validProvider,
validProvider,
},
},
errStrings: []string{duplicateProviderIDMsg},
}),
Entry("with multiple providers and skip provider button", &validateProvidersTableInput{
options: &options.Options{
SkipProviderButton: true,
Providers: options.Providers{
validProvider,
validLoginGovProvider,
},
},
errStrings: []string{skipButtonAndMultipleProvidersMsg},
}),
Entry("with valid OIDC signing algorithms", &validateProvidersTableInput{
options: &options.Options{
Providers: options.Providers{
validOIDCSigningAlgorithmsProvider,
},
},
errStrings: []string{},
}),
Entry("with an invalid OIDC signing algorithm", &validateProvidersTableInput{
options: &options.Options{
Providers: options.Providers{
invalidOIDCSigningAlgorithmsProvider,
},
},
errStrings: []string{invalidOIDCSigningAlgorithmMsg},
}),
Entry("with an OIDC signing algorithm using invalid casing", &validateProvidersTableInput{
options: &options.Options{
Providers: options.Providers{
invalidOIDCSigningAlgorithmCaseProvider,
},
},
errStrings: []string{invalidOIDCSigningAlgorithmCaseMsg},
}),
)
Context("with a client assertion file", func() {
var assertionFile string
BeforeEach(func() {
assertionFile = filepath.Join(GinkgoT().TempDir(), "token")
Expect(os.WriteFile(assertionFile, []byte("assertion"), 0600)).To(Succeed())
})
assertionProvider := func(providerType options.ProviderType, file string) options.Provider {
return options.Provider{
ID: "ProviderIDAssertion",
Type: providerType,
ClientID: "ClientID",
ClientAssertionFile: file,
}
}
validate := func(provider options.Provider) []string {
return validateProviders(&options.Options{Providers: options.Providers{provider}})
}
It("does not require a client secret", func() {
Expect(validate(assertionProvider(options.OIDCProvider, assertionFile))).To(BeEmpty())
})
It("is accepted by the keycloak-oidc provider", func() {
Expect(validate(assertionProvider(options.KeycloakOIDCProvider, assertionFile))).To(BeEmpty())
})
It("rejects a client secret set alongside it", func() {
provider := assertionProvider(options.OIDCProvider, assertionFile)
provider.ClientSecret = "ClientSecret"
Expect(validate(provider)).To(ConsistOf(
"provider ProviderIDAssertion: client-assertion-file is mutually exclusive with client-secret and client-secret-file",
))
})
It("rejects a client secret file set alongside it", func() {
provider := assertionProvider(options.OIDCProvider, assertionFile)
provider.ClientSecretFile = filepath.Join(GinkgoT().TempDir(), "client-secret")
Expect(os.WriteFile(provider.ClientSecretFile, []byte("secret"), 0600)).To(Succeed())
Expect(validate(provider)).To(ConsistOf(
"provider ProviderIDAssertion: client-assertion-file is mutually exclusive with client-secret and client-secret-file",
))
})
It("rejects unsupported providers", func() {
for _, providerType := range []options.ProviderType{
options.MicrosoftEntraIDProvider,
options.GoogleProvider,
options.GitLabProvider,
options.ADFSProvider,
options.CidaasProvider,
} {
Expect(validate(assertionProvider(providerType, assertionFile))).To(ConsistOf(
fmt.Sprintf("provider ProviderIDAssertion: client-assertion-file is not supported by the %q provider", providerType),
), string(providerType))
}
})
It("rejects an unreadable file", func() {
Expect(validate(assertionProvider(options.OIDCProvider, "/does/not/exist"))).To(ConsistOf(
"could not read client assertion file: /does/not/exist",
))
})
})
})