Skip to content

Commit f0f8eaa

Browse files
Add oidc mongodb version specific validation (#160)
# Summary This pull request introduces a new validation function to ensure OIDC provider configurations have unique `IssuerURI` values or unique `IssuerURI` + `Audience` combinations, depending on the MongoDB version. ### New Validation Logic for OIDC Provider Configurations: * Added `oidcProviderConfigUniqueIssuerURIValidation` to enforce unique `IssuerURI` values for MongoDB versions that do not support duplicate issuers, and unique `IssuerURI` + `Audience` combinations for versions that do. This ensures compatibility with MongoDB's documented behavior. ### Unit Tests for Validation: * Added `TestOIDCProviderConfigUniqueIssuerURIValidation` to cover various scenarios, including: - Duplicate `IssuerURI` values for MongoDB 6.0 (error). - Unique and duplicate `IssuerURI` + `Audience` combinations for MongoDB 7.0, 7.3, and 8.0 (success or warning as applicable). - Handling of enterprise versions with the `-ent` suffix. ## Proof of Work Unit tests pass --------- Co-authored-by: Maciej Karaś <[email protected]>
1 parent 3809e9a commit f0f8eaa

File tree

2 files changed

+176
-1
lines changed

2 files changed

+176
-1
lines changed

api/v1/mdb/mongodb_validation.go

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ func oidcAuthValidators(db DbCommonSpec) []func(DbCommonSpec) v1.ValidationResul
113113

114114
authentication := db.Security.Authentication
115115
validators = append(validators, oidcAuthModeValidator(authentication))
116+
validators = append(validators, oidcAuthRequiresEnterprise)
116117

117118
providerConfigs := authentication.OIDCProviderConfigs
118119
if len(providerConfigs) == 0 {
@@ -122,6 +123,7 @@ func oidcAuthValidators(db DbCommonSpec) []func(DbCommonSpec) v1.ValidationResul
122123
validators = append(validators,
123124
oidcProviderConfigsUniqueNameValidation(providerConfigs),
124125
oidcProviderConfigsSingleWorkforceIdentityFederationValidation(providerConfigs),
126+
oidcProviderConfigUniqueIssuerURIValidation(providerConfigs),
125127
)
126128

127129
for _, config := range providerConfigs {
@@ -130,13 +132,58 @@ func oidcAuthValidators(db DbCommonSpec) []func(DbCommonSpec) v1.ValidationResul
130132
oidcProviderConfigClientIdValidator(config),
131133
oidcProviderConfigRequestedScopesValidator(config),
132134
oidcProviderConfigAuthorizationTypeValidator(config),
133-
oidcAuthRequiresEnterprise,
134135
)
135136
}
136137

137138
return validators
138139
}
139140

