diff --git a/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go.tmpl b/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go.tmpl index 4be520144..5e602f31d 100644 --- a/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go.tmpl +++ b/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go.tmpl @@ -28,11 +28,69 @@ type LDAPIdentityProviderStatus struct { Phase LDAPIdentityProviderPhase `json:"phase,omitempty"` } +type LDAPIdentityProviderTLSSpec struct { + // X.509 Certificate Authority (base64-encoded PEM bundle) to trust when connecting to the LDAP provider. + // If omitted, a default set of system roots will be trusted. + // +optional + CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` +} + +type LDAPIdentityProviderBindSpec struct { + // SecretName contains the name of a namespace-local Secret object that provides the username and + // password for an LDAP bind user. This account will be used to perform LDAP searches. The Secret should be + // of type "kubernetes.io/basic-auth" which includes "username" and "password" keys. The username value + // should be the full DN of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". + // +kubebuilder:validation:MinLength=1 + SecretName string `json:"secretName"` +} + +type LDAPIdentityProviderUserSearchAttributesSpec struct { + // Username specifies the name of attribute in the LDAP entry which whose value shall become the username + // of the user after a successful authentication. This would typically be the same attribute name used in + // the user search filter. E.g. "mail" or "uid" or "userPrincipalName". + // +kubebuilder:validation:MinLength=1 + Username string `json:"username,omitempty"` + + // UniqueID specifies the name of the attribute in the LDAP entry which whose value shall be used to uniquely + // identify the user within this LDAP provider after a successful authentication. E.g. "uidNumber" or "objectGUID". + // +kubebuilder:validation:MinLength=1 + UniqueID string `json:"uniqueID,omitempty"` +} + +type LDAPIdentityProviderUserSearchSpec struct { + // Base is the DN that should be used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com". + // +kubebuilder:validation:MinLength=1 + Base string `json:"base,omitempty"` + + // Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur + // in the filter and will be dynamically replaced by the username for which the search is being run. E.g. "mail={}" + // or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. + // Optional. When not specified, the default will act as if the Filter were specified as the value from + // Attributes.Username appended by "={}". + // +optional + Filter string `json:"filter,omitempty"` + + // Attributes specifies how the user's information should be read from the LDAP entry which was found as + // the result of the user search. + // +optional + Attributes LDAPIdentityProviderUserSearchAttributesSpec `json:"attributes,omitempty"` +} + // Spec for configuring an LDAP identity provider. type LDAPIdentityProviderSpec struct { // Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636. // +kubebuilder:validation:MinLength=1 Host string `json:"host"` + + // TLS contains the connection settings for how to establish the connection to the Host. + TLS *LDAPIdentityProviderTLSSpec `json:"tls,omitempty"` + + // Bind contains the configuration for how to provide access credentials during an initial bind to the LDAP server + // to be allowed to perform searches and binds to validate a user's credentials during a user's authentication attempt. + Bind LDAPIdentityProviderBindSpec `json:"bind,omitempty"` + + // UserSearch contains the configuration for searching for a user by name in the LDAP provider. + UserSearch LDAPIdentityProviderUserSearchSpec `json:"userSearch,omitempty"` } // LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access diff --git a/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e70fb51dc..1e54d0433 100644 --- a/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/deploy/supervisor/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -51,11 +51,80 @@ spec: spec: description: Spec for configuring the identity provider. properties: + bind: + description: Bind contains the configuration for how to provide access + credentials during an initial bind to the LDAP server to be allowed + to perform searches and binds to validate a user's credentials during + a user's authentication attempt. + properties: + secretName: + description: SecretName contains the name of a namespace-local + Secret object that provides the username and password for an + LDAP bind user. This account will be used to perform LDAP searches. + The Secret should be of type "kubernetes.io/basic-auth" which + includes "username" and "password" keys. The username value + should be the full DN of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". + minLength: 1 + type: string + required: + - secretName + type: object host: description: 'Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636.' minLength: 1 type: string + tls: + description: TLS contains the connection settings for how to establish + the connection to the Host. + properties: + certificateAuthorityData: + description: X.509 Certificate Authority (base64-encoded PEM bundle) + to trust when connecting to the LDAP provider. If omitted, a + default set of system roots will be trusted. + type: string + type: object + userSearch: + description: UserSearch contains the configuration for searching for + a user by name in the LDAP provider. + properties: + attributes: + description: Attributes specifies how the user's information should + be read from the LDAP entry which was found as the result of + the user search. + properties: + uniqueID: + description: UniqueID specifies the name of the attribute + in the LDAP entry which whose value shall be used to uniquely + identify the user within this LDAP provider after a successful + authentication. E.g. "uidNumber" or "objectGUID". + minLength: 1 + type: string + username: + description: Username specifies the name of attribute in the + LDAP entry which whose value shall become the username of + the user after a successful authentication. This would typically + be the same attribute name used in the user search filter. + E.g. "mail" or "uid" or "userPrincipalName". + minLength: 1 + type: string + type: object + base: + description: Base is the DN that should be used as the search + base when searching for users. E.g. "ou=users,dc=example,dc=com". + minLength: 1 + type: string + filter: + description: Filter is the LDAP search filter which should be + applied when searching for users. The pattern "{}" must occur + in the filter and will be dynamically replaced by the username + for which the search is being run. E.g. "mail={}" or "&(objectClass=person)(uid={})". + For more information about LDAP filters, see https://ldap.com/ldap-filters. + Optional. When not specified, the default will act as if the + Filter were specified as the value from Attributes.Username + appended by "={}". + type: string + type: object required: - host type: object diff --git a/generated/1.17/README.adoc b/generated/1.17/README.adoc index 8e0e6fb47..dec89a341 100644 --- a/generated/1.17/README.adoc +++ b/generated/1.17/README.adoc @@ -708,6 +708,23 @@ LDAPIdentityProvider describes the configuration of an upstream Lightweight Dire |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityproviderbindspec"] +==== LDAPIdentityProviderBindSpec + + + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec[$$LDAPIdentityProviderSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`secretName`* __string__ | SecretName contains the name of a namespace-local Secret object that provides the username and password for an LDAP bind user. This account will be used to perform LDAP searches. The Secret should be of type "kubernetes.io/basic-auth" which includes "username" and "password" keys. The username value should be the full DN of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec"] @@ -724,6 +741,9 @@ Spec for configuring an LDAP identity provider. |=== | Field | Description | *`host`* __string__ | Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636. +| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityprovidertlsspec[$$LDAPIdentityProviderTLSSpec$$]__ | TLS contains the connection settings for how to establish the connection to the Host. +| *`bind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityproviderbindspec[$$LDAPIdentityProviderBindSpec$$]__ | Bind contains the configuration for how to provide access credentials during an initial bind to the LDAP server to be allowed to perform searches and binds to validate a user's credentials during a user's authentication attempt. +| *`userSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchspec[$$LDAPIdentityProviderUserSearchSpec$$]__ | UserSearch contains the configuration for searching for a user by name in the LDAP provider. |=== @@ -744,6 +764,60 @@ Status of an LDAP identity provider. |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityprovidertlsspec"] +==== LDAPIdentityProviderTLSSpec + + + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec[$$LDAPIdentityProviderSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle) to trust when connecting to the LDAP provider. If omitted, a default set of system roots will be trusted. +|=== + + +[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchattributesspec"] +==== LDAPIdentityProviderUserSearchAttributesSpec + + + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchspec[$$LDAPIdentityProviderUserSearchSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`username`* __string__ | Username specifies the name of attribute in the LDAP entry which whose value shall become the username of the user after a successful authentication. This would typically be the same attribute name used in the user search filter. E.g. "mail" or "uid" or "userPrincipalName". +| *`uniqueID`* __string__ | UniqueID specifies the name of the attribute in the LDAP entry which whose value shall be used to uniquely identify the user within this LDAP provider after a successful authentication. E.g. "uidNumber" or "objectGUID". +|=== + + +[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchspec"] +==== LDAPIdentityProviderUserSearchSpec + + + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec[$$LDAPIdentityProviderSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`base`* __string__ | Base is the DN that should be used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com". +| *`filter`* __string__ | Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur in the filter and will be dynamically replaced by the username for which the search is being run. E.g. "mail={}" or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. Optional. When not specified, the default will act as if the Filter were specified as the value from Attributes.Username appended by "={}". +| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchattributesspec[$$LDAPIdentityProviderUserSearchAttributesSpec$$]__ | Attributes specifies how the user's information should be read from the LDAP entry which was found as the result of the user search. +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-oidcauthorizationconfig"] ==== OIDCAuthorizationConfig diff --git a/generated/1.17/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go b/generated/1.17/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go index 4be520144..5e602f31d 100644 --- a/generated/1.17/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go +++ b/generated/1.17/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go @@ -28,11 +28,69 @@ type LDAPIdentityProviderStatus struct { Phase LDAPIdentityProviderPhase `json:"phase,omitempty"` } +type LDAPIdentityProviderTLSSpec struct { + // X.509 Certificate Authority (base64-encoded PEM bundle) to trust when connecting to the LDAP provider. + // If omitted, a default set of system roots will be trusted. + // +optional + CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` +} + +type LDAPIdentityProviderBindSpec struct { + // SecretName contains the name of a namespace-local Secret object that provides the username and + // password for an LDAP bind user. This account will be used to perform LDAP searches. The Secret should be + // of type "kubernetes.io/basic-auth" which includes "username" and "password" keys. The username value + // should be the full DN of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". + // +kubebuilder:validation:MinLength=1 + SecretName string `json:"secretName"` +} + +type LDAPIdentityProviderUserSearchAttributesSpec struct { + // Username specifies the name of attribute in the LDAP entry which whose value shall become the username + // of the user after a successful authentication. This would typically be the same attribute name used in + // the user search filter. E.g. "mail" or "uid" or "userPrincipalName". + // +kubebuilder:validation:MinLength=1 + Username string `json:"username,omitempty"` + + // UniqueID specifies the name of the attribute in the LDAP entry which whose value shall be used to uniquely + // identify the user within this LDAP provider after a successful authentication. E.g. "uidNumber" or "objectGUID". + // +kubebuilder:validation:MinLength=1 + UniqueID string `json:"uniqueID,omitempty"` +} + +type LDAPIdentityProviderUserSearchSpec struct { + // Base is the DN that should be used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com". + // +kubebuilder:validation:MinLength=1 + Base string `json:"base,omitempty"` + + // Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur + // in the filter and will be dynamically replaced by the username for which the search is being run. E.g. "mail={}" + // or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. + // Optional. When not specified, the default will act as if the Filter were specified as the value from + // Attributes.Username appended by "={}". + // +optional + Filter string `json:"filter,omitempty"` + + // Attributes specifies how the user's information should be read from the LDAP entry which was found as + // the result of the user search. + // +optional + Attributes LDAPIdentityProviderUserSearchAttributesSpec `json:"attributes,omitempty"` +} + // Spec for configuring an LDAP identity provider. type LDAPIdentityProviderSpec struct { // Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636. // +kubebuilder:validation:MinLength=1 Host string `json:"host"` + + // TLS contains the connection settings for how to establish the connection to the Host. + TLS *LDAPIdentityProviderTLSSpec `json:"tls,omitempty"` + + // Bind contains the configuration for how to provide access credentials during an initial bind to the LDAP server + // to be allowed to perform searches and binds to validate a user's credentials during a user's authentication attempt. + Bind LDAPIdentityProviderBindSpec `json:"bind,omitempty"` + + // UserSearch contains the configuration for searching for a user by name in the LDAP provider. + UserSearch LDAPIdentityProviderUserSearchSpec `json:"userSearch,omitempty"` } // LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access diff --git a/generated/1.17/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.17/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index bdd1bc952..6ddcebadd 100644 --- a/generated/1.17/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.17/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -33,7 +33,7 @@ func (in *LDAPIdentityProvider) DeepCopyInto(out *LDAPIdentityProvider) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) out.Status = in.Status return } @@ -56,6 +56,22 @@ func (in *LDAPIdentityProvider) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderBindSpec) DeepCopyInto(out *LDAPIdentityProviderBindSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderBindSpec. +func (in *LDAPIdentityProviderBindSpec) DeepCopy() *LDAPIdentityProviderBindSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderBindSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LDAPIdentityProviderList) DeepCopyInto(out *LDAPIdentityProviderList) { *out = *in @@ -92,6 +108,13 @@ func (in *LDAPIdentityProviderList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec) { *out = *in + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(LDAPIdentityProviderTLSSpec) + **out = **in + } + out.Bind = in.Bind + out.UserSearch = in.UserSearch return } @@ -121,6 +144,55 @@ func (in *LDAPIdentityProviderStatus) DeepCopy() *LDAPIdentityProviderStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderTLSSpec) DeepCopyInto(out *LDAPIdentityProviderTLSSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderTLSSpec. +func (in *LDAPIdentityProviderTLSSpec) DeepCopy() *LDAPIdentityProviderTLSSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderTLSSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderUserSearchAttributesSpec) DeepCopyInto(out *LDAPIdentityProviderUserSearchAttributesSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderUserSearchAttributesSpec. +func (in *LDAPIdentityProviderUserSearchAttributesSpec) DeepCopy() *LDAPIdentityProviderUserSearchAttributesSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderUserSearchAttributesSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderUserSearchSpec) DeepCopyInto(out *LDAPIdentityProviderUserSearchSpec) { + *out = *in + out.Attributes = in.Attributes + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderUserSearchSpec. +func (in *LDAPIdentityProviderUserSearchSpec) DeepCopy() *LDAPIdentityProviderUserSearchSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderUserSearchSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OIDCAuthorizationConfig) DeepCopyInto(out *OIDCAuthorizationConfig) { *out = *in diff --git a/generated/1.17/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.17/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e70fb51dc..1e54d0433 100644 --- a/generated/1.17/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.17/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -51,11 +51,80 @@ spec: spec: description: Spec for configuring the identity provider. properties: + bind: + description: Bind contains the configuration for how to provide access + credentials during an initial bind to the LDAP server to be allowed + to perform searches and binds to validate a user's credentials during + a user's authentication attempt. + properties: + secretName: + description: SecretName contains the name of a namespace-local + Secret object that provides the username and password for an + LDAP bind user. This account will be used to perform LDAP searches. + The Secret should be of type "kubernetes.io/basic-auth" which + includes "username" and "password" keys. The username value + should be the full DN of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". + minLength: 1 + type: string + required: + - secretName + type: object host: description: 'Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636.' minLength: 1 type: string + tls: + description: TLS contains the connection settings for how to establish + the connection to the Host. + properties: + certificateAuthorityData: + description: X.509 Certificate Authority (base64-encoded PEM bundle) + to trust when connecting to the LDAP provider. If omitted, a + default set of system roots will be trusted. + type: string + type: object + userSearch: + description: UserSearch contains the configuration for searching for + a user by name in the LDAP provider. + properties: + attributes: + description: Attributes specifies how the user's information should + be read from the LDAP entry which was found as the result of + the user search. + properties: + uniqueID: + description: UniqueID specifies the name of the attribute + in the LDAP entry which whose value shall be used to uniquely + identify the user within this LDAP provider after a successful + authentication. E.g. "uidNumber" or "objectGUID". + minLength: 1 + type: string + username: + description: Username specifies the name of attribute in the + LDAP entry which whose value shall become the username of + the user after a successful authentication. This would typically + be the same attribute name used in the user search filter. + E.g. "mail" or "uid" or "userPrincipalName". + minLength: 1 + type: string + type: object + base: + description: Base is the DN that should be used as the search + base when searching for users. E.g. "ou=users,dc=example,dc=com". + minLength: 1 + type: string + filter: + description: Filter is the LDAP search filter which should be + applied when searching for users. The pattern "{}" must occur + in the filter and will be dynamically replaced by the username + for which the search is being run. E.g. "mail={}" or "&(objectClass=person)(uid={})". + For more information about LDAP filters, see https://ldap.com/ldap-filters. + Optional. When not specified, the default will act as if the + Filter were specified as the value from Attributes.Username + appended by "={}". + type: string + type: object required: - host type: object diff --git a/generated/1.18/README.adoc b/generated/1.18/README.adoc index cd225272d..6609724eb 100644 --- a/generated/1.18/README.adoc +++ b/generated/1.18/README.adoc @@ -708,6 +708,23 @@ LDAPIdentityProvider describes the configuration of an upstream Lightweight Dire |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityproviderbindspec"] +==== LDAPIdentityProviderBindSpec + + + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec[$$LDAPIdentityProviderSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`secretName`* __string__ | SecretName contains the name of a namespace-local Secret object that provides the username and password for an LDAP bind user. This account will be used to perform LDAP searches. The Secret should be of type "kubernetes.io/basic-auth" which includes "username" and "password" keys. The username value should be the full DN of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec"] @@ -724,6 +741,9 @@ Spec for configuring an LDAP identity provider. |=== | Field | Description | *`host`* __string__ | Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636. +| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityprovidertlsspec[$$LDAPIdentityProviderTLSSpec$$]__ | TLS contains the connection settings for how to establish the connection to the Host. +| *`bind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityproviderbindspec[$$LDAPIdentityProviderBindSpec$$]__ | Bind contains the configuration for how to provide access credentials during an initial bind to the LDAP server to be allowed to perform searches and binds to validate a user's credentials during a user's authentication attempt. +| *`userSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchspec[$$LDAPIdentityProviderUserSearchSpec$$]__ | UserSearch contains the configuration for searching for a user by name in the LDAP provider. |=== @@ -744,6 +764,60 @@ Status of an LDAP identity provider. |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityprovidertlsspec"] +==== LDAPIdentityProviderTLSSpec + + + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec[$$LDAPIdentityProviderSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle) to trust when connecting to the LDAP provider. If omitted, a default set of system roots will be trusted. +|=== + + +[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchattributesspec"] +==== LDAPIdentityProviderUserSearchAttributesSpec + + + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchspec[$$LDAPIdentityProviderUserSearchSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`username`* __string__ | Username specifies the name of attribute in the LDAP entry which whose value shall become the username of the user after a successful authentication. This would typically be the same attribute name used in the user search filter. E.g. "mail" or "uid" or "userPrincipalName". +| *`uniqueID`* __string__ | UniqueID specifies the name of the attribute in the LDAP entry which whose value shall be used to uniquely identify the user within this LDAP provider after a successful authentication. E.g. "uidNumber" or "objectGUID". +|=== + + +[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchspec"] +==== LDAPIdentityProviderUserSearchSpec + + + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec[$$LDAPIdentityProviderSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`base`* __string__ | Base is the DN that should be used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com". +| *`filter`* __string__ | Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur in the filter and will be dynamically replaced by the username for which the search is being run. E.g. "mail={}" or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. Optional. When not specified, the default will act as if the Filter were specified as the value from Attributes.Username appended by "={}". +| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchattributesspec[$$LDAPIdentityProviderUserSearchAttributesSpec$$]__ | Attributes specifies how the user's information should be read from the LDAP entry which was found as the result of the user search. +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-oidcauthorizationconfig"] ==== OIDCAuthorizationConfig diff --git a/generated/1.18/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go b/generated/1.18/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go index 4be520144..5e602f31d 100644 --- a/generated/1.18/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go +++ b/generated/1.18/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go @@ -28,11 +28,69 @@ type LDAPIdentityProviderStatus struct { Phase LDAPIdentityProviderPhase `json:"phase,omitempty"` } +type LDAPIdentityProviderTLSSpec struct { + // X.509 Certificate Authority (base64-encoded PEM bundle) to trust when connecting to the LDAP provider. + // If omitted, a default set of system roots will be trusted. + // +optional + CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` +} + +type LDAPIdentityProviderBindSpec struct { + // SecretName contains the name of a namespace-local Secret object that provides the username and + // password for an LDAP bind user. This account will be used to perform LDAP searches. The Secret should be + // of type "kubernetes.io/basic-auth" which includes "username" and "password" keys. The username value + // should be the full DN of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". + // +kubebuilder:validation:MinLength=1 + SecretName string `json:"secretName"` +} + +type LDAPIdentityProviderUserSearchAttributesSpec struct { + // Username specifies the name of attribute in the LDAP entry which whose value shall become the username + // of the user after a successful authentication. This would typically be the same attribute name used in + // the user search filter. E.g. "mail" or "uid" or "userPrincipalName". + // +kubebuilder:validation:MinLength=1 + Username string `json:"username,omitempty"` + + // UniqueID specifies the name of the attribute in the LDAP entry which whose value shall be used to uniquely + // identify the user within this LDAP provider after a successful authentication. E.g. "uidNumber" or "objectGUID". + // +kubebuilder:validation:MinLength=1 + UniqueID string `json:"uniqueID,omitempty"` +} + +type LDAPIdentityProviderUserSearchSpec struct { + // Base is the DN that should be used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com". + // +kubebuilder:validation:MinLength=1 + Base string `json:"base,omitempty"` + + // Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur + // in the filter and will be dynamically replaced by the username for which the search is being run. E.g. "mail={}" + // or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. + // Optional. When not specified, the default will act as if the Filter were specified as the value from + // Attributes.Username appended by "={}". + // +optional + Filter string `json:"filter,omitempty"` + + // Attributes specifies how the user's information should be read from the LDAP entry which was found as + // the result of the user search. + // +optional + Attributes LDAPIdentityProviderUserSearchAttributesSpec `json:"attributes,omitempty"` +} + // Spec for configuring an LDAP identity provider. type LDAPIdentityProviderSpec struct { // Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636. // +kubebuilder:validation:MinLength=1 Host string `json:"host"` + + // TLS contains the connection settings for how to establish the connection to the Host. + TLS *LDAPIdentityProviderTLSSpec `json:"tls,omitempty"` + + // Bind contains the configuration for how to provide access credentials during an initial bind to the LDAP server + // to be allowed to perform searches and binds to validate a user's credentials during a user's authentication attempt. + Bind LDAPIdentityProviderBindSpec `json:"bind,omitempty"` + + // UserSearch contains the configuration for searching for a user by name in the LDAP provider. + UserSearch LDAPIdentityProviderUserSearchSpec `json:"userSearch,omitempty"` } // LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access diff --git a/generated/1.18/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.18/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index bdd1bc952..6ddcebadd 100644 --- a/generated/1.18/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.18/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -33,7 +33,7 @@ func (in *LDAPIdentityProvider) DeepCopyInto(out *LDAPIdentityProvider) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) out.Status = in.Status return } @@ -56,6 +56,22 @@ func (in *LDAPIdentityProvider) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderBindSpec) DeepCopyInto(out *LDAPIdentityProviderBindSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderBindSpec. +func (in *LDAPIdentityProviderBindSpec) DeepCopy() *LDAPIdentityProviderBindSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderBindSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LDAPIdentityProviderList) DeepCopyInto(out *LDAPIdentityProviderList) { *out = *in @@ -92,6 +108,13 @@ func (in *LDAPIdentityProviderList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec) { *out = *in + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(LDAPIdentityProviderTLSSpec) + **out = **in + } + out.Bind = in.Bind + out.UserSearch = in.UserSearch return } @@ -121,6 +144,55 @@ func (in *LDAPIdentityProviderStatus) DeepCopy() *LDAPIdentityProviderStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderTLSSpec) DeepCopyInto(out *LDAPIdentityProviderTLSSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderTLSSpec. +func (in *LDAPIdentityProviderTLSSpec) DeepCopy() *LDAPIdentityProviderTLSSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderTLSSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderUserSearchAttributesSpec) DeepCopyInto(out *LDAPIdentityProviderUserSearchAttributesSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderUserSearchAttributesSpec. +func (in *LDAPIdentityProviderUserSearchAttributesSpec) DeepCopy() *LDAPIdentityProviderUserSearchAttributesSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderUserSearchAttributesSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderUserSearchSpec) DeepCopyInto(out *LDAPIdentityProviderUserSearchSpec) { + *out = *in + out.Attributes = in.Attributes + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderUserSearchSpec. +func (in *LDAPIdentityProviderUserSearchSpec) DeepCopy() *LDAPIdentityProviderUserSearchSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderUserSearchSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OIDCAuthorizationConfig) DeepCopyInto(out *OIDCAuthorizationConfig) { *out = *in diff --git a/generated/1.18/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.18/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e70fb51dc..1e54d0433 100644 --- a/generated/1.18/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.18/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -51,11 +51,80 @@ spec: spec: description: Spec for configuring the identity provider. properties: + bind: + description: Bind contains the configuration for how to provide access + credentials during an initial bind to the LDAP server to be allowed + to perform searches and binds to validate a user's credentials during + a user's authentication attempt. + properties: + secretName: + description: SecretName contains the name of a namespace-local + Secret object that provides the username and password for an + LDAP bind user. This account will be used to perform LDAP searches. + The Secret should be of type "kubernetes.io/basic-auth" which + includes "username" and "password" keys. The username value + should be the full DN of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". + minLength: 1 + type: string + required: + - secretName + type: object host: description: 'Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636.' minLength: 1 type: string + tls: + description: TLS contains the connection settings for how to establish + the connection to the Host. + properties: + certificateAuthorityData: + description: X.509 Certificate Authority (base64-encoded PEM bundle) + to trust when connecting to the LDAP provider. If omitted, a + default set of system roots will be trusted. + type: string + type: object + userSearch: + description: UserSearch contains the configuration for searching for + a user by name in the LDAP provider. + properties: + attributes: + description: Attributes specifies how the user's information should + be read from the LDAP entry which was found as the result of + the user search. + properties: + uniqueID: + description: UniqueID specifies the name of the attribute + in the LDAP entry which whose value shall be used to uniquely + identify the user within this LDAP provider after a successful + authentication. E.g. "uidNumber" or "objectGUID". + minLength: 1 + type: string + username: + description: Username specifies the name of attribute in the + LDAP entry which whose value shall become the username of + the user after a successful authentication. This would typically + be the same attribute name used in the user search filter. + E.g. "mail" or "uid" or "userPrincipalName". + minLength: 1 + type: string + type: object + base: + description: Base is the DN that should be used as the search + base when searching for users. E.g. "ou=users,dc=example,dc=com". + minLength: 1 + type: string + filter: + description: Filter is the LDAP search filter which should be + applied when searching for users. The pattern "{}" must occur + in the filter and will be dynamically replaced by the username + for which the search is being run. E.g. "mail={}" or "&(objectClass=person)(uid={})". + For more information about LDAP filters, see https://ldap.com/ldap-filters. + Optional. When not specified, the default will act as if the + Filter were specified as the value from Attributes.Username + appended by "={}". + type: string + type: object required: - host type: object diff --git a/generated/1.19/README.adoc b/generated/1.19/README.adoc index e20d50b07..e9c1538ee 100644 --- a/generated/1.19/README.adoc +++ b/generated/1.19/README.adoc @@ -708,6 +708,23 @@ LDAPIdentityProvider describes the configuration of an upstream Lightweight Dire |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityproviderbindspec"] +==== LDAPIdentityProviderBindSpec + + + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec[$$LDAPIdentityProviderSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`secretName`* __string__ | SecretName contains the name of a namespace-local Secret object that provides the username and password for an LDAP bind user. This account will be used to perform LDAP searches. The Secret should be of type "kubernetes.io/basic-auth" which includes "username" and "password" keys. The username value should be the full DN of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec"] @@ -724,6 +741,9 @@ Spec for configuring an LDAP identity provider. |=== | Field | Description | *`host`* __string__ | Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636. +| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityprovidertlsspec[$$LDAPIdentityProviderTLSSpec$$]__ | TLS contains the connection settings for how to establish the connection to the Host. +| *`bind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityproviderbindspec[$$LDAPIdentityProviderBindSpec$$]__ | Bind contains the configuration for how to provide access credentials during an initial bind to the LDAP server to be allowed to perform searches and binds to validate a user's credentials during a user's authentication attempt. +| *`userSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchspec[$$LDAPIdentityProviderUserSearchSpec$$]__ | UserSearch contains the configuration for searching for a user by name in the LDAP provider. |=== @@ -744,6 +764,60 @@ Status of an LDAP identity provider. |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityprovidertlsspec"] +==== LDAPIdentityProviderTLSSpec + + + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec[$$LDAPIdentityProviderSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle) to trust when connecting to the LDAP provider. If omitted, a default set of system roots will be trusted. +|=== + + +[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchattributesspec"] +==== LDAPIdentityProviderUserSearchAttributesSpec + + + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchspec[$$LDAPIdentityProviderUserSearchSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`username`* __string__ | Username specifies the name of attribute in the LDAP entry which whose value shall become the username of the user after a successful authentication. This would typically be the same attribute name used in the user search filter. E.g. "mail" or "uid" or "userPrincipalName". +| *`uniqueID`* __string__ | UniqueID specifies the name of the attribute in the LDAP entry which whose value shall be used to uniquely identify the user within this LDAP provider after a successful authentication. E.g. "uidNumber" or "objectGUID". +|=== + + +[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchspec"] +==== LDAPIdentityProviderUserSearchSpec + + + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec[$$LDAPIdentityProviderSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`base`* __string__ | Base is the DN that should be used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com". +| *`filter`* __string__ | Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur in the filter and will be dynamically replaced by the username for which the search is being run. E.g. "mail={}" or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. Optional. When not specified, the default will act as if the Filter were specified as the value from Attributes.Username appended by "={}". +| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchattributesspec[$$LDAPIdentityProviderUserSearchAttributesSpec$$]__ | Attributes specifies how the user's information should be read from the LDAP entry which was found as the result of the user search. +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-oidcauthorizationconfig"] ==== OIDCAuthorizationConfig diff --git a/generated/1.19/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go b/generated/1.19/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go index 4be520144..5e602f31d 100644 --- a/generated/1.19/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go +++ b/generated/1.19/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go @@ -28,11 +28,69 @@ type LDAPIdentityProviderStatus struct { Phase LDAPIdentityProviderPhase `json:"phase,omitempty"` } +type LDAPIdentityProviderTLSSpec struct { + // X.509 Certificate Authority (base64-encoded PEM bundle) to trust when connecting to the LDAP provider. + // If omitted, a default set of system roots will be trusted. + // +optional + CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` +} + +type LDAPIdentityProviderBindSpec struct { + // SecretName contains the name of a namespace-local Secret object that provides the username and + // password for an LDAP bind user. This account will be used to perform LDAP searches. The Secret should be + // of type "kubernetes.io/basic-auth" which includes "username" and "password" keys. The username value + // should be the full DN of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". + // +kubebuilder:validation:MinLength=1 + SecretName string `json:"secretName"` +} + +type LDAPIdentityProviderUserSearchAttributesSpec struct { + // Username specifies the name of attribute in the LDAP entry which whose value shall become the username + // of the user after a successful authentication. This would typically be the same attribute name used in + // the user search filter. E.g. "mail" or "uid" or "userPrincipalName". + // +kubebuilder:validation:MinLength=1 + Username string `json:"username,omitempty"` + + // UniqueID specifies the name of the attribute in the LDAP entry which whose value shall be used to uniquely + // identify the user within this LDAP provider after a successful authentication. E.g. "uidNumber" or "objectGUID". + // +kubebuilder:validation:MinLength=1 + UniqueID string `json:"uniqueID,omitempty"` +} + +type LDAPIdentityProviderUserSearchSpec struct { + // Base is the DN that should be used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com". + // +kubebuilder:validation:MinLength=1 + Base string `json:"base,omitempty"` + + // Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur + // in the filter and will be dynamically replaced by the username for which the search is being run. E.g. "mail={}" + // or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. + // Optional. When not specified, the default will act as if the Filter were specified as the value from + // Attributes.Username appended by "={}". + // +optional + Filter string `json:"filter,omitempty"` + + // Attributes specifies how the user's information should be read from the LDAP entry which was found as + // the result of the user search. + // +optional + Attributes LDAPIdentityProviderUserSearchAttributesSpec `json:"attributes,omitempty"` +} + // Spec for configuring an LDAP identity provider. type LDAPIdentityProviderSpec struct { // Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636. // +kubebuilder:validation:MinLength=1 Host string `json:"host"` + + // TLS contains the connection settings for how to establish the connection to the Host. + TLS *LDAPIdentityProviderTLSSpec `json:"tls,omitempty"` + + // Bind contains the configuration for how to provide access credentials during an initial bind to the LDAP server + // to be allowed to perform searches and binds to validate a user's credentials during a user's authentication attempt. + Bind LDAPIdentityProviderBindSpec `json:"bind,omitempty"` + + // UserSearch contains the configuration for searching for a user by name in the LDAP provider. + UserSearch LDAPIdentityProviderUserSearchSpec `json:"userSearch,omitempty"` } // LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access diff --git a/generated/1.19/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.19/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index bdd1bc952..6ddcebadd 100644 --- a/generated/1.19/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.19/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -33,7 +33,7 @@ func (in *LDAPIdentityProvider) DeepCopyInto(out *LDAPIdentityProvider) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) out.Status = in.Status return } @@ -56,6 +56,22 @@ func (in *LDAPIdentityProvider) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderBindSpec) DeepCopyInto(out *LDAPIdentityProviderBindSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderBindSpec. +func (in *LDAPIdentityProviderBindSpec) DeepCopy() *LDAPIdentityProviderBindSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderBindSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LDAPIdentityProviderList) DeepCopyInto(out *LDAPIdentityProviderList) { *out = *in @@ -92,6 +108,13 @@ func (in *LDAPIdentityProviderList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec) { *out = *in + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(LDAPIdentityProviderTLSSpec) + **out = **in + } + out.Bind = in.Bind + out.UserSearch = in.UserSearch return } @@ -121,6 +144,55 @@ func (in *LDAPIdentityProviderStatus) DeepCopy() *LDAPIdentityProviderStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderTLSSpec) DeepCopyInto(out *LDAPIdentityProviderTLSSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderTLSSpec. +func (in *LDAPIdentityProviderTLSSpec) DeepCopy() *LDAPIdentityProviderTLSSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderTLSSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderUserSearchAttributesSpec) DeepCopyInto(out *LDAPIdentityProviderUserSearchAttributesSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderUserSearchAttributesSpec. +func (in *LDAPIdentityProviderUserSearchAttributesSpec) DeepCopy() *LDAPIdentityProviderUserSearchAttributesSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderUserSearchAttributesSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderUserSearchSpec) DeepCopyInto(out *LDAPIdentityProviderUserSearchSpec) { + *out = *in + out.Attributes = in.Attributes + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderUserSearchSpec. +func (in *LDAPIdentityProviderUserSearchSpec) DeepCopy() *LDAPIdentityProviderUserSearchSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderUserSearchSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OIDCAuthorizationConfig) DeepCopyInto(out *OIDCAuthorizationConfig) { *out = *in diff --git a/generated/1.19/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.19/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e70fb51dc..1e54d0433 100644 --- a/generated/1.19/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.19/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -51,11 +51,80 @@ spec: spec: description: Spec for configuring the identity provider. properties: + bind: + description: Bind contains the configuration for how to provide access + credentials during an initial bind to the LDAP server to be allowed + to perform searches and binds to validate a user's credentials during + a user's authentication attempt. + properties: + secretName: + description: SecretName contains the name of a namespace-local + Secret object that provides the username and password for an + LDAP bind user. This account will be used to perform LDAP searches. + The Secret should be of type "kubernetes.io/basic-auth" which + includes "username" and "password" keys. The username value + should be the full DN of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". + minLength: 1 + type: string + required: + - secretName + type: object host: description: 'Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636.' minLength: 1 type: string + tls: + description: TLS contains the connection settings for how to establish + the connection to the Host. + properties: + certificateAuthorityData: + description: X.509 Certificate Authority (base64-encoded PEM bundle) + to trust when connecting to the LDAP provider. If omitted, a + default set of system roots will be trusted. + type: string + type: object + userSearch: + description: UserSearch contains the configuration for searching for + a user by name in the LDAP provider. + properties: + attributes: + description: Attributes specifies how the user's information should + be read from the LDAP entry which was found as the result of + the user search. + properties: + uniqueID: + description: UniqueID specifies the name of the attribute + in the LDAP entry which whose value shall be used to uniquely + identify the user within this LDAP provider after a successful + authentication. E.g. "uidNumber" or "objectGUID". + minLength: 1 + type: string + username: + description: Username specifies the name of attribute in the + LDAP entry which whose value shall become the username of + the user after a successful authentication. This would typically + be the same attribute name used in the user search filter. + E.g. "mail" or "uid" or "userPrincipalName". + minLength: 1 + type: string + type: object + base: + description: Base is the DN that should be used as the search + base when searching for users. E.g. "ou=users,dc=example,dc=com". + minLength: 1 + type: string + filter: + description: Filter is the LDAP search filter which should be + applied when searching for users. The pattern "{}" must occur + in the filter and will be dynamically replaced by the username + for which the search is being run. E.g. "mail={}" or "&(objectClass=person)(uid={})". + For more information about LDAP filters, see https://ldap.com/ldap-filters. + Optional. When not specified, the default will act as if the + Filter were specified as the value from Attributes.Username + appended by "={}". + type: string + type: object required: - host type: object diff --git a/generated/1.20/README.adoc b/generated/1.20/README.adoc index 8edfa8db2..f49bf6307 100644 --- a/generated/1.20/README.adoc +++ b/generated/1.20/README.adoc @@ -708,6 +708,23 @@ LDAPIdentityProvider describes the configuration of an upstream Lightweight Dire |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityproviderbindspec"] +==== LDAPIdentityProviderBindSpec + + + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec[$$LDAPIdentityProviderSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`secretName`* __string__ | SecretName contains the name of a namespace-local Secret object that provides the username and password for an LDAP bind user. This account will be used to perform LDAP searches. The Secret should be of type "kubernetes.io/basic-auth" which includes "username" and "password" keys. The username value should be the full DN of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec"] @@ -724,6 +741,9 @@ Spec for configuring an LDAP identity provider. |=== | Field | Description | *`host`* __string__ | Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636. +| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityprovidertlsspec[$$LDAPIdentityProviderTLSSpec$$]__ | TLS contains the connection settings for how to establish the connection to the Host. +| *`bind`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityproviderbindspec[$$LDAPIdentityProviderBindSpec$$]__ | Bind contains the configuration for how to provide access credentials during an initial bind to the LDAP server to be allowed to perform searches and binds to validate a user's credentials during a user's authentication attempt. +| *`userSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchspec[$$LDAPIdentityProviderUserSearchSpec$$]__ | UserSearch contains the configuration for searching for a user by name in the LDAP provider. |=== @@ -744,6 +764,60 @@ Status of an LDAP identity provider. |=== +[id="{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityprovidertlsspec"] +==== LDAPIdentityProviderTLSSpec + + + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec[$$LDAPIdentityProviderSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`certificateAuthorityData`* __string__ | X.509 Certificate Authority (base64-encoded PEM bundle) to trust when connecting to the LDAP provider. If omitted, a default set of system roots will be trusted. +|=== + + +[id="{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchattributesspec"] +==== LDAPIdentityProviderUserSearchAttributesSpec + + + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchspec[$$LDAPIdentityProviderUserSearchSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`username`* __string__ | Username specifies the name of attribute in the LDAP entry which whose value shall become the username of the user after a successful authentication. This would typically be the same attribute name used in the user search filter. E.g. "mail" or "uid" or "userPrincipalName". +| *`uniqueID`* __string__ | UniqueID specifies the name of the attribute in the LDAP entry which whose value shall be used to uniquely identify the user within this LDAP provider after a successful authentication. E.g. "uidNumber" or "objectGUID". +|=== + + +[id="{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchspec"] +==== LDAPIdentityProviderUserSearchSpec + + + +.Appears In: +**** +- xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec[$$LDAPIdentityProviderSpec$$] +**** + +[cols="25a,75a", options="header"] +|=== +| Field | Description +| *`base`* __string__ | Base is the DN that should be used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com". +| *`filter`* __string__ | Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur in the filter and will be dynamically replaced by the username for which the search is being run. E.g. "mail={}" or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. Optional. When not specified, the default will act as if the Filter were specified as the value from Attributes.Username appended by "={}". +| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchattributesspec[$$LDAPIdentityProviderUserSearchAttributesSpec$$]__ | Attributes specifies how the user's information should be read from the LDAP entry which was found as the result of the user search. +|=== + + [id="{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-oidcauthorizationconfig"] ==== OIDCAuthorizationConfig diff --git a/generated/1.20/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go b/generated/1.20/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go index 4be520144..5e602f31d 100644 --- a/generated/1.20/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go +++ b/generated/1.20/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go @@ -28,11 +28,69 @@ type LDAPIdentityProviderStatus struct { Phase LDAPIdentityProviderPhase `json:"phase,omitempty"` } +type LDAPIdentityProviderTLSSpec struct { + // X.509 Certificate Authority (base64-encoded PEM bundle) to trust when connecting to the LDAP provider. + // If omitted, a default set of system roots will be trusted. + // +optional + CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` +} + +type LDAPIdentityProviderBindSpec struct { + // SecretName contains the name of a namespace-local Secret object that provides the username and + // password for an LDAP bind user. This account will be used to perform LDAP searches. The Secret should be + // of type "kubernetes.io/basic-auth" which includes "username" and "password" keys. The username value + // should be the full DN of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". + // +kubebuilder:validation:MinLength=1 + SecretName string `json:"secretName"` +} + +type LDAPIdentityProviderUserSearchAttributesSpec struct { + // Username specifies the name of attribute in the LDAP entry which whose value shall become the username + // of the user after a successful authentication. This would typically be the same attribute name used in + // the user search filter. E.g. "mail" or "uid" or "userPrincipalName". + // +kubebuilder:validation:MinLength=1 + Username string `json:"username,omitempty"` + + // UniqueID specifies the name of the attribute in the LDAP entry which whose value shall be used to uniquely + // identify the user within this LDAP provider after a successful authentication. E.g. "uidNumber" or "objectGUID". + // +kubebuilder:validation:MinLength=1 + UniqueID string `json:"uniqueID,omitempty"` +} + +type LDAPIdentityProviderUserSearchSpec struct { + // Base is the DN that should be used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com". + // +kubebuilder:validation:MinLength=1 + Base string `json:"base,omitempty"` + + // Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur + // in the filter and will be dynamically replaced by the username for which the search is being run. E.g. "mail={}" + // or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. + // Optional. When not specified, the default will act as if the Filter were specified as the value from + // Attributes.Username appended by "={}". + // +optional + Filter string `json:"filter,omitempty"` + + // Attributes specifies how the user's information should be read from the LDAP entry which was found as + // the result of the user search. + // +optional + Attributes LDAPIdentityProviderUserSearchAttributesSpec `json:"attributes,omitempty"` +} + // Spec for configuring an LDAP identity provider. type LDAPIdentityProviderSpec struct { // Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636. // +kubebuilder:validation:MinLength=1 Host string `json:"host"` + + // TLS contains the connection settings for how to establish the connection to the Host. + TLS *LDAPIdentityProviderTLSSpec `json:"tls,omitempty"` + + // Bind contains the configuration for how to provide access credentials during an initial bind to the LDAP server + // to be allowed to perform searches and binds to validate a user's credentials during a user's authentication attempt. + Bind LDAPIdentityProviderBindSpec `json:"bind,omitempty"` + + // UserSearch contains the configuration for searching for a user by name in the LDAP provider. + UserSearch LDAPIdentityProviderUserSearchSpec `json:"userSearch,omitempty"` } // LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access diff --git a/generated/1.20/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/1.20/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index bdd1bc952..6ddcebadd 100644 --- a/generated/1.20/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/1.20/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -33,7 +33,7 @@ func (in *LDAPIdentityProvider) DeepCopyInto(out *LDAPIdentityProvider) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) out.Status = in.Status return } @@ -56,6 +56,22 @@ func (in *LDAPIdentityProvider) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderBindSpec) DeepCopyInto(out *LDAPIdentityProviderBindSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderBindSpec. +func (in *LDAPIdentityProviderBindSpec) DeepCopy() *LDAPIdentityProviderBindSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderBindSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LDAPIdentityProviderList) DeepCopyInto(out *LDAPIdentityProviderList) { *out = *in @@ -92,6 +108,13 @@ func (in *LDAPIdentityProviderList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec) { *out = *in + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(LDAPIdentityProviderTLSSpec) + **out = **in + } + out.Bind = in.Bind + out.UserSearch = in.UserSearch return } @@ -121,6 +144,55 @@ func (in *LDAPIdentityProviderStatus) DeepCopy() *LDAPIdentityProviderStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderTLSSpec) DeepCopyInto(out *LDAPIdentityProviderTLSSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderTLSSpec. +func (in *LDAPIdentityProviderTLSSpec) DeepCopy() *LDAPIdentityProviderTLSSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderTLSSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderUserSearchAttributesSpec) DeepCopyInto(out *LDAPIdentityProviderUserSearchAttributesSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderUserSearchAttributesSpec. +func (in *LDAPIdentityProviderUserSearchAttributesSpec) DeepCopy() *LDAPIdentityProviderUserSearchAttributesSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderUserSearchAttributesSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderUserSearchSpec) DeepCopyInto(out *LDAPIdentityProviderUserSearchSpec) { + *out = *in + out.Attributes = in.Attributes + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderUserSearchSpec. +func (in *LDAPIdentityProviderUserSearchSpec) DeepCopy() *LDAPIdentityProviderUserSearchSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderUserSearchSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OIDCAuthorizationConfig) DeepCopyInto(out *OIDCAuthorizationConfig) { *out = *in diff --git a/generated/1.20/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml b/generated/1.20/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml index e70fb51dc..1e54d0433 100644 --- a/generated/1.20/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml +++ b/generated/1.20/crds/idp.supervisor.pinniped.dev_ldapidentityproviders.yaml @@ -51,11 +51,80 @@ spec: spec: description: Spec for configuring the identity provider. properties: + bind: + description: Bind contains the configuration for how to provide access + credentials during an initial bind to the LDAP server to be allowed + to perform searches and binds to validate a user's credentials during + a user's authentication attempt. + properties: + secretName: + description: SecretName contains the name of a namespace-local + Secret object that provides the username and password for an + LDAP bind user. This account will be used to perform LDAP searches. + The Secret should be of type "kubernetes.io/basic-auth" which + includes "username" and "password" keys. The username value + should be the full DN of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". + minLength: 1 + type: string + required: + - secretName + type: object host: description: 'Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636.' minLength: 1 type: string + tls: + description: TLS contains the connection settings for how to establish + the connection to the Host. + properties: + certificateAuthorityData: + description: X.509 Certificate Authority (base64-encoded PEM bundle) + to trust when connecting to the LDAP provider. If omitted, a + default set of system roots will be trusted. + type: string + type: object + userSearch: + description: UserSearch contains the configuration for searching for + a user by name in the LDAP provider. + properties: + attributes: + description: Attributes specifies how the user's information should + be read from the LDAP entry which was found as the result of + the user search. + properties: + uniqueID: + description: UniqueID specifies the name of the attribute + in the LDAP entry which whose value shall be used to uniquely + identify the user within this LDAP provider after a successful + authentication. E.g. "uidNumber" or "objectGUID". + minLength: 1 + type: string + username: + description: Username specifies the name of attribute in the + LDAP entry which whose value shall become the username of + the user after a successful authentication. This would typically + be the same attribute name used in the user search filter. + E.g. "mail" or "uid" or "userPrincipalName". + minLength: 1 + type: string + type: object + base: + description: Base is the DN that should be used as the search + base when searching for users. E.g. "ou=users,dc=example,dc=com". + minLength: 1 + type: string + filter: + description: Filter is the LDAP search filter which should be + applied when searching for users. The pattern "{}" must occur + in the filter and will be dynamically replaced by the username + for which the search is being run. E.g. "mail={}" or "&(objectClass=person)(uid={})". + For more information about LDAP filters, see https://ldap.com/ldap-filters. + Optional. When not specified, the default will act as if the + Filter were specified as the value from Attributes.Username + appended by "={}". + type: string + type: object required: - host type: object diff --git a/generated/latest/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go b/generated/latest/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go index 4be520144..5e602f31d 100644 --- a/generated/latest/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go +++ b/generated/latest/apis/supervisor/idp/v1alpha1/types_ldapidentityprovider.go @@ -28,11 +28,69 @@ type LDAPIdentityProviderStatus struct { Phase LDAPIdentityProviderPhase `json:"phase,omitempty"` } +type LDAPIdentityProviderTLSSpec struct { + // X.509 Certificate Authority (base64-encoded PEM bundle) to trust when connecting to the LDAP provider. + // If omitted, a default set of system roots will be trusted. + // +optional + CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"` +} + +type LDAPIdentityProviderBindSpec struct { + // SecretName contains the name of a namespace-local Secret object that provides the username and + // password for an LDAP bind user. This account will be used to perform LDAP searches. The Secret should be + // of type "kubernetes.io/basic-auth" which includes "username" and "password" keys. The username value + // should be the full DN of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". + // +kubebuilder:validation:MinLength=1 + SecretName string `json:"secretName"` +} + +type LDAPIdentityProviderUserSearchAttributesSpec struct { + // Username specifies the name of attribute in the LDAP entry which whose value shall become the username + // of the user after a successful authentication. This would typically be the same attribute name used in + // the user search filter. E.g. "mail" or "uid" or "userPrincipalName". + // +kubebuilder:validation:MinLength=1 + Username string `json:"username,omitempty"` + + // UniqueID specifies the name of the attribute in the LDAP entry which whose value shall be used to uniquely + // identify the user within this LDAP provider after a successful authentication. E.g. "uidNumber" or "objectGUID". + // +kubebuilder:validation:MinLength=1 + UniqueID string `json:"uniqueID,omitempty"` +} + +type LDAPIdentityProviderUserSearchSpec struct { + // Base is the DN that should be used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com". + // +kubebuilder:validation:MinLength=1 + Base string `json:"base,omitempty"` + + // Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur + // in the filter and will be dynamically replaced by the username for which the search is being run. E.g. "mail={}" + // or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. + // Optional. When not specified, the default will act as if the Filter were specified as the value from + // Attributes.Username appended by "={}". + // +optional + Filter string `json:"filter,omitempty"` + + // Attributes specifies how the user's information should be read from the LDAP entry which was found as + // the result of the user search. + // +optional + Attributes LDAPIdentityProviderUserSearchAttributesSpec `json:"attributes,omitempty"` +} + // Spec for configuring an LDAP identity provider. type LDAPIdentityProviderSpec struct { // Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636. // +kubebuilder:validation:MinLength=1 Host string `json:"host"` + + // TLS contains the connection settings for how to establish the connection to the Host. + TLS *LDAPIdentityProviderTLSSpec `json:"tls,omitempty"` + + // Bind contains the configuration for how to provide access credentials during an initial bind to the LDAP server + // to be allowed to perform searches and binds to validate a user's credentials during a user's authentication attempt. + Bind LDAPIdentityProviderBindSpec `json:"bind,omitempty"` + + // UserSearch contains the configuration for searching for a user by name in the LDAP provider. + UserSearch LDAPIdentityProviderUserSearchSpec `json:"userSearch,omitempty"` } // LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access diff --git a/generated/latest/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go b/generated/latest/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go index bdd1bc952..6ddcebadd 100644 --- a/generated/latest/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go +++ b/generated/latest/apis/supervisor/idp/v1alpha1/zz_generated.deepcopy.go @@ -33,7 +33,7 @@ func (in *LDAPIdentityProvider) DeepCopyInto(out *LDAPIdentityProvider) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) out.Status = in.Status return } @@ -56,6 +56,22 @@ func (in *LDAPIdentityProvider) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderBindSpec) DeepCopyInto(out *LDAPIdentityProviderBindSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderBindSpec. +func (in *LDAPIdentityProviderBindSpec) DeepCopy() *LDAPIdentityProviderBindSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderBindSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LDAPIdentityProviderList) DeepCopyInto(out *LDAPIdentityProviderList) { *out = *in @@ -92,6 +108,13 @@ func (in *LDAPIdentityProviderList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec) { *out = *in + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(LDAPIdentityProviderTLSSpec) + **out = **in + } + out.Bind = in.Bind + out.UserSearch = in.UserSearch return } @@ -121,6 +144,55 @@ func (in *LDAPIdentityProviderStatus) DeepCopy() *LDAPIdentityProviderStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderTLSSpec) DeepCopyInto(out *LDAPIdentityProviderTLSSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderTLSSpec. +func (in *LDAPIdentityProviderTLSSpec) DeepCopy() *LDAPIdentityProviderTLSSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderTLSSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderUserSearchAttributesSpec) DeepCopyInto(out *LDAPIdentityProviderUserSearchAttributesSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderUserSearchAttributesSpec. +func (in *LDAPIdentityProviderUserSearchAttributesSpec) DeepCopy() *LDAPIdentityProviderUserSearchAttributesSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderUserSearchAttributesSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LDAPIdentityProviderUserSearchSpec) DeepCopyInto(out *LDAPIdentityProviderUserSearchSpec) { + *out = *in + out.Attributes = in.Attributes + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderUserSearchSpec. +func (in *LDAPIdentityProviderUserSearchSpec) DeepCopy() *LDAPIdentityProviderUserSearchSpec { + if in == nil { + return nil + } + out := new(LDAPIdentityProviderUserSearchSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OIDCAuthorizationConfig) DeepCopyInto(out *OIDCAuthorizationConfig) { *out = *in diff --git a/hack/prepare-for-integration-tests.sh b/hack/prepare-for-integration-tests.sh index c52e77764..62c6e09a6 100755 --- a/hack/prepare-for-integration-tests.sh +++ b/hack/prepare-for-integration-tests.sh @@ -317,8 +317,7 @@ export PINNIPED_TEST_SUPERVISOR_CUSTOM_LABELS='${supervisor_custom_labels}' export PINNIPED_TEST_SUPERVISOR_HTTP_ADDRESS="127.0.0.1:12345" export PINNIPED_TEST_SUPERVISOR_HTTPS_ADDRESS="localhost:12344" export PINNIPED_TEST_PROXY=http://127.0.0.1:12346 -export PINNIPED_TEST_LDAP_LDAP_URL=ldap://ldap.tools.svc.cluster.local -export PINNIPED_TEST_LDAP_LDAPS_URL=ldaps://ldap.tools.svc.cluster.local +export PINNIPED_TEST_LDAP_HOST=ldap.tools.svc.cluster.local export PINNIPED_TEST_LDAP_LDAPS_CA_BUNDLE="${test_ca_bundle_pem}" export PINNIPED_TEST_LDAP_BIND_ACCOUNT_USERNAME="cn=admin,dc=pinniped,dc=dev" export PINNIPED_TEST_LDAP_BIND_ACCOUNT_PASSWORD=password @@ -327,6 +326,8 @@ export PINNIPED_TEST_LDAP_GROUPS_SEARCH_BASE="ou=groups,dc=pinniped,dc=dev" export PINNIPED_TEST_LDAP_USER_DN="cn=pinny,ou=users,dc=pinniped,dc=dev" export PINNIPED_TEST_LDAP_USER_CN="pinny" export PINNIPED_TEST_LDAP_USER_PASSWORD=${ldap_test_password} +export PINNIPED_TEST_LDAP_USER_UNIQUE_ID_ATTRIBUTE_NAME="uidNumber" +export PINNIPED_TEST_LDAP_USER_UNIQUE_ID_ATTRIBUTE_VALUE="1000" export PINNIPED_TEST_LDAP_USER_EMAIL_ATTRIBUTE_NAME="mail" export PINNIPED_TEST_LDAP_USER_EMAIL_ATTRIBUTE_VALUE="pinny.ldap@example.com" export PINNIPED_TEST_LDAP_EXPECTED_DIRECT_GROUPS_DN="cn=ball-game-players,ou=beach-groups,ou=groups,dc=pinniped,dc=dev;cn=seals,ou=groups,dc=pinniped,dc=dev" diff --git a/test/deploy/tools/ldap.yaml b/test/deploy/tools/ldap.yaml index 9e1784077..200405993 100644 --- a/test/deploy/tools/ldap.yaml +++ b/test/deploy/tools/ldap.yaml @@ -140,9 +140,7 @@ spec: spec: containers: - name: ldap - #! An issue was reported and will be fixed in bitnami/openldap soon. - image: ghcr.io/pinniped-ci-bot/bitnami-openldap-forked:2.4.58-debian-10-r15 #! our own fork of docker.io/bitnami/openldap - #! image: docker.io/bitnami/openldap + image: docker.io/bitnami/openldap imagePullPolicy: Always ports: - name: ldap @@ -182,9 +180,6 @@ spec: value: "/var/certs/ldap-key.pem" - name: LDAP_TLS_CA_FILE value: "/var/certs/ca.pem" - #! This env var was added in our fork to reduce slapd memory consumption from ~700 MB to ~12 MB. - - name: LDAP_ULIMIT_MAX_FILES - value: "1024" #! Note that the custom LDIF file is only read at pod start-up time. - name: LDAP_CUSTOM_LDIF_DIR value: "/var/ldifs" diff --git a/test/integration/cli_test.go b/test/integration/cli_test.go index 1221a6046..149b91946 100644 --- a/test/integration/cli_test.go +++ b/test/integration/cli_test.go @@ -190,9 +190,9 @@ func TestCLILoginOIDC(t *testing.T) { require.NoError(t, err) claims := map[string]interface{}{} require.NoError(t, json.Unmarshal(jws.UnsafePayloadWithoutVerification(), &claims)) - require.Equal(t, env.CLITestUpstream.Issuer, claims["iss"]) - require.Equal(t, env.CLITestUpstream.ClientID, claims["aud"]) - require.Equal(t, env.CLITestUpstream.Username, claims["email"]) + require.Equal(t, env.CLIUpstreamOIDC.Issuer, claims["iss"]) + require.Equal(t, env.CLIUpstreamOIDC.ClientID, claims["aud"]) + require.Equal(t, env.CLIUpstreamOIDC.Username, claims["email"]) require.NotEmpty(t, claims["nonce"]) // Run the CLI again with the same session cache and login parameters. @@ -211,10 +211,10 @@ func TestCLILoginOIDC(t *testing.T) { t.Logf("overwriting cache to remove valid ID token") cache := filesession.New(sessionCachePath) cacheKey := oidcclient.SessionCacheKey{ - Issuer: env.CLITestUpstream.Issuer, - ClientID: env.CLITestUpstream.ClientID, + Issuer: env.CLIUpstreamOIDC.Issuer, + ClientID: env.CLIUpstreamOIDC.ClientID, Scopes: []string{"email", "offline_access", "openid", "profile"}, - RedirectURI: strings.ReplaceAll(env.CLITestUpstream.CallbackURL, "127.0.0.1", "localhost"), + RedirectURI: strings.ReplaceAll(env.CLIUpstreamOIDC.CallbackURL, "127.0.0.1", "localhost"), } cached := cache.GetToken(cacheKey) require.NotNil(t, cached) @@ -325,11 +325,11 @@ func runPinnipedLoginOIDC( require.NoError(t, page.Navigate(loginURL)) // Expect to be redirected to the upstream provider and log in. - browsertest.LoginToUpstream(t, page, env.CLITestUpstream) + browsertest.LoginToUpstream(t, page, env.CLIUpstreamOIDC) // Expect to be redirected to the localhost callback. t.Logf("waiting for redirect to callback") - callbackURLPattern := regexp.MustCompile(`\A` + regexp.QuoteMeta(env.CLITestUpstream.CallbackURL) + `\?.+\z`) + callbackURLPattern := regexp.MustCompile(`\A` + regexp.QuoteMeta(env.CLIUpstreamOIDC.CallbackURL) + `\?.+\z`) browsertest.WaitForURL(t, page, callbackURLPattern) // Wait for the "pre" element that gets rendered for a `text/plain` page, and @@ -375,11 +375,11 @@ func spawnTestGoroutine(t *testing.T, f func() error) { func oidcLoginCommand(ctx context.Context, t *testing.T, pinnipedExe string, sessionCachePath string) *exec.Cmd { env := library.IntegrationEnv(t) - callbackURL, err := url.Parse(env.CLITestUpstream.CallbackURL) + callbackURL, err := url.Parse(env.CLIUpstreamOIDC.CallbackURL) require.NoError(t, err) cmd := exec.CommandContext(ctx, pinnipedExe, "login", "oidc", - "--issuer", env.CLITestUpstream.Issuer, - "--client-id", env.CLITestUpstream.ClientID, + "--issuer", env.CLIUpstreamOIDC.Issuer, + "--client-id", env.CLIUpstreamOIDC.ClientID, "--scopes", "offline_access,openid,email,profile", "--listen-port", callbackURL.Port(), "--session-cache", sessionCachePath, @@ -387,9 +387,9 @@ func oidcLoginCommand(ctx context.Context, t *testing.T, pinnipedExe string, ses ) // If there is a custom CA bundle, pass it via --ca-bundle and a temporary file. - if env.CLITestUpstream.CABundle != "" { + if env.CLIUpstreamOIDC.CABundle != "" { path := filepath.Join(testutil.TempDir(t), "test-ca.pem") - require.NoError(t, ioutil.WriteFile(path, []byte(env.CLITestUpstream.CABundle), 0600)) + require.NoError(t, ioutil.WriteFile(path, []byte(env.CLIUpstreamOIDC.CABundle), 0600)) cmd.Args = append(cmd.Args, "--ca-bundle", path) } diff --git a/test/integration/e2e_test.go b/test/integration/e2e_test.go index 2cb207e7e..3ab00ffb5 100644 --- a/test/integration/e2e_test.go +++ b/test/integration/e2e_test.go @@ -53,7 +53,7 @@ func TestE2EFullIntegration(t *testing.T) { page := browsertest.Open(t) // Infer the downstream issuer URL from the callback associated with the upstream test client registration. - issuerURL, err := url.Parse(env.SupervisorTestUpstream.CallbackURL) + issuerURL, err := url.Parse(env.SupervisorUpstreamOIDC.CallbackURL) require.NoError(t, err) require.True(t, strings.HasSuffix(issuerURL.Path, "/callback")) issuerURL.Path = strings.TrimSuffix(issuerURL.Path, "/callback") @@ -66,7 +66,7 @@ func TestE2EFullIntegration(t *testing.T) { // Save that bundle plus the one that signs the upstream issuer, for test purposes. testCABundlePath := filepath.Join(tempDir, "test-ca.pem") - testCABundlePEM := []byte(string(ca.Bundle()) + "\n" + env.SupervisorTestUpstream.CABundle) + testCABundlePEM := []byte(string(ca.Bundle()) + "\n" + env.SupervisorUpstreamOIDC.CABundle) testCABundleBase64 := base64.StdEncoding.EncodeToString(testCABundlePEM) require.NoError(t, ioutil.WriteFile(testCABundlePath, testCABundlePEM, 0600)) @@ -94,19 +94,19 @@ func TestE2EFullIntegration(t *testing.T) { // Create upstream OIDC provider and wait for it to become ready. library.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{ - Issuer: env.SupervisorTestUpstream.Issuer, + Issuer: env.SupervisorUpstreamOIDC.Issuer, TLS: &idpv1alpha1.TLSSpec{ - CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorTestUpstream.CABundle)), + CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)), }, AuthorizationConfig: idpv1alpha1.OIDCAuthorizationConfig{ - AdditionalScopes: env.SupervisorTestUpstream.AdditionalScopes, + AdditionalScopes: env.SupervisorUpstreamOIDC.AdditionalScopes, }, Claims: idpv1alpha1.OIDCClaims{ - Username: env.SupervisorTestUpstream.UsernameClaim, - Groups: env.SupervisorTestUpstream.GroupsClaim, + Username: env.SupervisorUpstreamOIDC.UsernameClaim, + Groups: env.SupervisorUpstreamOIDC.GroupsClaim, }, Client: idpv1alpha1.OIDCClient{ - SecretName: library.CreateClientCredsSecret(t, env.SupervisorTestUpstream.ClientID, env.SupervisorTestUpstream.ClientSecret).Name, + SecretName: library.CreateClientCredsSecret(t, env.SupervisorUpstreamOIDC.ClientID, env.SupervisorUpstreamOIDC.ClientSecret).Name, }, }, idpv1alpha1.PhaseReady) @@ -120,10 +120,10 @@ func TestE2EFullIntegration(t *testing.T) { // Create a ClusterRoleBinding to give our test user from the upstream read-only access to the cluster. library.CreateTestClusterRoleBinding(t, - rbacv1.Subject{Kind: rbacv1.UserKind, APIGroup: rbacv1.GroupName, Name: env.SupervisorTestUpstream.Username}, + rbacv1.Subject{Kind: rbacv1.UserKind, APIGroup: rbacv1.GroupName, Name: env.SupervisorUpstreamOIDC.Username}, rbacv1.RoleRef{Kind: "ClusterRole", APIGroup: rbacv1.GroupName, Name: "view"}, ) - library.WaitForUserToHaveAccess(t, env.SupervisorTestUpstream.Username, []string{}, &authorizationv1.ResourceAttributes{ + library.WaitForUserToHaveAccess(t, env.SupervisorUpstreamOIDC.Username, []string{}, &authorizationv1.ResourceAttributes{ Verb: "get", Group: "", Version: "v1", @@ -240,7 +240,7 @@ func TestE2EFullIntegration(t *testing.T) { require.NoError(t, page.Navigate(loginURL)) // Expect to be redirected to the upstream provider and log in. - browsertest.LoginToUpstream(t, page, env.SupervisorTestUpstream) + browsertest.LoginToUpstream(t, page, env.SupervisorUpstreamOIDC) // Expect to be redirected to the localhost callback. t.Logf("waiting for redirect to callback") @@ -290,11 +290,11 @@ func TestE2EFullIntegration(t *testing.T) { require.NotNil(t, token) idTokenClaims := token.IDToken.Claims - require.Equal(t, env.SupervisorTestUpstream.Username, idTokenClaims[oidc.DownstreamUsernameClaim]) + require.Equal(t, env.SupervisorUpstreamOIDC.Username, idTokenClaims[oidc.DownstreamUsernameClaim]) // The groups claim in the file ends up as an []interface{}, so adjust our expectation to match. - expectedGroups := make([]interface{}, 0, len(env.SupervisorTestUpstream.ExpectedGroups)) - for _, g := range env.SupervisorTestUpstream.ExpectedGroups { + expectedGroups := make([]interface{}, 0, len(env.SupervisorUpstreamOIDC.ExpectedGroups)) + for _, g := range env.SupervisorUpstreamOIDC.ExpectedGroups { expectedGroups = append(expectedGroups, g) } require.Equal(t, expectedGroups, idTokenClaims[oidc.DownstreamGroupsClaim]) @@ -302,7 +302,7 @@ func TestE2EFullIntegration(t *testing.T) { // confirm we are the right user according to Kube expectedYAMLGroups := func() string { var b strings.Builder - for _, g := range env.SupervisorTestUpstream.ExpectedGroups { + for _, g := range env.SupervisorUpstreamOIDC.ExpectedGroups { b.WriteString("\n") b.WriteString(` - `) b.WriteString(g) @@ -328,7 +328,7 @@ status: user: groups:`+expectedYAMLGroups+` - system:authenticated - username: `+env.SupervisorTestUpstream.Username+` + username: `+env.SupervisorUpstreamOIDC.Username+` `, string(kubectlOutput3)) @@ -339,7 +339,7 @@ status: true, pinnipedExe, kubeconfigPath, - env.SupervisorTestUpstream.Username, - append(env.SupervisorTestUpstream.ExpectedGroups, "system:authenticated"), + env.SupervisorUpstreamOIDC.Username, + append(env.SupervisorUpstreamOIDC.ExpectedGroups, "system:authenticated"), ) } diff --git a/test/integration/supervisor_login_test.go b/test/integration/supervisor_login_test.go index 3af758266..909d6b1be 100644 --- a/test/integration/supervisor_login_test.go +++ b/test/integration/supervisor_login_test.go @@ -9,6 +9,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "io/ioutil" "net/http" "net/http/httptest" "net/url" @@ -36,43 +37,89 @@ import ( ) func TestSupervisorLogin(t *testing.T) { + env := library.IntegrationEnv(t) + tests := []struct { - name string - createIDP func(t *testing.T) - requestAuthorization func(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string) + name string + createIDP func(t *testing.T) + requestAuthorization func(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string, httpClient *http.Client) + wantDownstreamIDTokenSubjectToMatch string + wantDownstreamIDTokenUsernameToMatch string }{ { name: "oidc", createIDP: func(t *testing.T) { t.Helper() - env := library.IntegrationEnv(t) library.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{ - Issuer: env.SupervisorTestUpstream.Issuer, + Issuer: env.SupervisorUpstreamOIDC.Issuer, TLS: &idpv1alpha1.TLSSpec{ - CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorTestUpstream.CABundle)), + CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)), }, Client: idpv1alpha1.OIDCClient{ - SecretName: library.CreateClientCredsSecret(t, env.SupervisorTestUpstream.ClientID, env.SupervisorTestUpstream.ClientSecret).Name, + SecretName: library.CreateClientCredsSecret(t, env.SupervisorUpstreamOIDC.ClientID, env.SupervisorUpstreamOIDC.ClientSecret).Name, }, }, idpv1alpha1.PhaseReady) }, requestAuthorization: requestAuthorizationUsingOIDCIdentityProvider, + // the ID token Subject should include the upstream user ID after the upstream issuer name + wantDownstreamIDTokenSubjectToMatch: regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+", + // the ID token Username should include the upstream user ID after the upstream issuer name + wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+", }, + // TODO add more variations of this LDAP test to try using different user search filters and attributes { name: "ldap", createIDP: func(t *testing.T) { t.Helper() + secret := library.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth, + map[string]string{ + v1.BasicAuthUsernameKey: env.SupervisorUpstreamLDAP.BindUsername, + v1.BasicAuthPasswordKey: env.SupervisorUpstreamLDAP.BindPassword, + }, + ) library.CreateTestLDAPIdentityProvider(t, idpv1alpha1.LDAPIdentityProviderSpec{ - Host: "something", - }, "") // TODO: this should be Ready! + Host: env.SupervisorUpstreamLDAP.Host, + TLS: &idpv1alpha1.LDAPIdentityProviderTLSSpec{ + CertificateAuthorityData: env.SupervisorUpstreamLDAP.CABundle, + }, + Bind: idpv1alpha1.LDAPIdentityProviderBindSpec{ + SecretName: secret.Name, + }, + UserSearch: idpv1alpha1.LDAPIdentityProviderUserSearchSpec{ + Base: env.SupervisorUpstreamLDAP.UserSearchBase, + Filter: "", + Attributes: idpv1alpha1.LDAPIdentityProviderUserSearchAttributesSpec{ + Username: env.SupervisorUpstreamLDAP.TestUserMailAttributeName, + UniqueID: env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeName, + }, + }, + }, "") // TODO: this should be idpv1alpha1.LDAPPhaseReady once we have a controller }, - requestAuthorization: requestAuthorizationUsingLDAPIdentityProvider, + requestAuthorization: func(t *testing.T, downstreamAuthorizeURL, _ string, httpClient *http.Client) { + requestAuthorizationUsingLDAPIdentityProvider(t, + downstreamAuthorizeURL, + env.SupervisorUpstreamLDAP.TestUserMailAttributeValue, // username to present to server during login + env.SupervisorUpstreamLDAP.TestUserPassword, // password to present to server during login + httpClient, + ) + }, + // the ID token Subject should be the Host URL plus the value pulled from the requested UserSearch.Attributes.UID attribute + wantDownstreamIDTokenSubjectToMatch: regexp.QuoteMeta( + "ldaps://" + env.SupervisorUpstreamLDAP.Host + "?sub=" + env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeValue, + ), + // the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute + wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue), }, } for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { - testSupervisorLogin(t, test.createIDP, test.requestAuthorization) + testSupervisorLogin(t, + test.createIDP, + test.requestAuthorization, + test.wantDownstreamIDTokenSubjectToMatch, + test.wantDownstreamIDTokenUsernameToMatch, + ) }) } } @@ -80,7 +127,8 @@ func TestSupervisorLogin(t *testing.T) { func testSupervisorLogin( t *testing.T, createIDP func(t *testing.T), - requestAuthorization func(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string), + requestAuthorization func(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string, httpClient *http.Client), + wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch string, ) { env := library.IntegrationEnv(t) @@ -88,7 +136,7 @@ func testSupervisorLogin( defer cancel() // Infer the downstream issuer URL from the callback associated with the upstream test client registration. - issuerURL, err := url.Parse(env.SupervisorTestUpstream.CallbackURL) + issuerURL, err := url.Parse(env.SupervisorUpstreamOIDC.CallbackURL) require.NoError(t, err) require.True(t, strings.HasSuffix(issuerURL.Path, "/callback")) issuerURL.Path = strings.TrimSuffix(issuerURL.Path, "/callback") @@ -201,7 +249,7 @@ func testSupervisorLogin( pkceParam.Method(), ) - // Make the authorize request one "manually" so we can check its response headers. + // Make the authorize request once "manually" so we can check its response security headers. authorizeRequest, err := http.NewRequestWithContext(ctx, http.MethodGet, downstreamAuthorizeURL, nil) require.NoError(t, err) authorizeResp, err := httpClient.Do(authorizeRequest) @@ -210,7 +258,7 @@ func testSupervisorLogin( expectSecurityHeaders(t, authorizeResp) // Perform parameterized auth code acquisition. - requestAuthorization(t, downstreamAuthorizeURL, localCallbackServer.URL) + requestAuthorization(t, downstreamAuthorizeURL, localCallbackServer.URL, httpClient) // Expect that our callback handler was invoked. callback := localCallbackServer.waitForCallback(10 * time.Second) @@ -225,7 +273,9 @@ func testSupervisorLogin( require.NoError(t, err) expectedIDTokenClaims := []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "nonce", "rat", "username", "groups"} - verifyTokenResponse(t, tokenResponse, discovery, downstreamOAuth2Config, env.SupervisorTestUpstream.Issuer, nonceParam, expectedIDTokenClaims) + verifyTokenResponse(t, + tokenResponse, discovery, downstreamOAuth2Config, nonceParam, + expectedIDTokenClaims, wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch) // token exchange on the original token doTokenExchange(t, &downstreamOAuth2Config, tokenResponse, httpClient, discovery) @@ -236,7 +286,9 @@ func testSupervisorLogin( require.NoError(t, err) expectedIDTokenClaims = append(expectedIDTokenClaims, "at_hash") - verifyTokenResponse(t, refreshedTokenResponse, discovery, downstreamOAuth2Config, env.SupervisorTestUpstream.Issuer, "", expectedIDTokenClaims) + verifyTokenResponse(t, + refreshedTokenResponse, discovery, downstreamOAuth2Config, "", + expectedIDTokenClaims, wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch) require.NotEqual(t, tokenResponse.AccessToken, refreshedTokenResponse.AccessToken) require.NotEqual(t, tokenResponse.RefreshToken, refreshedTokenResponse.RefreshToken) @@ -251,9 +303,9 @@ func verifyTokenResponse( tokenResponse *oauth2.Token, discovery *coreosoidc.Provider, downstreamOAuth2Config oauth2.Config, - upstreamIssuerName string, nonceParam nonce.Nonce, expectedIDTokenClaims []string, + wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch string, ) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -265,14 +317,17 @@ func verifyTokenResponse( idToken, err := verifier.Verify(ctx, rawIDToken) require.NoError(t, err) - // Check the claims of the ID token. - expectedSubjectPrefix := upstreamIssuerName + "?sub=" - require.True(t, strings.HasPrefix(idToken.Subject, expectedSubjectPrefix)) - require.Greater(t, len(idToken.Subject), len(expectedSubjectPrefix), - "the ID token Subject should include the upstream user ID after the upstream issuer name") + // Check the sub claim of the ID token. + require.Regexp(t, wantDownstreamIDTokenSubjectToMatch, idToken.Subject) + + // Check the nonce claim of the ID token. require.NoError(t, nonceParam.Validate(idToken)) + + // Check the exp claim of the ID token. expectedIDTokenLifetime := oidc.DefaultOIDCTimeoutsConfiguration().IDTokenLifespan testutil.RequireTimeInDelta(t, time.Now().UTC().Add(expectedIDTokenLifetime), idToken.Expiry, time.Second*30) + + // Check the full list of claim names of the ID token. idTokenClaims := map[string]interface{}{} err = idToken.Claims(&idTokenClaims) require.NoError(t, err) @@ -281,10 +336,9 @@ func verifyTokenResponse( idTokenClaimNames = append(idTokenClaimNames, k) } require.ElementsMatch(t, expectedIDTokenClaims, idTokenClaimNames) - expectedUsernamePrefix := upstreamIssuerName + "?sub=" - require.True(t, strings.HasPrefix(idTokenClaims["username"].(string), expectedUsernamePrefix)) - require.Greater(t, len(idTokenClaims["username"].(string)), len(expectedUsernamePrefix), - "the ID token Username should include the upstream user ID after the upstream issuer name") + + // Check username claim of the ID token. + require.Regexp(t, wantDownstreamIDTokenUsernameToMatch, idTokenClaims["username"].(string)) // Some light verification of the other tokens that were returned. require.NotEmpty(t, tokenResponse.AccessToken) @@ -296,7 +350,7 @@ func verifyTokenResponse( require.NotEmpty(t, tokenResponse.RefreshToken) } -func requestAuthorizationUsingOIDCIdentityProvider(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string) { +func requestAuthorizationUsingOIDCIdentityProvider(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string, _ *http.Client) { t.Helper() env := library.IntegrationEnv(t) @@ -306,7 +360,7 @@ func requestAuthorizationUsingOIDCIdentityProvider(t *testing.T, downstreamAutho require.NoError(t, page.Navigate(downstreamAuthorizeURL)) // Expect to be redirected to the upstream provider and log in. - browsertest.LoginToUpstream(t, page, env.SupervisorTestUpstream) + browsertest.LoginToUpstream(t, page, env.SupervisorUpstreamOIDC) // Wait for the login to happen and us be redirected back to a localhost callback. t.Logf("waiting for redirect to callback") @@ -314,9 +368,29 @@ func requestAuthorizationUsingOIDCIdentityProvider(t *testing.T, downstreamAutho browsertest.WaitForURL(t, page, callbackURLPattern) } -func requestAuthorizationUsingLDAPIdentityProvider(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string) { +func requestAuthorizationUsingLDAPIdentityProvider(t *testing.T, downstreamAuthorizeURL, upstreamUsername, upstreamPassword string, httpClient *http.Client) { t.Helper() - t.Skip("implement me!") + + ctx, cancelFunc := context.WithTimeout(context.Background(), time.Minute) + defer cancelFunc() + + authRequest, err := http.NewRequestWithContext(ctx, http.MethodGet, downstreamAuthorizeURL, nil) + require.NoError(t, err) + + // Set the custom username/password headers for the LDAP authorize request. + authRequest.Header.Set("X-Pinniped-Upstream-Username", upstreamUsername) + authRequest.Header.Set("X-Pinniped-Upstream-Password", upstreamPassword) + + // The authorize request is supposed to redirect to this test's callback handler, which in turn is supposed to return 200 OK. + authResponse, err := httpClient.Do(authRequest) + require.NoError(t, err) + responseBody, err := ioutil.ReadAll(authResponse.Body) + defer authResponse.Body.Close() + require.NoError(t, err) + + t.Skip("The rest of this test will not work until we implement the corresponding production code.") // TODO remove this skip + + require.Equalf(t, http.StatusOK, authResponse.StatusCode, "response body was: %s", string(responseBody)) } func startLocalCallbackServer(t *testing.T) *localCallbackServer { diff --git a/test/integration/supervisor_upstream_test.go b/test/integration/supervisor_upstream_test.go index b43735a63..5b4c0cfdc 100644 --- a/test/integration/supervisor_upstream_test.go +++ b/test/integration/supervisor_upstream_test.go @@ -45,9 +45,9 @@ func TestSupervisorUpstreamOIDCDiscovery(t *testing.T) { t.Run("valid", func(t *testing.T) { t.Parallel() spec := v1alpha1.OIDCIdentityProviderSpec{ - Issuer: env.SupervisorTestUpstream.Issuer, + Issuer: env.SupervisorUpstreamOIDC.Issuer, TLS: &v1alpha1.TLSSpec{ - CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorTestUpstream.CABundle)), + CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)), }, AuthorizationConfig: v1alpha1.OIDCAuthorizationConfig{ AdditionalScopes: []string{"email", "profile"}, diff --git a/test/library/client.go b/test/library/client.go index acbf76263..ef242613a 100644 --- a/test/library/client.go +++ b/test/library/client.go @@ -190,13 +190,13 @@ func CreateTestWebhookAuthenticator(ctx context.Context, t *testing.T) corev1.Ty // test's lifetime. It returns a corev1.TypedLocalObjectReference which describes the test JWT // authenticator within the test namespace. // -// CreateTestJWTAuthenticatorForCLIUpstream gets the OIDC issuer info from IntegrationEnv().CLITestUpstream. +// CreateTestJWTAuthenticatorForCLIUpstream gets the OIDC issuer info from IntegrationEnv().CLIUpstreamOIDC. func CreateTestJWTAuthenticatorForCLIUpstream(ctx context.Context, t *testing.T) corev1.TypedLocalObjectReference { t.Helper() testEnv := IntegrationEnv(t) spec := auth1alpha1.JWTAuthenticatorSpec{ - Issuer: testEnv.CLITestUpstream.Issuer, - Audience: testEnv.CLITestUpstream.ClientID, + Issuer: testEnv.CLIUpstreamOIDC.Issuer, + Audience: testEnv.CLIUpstreamOIDC.ClientID, // The default UsernameClaim is "username" but the upstreams that we use for // integration tests won't necessarily have that claim, so use "sub" here. Claims: auth1alpha1.JWTTokenClaims{Username: "sub"}, @@ -204,9 +204,9 @@ func CreateTestJWTAuthenticatorForCLIUpstream(ctx context.Context, t *testing.T) // If the test upstream does not have a CA bundle specified, then don't configure one in the // JWTAuthenticator. Leaving TLSSpec set to nil will result in OIDC discovery using the OS's root // CA store. - if testEnv.CLITestUpstream.CABundle != "" { + if testEnv.CLIUpstreamOIDC.CABundle != "" { spec.TLS = &auth1alpha1.TLSSpec{ - CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(testEnv.CLITestUpstream.CABundle)), + CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(testEnv.CLIUpstreamOIDC.CABundle)), } } return CreateTestJWTAuthenticator(ctx, t, spec) @@ -250,8 +250,7 @@ func CreateTestJWTAuthenticator(ctx context.Context, t *testing.T, spec auth1alp // CreateTestFederationDomain creates and returns a test FederationDomain in // $PINNIPED_TEST_SUPERVISOR_NAMESPACE, which will be automatically deleted at the end of the -// current test's lifetime. It generates a random, valid, issuer for the FederationDomain. -// +// current test's lifetime. // If the provided issuer is not the empty string, then it will be used for the // FederationDomain.Spec.Issuer field. Else, a random issuer will be generated. func CreateTestFederationDomain(ctx context.Context, t *testing.T, issuer string, certSecretName string, expectStatus configv1alpha1.FederationDomainStatusCondition) *configv1alpha1.FederationDomain { diff --git a/test/library/env.go b/test/library/env.go index a19aaa2e1..6ecc699f5 100644 --- a/test/library/env.go +++ b/test/library/env.go @@ -49,8 +49,9 @@ type TestEnv struct { ExpectedGroups []string `json:"expectedGroups"` } `json:"testUser"` - CLITestUpstream TestOIDCUpstream `json:"cliOIDCUpstream"` - SupervisorTestUpstream TestOIDCUpstream `json:"supervisorOIDCUpstream"` + CLIUpstreamOIDC TestOIDCUpstream `json:"cliOIDCUpstream"` + SupervisorUpstreamOIDC TestOIDCUpstream `json:"supervisorOIDCUpstream"` + SupervisorUpstreamLDAP TestLDAPUpstream `json:"supervisorLDAPUpstream"` } type TestOIDCUpstream struct { @@ -67,6 +68,21 @@ type TestOIDCUpstream struct { ExpectedGroups []string `json:"expectedGroups"` } +type TestLDAPUpstream struct { + Host string `json:"host"` + CABundle string `json:"caBundle"` + BindUsername string `json:"bindUsername"` + BindPassword string `json:"bindPassword"` + UserSearchBase string `json:"userSearchBase"` + TestUserDN string `json:"testUserDN"` + TestUserCN string `json:"testUserCN"` + TestUserPassword string `json:"testUserPassword"` + TestUserMailAttributeName string `json:"testUserMailAttributeName"` + TestUserMailAttributeValue string `json:"testUserMailAttributeValue"` + TestUserUniqueIDAttributeName string `json:"testUserUniqueIDAttributeName"` + TestUserUniqueIDAttributeValue string `json:"testUserUniqueIDAttributeValue"` +} + // ProxyEnv returns a set of environment variable strings (e.g., to combine with os.Environ()) which set up the configured test HTTP proxy. func (e *TestEnv) ProxyEnv() []string { if e.Proxy == "" { @@ -180,7 +196,7 @@ func loadEnvVars(t *testing.T, result *TestEnv) { result.Proxy = os.Getenv("PINNIPED_TEST_PROXY") result.APIGroupSuffix = wantEnv("PINNIPED_TEST_API_GROUP_SUFFIX", "pinniped.dev") - result.CLITestUpstream = TestOIDCUpstream{ + result.CLIUpstreamOIDC = TestOIDCUpstream{ Issuer: needEnv(t, "PINNIPED_TEST_CLI_OIDC_ISSUER"), CABundle: os.Getenv("PINNIPED_TEST_CLI_OIDC_ISSUER_CA_BUNDLE"), ClientID: needEnv(t, "PINNIPED_TEST_CLI_OIDC_CLIENT_ID"), @@ -189,7 +205,7 @@ func loadEnvVars(t *testing.T, result *TestEnv) { Password: needEnv(t, "PINNIPED_TEST_CLI_OIDC_PASSWORD"), } - result.SupervisorTestUpstream = TestOIDCUpstream{ + result.SupervisorUpstreamOIDC = TestOIDCUpstream{ Issuer: needEnv(t, "PINNIPED_TEST_SUPERVISOR_UPSTREAM_OIDC_ISSUER"), CABundle: os.Getenv("PINNIPED_TEST_SUPERVISOR_UPSTREAM_OIDC_ISSUER_CA_BUNDLE"), AdditionalScopes: strings.Fields(os.Getenv("PINNIPED_TEST_SUPERVISOR_UPSTREAM_OIDC_ADDITIONAL_SCOPES")), @@ -202,6 +218,21 @@ func loadEnvVars(t *testing.T, result *TestEnv) { Password: needEnv(t, "PINNIPED_TEST_SUPERVISOR_UPSTREAM_OIDC_PASSWORD"), ExpectedGroups: filterEmpty(strings.Split(strings.ReplaceAll(os.Getenv("PINNIPED_TEST_SUPERVISOR_UPSTREAM_OIDC_EXPECTED_GROUPS"), " ", ""), ",")), } + + result.SupervisorUpstreamLDAP = TestLDAPUpstream{ + Host: needEnv(t, "PINNIPED_TEST_LDAP_HOST"), + CABundle: needEnv(t, "PINNIPED_TEST_LDAP_LDAPS_CA_BUNDLE"), + BindUsername: needEnv(t, "PINNIPED_TEST_LDAP_BIND_ACCOUNT_USERNAME"), + BindPassword: needEnv(t, "PINNIPED_TEST_LDAP_BIND_ACCOUNT_PASSWORD"), + UserSearchBase: needEnv(t, "PINNIPED_TEST_LDAP_USERS_SEARCH_BASE"), + TestUserDN: needEnv(t, "PINNIPED_TEST_LDAP_USER_DN"), + TestUserCN: needEnv(t, "PINNIPED_TEST_LDAP_USER_CN"), + TestUserUniqueIDAttributeName: needEnv(t, "PINNIPED_TEST_LDAP_USER_UNIQUE_ID_ATTRIBUTE_NAME"), + TestUserUniqueIDAttributeValue: needEnv(t, "PINNIPED_TEST_LDAP_USER_UNIQUE_ID_ATTRIBUTE_VALUE"), + TestUserMailAttributeName: needEnv(t, "PINNIPED_TEST_LDAP_USER_EMAIL_ATTRIBUTE_NAME"), + TestUserMailAttributeValue: needEnv(t, "PINNIPED_TEST_LDAP_USER_EMAIL_ATTRIBUTE_VALUE"), + TestUserPassword: needEnv(t, "PINNIPED_TEST_LDAP_USER_PASSWORD"), + } } func (e *TestEnv) HasCapability(cap Capability) bool {