Skip to content

Commit

Permalink
Add validation rule for Upstream auth
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffy-mathew committed Oct 29, 2024
1 parent dafbab6 commit fbab56a
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 8 deletions.
3 changes: 3 additions & 0 deletions apidef/api_definitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ const (
OAuthType = "oauth"
ExternalOAuthType = "externalOAuth"
OIDCType = "oidc"

OAuthAuthorizationTypeClientCredentials = "clientCredentials"
OAuthAuthorizationTypePassword = "password"
)

var (
Expand Down
45 changes: 45 additions & 0 deletions apidef/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ var DefaultValidationRuleSet = ValidationRuleSet{
&RuleAtLeastEnableOneAuthSource{},
&RuleValidateIPList{},
&RuleValidateEnforceTimeout{},
&RuleUpstreamAuth{},
}

func Validate(definition *APIDefinition, ruleSet ValidationRuleSet) ValidationResult {
Expand Down Expand Up @@ -199,3 +200,47 @@ func (r *RuleValidateEnforceTimeout) Validate(apiDef *APIDefinition, validationR
}
}
}

var (
ErrMultipleUpstreamAuthEnabled = errors.New("multiple upstream authentication modes not allowed")
ErrMultipleUpstreamOAuthAuthorizationType = errors.New("multiple upstream OAuth authorization modes not allowed")
ErrUpstreamOAuthAuthorizationTypeRequired = errors.New("upstream OAuth authorization type is required")
ErrInvalidUpstreamOAuthAuthorizationType = errors.New("invalid OAuth authorization type")
)

type RuleUpstreamAuth struct{}

func (r *RuleUpstreamAuth) Validate(apiDef *APIDefinition, validationResult *ValidationResult) {
upstreamAuth := apiDef.UpstreamAuth

if !upstreamAuth.IsEnabled() {
return
}

if upstreamAuth.BasicAuth.Enabled && upstreamAuth.OAuth.Enabled {
validationResult.IsValid = false
validationResult.AppendError(ErrMultipleUpstreamAuthEnabled)
}

upstreamOAuth := upstreamAuth.OAuth
// only OAuth checks moving forward
if !upstreamOAuth.IsEnabled() {
return
}

if len(upstreamOAuth.AllowedAuthorizeTypes) == 0 {
validationResult.IsValid = false
validationResult.AppendError(ErrUpstreamOAuthAuthorizationTypeRequired)
return
}

if len(upstreamAuth.OAuth.AllowedAuthorizeTypes) > 1 {
validationResult.IsValid = false
validationResult.AppendError(ErrMultipleUpstreamOAuthAuthorizationType)
}

if authType := upstreamAuth.OAuth.AllowedAuthorizeTypes[0]; authType != OAuthAuthorizationTypeClientCredentials && authType != OAuthAuthorizationTypePassword {
validationResult.IsValid = false
validationResult.AppendError(ErrInvalidUpstreamOAuthAuthorizationType)
}
}
92 changes: 92 additions & 0 deletions apidef/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,3 +416,95 @@ func TestRuleValidateEnforceTimeout_Validate(t *testing.T) {
t.Run(tc.name, runValidationTest(tc.apiDef, ruleSet, tc.result))
}
}