141+
// oidcProviderConfigUniqueIssuerURIValidation is based on the documentation here:
142+
// https://www.mongodb.com/docs/manual/reference/parameters/#oidcidentityproviders-fields
143+
func oidcProviderConfigUniqueIssuerURIValidation(configs []OIDCProviderConfig) func(DbCommonSpec) v1.ValidationResult {
144+
return func(d DbCommonSpec) v1.ValidationResult {
145+
if len(configs) == 0 {
146+
return v1.ValidationSuccess()
147+
}
148+
149+
// Check if version supports duplicate issuers (7.0, 7.3, or 8.0+)
150+
versionParts := strings.Split(strings.TrimSuffix(d.Version, "-ent"), ".")
151+
supportsMultipleIssuers := false
152+
if len(versionParts) >= 2 {
153+
major := versionParts[0]
154+
minor := versionParts[1]
155+
if major == "8" || (major == "7" && (minor == "0" || minor == "3")) {
156+
supportsMultipleIssuers = true
157+
}
158+
}
159+
160+
if supportsMultipleIssuers {
161+
// Track issuer+audience combinations
162+
issuerAudienceCombos := make(map[string]string)
163+
for _, config := range configs {
164+
comboKey := config.IssuerURI + ":" + config.Audience
165+
if previousConfig, exists := issuerAudienceCombos[comboKey]; exists {
166+
return v1.ValidationWarning("OIDC provider configs %q and %q have duplicate IssuerURI and Audience combination",
167+
previousConfig, config.ConfigurationName)
168+
}
169+
issuerAudienceCombos[comboKey] = config.ConfigurationName
170+
}
171+
} else {
172+
// For older versions, require unique issuers
173+
uris := make(map[string]string)
174+
for _, config := range configs {
175+
if previousConfig, exists := uris[config.IssuerURI]; exists {
176+
return v1.ValidationError("OIDC provider configs %q and %q have duplicate IssuerURI: %s",
177+
previousConfig, config.ConfigurationName, config.IssuerURI)
178+
}
179+
uris[config.IssuerURI] = config.ConfigurationName
180+
}
181+
}
182+
183+
return v1.ValidationSuccess()
184+
}
185+
}
186+
140187
func oidcAuthModeValidator(authentication *Authentication) func(DbCommonSpec) v1.ValidationResult {
141188
return func(spec DbCommonSpec) v1.ValidationResult {
142189
// OIDC cannot be used for agent authentication so other auth mode has to enabled as well

api/v1/mdb/mongodb_validation_test.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,3 +497,131 @@ func TestOIDCAuthValidation(t *testing.T) {
497497
})
498498
}
499499
}
500+
501+
func TestOIDCProviderConfigUniqueIssuerURIValidation(t *testing.T) {
502+
tests := []struct {
503+
name string
504+
mongoVersion string
505+
configs []OIDCProviderConfig
506+
expectedResult v1.ValidationResult
507+
}{
508+
{
509+
name: "MongoDB 6.0 with duplicate issuer URIs - error",
510+
mongoVersion: "6.0.0",
511+
configs: []OIDCProviderConfig{
512+
{
513+
ConfigurationName: "config1",
514+
IssuerURI: "https://provider.com",
515+
Audience: "audience1",
516+
},
517+
{
518+
ConfigurationName: "config2",
519+
IssuerURI: "https://provider.com",
520+
Audience: "audience2",
521+
},
522+
},
523+
expectedResult: v1.ValidationError("OIDC provider configs %q and %q have duplicate IssuerURI: %s",
524+
"config1", "config2", "https://provider.com"),
525+
},
526+
{
527+
name: "MongoDB 7.0 with unique issuer+audience combinations",
528+
mongoVersion: "7.0.0",
529+
configs: []OIDCProviderConfig{
530+
{
531+
ConfigurationName: "config1",
532+
IssuerURI: "https://provider.com",
533+
Audience: "audience1",
534+
},
535+
{
536+
ConfigurationName: "config2",
537+
IssuerURI: "https://provider.com",
538+
Audience: "audience2",
539+
},
540+
},
541+
expectedResult: v1.ValidationSuccess(),
542+
},
543+
{
544+
name: "MongoDB 7.0 with duplicate issuer+audience combinations - warning",
545+
mongoVersion: "7.0.0",
546+
configs: []OIDCProviderConfig{
547+
{
548+
ConfigurationName: "config1",
549+
IssuerURI: "https://provider.com",
550+
Audience: "audience1",
551+
},
552+
{
553+
ConfigurationName: "config2",
554+
IssuerURI: "https://provider.com",
555+
Audience: "audience1",
556+
},
557+
},
558+
expectedResult: v1.ValidationWarning("OIDC provider configs %q and %q have duplicate IssuerURI and Audience combination",
559+
"config1", "config2"),
560+
},
561+
{
562+
name: "MongoDB 7.3 with unique issuer+audience combinations",
563+
mongoVersion: "7.3.0",
564+
configs: []OIDCProviderConfig{
565+
{
566+
ConfigurationName: "config1",
567+
IssuerURI: "https://provider.com",
568+
Audience: "audience1",
569+
},
570+
{
571+
ConfigurationName: "config2",
572+
IssuerURI: "https://provider.com",
573+
Audience: "audience2",
574+
},
575+
},
576+
expectedResult: v1.ValidationSuccess(),
577+
},
578+
{
579+
name: "MongoDB 8.0 with unique issuer+audience combinations",
580+
mongoVersion: "8.0.0",
581+
configs: []OIDCProviderConfig{
582+
{
583+
ConfigurationName: "config1",
584+
IssuerURI: "https://provider.com",
585+
Audience: "audience1",
586+
},
587+
{
588+
ConfigurationName: "config2",
589+
IssuerURI: "https://provider.com",
590+
Audience: "audience2",
591+
},
592+
},
593+
expectedResult: v1.ValidationSuccess(),
594+
},
595+
{
596+
name: "MongoDB enterprise version with -ent suffix",
597+
mongoVersion: "7.0.0-ent",
598+
configs: []OIDCProviderConfig{
599+
{
600+
ConfigurationName: "config1",
601+
IssuerURI: "https://provider.com",
602+
Audience: "audience1",
603+
},
604+
{
605+
ConfigurationName: "config2",
606+
IssuerURI: "https://provider.com",
607+
Audience: "audience2",
608+
},
609+
},
610+
expectedResult: v1.ValidationSuccess(),
611+
},
612+
}
613+
614+
for _, tt := range tests {
615+
t.Run(tt.name, func(t *testing.T) {
616+
validationFunc := oidcProviderConfigUniqueIssuerURIValidation(tt.configs)
617+
618+
dbSpec := DbCommonSpec{
619+
Version: tt.mongoVersion,
620+
}
621+
622+
result := validationFunc(dbSpec)
623+
624+
assert.Equal(t, tt.expectedResult, result)
625+
})
626+
}
627+
}

0 commit comments

Comments
 (0)