authelia/internal/configuration/validator/identity_providers.go
James Elliott a7e867a699
feat(configuration): replace viper with koanf (#2053)
This commit replaces github.com/spf13/viper with github.com/knadh/koanf. Koanf is very similar library to viper, with less dependencies and several quality of life differences. This also allows most config options to be defined by ENV. Lastly it also enables the use of split configuration files which can be configured by setting the --config flag multiple times.

Co-authored-by: Amir Zarrinkafsh <nightah@me.com>
2021-08-03 19:55:21 +10:00

198 lines
6.8 KiB
Go

package validator
import (
"fmt"
"net/url"
"strings"
"time"
"github.com/authelia/authelia/internal/configuration/schema"
"github.com/authelia/authelia/internal/utils"
)
// ValidateIdentityProviders validates and update IdentityProviders configuration.
func ValidateIdentityProviders(configuration *schema.IdentityProvidersConfiguration, validator *schema.StructValidator) {
validateOIDC(configuration.OIDC, validator)
}
func validateOIDC(configuration *schema.OpenIDConnectConfiguration, validator *schema.StructValidator) {
if configuration != nil {
if configuration.IssuerPrivateKey == "" {
validator.Push(fmt.Errorf(errFmtOIDCNoPrivateKey))
}
if configuration.AccessTokenLifespan == time.Duration(0) {
configuration.AccessTokenLifespan = schema.DefaultOpenIDConnectConfiguration.AccessTokenLifespan
}
if configuration.AuthorizeCodeLifespan == time.Duration(0) {
configuration.AuthorizeCodeLifespan = schema.DefaultOpenIDConnectConfiguration.AuthorizeCodeLifespan
}
if configuration.IDTokenLifespan == time.Duration(0) {
configuration.IDTokenLifespan = schema.DefaultOpenIDConnectConfiguration.IDTokenLifespan
}
if configuration.RefreshTokenLifespan == time.Duration(0) {
configuration.RefreshTokenLifespan = schema.DefaultOpenIDConnectConfiguration.RefreshTokenLifespan
}
if configuration.MinimumParameterEntropy != 0 && configuration.MinimumParameterEntropy < 8 {
validator.PushWarning(fmt.Errorf(errFmtOIDCServerInsecureParameterEntropy, configuration.MinimumParameterEntropy))
}
validateOIDCClients(configuration, validator)
if len(configuration.Clients) == 0 {
validator.Push(fmt.Errorf(errFmtOIDCNoClientsConfigured))
}
}
}
func validateOIDCClients(configuration *schema.OpenIDConnectConfiguration, validator *schema.StructValidator) {
invalidID, duplicateIDs := false, false
var ids []string
for c, client := range configuration.Clients {
if client.ID == "" {
invalidID = true
} else {
if client.Description == "" {
configuration.Clients[c].Description = client.ID
}
if utils.IsStringInSliceFold(client.ID, ids) {
duplicateIDs = true
}
ids = append(ids, client.ID)
}
if client.Public {
if client.Secret != "" {
validator.Push(fmt.Errorf(errFmtOIDCClientPublicInvalidSecret, client.ID))
}
} else {
if client.Secret == "" {
validator.Push(fmt.Errorf(errFmtOIDCClientInvalidSecret, client.ID))
}
}
if client.Policy == "" {
configuration.Clients[c].Policy = schema.DefaultOpenIDConnectClientConfiguration.Policy
} else if client.Policy != policyOneFactor && client.Policy != policyTwoFactor {
validator.Push(fmt.Errorf(errFmtOIDCClientInvalidPolicy, client.ID, client.Policy))
}
validateOIDCClientScopes(c, configuration, validator)
validateOIDCClientGrantTypes(c, configuration, validator)
validateOIDCClientResponseTypes(c, configuration, validator)
validateOIDCClientResponseModes(c, configuration, validator)
validateOIDDClientUserinfoAlgorithm(c, configuration, validator)
validateOIDCClientRedirectURIs(client, validator)
}
if invalidID {
validator.Push(fmt.Errorf(errFmtOIDCClientsWithEmptyID))
}
if duplicateIDs {
validator.Push(fmt.Errorf(errFmtOIDCClientsDuplicateID))
}
}
func validateOIDCClientScopes(c int, configuration *schema.OpenIDConnectConfiguration, validator *schema.StructValidator) {
if len(configuration.Clients[c].Scopes) == 0 {
configuration.Clients[c].Scopes = schema.DefaultOpenIDConnectClientConfiguration.Scopes
return
}
if !utils.IsStringInSlice("openid", configuration.Clients[c].Scopes) {
configuration.Clients[c].Scopes = append(configuration.Clients[c].Scopes, "openid")
}
for _, scope := range configuration.Clients[c].Scopes {
if !utils.IsStringInSlice(scope, validOIDCScopes) {
validator.Push(fmt.Errorf(
errFmtOIDCClientInvalidScope,
configuration.Clients[c].ID, scope, strings.Join(validOIDCScopes, "', '")))
}
}
}
func validateOIDCClientGrantTypes(c int, configuration *schema.OpenIDConnectConfiguration, validator *schema.StructValidator) {
if len(configuration.Clients[c].GrantTypes) == 0 {
configuration.Clients[c].GrantTypes = schema.DefaultOpenIDConnectClientConfiguration.GrantTypes
return
}
for _, grantType := range configuration.Clients[c].GrantTypes {
if !utils.IsStringInSlice(grantType, validOIDCGrantTypes) {
validator.Push(fmt.Errorf(
errFmtOIDCClientInvalidGrantType,
configuration.Clients[c].ID, grantType, strings.Join(validOIDCGrantTypes, "', '")))
}
}
}
func validateOIDCClientResponseTypes(c int, configuration *schema.OpenIDConnectConfiguration, _ *schema.StructValidator) {
if len(configuration.Clients[c].ResponseTypes) == 0 {
configuration.Clients[c].ResponseTypes = schema.DefaultOpenIDConnectClientConfiguration.ResponseTypes
return
}
}
func validateOIDCClientResponseModes(c int, configuration *schema.OpenIDConnectConfiguration, validator *schema.StructValidator) {
if len(configuration.Clients[c].ResponseModes) == 0 {
configuration.Clients[c].ResponseModes = schema.DefaultOpenIDConnectClientConfiguration.ResponseModes
return
}
for _, responseMode := range configuration.Clients[c].ResponseModes {
if !utils.IsStringInSlice(responseMode, validOIDCResponseModes) {
validator.Push(fmt.Errorf(
errFmtOIDCClientInvalidResponseMode,
configuration.Clients[c].ID, responseMode, strings.Join(validOIDCResponseModes, "', '")))
}
}
}
func validateOIDDClientUserinfoAlgorithm(c int, configuration *schema.OpenIDConnectConfiguration, validator *schema.StructValidator) {
if configuration.Clients[c].UserinfoSigningAlgorithm == "" {
configuration.Clients[c].UserinfoSigningAlgorithm = schema.DefaultOpenIDConnectClientConfiguration.UserinfoSigningAlgorithm
} else if !utils.IsStringInSlice(configuration.Clients[c].UserinfoSigningAlgorithm, validOIDCUserinfoAlgorithms) {
validator.Push(fmt.Errorf(errFmtOIDCClientInvalidUserinfoAlgorithm,
configuration.Clients[c].ID, configuration.Clients[c].UserinfoSigningAlgorithm, strings.Join(validOIDCUserinfoAlgorithms, ", ")))
}
}
func validateOIDCClientRedirectURIs(client schema.OpenIDConnectClientConfiguration, validator *schema.StructValidator) {
for _, redirectURI := range client.RedirectURIs {
if redirectURI == oauth2InstalledApp {
if client.Public {
continue
}
validator.Push(fmt.Errorf(errFmtOIDCClientRedirectURIPublic, client.ID, redirectURI))
continue
}
parsedURL, err := url.Parse(redirectURI)
if err != nil {
validator.Push(fmt.Errorf(errFmtOIDCClientRedirectURICantBeParsed, client.ID, redirectURI, err))
continue
}
if !parsedURL.IsAbs() {
validator.Push(fmt.Errorf(errFmtOIDCClientRedirectURIAbsolute, client.ID, redirectURI))
return
}
if parsedURL.Scheme != schemeHTTPS && parsedURL.Scheme != schemeHTTP {
validator.Push(fmt.Errorf(errFmtOIDCClientRedirectURI, client.ID, redirectURI, parsedURL.Scheme))
}
}
}