Skip to content

Commit aa1ba2e

Browse files
MaciejKaraslucian-tosaanandsyncs
authored
CLOUDP-314901 OIDC CRD changes + validation (#50)
# Summary Adding new `OIDCProviderConfig` struct to `Authentication` struct and new AuthMode `OIDC`. ```go type OIDCProviderConfig struct { // Unique label that identifies this configuration. This label is visible to your Ops Manager users and is used when // creating users and roles for authorization. It is case-sensitive and can only contain the following characters: // - alphanumeric characters (combination of a to z and 0 to 9) // - hyphens (-) // - underscores (_) // +kubebuilder:validation:Pattern="^[a-zA-Z0-9-_]+$" // +kubebuilder:validation:Required ConfigurationName string `json:"configurationName"` // Issuer value provided by your registered IdP application. Using this URI, MongoDB finds an OpenID Provider // Configuration Document, which should be available in the /.wellknown/open-id-configuration endpoint. // +kubebuilder:validation:Required IssuerURI string `json:"issuerURI"` // Entity that your external identity provider intends the token for. // Enter the audience value from the app you registered with external Identity Provider. // +kubebuilder:validation:Required Audience string `json:"audience"` // Select GroupMembership to grant authorization based on IdP user group membership, or select UserID to grant // an individual user authorization. // +kubebuilder:validation:Required AuthorizationType OIDCAuthorizationType `json:"authorizationType"` // The identifier of the claim that includes the user principal identity. // Accept the default value unless your IdP uses a different claim. // +kubebuilder:default=sub // +kubebuilder:validation:Required UserClaim string `json:"userClaim"` // The identifier of the claim that includes the principal's IdP user group membership information. // Accept the default value unless your IdP uses a different claim, or you need a custom claim. // Required when selected GroupMembership as the authorization type, ignored otherwise // +kubebuilder:default=groups // +kubebuilder:validation:Optional GroupsClaim string `json:"groupsClaim,omitempty"` // Configure single-sign-on for human user access to Ops Manager deployments with Workforce Identity Federation. // For programmatic, application access to Ops Manager deployments use Workload Identity Federation. // Only one Workforce Identity Federation IdP can be configured per MongoDB resource // +kubebuilder:validation:Required AuthorizationMethod OIDCAuthorizationMethod `json:"authorizationMethod"` // Unique identifier for your registered application. Enter the clientId value from the app you // registered with an external Identity Provider. // Required when selected Workforce Identity Federation authorization method // +kubebuilder:validation:Optional ClientId string `json:"clientId,omitempty"` // Tokens that give users permission to request data from the authorization endpoint. // Only used for Workforce Identity Federation authorization method // +kubebuilder:validation:Optional RequestedScopes []string `json:"requestedScopes,omitempty"` } // +kubebuilder:validation:Enum=GroupMembership;UserID type OIDCAuthorizationType string // +kubebuilder:validation:Enum=WorkforceIdentityFederation;WorkloadIdentityFederation type OIDCAuthorizationMethod string ``` ⚠️ Because `Security.Authentication` struct is reused also in AppDBSpec it will be available there as well. It will be as usual overridden in https://github.com/mongodb/mongodb-kubernetes/blob/f0050b8942545701e8cb9e42d54d14f0cb58ee6a/api/v1/om/opsmanager_types.go#L622 I believe it is worth noting this behaviour, but not change it as part of this project. ## Proof of Work New Unit test that verify validation and another set of webhook tests are under way. ## Next steps - [ ] Add release notes - [ ] Add validation tests based on webhooks - [ ] Start discussion with docs team ## Checklist - [x] Have you linked a jira ticket and/or is the ticket in the title? - [ ] Have you checked whether your jira ticket required DOCSP changes? - [ ] Have you checked for release_note changes? --------- Co-authored-by: Lucian Tosa <[email protected]> Co-authored-by: Anand <[email protected]> Co-authored-by: Lucian Tosa <[email protected]>
1 parent 3b5eec1 commit aa1ba2e

16 files changed

+1407
-11
lines changed

api/v1/mdb/mongodb_types.go

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ const (
5757
ClusterTopologySingleCluster = "SingleCluster"
5858
ClusterTopologyMultiCluster = "MultiCluster"
5959

60+
OIDCAuthorizationTypeGroupMembership = "GroupMembership"
61+
OIDCAuthorizationTypeUserID = "UserID"
62+
63+
OIDCAuthorizationMethodWorkforceIdentityFederation = "WorkforceIdentityFederation"
64+
OIDCAuthorizationMethodWorkloadIdentityFederation = "WorkloadIdentityFederation"
65+
6066
LabelResourceOwner = "mongodb.com/v1.mongodbResourceOwner"
6167
)
6268

@@ -801,6 +807,13 @@ func (s *Security) IsTLSEnabled() bool {
801807
return s.CertificatesSecretsPrefix != ""
802808
}
803809

810+
func (s *Security) IsOIDCEnabled() bool {
811+
if s == nil || s.Authentication == nil || !s.Authentication.Enabled {
812+
return false
813+
}
814+
return s.Authentication.IsOIDCEnabled()
815+
}
816+
804817
// GetAgentMechanism returns the authentication mechanism that the agents will be using.
805818
// The agents will use X509 if it is the only mechanism specified, otherwise they will use SCRAM if specified
806819
// and no auth if no mechanisms exist.
@@ -878,7 +891,7 @@ func (s Security) RequiresClientTLSAuthentication() bool {
878891
return false
879892
}
880893

881-
if len(s.Authentication.Modes) == 1 && IsAuthPresent(s.Authentication.Modes, util.X509) {
894+
if len(s.Authentication.Modes) == 1 && s.Authentication.IsX509Enabled() {
882895
return true
883896
}
884897

@@ -912,6 +925,10 @@ type Authentication struct {
912925
// +optional
913926
Ldap *Ldap `json:"ldap,omitempty"`
914927

928+
// Configuration for OIDC providers
929+
// +optional
930+
OIDCProviderConfigs []OIDCProviderConfig `json:"oidcProviderConfigs,omitempty"`
931+
915932
// Agents contains authentication configuration properties for the agents
916933
// +optional
917934
Agents AgentAuthentication `json:"agents,omitempty"`
@@ -920,7 +937,7 @@ type Authentication struct {
920937
RequiresClientTLSAuthentication bool `json:"requireClientTLSAuthentication,omitempty"`
921938
}
922939

923-
// +kubebuilder:validation:Enum=X509;SCRAM;SCRAM-SHA-1;MONGODB-CR;SCRAM-SHA-256;LDAP
940+
// +kubebuilder:validation:Enum=X509;SCRAM;SCRAM-SHA-1;MONGODB-CR;SCRAM-SHA-256;LDAP;OIDC
924941
type AuthMode string
925942

926943
func ConvertAuthModesToStrings(authModes []AuthMode) []string {
@@ -993,10 +1010,15 @@ func (a *Authentication) IsX509Enabled() bool {
9931010
}
9941011

9951012
// IsLDAPEnabled determines if LDAP is to be enabled at the project level
996-
func (a *Authentication) isLDAPEnabled() bool {
1013+
func (a *Authentication) IsLDAPEnabled() bool {
9971014
return stringutil.Contains(a.GetModes(), util.LDAP)
9981015
}
9991016

1017+
// IsOIDCEnabled determines if OIDC is to be enabled at the project level
1018+
func (a *Authentication) IsOIDCEnabled() bool {
1019+
return stringutil.Contains(a.GetModes(), util.OIDC)
1020+
}
1021+
10001022
// GetModes returns the modes of the Authentication instance of an empty
10011023
// list if it is nil
10021024
func (a *Authentication) GetModes() []string {
@@ -1033,6 +1055,68 @@ type Ldap struct {
10331055
UserCacheInvalidationInterval int `json:"userCacheInvalidationInterval"`
10341056
}
10351057

1058+
type OIDCProviderConfig struct {
1059+
// Unique label that identifies this configuration. This label is visible to your Ops Manager users and is used when
1060+
// creating users and roles for authorization. It is case-sensitive and can only contain the following characters:
1061+
// - alphanumeric characters (combination of a to z and 0 to 9)
1062+
// - hyphens (-)
1063+
// - underscores (_)
1064+
// +kubebuilder:validation:Pattern="^[a-zA-Z0-9-_]+$"
1065+
// +kubebuilder:validation:Required
1066+
ConfigurationName string `json:"configurationName"`
1067+
1068+
// Issuer value provided by your registered IdP application. Using this URI, MongoDB finds an OpenID Provider
1069+
// Configuration Document, which should be available in the /.wellknown/open-id-configuration endpoint.
1070+
// +kubebuilder:validation:Required
1071+
IssuerURI string `json:"issuerURI"`
1072+
1073+
// Entity that your external identity provider intends the token for.
1074+
// Enter the audience value from the app you registered with external Identity Provider.
1075+
// +kubebuilder:validation:Required
1076+
Audience string `json:"audience"`
1077+
1078+
// Select GroupMembership to grant authorization based on IdP user group membership, or select UserID to grant
1079+
// an individual user authorization.
1080+
// +kubebuilder:validation:Required
1081+
AuthorizationType OIDCAuthorizationType `json:"authorizationType"`
1082+
1083+
// The identifier of the claim that includes the user principal identity.
1084+
// Accept the default value unless your IdP uses a different claim.
1085+
// +kubebuilder:default=sub
1086+
// +kubebuilder:validation:Required
1087+
UserClaim string `json:"userClaim"`
1088+
1089+
// The identifier of the claim that includes the principal's IdP user group membership information.
1090+
// Accept the default value unless your IdP uses a different claim, or you need a custom claim.
1091+
// Required when selected GroupMembership as the authorization type, ignored otherwise
1092+
// +kubebuilder:default=groups
1093+
// +kubebuilder:validation:Optional
1094+
GroupsClaim string `json:"groupsClaim,omitempty"`
1095+
1096+
// Configure single-sign-on for human user access to Ops Manager deployments with Workforce Identity Federation.
1097+
// For programmatic, application access to Ops Manager deployments use Workload Identity Federation.
1098+
// Only one Workforce Identity Federation IdP can be configured per MongoDB resource
1099+
// +kubebuilder:validation:Required
1100+
AuthorizationMethod OIDCAuthorizationMethod `json:"authorizationMethod"`
1101+
1102+
// Unique identifier for your registered application. Enter the clientId value from the app you
1103+
// registered with an external Identity Provider.
1104+
// Required when selected Workforce Identity Federation authorization method
1105+
// +kubebuilder:validation:Optional
1106+
ClientId string `json:"clientId,omitempty"`
1107+
1108+
// Tokens that give users permission to request data from the authorization endpoint.
1109+
// Only used for Workforce Identity Federation authorization method
1110+
// +kubebuilder:validation:Optional
1111+
RequestedScopes []string `json:"requestedScopes,omitempty"`
1112+
}
1113+
1114+
// +kubebuilder:validation:Enum=GroupMembership;UserID
1115+
type OIDCAuthorizationType string
1116+
1117+
// +kubebuilder:validation:Enum=WorkforceIdentityFederation;WorkloadIdentityFederation
1118+
type OIDCAuthorizationMethod string
1119+
10361120
type SecretRef struct {
10371121
// +kubebuilder:validation:Required
10381122
Name string `json:"name"`
@@ -1142,7 +1226,14 @@ func (m *MongoDB) IsLDAPEnabled() bool {
11421226
if m.Spec.Security == nil || m.Spec.Security.Authentication == nil {
11431227
return false
11441228
}
1145-
return IsAuthPresent(m.Spec.Security.Authentication.Modes, util.LDAP)
1229+
return m.Spec.Security.Authentication.IsLDAPEnabled()
1230+
}
1231+
1232+
func (m *MongoDB) IsOIDCEnabled() bool {
1233+
if m.Spec.Security == nil || m.Spec.Security.Authentication == nil {
1234+
return false
1235+
}
1236+
return m.Spec.Security.Authentication.IsOIDCEnabled()
11461237
}
11471238

11481239
func (m *MongoDB) UpdateStatus(phase status.Phase, statusOptions ...status.Option) {
@@ -1203,6 +1294,10 @@ func (m *MongoDB) GetStatus(...status.Option) interface{} {
12031294
return m.Status
12041295
}
12051296

1297+
func (m *MongoDB) GetStatusWarnings() []status.Warning {
1298+
return m.Status.Warnings
1299+
}
1300+
12061301
func (m *MongoDB) GetCommonStatus(...status.Option) *status.Common {
12071302
return &m.Status.Common
12081303
}

api/v1/mdb/mongodb_types_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,75 @@ func TestGetAgentAuthentication(t *testing.T) {
6161
assert.Equal(t, util.X509, sec.GetAgentMechanism("SCRAM-SHA-256"), "transitioning from SCRAM -> X509 is allowed")
6262
}
6363

64+
func TestGetAuthenticationIsEnabledMethods(t *testing.T) {
65+
tests := []struct {
66+
name string
67+
authentication *Authentication
68+
expectedX509 bool
69+
expectedLDAP bool
70+
expectedOIDC bool
71+
}{
72+
{
73+
name: "Nil authentication",
74+
authentication: nil,
75+
expectedX509: false,
76+
expectedLDAP: false,
77+
expectedOIDC: false,
78+
},
79+
{
80+
name: "Empty authentication",
81+
authentication: newAuthentication(),
82+
expectedX509: false,
83+
expectedLDAP: false,
84+
expectedOIDC: false,
85+
},
86+
{
87+
name: "Authentication with x509 only",
88+
authentication: &Authentication{
89+
Modes: []AuthMode{util.X509},
90+
},
91+
expectedX509: true,
92+
expectedLDAP: false,
93+
expectedOIDC: false,
94+
},
95+
{
96+
name: "Authentication with LDAP only",
97+
authentication: &Authentication{
98+
Modes: []AuthMode{util.LDAP},
99+
},
100+
expectedX509: false,
101+
expectedLDAP: true,
102+
expectedOIDC: false,
103+
},
104+
{
105+
name: "Authentication with OIDC only",
106+
authentication: &Authentication{
107+
Modes: []AuthMode{util.OIDC},
108+
},
109+
expectedX509: false,
110+
expectedLDAP: false,
111+
expectedOIDC: true,
112+
},
113+
{
114+
name: "Authentication with multiple modes",
115+
authentication: &Authentication{
116+
Modes: []AuthMode{util.X509, util.LDAP, util.OIDC, util.SCRAM},
117+
},
118+
expectedX509: true,
119+
expectedLDAP: true,
120+
expectedOIDC: true,
121+
},
122+
}
123+
for _, test := range tests {
124+
t.Run(test.name, func(t *testing.T) {
125+
auth := test.authentication
126+
assert.Equal(t, test.expectedX509, auth.IsX509Enabled())
127+
assert.Equal(t, test.expectedLDAP, auth.IsLDAPEnabled())
128+
assert.Equal(t, test.expectedOIDC, auth.IsOIDCEnabled())
129+
})
130+
}
131+
}
132+
64133
func TestMinimumMajorVersion(t *testing.T) {
65134
mdbSpec := MongoDbSpec{
66135
DbCommonSpec: DbCommonSpec{

0 commit comments

Comments
 (0)