func TestRuleUpstreamAuth_Validate(t *testing.T) {
ruleSet := ValidationRuleSet{
&RuleUpstreamAuth{},
}

testCases := []struct {
name string
upstreamAuth UpstreamAuth
result ValidationResult
}{
{
name: "not enabled",
upstreamAuth: UpstreamAuth{
Enabled: false,
},
result: ValidationResult{
IsValid: true,
Errors: nil,
},
},
{
name: "basic auth and OAuth enabled",
upstreamAuth: UpstreamAuth{
Enabled: true,
BasicAuth: UpstreamBasicAuth{
Enabled: true,
},
OAuth: UpstreamOAuth{
Enabled: true,
AllowedAuthorizeTypes: []string{OAuthAuthorizationTypeClientCredentials},
},
},
result: ValidationResult{
IsValid: false,
Errors: []error{
ErrMultipleUpstreamAuthEnabled,
},
},
},
{
name: "no upstream OAuth authorization type specified",
upstreamAuth: UpstreamAuth{
Enabled: true,
OAuth: UpstreamOAuth{
Enabled: true,
AllowedAuthorizeTypes: []string{},
},
},
result: ValidationResult{
IsValid: false,
Errors: []error{ErrUpstreamOAuthAuthorizationTypeRequired},
},
},
{
name: "multiple upstream OAuth authorization type specified",
upstreamAuth: UpstreamAuth{
Enabled: true,
OAuth: UpstreamOAuth{
Enabled: true,
AllowedAuthorizeTypes: []string{OAuthAuthorizationTypeClientCredentials, OAuthAuthorizationTypePassword},
},
},
result: ValidationResult{
IsValid: false,
Errors: []error{ErrMultipleUpstreamOAuthAuthorizationType},
},
},
{
name: "invalid upstream OAuth authorization type specified",
upstreamAuth: UpstreamAuth{
Enabled: true,
OAuth: UpstreamOAuth{
Enabled: true,
AllowedAuthorizeTypes: []string{"auth-type1"},
},
},
result: ValidationResult{
IsValid: false,
Errors: []error{ErrInvalidUpstreamOAuthAuthorizationType},
},
},
}

for _, tc := range testCases {
apiDef := &APIDefinition{
UpstreamAuth: tc.upstreamAuth,
}

t.Run(tc.name, runValidationTest(apiDef, ruleSet, tc.result))
}
}
10 changes: 4 additions & 6 deletions gateway/mw_oauth2_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@ import (
)

const (
UpstreamOAuthErrorEventName = "UpstreamOAuthError"
UpstreamOAuthMiddlewareName = "UpstreamOAuth"
ClientCredentialsAuthorizeType = "clientCredentials"
PasswordAuthorizeType = "password"
UpstreamOAuthErrorEventName = "UpstreamOAuthError"
UpstreamOAuthMiddlewareName = "UpstreamOAuth"
)

type OAuthHeaderProvider interface {
Expand Down Expand Up @@ -172,9 +170,9 @@ func getOAuthHeaderProvider(oauthConfig apidef.UpstreamOAuth) (OAuthHeaderProvid
return nil, fmt.Errorf("no OAuth configuration selected")
case len(oauthConfig.AllowedAuthorizeTypes) > 1:
return nil, fmt.Errorf("both client credentials and password authentication are provided")
case oauthConfig.AllowedAuthorizeTypes[0] == ClientCredentialsAuthorizeType:
case oauthConfig.AllowedAuthorizeTypes[0] == apidef.OAuthAuthorizationTypeClientCredentials:
return &ClientCredentialsOAuthProvider{}, nil
case oauthConfig.AllowedAuthorizeTypes[0] == PasswordAuthorizeType:
case oauthConfig.AllowedAuthorizeTypes[0] == apidef.OAuthAuthorizationTypePassword:
return &PasswordOAuthProvider{}, nil
default:
return nil, fmt.Errorf("no valid OAuth configuration provided")
Expand Down
4 changes: 2 additions & 2 deletions gateway/mw_oauth2_auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func TestUpstreamOauth2(t *testing.T) {
OAuth: apidef.UpstreamOAuth{
Enabled: true,
ClientCredentials: cfg,
AllowedAuthorizeTypes: []string{ClientCredentialsAuthorizeType},
AllowedAuthorizeTypes: []string{apidef.OAuthAuthorizationTypeClientCredentials},
},
}
spec.Proxy.StripListenPath = true
Expand Down Expand Up @@ -150,7 +150,7 @@ func TestPasswordCredentialsTokenRequest(t *testing.T) {
OAuth: apidef.UpstreamOAuth{
Enabled: true,
PasswordAuthentication: cfg,
AllowedAuthorizeTypes: []string{PasswordAuthorizeType},
AllowedAuthorizeTypes: []string{apidef.OAuthAuthorizationTypePassword},
},
}
spec.Proxy.StripListenPath = true
Expand Down

0 comments on commit fbab56a

Please sign in to comment.