Merge branch 'initial_ldap_group_support' into ldap_starttls
This commit is contained in:
commit
b5063e59ab
@ -39,14 +39,14 @@ type LDAPIdentityProviderBind struct {
|
|||||||
// SecretName contains the name of a namespace-local Secret object that provides the username and
|
// 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
|
// 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
|
// 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".
|
// should be the full dn (distinguished name) of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com".
|
||||||
// The password must be non-empty.
|
// The password must be non-empty.
|
||||||
// +kubebuilder:validation:MinLength=1
|
// +kubebuilder:validation:MinLength=1
|
||||||
SecretName string `json:"secretName"`
|
SecretName string `json:"secretName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LDAPIdentityProviderUserSearchAttributes struct {
|
type LDAPIdentityProviderUserSearchAttributes struct {
|
||||||
// Username specifies the name of attribute in the LDAP entry which whose value shall become the username
|
// Username specifies the name of the attribute in the LDAP entry whose value shall become the username
|
||||||
// of the user after a successful authentication. This would typically be the same attribute name used in
|
// of the user after a successful authentication. This would typically be the same attribute name used in
|
||||||
// the user search filter, although it can be different. E.g. "mail" or "uid" or "userPrincipalName".
|
// the user search filter, although it can be different. E.g. "mail" or "uid" or "userPrincipalName".
|
||||||
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||||
@ -64,14 +64,26 @@ type LDAPIdentityProviderUserSearchAttributes struct {
|
|||||||
UID string `json:"uid,omitempty"`
|
UID string `json:"uid,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LDAPIdentityProviderGroupSearchAttributes struct {
|
||||||
|
// GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name
|
||||||
|
// in the user's list of groups after a successful authentication.
|
||||||
|
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||||
|
// server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
||||||
|
// Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||||
|
// +optional
|
||||||
|
GroupName string `json:"groupName,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type LDAPIdentityProviderUserSearch struct {
|
type LDAPIdentityProviderUserSearch 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".
|
// Base is the dn (distinguished name) that should be used as the search base when searching for users.
|
||||||
|
// E.g. "ou=users,dc=example,dc=com".
|
||||||
// +kubebuilder:validation:MinLength=1
|
// +kubebuilder:validation:MinLength=1
|
||||||
Base string `json:"base,omitempty"`
|
Base string `json:"base,omitempty"`
|
||||||
|
|
||||||
// Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur
|
// 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={}"
|
// in the filter at least once and will be dynamically replaced by the username for which the search is being run.
|
||||||
// or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters.
|
// E.g. "mail={}" or "&(objectClass=person)(uid={})". For more information about LDAP filters, see
|
||||||
|
// https://ldap.com/ldap-filters.
|
||||||
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
||||||
// Optional. When not specified, the default will act as if the Filter were specified as the value from
|
// Optional. When not specified, the default will act as if the Filter were specified as the value from
|
||||||
// Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be
|
// Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be
|
||||||
@ -85,6 +97,30 @@ type LDAPIdentityProviderUserSearch struct {
|
|||||||
Attributes LDAPIdentityProviderUserSearchAttributes `json:"attributes,omitempty"`
|
Attributes LDAPIdentityProviderUserSearchAttributes `json:"attributes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LDAPIdentityProviderGroupSearch struct {
|
||||||
|
// Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g.
|
||||||
|
// "ou=groups,dc=example,dc=com". When not specified, no group search will be performed and
|
||||||
|
// authenticated users will not belong to any groups from the LDAP provider. Also, when not specified,
|
||||||
|
// the values of Filter and Attributes are ignored.
|
||||||
|
// +optional
|
||||||
|
Base string `json:"base,omitempty"`
|
||||||
|
|
||||||
|
// Filter is the LDAP search filter which should be applied when searching for groups for a user.
|
||||||
|
// The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the
|
||||||
|
// dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or
|
||||||
|
// "&(objectClass=groupOfNames)(member={})". For more information about LDAP filters, see
|
||||||
|
// https://ldap.com/ldap-filters.
|
||||||
|
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
||||||
|
// Optional. When not specified, the default will act as if the Filter were specified as "member={}".
|
||||||
|
// +optional
|
||||||
|
Filter string `json:"filter,omitempty"`
|
||||||
|
|
||||||
|
// Attributes specifies how the group's information should be read from each LDAP entry which was found as
|
||||||
|
// the result of the group search.
|
||||||
|
// +optional
|
||||||
|
Attributes LDAPIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// Spec for configuring an LDAP identity provider.
|
// Spec for configuring an LDAP identity provider.
|
||||||
type LDAPIdentityProviderSpec struct {
|
type LDAPIdentityProviderSpec struct {
|
||||||
// Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636.
|
// Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636.
|
||||||
@ -100,6 +136,9 @@ type LDAPIdentityProviderSpec struct {
|
|||||||
|
|
||||||
// UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
// UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
||||||
UserSearch LDAPIdentityProviderUserSearch `json:"userSearch,omitempty"`
|
UserSearch LDAPIdentityProviderUserSearch `json:"userSearch,omitempty"`
|
||||||
|
|
||||||
|
// GroupSearch contains the configuration for searching for a user's group membership in the LDAP provider.
|
||||||
|
GroupSearch LDAPIdentityProviderGroupSearch `json:"groupSearch,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access
|
// LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access
|
||||||
|
@ -63,13 +63,55 @@ spec:
|
|||||||
LDAP bind user. This account will be used to perform LDAP searches.
|
LDAP bind user. This account will be used to perform LDAP searches.
|
||||||
The Secret should be of type "kubernetes.io/basic-auth" which
|
The Secret should be of type "kubernetes.io/basic-auth" which
|
||||||
includes "username" and "password" keys. The username value
|
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".
|
should be the full dn (distinguished name) of your bind account,
|
||||||
The password must be non-empty.
|
e.g. "cn=bind-account,ou=users,dc=example,dc=com". The password
|
||||||
|
must be non-empty.
|
||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- secretName
|
- secretName
|
||||||
type: object
|
type: object
|
||||||
|
groupSearch:
|
||||||
|
description: GroupSearch contains the configuration for searching
|
||||||
|
for a user's group membership in the LDAP provider.
|
||||||
|
properties:
|
||||||
|
attributes:
|
||||||
|
description: Attributes specifies how the group's information
|
||||||
|
should be read from each LDAP entry which was found as the result
|
||||||
|
of the group search.
|
||||||
|
properties:
|
||||||
|
groupName:
|
||||||
|
description: GroupName specifies the name of the attribute
|
||||||
|
in the LDAP entries whose value shall become a group name
|
||||||
|
in the user's list of groups after a successful authentication.
|
||||||
|
The value of this field is case-sensitive and must match
|
||||||
|
the case of the attribute name returned by the LDAP server
|
||||||
|
in the user's entry. Distinguished names can be used by
|
||||||
|
specifying lower-case "dn". Optional. When not specified,
|
||||||
|
the default will act as if the GroupName were specified
|
||||||
|
as "cn" (common name).
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
base:
|
||||||
|
description: Base is the dn (distinguished name) that should be
|
||||||
|
used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com".
|
||||||
|
When not specified, no group search will be performed and authenticated
|
||||||
|
users will not belong to any groups from the LDAP provider.
|
||||||
|
Also, when not specified, the values of Filter and Attributes
|
||||||
|
are ignored.
|
||||||
|
type: string
|
||||||
|
filter:
|
||||||
|
description: Filter is the LDAP search filter which should be
|
||||||
|
applied when searching for groups for a user. The pattern "{}"
|
||||||
|
must occur in the filter at least once and will be dynamically
|
||||||
|
replaced by the dn (distinguished name) of the user entry found
|
||||||
|
as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})".
|
||||||
|
For more information about LDAP filters, see https://ldap.com/ldap-filters.
|
||||||
|
Note that the dn (distinguished name) is not an attribute of
|
||||||
|
an entry, so "dn={}" cannot be used. Optional. When not specified,
|
||||||
|
the default will act as if the Filter were specified as "member={}".
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
host:
|
host:
|
||||||
description: 'Host is the hostname of this LDAP identity provider,
|
description: 'Host is the hostname of this LDAP identity provider,
|
||||||
i.e., where to connect. For example: ldap.example.com:636.'
|
i.e., where to connect. For example: ldap.example.com:636.'
|
||||||
@ -104,37 +146,39 @@ spec:
|
|||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
username:
|
username:
|
||||||
description: Username specifies the name of attribute in the
|
description: Username specifies the name of the attribute
|
||||||
LDAP entry which whose value shall become the username of
|
in the LDAP entry whose value shall become the username
|
||||||
the user after a successful authentication. This would typically
|
of the user after a successful authentication. This would
|
||||||
be the same attribute name used in the user search filter,
|
typically be the same attribute name used in the user search
|
||||||
although it can be different. E.g. "mail" or "uid" or "userPrincipalName".
|
filter, although it can be different. E.g. "mail" or "uid"
|
||||||
The value of this field is case-sensitive and must match
|
or "userPrincipalName". The value of this field is case-sensitive
|
||||||
the case of the attribute name returned by the LDAP server
|
and must match the case of the attribute name returned by
|
||||||
in the user's entry. Distinguished names can be used by
|
the LDAP server in the user's entry. Distinguished names
|
||||||
specifying lower-case "dn". When this field is set to "dn"
|
can be used by specifying lower-case "dn". When this field
|
||||||
then the LDAPIdentityProviderUserSearch's Filter field cannot
|
is set to "dn" then the LDAPIdentityProviderUserSearch's
|
||||||
be blank, since the default value of "dn={}" would not work.
|
Filter field cannot be blank, since the default value of
|
||||||
|
"dn={}" would not work.
|
||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
base:
|
base:
|
||||||
description: Base is the DN that should be used as the search
|
description: Base is the dn (distinguished name) that should be
|
||||||
base when searching for users. E.g. "ou=users,dc=example,dc=com".
|
used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com".
|
||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
filter:
|
filter:
|
||||||
description: Filter is the LDAP search filter which should be
|
description: Filter is the LDAP search filter which should be
|
||||||
applied when searching for users. The pattern "{}" must occur
|
applied when searching for users. The pattern "{}" must occur
|
||||||
in the filter and will be dynamically replaced by the username
|
in the filter at least once and will be dynamically replaced
|
||||||
for which the search is being run. E.g. "mail={}" or "&(objectClass=person)(uid={})".
|
by the username for which the search is being run. E.g. "mail={}"
|
||||||
For more information about LDAP filters, see https://ldap.com/ldap-filters.
|
or "&(objectClass=person)(uid={})". For more information about
|
||||||
Note that the dn (distinguished name) is not an attribute of
|
LDAP filters, see https://ldap.com/ldap-filters. Note that the
|
||||||
an entry, so "dn={}" cannot be used. Optional. When not specified,
|
dn (distinguished name) is not an attribute of an entry, so
|
||||||
the default will act as if the Filter were specified as the
|
"dn={}" cannot be used. Optional. When not specified, the default
|
||||||
value from Attributes.Username appended by "={}". When the Attributes.Username
|
will act as if the Filter were specified as the value from Attributes.Username
|
||||||
is set to "dn" then the Filter must be explicitly specified,
|
appended by "={}". When the Attributes.Username is set to "dn"
|
||||||
since the default value of "dn={}" would not work.
|
then the Filter must be explicitly specified, since the default
|
||||||
|
value of "dn={}" would not work.
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
|
45
generated/1.17/README.adoc
generated
45
generated/1.17/README.adoc
generated
@ -734,7 +734,43 @@ LDAPIdentityProvider describes the configuration of an upstream Lightweight Dire
|
|||||||
[cols="25a,75a", options="header"]
|
[cols="25a,75a", options="header"]
|
||||||
|===
|
|===
|
||||||
| Field | Description
|
| 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". The password must be non-empty.
|
| *`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 (distinguished name) of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". The password must be non-empty.
|
||||||
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearch"]
|
||||||
|
==== LDAPIdentityProviderGroupSearch
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.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 (distinguished name) that should be used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com". When not specified, no group search will be performed and authenticated users will not belong to any groups from the LDAP provider. Also, when not specified, the values of Filter and Attributes are ignored.
|
||||||
|
| *`filter`* __string__ | Filter is the LDAP search filter which should be applied when searching for groups for a user. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as "member={}".
|
||||||
|
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearchattributes[$$LDAPIdentityProviderGroupSearchAttributes$$]__ | Attributes specifies how the group's information should be read from each LDAP entry which was found as the result of the group search.
|
||||||
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearchattributes"]
|
||||||
|
==== LDAPIdentityProviderGroupSearchAttributes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.Appears In:
|
||||||
|
****
|
||||||
|
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearch[$$LDAPIdentityProviderGroupSearch$$]
|
||||||
|
****
|
||||||
|
|
||||||
|
[cols="25a,75a", options="header"]
|
||||||
|
|===
|
||||||
|
| Field | Description
|
||||||
|
| *`groupName`* __string__ | GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name in the user's list of groups after a successful authentication. The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn". Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
@ -757,6 +793,7 @@ Spec for configuring an LDAP identity provider.
|
|||||||
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$]__ | TLS contains the connection settings for how to establish the connection to the Host.
|
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$]__ | 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-ldapidentityproviderbind[$$LDAPIdentityProviderBind$$]__ | 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`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityproviderbind[$$LDAPIdentityProviderBind$$]__ | 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-ldapidentityproviderusersearch[$$LDAPIdentityProviderUserSearch$$]__ | UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
| *`userSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearch[$$LDAPIdentityProviderUserSearch$$]__ | UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
||||||
|
| *`groupSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearch[$$LDAPIdentityProviderGroupSearch$$]__ | GroupSearch contains the configuration for searching for a user's group membership in the LDAP provider.
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
@ -791,8 +828,8 @@ Status of an LDAP identity provider.
|
|||||||
[cols="25a,75a", options="header"]
|
[cols="25a,75a", options="header"]
|
||||||
|===
|
|===
|
||||||
| Field | Description
|
| 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".
|
| *`base`* __string__ | Base is the dn (distinguished name) 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. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as the value from Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be explicitly specified, since the default value of "dn={}" would not work.
|
| *`filter`* __string__ | Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur in the filter at least once 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. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as the value from Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be explicitly specified, since the default value of "dn={}" would not work.
|
||||||
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchattributes[$$LDAPIdentityProviderUserSearchAttributes$$]__ | Attributes specifies how the user's information should be read from the LDAP entry which was found as the result of the user search.
|
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchattributes[$$LDAPIdentityProviderUserSearchAttributes$$]__ | Attributes specifies how the user's information should be read from the LDAP entry which was found as the result of the user search.
|
||||||
|===
|
|===
|
||||||
|
|
||||||
@ -810,7 +847,7 @@ Status of an LDAP identity provider.
|
|||||||
[cols="25a,75a", options="header"]
|
[cols="25a,75a", options="header"]
|
||||||
|===
|
|===
|
||||||
| Field | Description
|
| 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, although it can be different. E.g. "mail" or "uid" or "userPrincipalName". The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn". When this field is set to "dn" then the LDAPIdentityProviderUserSearch's Filter field cannot be blank, since the default value of "dn={}" would not work.
|
| *`username`* __string__ | Username specifies the name of the attribute in the LDAP entry 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, although it can be different. E.g. "mail" or "uid" or "userPrincipalName". The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn". When this field is set to "dn" then the LDAPIdentityProviderUserSearch's Filter field cannot be blank, since the default value of "dn={}" would not work.
|
||||||
| *`uid`* __string__ | UID 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". The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
| *`uid`* __string__ | UID 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". The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
@ -39,14 +39,14 @@ type LDAPIdentityProviderBind struct {
|
|||||||
// SecretName contains the name of a namespace-local Secret object that provides the username and
|
// 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
|
// 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
|
// 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".
|
// should be the full dn (distinguished name) of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com".
|
||||||
// The password must be non-empty.
|
// The password must be non-empty.
|
||||||
// +kubebuilder:validation:MinLength=1
|
// +kubebuilder:validation:MinLength=1
|
||||||
SecretName string `json:"secretName"`
|
SecretName string `json:"secretName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LDAPIdentityProviderUserSearchAttributes struct {
|
type LDAPIdentityProviderUserSearchAttributes struct {
|
||||||
// Username specifies the name of attribute in the LDAP entry which whose value shall become the username
|
// Username specifies the name of the attribute in the LDAP entry whose value shall become the username
|
||||||
// of the user after a successful authentication. This would typically be the same attribute name used in
|
// of the user after a successful authentication. This would typically be the same attribute name used in
|
||||||
// the user search filter, although it can be different. E.g. "mail" or "uid" or "userPrincipalName".
|
// the user search filter, although it can be different. E.g. "mail" or "uid" or "userPrincipalName".
|
||||||
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||||
@ -64,14 +64,26 @@ type LDAPIdentityProviderUserSearchAttributes struct {
|
|||||||
UID string `json:"uid,omitempty"`
|
UID string `json:"uid,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LDAPIdentityProviderGroupSearchAttributes struct {
|
||||||
|
// GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name
|
||||||
|
// in the user's list of groups after a successful authentication.
|
||||||
|
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||||
|
// server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
||||||
|
// Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||||
|
// +optional
|
||||||
|
GroupName string `json:"groupName,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type LDAPIdentityProviderUserSearch struct {
|
type LDAPIdentityProviderUserSearch 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".
|
// Base is the dn (distinguished name) that should be used as the search base when searching for users.
|
||||||
|
// E.g. "ou=users,dc=example,dc=com".
|
||||||
// +kubebuilder:validation:MinLength=1
|
// +kubebuilder:validation:MinLength=1
|
||||||
Base string `json:"base,omitempty"`
|
Base string `json:"base,omitempty"`
|
||||||
|
|
||||||
// Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur
|
// 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={}"
|
// in the filter at least once and will be dynamically replaced by the username for which the search is being run.
|
||||||
// or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters.
|
// E.g. "mail={}" or "&(objectClass=person)(uid={})". For more information about LDAP filters, see
|
||||||
|
// https://ldap.com/ldap-filters.
|
||||||
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
||||||
// Optional. When not specified, the default will act as if the Filter were specified as the value from
|
// Optional. When not specified, the default will act as if the Filter were specified as the value from
|
||||||
// Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be
|
// Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be
|
||||||
@ -85,6 +97,30 @@ type LDAPIdentityProviderUserSearch struct {
|
|||||||
Attributes LDAPIdentityProviderUserSearchAttributes `json:"attributes,omitempty"`
|
Attributes LDAPIdentityProviderUserSearchAttributes `json:"attributes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LDAPIdentityProviderGroupSearch struct {
|
||||||
|
// Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g.
|
||||||
|
// "ou=groups,dc=example,dc=com". When not specified, no group search will be performed and
|
||||||
|
// authenticated users will not belong to any groups from the LDAP provider. Also, when not specified,
|
||||||
|
// the values of Filter and Attributes are ignored.
|
||||||
|
// +optional
|
||||||
|
Base string `json:"base,omitempty"`
|
||||||
|
|
||||||
|
// Filter is the LDAP search filter which should be applied when searching for groups for a user.
|
||||||
|
// The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the
|
||||||
|
// dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or
|
||||||
|
// "&(objectClass=groupOfNames)(member={})". For more information about LDAP filters, see
|
||||||
|
// https://ldap.com/ldap-filters.
|
||||||
|
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
||||||
|
// Optional. When not specified, the default will act as if the Filter were specified as "member={}".
|
||||||
|
// +optional
|
||||||
|
Filter string `json:"filter,omitempty"`
|
||||||
|
|
||||||
|
// Attributes specifies how the group's information should be read from each LDAP entry which was found as
|
||||||
|
// the result of the group search.
|
||||||
|
// +optional
|
||||||
|
Attributes LDAPIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// Spec for configuring an LDAP identity provider.
|
// Spec for configuring an LDAP identity provider.
|
||||||
type LDAPIdentityProviderSpec struct {
|
type LDAPIdentityProviderSpec struct {
|
||||||
// Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636.
|
// Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636.
|
||||||
@ -100,6 +136,9 @@ type LDAPIdentityProviderSpec struct {
|
|||||||
|
|
||||||
// UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
// UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
||||||
UserSearch LDAPIdentityProviderUserSearch `json:"userSearch,omitempty"`
|
UserSearch LDAPIdentityProviderUserSearch `json:"userSearch,omitempty"`
|
||||||
|
|
||||||
|
// GroupSearch contains the configuration for searching for a user's group membership in the LDAP provider.
|
||||||
|
GroupSearch LDAPIdentityProviderGroupSearch `json:"groupSearch,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access
|
// LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access
|
||||||
|
@ -72,6 +72,39 @@ func (in *LDAPIdentityProviderBind) DeepCopy() *LDAPIdentityProviderBind {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearch) DeepCopyInto(out *LDAPIdentityProviderGroupSearch) {
|
||||||
|
*out = *in
|
||||||
|
out.Attributes = in.Attributes
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderGroupSearch.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearch) DeepCopy() *LDAPIdentityProviderGroupSearch {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(LDAPIdentityProviderGroupSearch)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearchAttributes) DeepCopyInto(out *LDAPIdentityProviderGroupSearchAttributes) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderGroupSearchAttributes.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearchAttributes) DeepCopy() *LDAPIdentityProviderGroupSearchAttributes {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(LDAPIdentityProviderGroupSearchAttributes)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *LDAPIdentityProviderList) DeepCopyInto(out *LDAPIdentityProviderList) {
|
func (in *LDAPIdentityProviderList) DeepCopyInto(out *LDAPIdentityProviderList) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@ -115,6 +148,7 @@ func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec)
|
|||||||
}
|
}
|
||||||
out.Bind = in.Bind
|
out.Bind = in.Bind
|
||||||
out.UserSearch = in.UserSearch
|
out.UserSearch = in.UserSearch
|
||||||
|
out.GroupSearch = in.GroupSearch
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,13 +63,55 @@ spec:
|
|||||||
LDAP bind user. This account will be used to perform LDAP searches.
|
LDAP bind user. This account will be used to perform LDAP searches.
|
||||||
The Secret should be of type "kubernetes.io/basic-auth" which
|
The Secret should be of type "kubernetes.io/basic-auth" which
|
||||||
includes "username" and "password" keys. The username value
|
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".
|
should be the full dn (distinguished name) of your bind account,
|
||||||
The password must be non-empty.
|
e.g. "cn=bind-account,ou=users,dc=example,dc=com". The password
|
||||||
|
must be non-empty.
|
||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- secretName
|
- secretName
|
||||||
type: object
|
type: object
|
||||||
|
groupSearch:
|
||||||
|
description: GroupSearch contains the configuration for searching
|
||||||
|
for a user's group membership in the LDAP provider.
|
||||||
|
properties:
|
||||||
|
attributes:
|
||||||
|
description: Attributes specifies how the group's information
|
||||||
|
should be read from each LDAP entry which was found as the result
|
||||||
|
of the group search.
|
||||||
|
properties:
|
||||||
|
groupName:
|
||||||
|
description: GroupName specifies the name of the attribute
|
||||||
|
in the LDAP entries whose value shall become a group name
|
||||||
|
in the user's list of groups after a successful authentication.
|
||||||
|
The value of this field is case-sensitive and must match
|
||||||
|
the case of the attribute name returned by the LDAP server
|
||||||
|
in the user's entry. Distinguished names can be used by
|
||||||
|
specifying lower-case "dn". Optional. When not specified,
|
||||||
|
the default will act as if the GroupName were specified
|
||||||
|
as "cn" (common name).
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
base:
|
||||||
|
description: Base is the dn (distinguished name) that should be
|
||||||
|
used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com".
|
||||||
|
When not specified, no group search will be performed and authenticated
|
||||||
|
users will not belong to any groups from the LDAP provider.
|
||||||
|
Also, when not specified, the values of Filter and Attributes
|
||||||
|
are ignored.
|
||||||
|
type: string
|
||||||
|
filter:
|
||||||
|
description: Filter is the LDAP search filter which should be
|
||||||
|
applied when searching for groups for a user. The pattern "{}"
|
||||||
|
must occur in the filter at least once and will be dynamically
|
||||||
|
replaced by the dn (distinguished name) of the user entry found
|
||||||
|
as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})".
|
||||||
|
For more information about LDAP filters, see https://ldap.com/ldap-filters.
|
||||||
|
Note that the dn (distinguished name) is not an attribute of
|
||||||
|
an entry, so "dn={}" cannot be used. Optional. When not specified,
|
||||||
|
the default will act as if the Filter were specified as "member={}".
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
host:
|
host:
|
||||||
description: 'Host is the hostname of this LDAP identity provider,
|
description: 'Host is the hostname of this LDAP identity provider,
|
||||||
i.e., where to connect. For example: ldap.example.com:636.'
|
i.e., where to connect. For example: ldap.example.com:636.'
|
||||||
@ -104,37 +146,39 @@ spec:
|
|||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
username:
|
username:
|
||||||
description: Username specifies the name of attribute in the
|
description: Username specifies the name of the attribute
|
||||||
LDAP entry which whose value shall become the username of
|
in the LDAP entry whose value shall become the username
|
||||||
the user after a successful authentication. This would typically
|
of the user after a successful authentication. This would
|
||||||
be the same attribute name used in the user search filter,
|
typically be the same attribute name used in the user search
|
||||||
although it can be different. E.g. "mail" or "uid" or "userPrincipalName".
|
filter, although it can be different. E.g. "mail" or "uid"
|
||||||
The value of this field is case-sensitive and must match
|
or "userPrincipalName". The value of this field is case-sensitive
|
||||||
the case of the attribute name returned by the LDAP server
|
and must match the case of the attribute name returned by
|
||||||
in the user's entry. Distinguished names can be used by
|
the LDAP server in the user's entry. Distinguished names
|
||||||
specifying lower-case "dn". When this field is set to "dn"
|
can be used by specifying lower-case "dn". When this field
|
||||||
then the LDAPIdentityProviderUserSearch's Filter field cannot
|
is set to "dn" then the LDAPIdentityProviderUserSearch's
|
||||||
be blank, since the default value of "dn={}" would not work.
|
Filter field cannot be blank, since the default value of
|
||||||
|
"dn={}" would not work.
|
||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
base:
|
base:
|
||||||
description: Base is the DN that should be used as the search
|
description: Base is the dn (distinguished name) that should be
|
||||||
base when searching for users. E.g. "ou=users,dc=example,dc=com".
|
used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com".
|
||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
filter:
|
filter:
|
||||||
description: Filter is the LDAP search filter which should be
|
description: Filter is the LDAP search filter which should be
|
||||||
applied when searching for users. The pattern "{}" must occur
|
applied when searching for users. The pattern "{}" must occur
|
||||||
in the filter and will be dynamically replaced by the username
|
in the filter at least once and will be dynamically replaced
|
||||||
for which the search is being run. E.g. "mail={}" or "&(objectClass=person)(uid={})".
|
by the username for which the search is being run. E.g. "mail={}"
|
||||||
For more information about LDAP filters, see https://ldap.com/ldap-filters.
|
or "&(objectClass=person)(uid={})". For more information about
|
||||||
Note that the dn (distinguished name) is not an attribute of
|
LDAP filters, see https://ldap.com/ldap-filters. Note that the
|
||||||
an entry, so "dn={}" cannot be used. Optional. When not specified,
|
dn (distinguished name) is not an attribute of an entry, so
|
||||||
the default will act as if the Filter were specified as the
|
"dn={}" cannot be used. Optional. When not specified, the default
|
||||||
value from Attributes.Username appended by "={}". When the Attributes.Username
|
will act as if the Filter were specified as the value from Attributes.Username
|
||||||
is set to "dn" then the Filter must be explicitly specified,
|
appended by "={}". When the Attributes.Username is set to "dn"
|
||||||
since the default value of "dn={}" would not work.
|
then the Filter must be explicitly specified, since the default
|
||||||
|
value of "dn={}" would not work.
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
|
45
generated/1.18/README.adoc
generated
45
generated/1.18/README.adoc
generated
@ -734,7 +734,43 @@ LDAPIdentityProvider describes the configuration of an upstream Lightweight Dire
|
|||||||
[cols="25a,75a", options="header"]
|
[cols="25a,75a", options="header"]
|
||||||
|===
|
|===
|
||||||
| Field | Description
|
| 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". The password must be non-empty.
|
| *`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 (distinguished name) of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". The password must be non-empty.
|
||||||
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearch"]
|
||||||
|
==== LDAPIdentityProviderGroupSearch
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.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 (distinguished name) that should be used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com". When not specified, no group search will be performed and authenticated users will not belong to any groups from the LDAP provider. Also, when not specified, the values of Filter and Attributes are ignored.
|
||||||
|
| *`filter`* __string__ | Filter is the LDAP search filter which should be applied when searching for groups for a user. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as "member={}".
|
||||||
|
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearchattributes[$$LDAPIdentityProviderGroupSearchAttributes$$]__ | Attributes specifies how the group's information should be read from each LDAP entry which was found as the result of the group search.
|
||||||
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearchattributes"]
|
||||||
|
==== LDAPIdentityProviderGroupSearchAttributes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.Appears In:
|
||||||
|
****
|
||||||
|
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearch[$$LDAPIdentityProviderGroupSearch$$]
|
||||||
|
****
|
||||||
|
|
||||||
|
[cols="25a,75a", options="header"]
|
||||||
|
|===
|
||||||
|
| Field | Description
|
||||||
|
| *`groupName`* __string__ | GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name in the user's list of groups after a successful authentication. The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn". Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
@ -757,6 +793,7 @@ Spec for configuring an LDAP identity provider.
|
|||||||
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$]__ | TLS contains the connection settings for how to establish the connection to the Host.
|
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$]__ | 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-ldapidentityproviderbind[$$LDAPIdentityProviderBind$$]__ | 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`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityproviderbind[$$LDAPIdentityProviderBind$$]__ | 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-ldapidentityproviderusersearch[$$LDAPIdentityProviderUserSearch$$]__ | UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
| *`userSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearch[$$LDAPIdentityProviderUserSearch$$]__ | UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
||||||
|
| *`groupSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearch[$$LDAPIdentityProviderGroupSearch$$]__ | GroupSearch contains the configuration for searching for a user's group membership in the LDAP provider.
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
@ -791,8 +828,8 @@ Status of an LDAP identity provider.
|
|||||||
[cols="25a,75a", options="header"]
|
[cols="25a,75a", options="header"]
|
||||||
|===
|
|===
|
||||||
| Field | Description
|
| 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".
|
| *`base`* __string__ | Base is the dn (distinguished name) 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. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as the value from Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be explicitly specified, since the default value of "dn={}" would not work.
|
| *`filter`* __string__ | Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur in the filter at least once 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. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as the value from Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be explicitly specified, since the default value of "dn={}" would not work.
|
||||||
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchattributes[$$LDAPIdentityProviderUserSearchAttributes$$]__ | Attributes specifies how the user's information should be read from the LDAP entry which was found as the result of the user search.
|
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchattributes[$$LDAPIdentityProviderUserSearchAttributes$$]__ | Attributes specifies how the user's information should be read from the LDAP entry which was found as the result of the user search.
|
||||||
|===
|
|===
|
||||||
|
|
||||||
@ -810,7 +847,7 @@ Status of an LDAP identity provider.
|
|||||||
[cols="25a,75a", options="header"]
|
[cols="25a,75a", options="header"]
|
||||||
|===
|
|===
|
||||||
| Field | Description
|
| 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, although it can be different. E.g. "mail" or "uid" or "userPrincipalName". The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn". When this field is set to "dn" then the LDAPIdentityProviderUserSearch's Filter field cannot be blank, since the default value of "dn={}" would not work.
|
| *`username`* __string__ | Username specifies the name of the attribute in the LDAP entry 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, although it can be different. E.g. "mail" or "uid" or "userPrincipalName". The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn". When this field is set to "dn" then the LDAPIdentityProviderUserSearch's Filter field cannot be blank, since the default value of "dn={}" would not work.
|
||||||
| *`uid`* __string__ | UID 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". The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
| *`uid`* __string__ | UID 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". The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
@ -39,14 +39,14 @@ type LDAPIdentityProviderBind struct {
|
|||||||
// SecretName contains the name of a namespace-local Secret object that provides the username and
|
// 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
|
// 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
|
// 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".
|
// should be the full dn (distinguished name) of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com".
|
||||||
// The password must be non-empty.
|
// The password must be non-empty.
|
||||||
// +kubebuilder:validation:MinLength=1
|
// +kubebuilder:validation:MinLength=1
|
||||||
SecretName string `json:"secretName"`
|
SecretName string `json:"secretName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LDAPIdentityProviderUserSearchAttributes struct {
|
type LDAPIdentityProviderUserSearchAttributes struct {
|
||||||
// Username specifies the name of attribute in the LDAP entry which whose value shall become the username
|
// Username specifies the name of the attribute in the LDAP entry whose value shall become the username
|
||||||
// of the user after a successful authentication. This would typically be the same attribute name used in
|
// of the user after a successful authentication. This would typically be the same attribute name used in
|
||||||
// the user search filter, although it can be different. E.g. "mail" or "uid" or "userPrincipalName".
|
// the user search filter, although it can be different. E.g. "mail" or "uid" or "userPrincipalName".
|
||||||
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||||
@ -64,14 +64,26 @@ type LDAPIdentityProviderUserSearchAttributes struct {
|
|||||||
UID string `json:"uid,omitempty"`
|
UID string `json:"uid,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LDAPIdentityProviderGroupSearchAttributes struct {
|
||||||
|
// GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name
|
||||||
|
// in the user's list of groups after a successful authentication.
|
||||||
|
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||||
|
// server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
||||||
|
// Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||||
|
// +optional
|
||||||
|
GroupName string `json:"groupName,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type LDAPIdentityProviderUserSearch struct {
|
type LDAPIdentityProviderUserSearch 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".
|
// Base is the dn (distinguished name) that should be used as the search base when searching for users.
|
||||||
|
// E.g. "ou=users,dc=example,dc=com".
|
||||||
// +kubebuilder:validation:MinLength=1
|
// +kubebuilder:validation:MinLength=1
|
||||||
Base string `json:"base,omitempty"`
|
Base string `json:"base,omitempty"`
|
||||||
|
|
||||||
// Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur
|
// 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={}"
|
// in the filter at least once and will be dynamically replaced by the username for which the search is being run.
|
||||||
// or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters.
|
// E.g. "mail={}" or "&(objectClass=person)(uid={})". For more information about LDAP filters, see
|
||||||
|
// https://ldap.com/ldap-filters.
|
||||||
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
||||||
// Optional. When not specified, the default will act as if the Filter were specified as the value from
|
// Optional. When not specified, the default will act as if the Filter were specified as the value from
|
||||||
// Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be
|
// Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be
|
||||||
@ -85,6 +97,30 @@ type LDAPIdentityProviderUserSearch struct {
|
|||||||
Attributes LDAPIdentityProviderUserSearchAttributes `json:"attributes,omitempty"`
|
Attributes LDAPIdentityProviderUserSearchAttributes `json:"attributes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LDAPIdentityProviderGroupSearch struct {
|
||||||
|
// Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g.
|
||||||
|
// "ou=groups,dc=example,dc=com". When not specified, no group search will be performed and
|
||||||
|
// authenticated users will not belong to any groups from the LDAP provider. Also, when not specified,
|
||||||
|
// the values of Filter and Attributes are ignored.
|
||||||
|
// +optional
|
||||||
|
Base string `json:"base,omitempty"`
|
||||||
|
|
||||||
|
// Filter is the LDAP search filter which should be applied when searching for groups for a user.
|
||||||
|
// The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the
|
||||||
|
// dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or
|
||||||
|
// "&(objectClass=groupOfNames)(member={})". For more information about LDAP filters, see
|
||||||
|
// https://ldap.com/ldap-filters.
|
||||||
|
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
||||||
|
// Optional. When not specified, the default will act as if the Filter were specified as "member={}".
|
||||||
|
// +optional
|
||||||
|
Filter string `json:"filter,omitempty"`
|
||||||
|
|
||||||
|
// Attributes specifies how the group's information should be read from each LDAP entry which was found as
|
||||||
|
// the result of the group search.
|
||||||
|
// +optional
|
||||||
|
Attributes LDAPIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// Spec for configuring an LDAP identity provider.
|
// Spec for configuring an LDAP identity provider.
|
||||||
type LDAPIdentityProviderSpec struct {
|
type LDAPIdentityProviderSpec struct {
|
||||||
// Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636.
|
// Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636.
|
||||||
@ -100,6 +136,9 @@ type LDAPIdentityProviderSpec struct {
|
|||||||
|
|
||||||
// UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
// UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
||||||
UserSearch LDAPIdentityProviderUserSearch `json:"userSearch,omitempty"`
|
UserSearch LDAPIdentityProviderUserSearch `json:"userSearch,omitempty"`
|
||||||
|
|
||||||
|
// GroupSearch contains the configuration for searching for a user's group membership in the LDAP provider.
|
||||||
|
GroupSearch LDAPIdentityProviderGroupSearch `json:"groupSearch,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access
|
// LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access
|
||||||
|
@ -72,6 +72,39 @@ func (in *LDAPIdentityProviderBind) DeepCopy() *LDAPIdentityProviderBind {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearch) DeepCopyInto(out *LDAPIdentityProviderGroupSearch) {
|
||||||
|
*out = *in
|
||||||
|
out.Attributes = in.Attributes
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderGroupSearch.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearch) DeepCopy() *LDAPIdentityProviderGroupSearch {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(LDAPIdentityProviderGroupSearch)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearchAttributes) DeepCopyInto(out *LDAPIdentityProviderGroupSearchAttributes) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderGroupSearchAttributes.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearchAttributes) DeepCopy() *LDAPIdentityProviderGroupSearchAttributes {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(LDAPIdentityProviderGroupSearchAttributes)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *LDAPIdentityProviderList) DeepCopyInto(out *LDAPIdentityProviderList) {
|
func (in *LDAPIdentityProviderList) DeepCopyInto(out *LDAPIdentityProviderList) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@ -115,6 +148,7 @@ func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec)
|
|||||||
}
|
}
|
||||||
out.Bind = in.Bind
|
out.Bind = in.Bind
|
||||||
out.UserSearch = in.UserSearch
|
out.UserSearch = in.UserSearch
|
||||||
|
out.GroupSearch = in.GroupSearch
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,13 +63,55 @@ spec:
|
|||||||
LDAP bind user. This account will be used to perform LDAP searches.
|
LDAP bind user. This account will be used to perform LDAP searches.
|
||||||
The Secret should be of type "kubernetes.io/basic-auth" which
|
The Secret should be of type "kubernetes.io/basic-auth" which
|
||||||
includes "username" and "password" keys. The username value
|
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".
|
should be the full dn (distinguished name) of your bind account,
|
||||||
The password must be non-empty.
|
e.g. "cn=bind-account,ou=users,dc=example,dc=com". The password
|
||||||
|
must be non-empty.
|
||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- secretName
|
- secretName
|
||||||
type: object
|
type: object
|
||||||
|
groupSearch:
|
||||||
|
description: GroupSearch contains the configuration for searching
|
||||||
|
for a user's group membership in the LDAP provider.
|
||||||
|
properties:
|
||||||
|
attributes:
|
||||||
|
description: Attributes specifies how the group's information
|
||||||
|
should be read from each LDAP entry which was found as the result
|
||||||
|
of the group search.
|
||||||
|
properties:
|
||||||
|
groupName:
|
||||||
|
description: GroupName specifies the name of the attribute
|
||||||
|
in the LDAP entries whose value shall become a group name
|
||||||
|
in the user's list of groups after a successful authentication.
|
||||||
|
The value of this field is case-sensitive and must match
|
||||||
|
the case of the attribute name returned by the LDAP server
|
||||||
|
in the user's entry. Distinguished names can be used by
|
||||||
|
specifying lower-case "dn". Optional. When not specified,
|
||||||
|
the default will act as if the GroupName were specified
|
||||||
|
as "cn" (common name).
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
base:
|
||||||
|
description: Base is the dn (distinguished name) that should be
|
||||||
|
used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com".
|
||||||
|
When not specified, no group search will be performed and authenticated
|
||||||
|
users will not belong to any groups from the LDAP provider.
|
||||||
|
Also, when not specified, the values of Filter and Attributes
|
||||||
|
are ignored.
|
||||||
|
type: string
|
||||||
|
filter:
|
||||||
|
description: Filter is the LDAP search filter which should be
|
||||||
|
applied when searching for groups for a user. The pattern "{}"
|
||||||
|
must occur in the filter at least once and will be dynamically
|
||||||
|
replaced by the dn (distinguished name) of the user entry found
|
||||||
|
as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})".
|
||||||
|
For more information about LDAP filters, see https://ldap.com/ldap-filters.
|
||||||
|
Note that the dn (distinguished name) is not an attribute of
|
||||||
|
an entry, so "dn={}" cannot be used. Optional. When not specified,
|
||||||
|
the default will act as if the Filter were specified as "member={}".
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
host:
|
host:
|
||||||
description: 'Host is the hostname of this LDAP identity provider,
|
description: 'Host is the hostname of this LDAP identity provider,
|
||||||
i.e., where to connect. For example: ldap.example.com:636.'
|
i.e., where to connect. For example: ldap.example.com:636.'
|
||||||
@ -104,37 +146,39 @@ spec:
|
|||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
username:
|
username:
|
||||||
description: Username specifies the name of attribute in the
|
description: Username specifies the name of the attribute
|
||||||
LDAP entry which whose value shall become the username of
|
in the LDAP entry whose value shall become the username
|
||||||
the user after a successful authentication. This would typically
|
of the user after a successful authentication. This would
|
||||||
be the same attribute name used in the user search filter,
|
typically be the same attribute name used in the user search
|
||||||
although it can be different. E.g. "mail" or "uid" or "userPrincipalName".
|
filter, although it can be different. E.g. "mail" or "uid"
|
||||||
The value of this field is case-sensitive and must match
|
or "userPrincipalName". The value of this field is case-sensitive
|
||||||
the case of the attribute name returned by the LDAP server
|
and must match the case of the attribute name returned by
|
||||||
in the user's entry. Distinguished names can be used by
|
the LDAP server in the user's entry. Distinguished names
|
||||||
specifying lower-case "dn". When this field is set to "dn"
|
can be used by specifying lower-case "dn". When this field
|
||||||
then the LDAPIdentityProviderUserSearch's Filter field cannot
|
is set to "dn" then the LDAPIdentityProviderUserSearch's
|
||||||
be blank, since the default value of "dn={}" would not work.
|
Filter field cannot be blank, since the default value of
|
||||||
|
"dn={}" would not work.
|
||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
base:
|
base:
|
||||||
description: Base is the DN that should be used as the search
|
description: Base is the dn (distinguished name) that should be
|
||||||
base when searching for users. E.g. "ou=users,dc=example,dc=com".
|
used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com".
|
||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
filter:
|
filter:
|
||||||
description: Filter is the LDAP search filter which should be
|
description: Filter is the LDAP search filter which should be
|
||||||
applied when searching for users. The pattern "{}" must occur
|
applied when searching for users. The pattern "{}" must occur
|
||||||
in the filter and will be dynamically replaced by the username
|
in the filter at least once and will be dynamically replaced
|
||||||
for which the search is being run. E.g. "mail={}" or "&(objectClass=person)(uid={})".
|
by the username for which the search is being run. E.g. "mail={}"
|
||||||
For more information about LDAP filters, see https://ldap.com/ldap-filters.
|
or "&(objectClass=person)(uid={})". For more information about
|
||||||
Note that the dn (distinguished name) is not an attribute of
|
LDAP filters, see https://ldap.com/ldap-filters. Note that the
|
||||||
an entry, so "dn={}" cannot be used. Optional. When not specified,
|
dn (distinguished name) is not an attribute of an entry, so
|
||||||
the default will act as if the Filter were specified as the
|
"dn={}" cannot be used. Optional. When not specified, the default
|
||||||
value from Attributes.Username appended by "={}". When the Attributes.Username
|
will act as if the Filter were specified as the value from Attributes.Username
|
||||||
is set to "dn" then the Filter must be explicitly specified,
|
appended by "={}". When the Attributes.Username is set to "dn"
|
||||||
since the default value of "dn={}" would not work.
|
then the Filter must be explicitly specified, since the default
|
||||||
|
value of "dn={}" would not work.
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
|
45
generated/1.19/README.adoc
generated
45
generated/1.19/README.adoc
generated
@ -734,7 +734,43 @@ LDAPIdentityProvider describes the configuration of an upstream Lightweight Dire
|
|||||||
[cols="25a,75a", options="header"]
|
[cols="25a,75a", options="header"]
|
||||||
|===
|
|===
|
||||||
| Field | Description
|
| 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". The password must be non-empty.
|
| *`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 (distinguished name) of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". The password must be non-empty.
|
||||||
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearch"]
|
||||||
|
==== LDAPIdentityProviderGroupSearch
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.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 (distinguished name) that should be used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com". When not specified, no group search will be performed and authenticated users will not belong to any groups from the LDAP provider. Also, when not specified, the values of Filter and Attributes are ignored.
|
||||||
|
| *`filter`* __string__ | Filter is the LDAP search filter which should be applied when searching for groups for a user. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as "member={}".
|
||||||
|
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearchattributes[$$LDAPIdentityProviderGroupSearchAttributes$$]__ | Attributes specifies how the group's information should be read from each LDAP entry which was found as the result of the group search.
|
||||||
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearchattributes"]
|
||||||
|
==== LDAPIdentityProviderGroupSearchAttributes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.Appears In:
|
||||||
|
****
|
||||||
|
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearch[$$LDAPIdentityProviderGroupSearch$$]
|
||||||
|
****
|
||||||
|
|
||||||
|
[cols="25a,75a", options="header"]
|
||||||
|
|===
|
||||||
|
| Field | Description
|
||||||
|
| *`groupName`* __string__ | GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name in the user's list of groups after a successful authentication. The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn". Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
@ -757,6 +793,7 @@ Spec for configuring an LDAP identity provider.
|
|||||||
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$]__ | TLS contains the connection settings for how to establish the connection to the Host.
|
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$]__ | 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-ldapidentityproviderbind[$$LDAPIdentityProviderBind$$]__ | 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`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityproviderbind[$$LDAPIdentityProviderBind$$]__ | 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-ldapidentityproviderusersearch[$$LDAPIdentityProviderUserSearch$$]__ | UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
| *`userSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearch[$$LDAPIdentityProviderUserSearch$$]__ | UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
||||||
|
| *`groupSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearch[$$LDAPIdentityProviderGroupSearch$$]__ | GroupSearch contains the configuration for searching for a user's group membership in the LDAP provider.
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
@ -791,8 +828,8 @@ Status of an LDAP identity provider.
|
|||||||
[cols="25a,75a", options="header"]
|
[cols="25a,75a", options="header"]
|
||||||
|===
|
|===
|
||||||
| Field | Description
|
| 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".
|
| *`base`* __string__ | Base is the dn (distinguished name) 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. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as the value from Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be explicitly specified, since the default value of "dn={}" would not work.
|
| *`filter`* __string__ | Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur in the filter at least once 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. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as the value from Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be explicitly specified, since the default value of "dn={}" would not work.
|
||||||
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchattributes[$$LDAPIdentityProviderUserSearchAttributes$$]__ | Attributes specifies how the user's information should be read from the LDAP entry which was found as the result of the user search.
|
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchattributes[$$LDAPIdentityProviderUserSearchAttributes$$]__ | Attributes specifies how the user's information should be read from the LDAP entry which was found as the result of the user search.
|
||||||
|===
|
|===
|
||||||
|
|
||||||
@ -810,7 +847,7 @@ Status of an LDAP identity provider.
|
|||||||
[cols="25a,75a", options="header"]
|
[cols="25a,75a", options="header"]
|
||||||
|===
|
|===
|
||||||
| Field | Description
|
| 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, although it can be different. E.g. "mail" or "uid" or "userPrincipalName". The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn". When this field is set to "dn" then the LDAPIdentityProviderUserSearch's Filter field cannot be blank, since the default value of "dn={}" would not work.
|
| *`username`* __string__ | Username specifies the name of the attribute in the LDAP entry 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, although it can be different. E.g. "mail" or "uid" or "userPrincipalName". The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn". When this field is set to "dn" then the LDAPIdentityProviderUserSearch's Filter field cannot be blank, since the default value of "dn={}" would not work.
|
||||||
| *`uid`* __string__ | UID 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". The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
| *`uid`* __string__ | UID 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". The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
@ -39,14 +39,14 @@ type LDAPIdentityProviderBind struct {
|
|||||||
// SecretName contains the name of a namespace-local Secret object that provides the username and
|
// 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
|
// 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
|
// 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".
|
// should be the full dn (distinguished name) of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com".
|
||||||
// The password must be non-empty.
|
// The password must be non-empty.
|
||||||
// +kubebuilder:validation:MinLength=1
|
// +kubebuilder:validation:MinLength=1
|
||||||
SecretName string `json:"secretName"`
|
SecretName string `json:"secretName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LDAPIdentityProviderUserSearchAttributes struct {
|
type LDAPIdentityProviderUserSearchAttributes struct {
|
||||||
// Username specifies the name of attribute in the LDAP entry which whose value shall become the username
|
// Username specifies the name of the attribute in the LDAP entry whose value shall become the username
|
||||||
// of the user after a successful authentication. This would typically be the same attribute name used in
|
// of the user after a successful authentication. This would typically be the same attribute name used in
|
||||||
// the user search filter, although it can be different. E.g. "mail" or "uid" or "userPrincipalName".
|
// the user search filter, although it can be different. E.g. "mail" or "uid" or "userPrincipalName".
|
||||||
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||||
@ -64,14 +64,26 @@ type LDAPIdentityProviderUserSearchAttributes struct {
|
|||||||
UID string `json:"uid,omitempty"`
|
UID string `json:"uid,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LDAPIdentityProviderGroupSearchAttributes struct {
|
||||||
|
// GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name
|
||||||
|
// in the user's list of groups after a successful authentication.
|
||||||
|
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||||
|
// server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
||||||
|
// Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||||
|
// +optional
|
||||||
|
GroupName string `json:"groupName,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type LDAPIdentityProviderUserSearch struct {
|
type LDAPIdentityProviderUserSearch 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".
|
// Base is the dn (distinguished name) that should be used as the search base when searching for users.
|
||||||
|
// E.g. "ou=users,dc=example,dc=com".
|
||||||
// +kubebuilder:validation:MinLength=1
|
// +kubebuilder:validation:MinLength=1
|
||||||
Base string `json:"base,omitempty"`
|
Base string `json:"base,omitempty"`
|
||||||
|
|
||||||
// Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur
|
// 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={}"
|
// in the filter at least once and will be dynamically replaced by the username for which the search is being run.
|
||||||
// or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters.
|
// E.g. "mail={}" or "&(objectClass=person)(uid={})". For more information about LDAP filters, see
|
||||||
|
// https://ldap.com/ldap-filters.
|
||||||
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
||||||
// Optional. When not specified, the default will act as if the Filter were specified as the value from
|
// Optional. When not specified, the default will act as if the Filter were specified as the value from
|
||||||
// Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be
|
// Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be
|
||||||
@ -85,6 +97,30 @@ type LDAPIdentityProviderUserSearch struct {
|
|||||||
Attributes LDAPIdentityProviderUserSearchAttributes `json:"attributes,omitempty"`
|
Attributes LDAPIdentityProviderUserSearchAttributes `json:"attributes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LDAPIdentityProviderGroupSearch struct {
|
||||||
|
// Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g.
|
||||||
|
// "ou=groups,dc=example,dc=com". When not specified, no group search will be performed and
|
||||||
|
// authenticated users will not belong to any groups from the LDAP provider. Also, when not specified,
|
||||||
|
// the values of Filter and Attributes are ignored.
|
||||||
|
// +optional
|
||||||
|
Base string `json:"base,omitempty"`
|
||||||
|
|
||||||
|
// Filter is the LDAP search filter which should be applied when searching for groups for a user.
|
||||||
|
// The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the
|
||||||
|
// dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or
|
||||||
|
// "&(objectClass=groupOfNames)(member={})". For more information about LDAP filters, see
|
||||||
|
// https://ldap.com/ldap-filters.
|
||||||
|
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
||||||
|
// Optional. When not specified, the default will act as if the Filter were specified as "member={}".
|
||||||
|
// +optional
|
||||||
|
Filter string `json:"filter,omitempty"`
|
||||||
|
|
||||||
|
// Attributes specifies how the group's information should be read from each LDAP entry which was found as
|
||||||
|
// the result of the group search.
|
||||||
|
// +optional
|
||||||
|
Attributes LDAPIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// Spec for configuring an LDAP identity provider.
|
// Spec for configuring an LDAP identity provider.
|
||||||
type LDAPIdentityProviderSpec struct {
|
type LDAPIdentityProviderSpec struct {
|
||||||
// Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636.
|
// Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636.
|
||||||
@ -100,6 +136,9 @@ type LDAPIdentityProviderSpec struct {
|
|||||||
|
|
||||||
// UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
// UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
||||||
UserSearch LDAPIdentityProviderUserSearch `json:"userSearch,omitempty"`
|
UserSearch LDAPIdentityProviderUserSearch `json:"userSearch,omitempty"`
|
||||||
|
|
||||||
|
// GroupSearch contains the configuration for searching for a user's group membership in the LDAP provider.
|
||||||
|
GroupSearch LDAPIdentityProviderGroupSearch `json:"groupSearch,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access
|
// LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access
|
||||||
|
@ -72,6 +72,39 @@ func (in *LDAPIdentityProviderBind) DeepCopy() *LDAPIdentityProviderBind {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearch) DeepCopyInto(out *LDAPIdentityProviderGroupSearch) {
|
||||||
|
*out = *in
|
||||||
|
out.Attributes = in.Attributes
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderGroupSearch.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearch) DeepCopy() *LDAPIdentityProviderGroupSearch {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(LDAPIdentityProviderGroupSearch)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearchAttributes) DeepCopyInto(out *LDAPIdentityProviderGroupSearchAttributes) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderGroupSearchAttributes.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearchAttributes) DeepCopy() *LDAPIdentityProviderGroupSearchAttributes {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(LDAPIdentityProviderGroupSearchAttributes)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *LDAPIdentityProviderList) DeepCopyInto(out *LDAPIdentityProviderList) {
|
func (in *LDAPIdentityProviderList) DeepCopyInto(out *LDAPIdentityProviderList) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@ -115,6 +148,7 @@ func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec)
|
|||||||
}
|
}
|
||||||
out.Bind = in.Bind
|
out.Bind = in.Bind
|
||||||
out.UserSearch = in.UserSearch
|
out.UserSearch = in.UserSearch
|
||||||
|
out.GroupSearch = in.GroupSearch
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,13 +63,55 @@ spec:
|
|||||||
LDAP bind user. This account will be used to perform LDAP searches.
|
LDAP bind user. This account will be used to perform LDAP searches.
|
||||||
The Secret should be of type "kubernetes.io/basic-auth" which
|
The Secret should be of type "kubernetes.io/basic-auth" which
|
||||||
includes "username" and "password" keys. The username value
|
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".
|
should be the full dn (distinguished name) of your bind account,
|
||||||
The password must be non-empty.
|
e.g. "cn=bind-account,ou=users,dc=example,dc=com". The password
|
||||||
|
must be non-empty.
|
||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- secretName
|
- secretName
|
||||||
type: object
|
type: object
|
||||||
|
groupSearch:
|
||||||
|
description: GroupSearch contains the configuration for searching
|
||||||
|
for a user's group membership in the LDAP provider.
|
||||||
|
properties:
|
||||||
|
attributes:
|
||||||
|
description: Attributes specifies how the group's information
|
||||||
|
should be read from each LDAP entry which was found as the result
|
||||||
|
of the group search.
|
||||||
|
properties:
|
||||||
|
groupName:
|
||||||
|
description: GroupName specifies the name of the attribute
|
||||||
|
in the LDAP entries whose value shall become a group name
|
||||||
|
in the user's list of groups after a successful authentication.
|
||||||
|
The value of this field is case-sensitive and must match
|
||||||
|
the case of the attribute name returned by the LDAP server
|
||||||
|
in the user's entry. Distinguished names can be used by
|
||||||
|
specifying lower-case "dn". Optional. When not specified,
|
||||||
|
the default will act as if the GroupName were specified
|
||||||
|
as "cn" (common name).
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
base:
|
||||||
|
description: Base is the dn (distinguished name) that should be
|
||||||
|
used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com".
|
||||||
|
When not specified, no group search will be performed and authenticated
|
||||||
|
users will not belong to any groups from the LDAP provider.
|
||||||
|
Also, when not specified, the values of Filter and Attributes
|
||||||
|
are ignored.
|
||||||
|
type: string
|
||||||
|
filter:
|
||||||
|
description: Filter is the LDAP search filter which should be
|
||||||
|
applied when searching for groups for a user. The pattern "{}"
|
||||||
|
must occur in the filter at least once and will be dynamically
|
||||||
|
replaced by the dn (distinguished name) of the user entry found
|
||||||
|
as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})".
|
||||||
|
For more information about LDAP filters, see https://ldap.com/ldap-filters.
|
||||||
|
Note that the dn (distinguished name) is not an attribute of
|
||||||
|
an entry, so "dn={}" cannot be used. Optional. When not specified,
|
||||||
|
the default will act as if the Filter were specified as "member={}".
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
host:
|
host:
|
||||||
description: 'Host is the hostname of this LDAP identity provider,
|
description: 'Host is the hostname of this LDAP identity provider,
|
||||||
i.e., where to connect. For example: ldap.example.com:636.'
|
i.e., where to connect. For example: ldap.example.com:636.'
|
||||||
@ -104,37 +146,39 @@ spec:
|
|||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
username:
|
username:
|
||||||
description: Username specifies the name of attribute in the
|
description: Username specifies the name of the attribute
|
||||||
LDAP entry which whose value shall become the username of
|
in the LDAP entry whose value shall become the username
|
||||||
the user after a successful authentication. This would typically
|
of the user after a successful authentication. This would
|
||||||
be the same attribute name used in the user search filter,
|
typically be the same attribute name used in the user search
|
||||||
although it can be different. E.g. "mail" or "uid" or "userPrincipalName".
|
filter, although it can be different. E.g. "mail" or "uid"
|
||||||
The value of this field is case-sensitive and must match
|
or "userPrincipalName". The value of this field is case-sensitive
|
||||||
the case of the attribute name returned by the LDAP server
|
and must match the case of the attribute name returned by
|
||||||
in the user's entry. Distinguished names can be used by
|
the LDAP server in the user's entry. Distinguished names
|
||||||
specifying lower-case "dn". When this field is set to "dn"
|
can be used by specifying lower-case "dn". When this field
|
||||||
then the LDAPIdentityProviderUserSearch's Filter field cannot
|
is set to "dn" then the LDAPIdentityProviderUserSearch's
|
||||||
be blank, since the default value of "dn={}" would not work.
|
Filter field cannot be blank, since the default value of
|
||||||
|
"dn={}" would not work.
|
||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
base:
|
base:
|
||||||
description: Base is the DN that should be used as the search
|
description: Base is the dn (distinguished name) that should be
|
||||||
base when searching for users. E.g. "ou=users,dc=example,dc=com".
|
used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com".
|
||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
filter:
|
filter:
|
||||||
description: Filter is the LDAP search filter which should be
|
description: Filter is the LDAP search filter which should be
|
||||||
applied when searching for users. The pattern "{}" must occur
|
applied when searching for users. The pattern "{}" must occur
|
||||||
in the filter and will be dynamically replaced by the username
|
in the filter at least once and will be dynamically replaced
|
||||||
for which the search is being run. E.g. "mail={}" or "&(objectClass=person)(uid={})".
|
by the username for which the search is being run. E.g. "mail={}"
|
||||||
For more information about LDAP filters, see https://ldap.com/ldap-filters.
|
or "&(objectClass=person)(uid={})". For more information about
|
||||||
Note that the dn (distinguished name) is not an attribute of
|
LDAP filters, see https://ldap.com/ldap-filters. Note that the
|
||||||
an entry, so "dn={}" cannot be used. Optional. When not specified,
|
dn (distinguished name) is not an attribute of an entry, so
|
||||||
the default will act as if the Filter were specified as the
|
"dn={}" cannot be used. Optional. When not specified, the default
|
||||||
value from Attributes.Username appended by "={}". When the Attributes.Username
|
will act as if the Filter were specified as the value from Attributes.Username
|
||||||
is set to "dn" then the Filter must be explicitly specified,
|
appended by "={}". When the Attributes.Username is set to "dn"
|
||||||
since the default value of "dn={}" would not work.
|
then the Filter must be explicitly specified, since the default
|
||||||
|
value of "dn={}" would not work.
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
|
45
generated/1.20/README.adoc
generated
45
generated/1.20/README.adoc
generated
@ -734,7 +734,43 @@ LDAPIdentityProvider describes the configuration of an upstream Lightweight Dire
|
|||||||
[cols="25a,75a", options="header"]
|
[cols="25a,75a", options="header"]
|
||||||
|===
|
|===
|
||||||
| Field | Description
|
| 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". The password must be non-empty.
|
| *`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 (distinguished name) of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com". The password must be non-empty.
|
||||||
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
[id="{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearch"]
|
||||||
|
==== LDAPIdentityProviderGroupSearch
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.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 (distinguished name) that should be used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com". When not specified, no group search will be performed and authenticated users will not belong to any groups from the LDAP provider. Also, when not specified, the values of Filter and Attributes are ignored.
|
||||||
|
| *`filter`* __string__ | Filter is the LDAP search filter which should be applied when searching for groups for a user. The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})". For more information about LDAP filters, see https://ldap.com/ldap-filters. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as "member={}".
|
||||||
|
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearchattributes[$$LDAPIdentityProviderGroupSearchAttributes$$]__ | Attributes specifies how the group's information should be read from each LDAP entry which was found as the result of the group search.
|
||||||
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
[id="{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearchattributes"]
|
||||||
|
==== LDAPIdentityProviderGroupSearchAttributes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.Appears In:
|
||||||
|
****
|
||||||
|
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearch[$$LDAPIdentityProviderGroupSearch$$]
|
||||||
|
****
|
||||||
|
|
||||||
|
[cols="25a,75a", options="header"]
|
||||||
|
|===
|
||||||
|
| Field | Description
|
||||||
|
| *`groupName`* __string__ | GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name in the user's list of groups after a successful authentication. The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn". Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
@ -757,6 +793,7 @@ Spec for configuring an LDAP identity provider.
|
|||||||
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$]__ | TLS contains the connection settings for how to establish the connection to the Host.
|
| *`tls`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-tlsspec[$$TLSSpec$$]__ | 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-ldapidentityproviderbind[$$LDAPIdentityProviderBind$$]__ | 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`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityproviderbind[$$LDAPIdentityProviderBind$$]__ | 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-ldapidentityproviderusersearch[$$LDAPIdentityProviderUserSearch$$]__ | UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
| *`userSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearch[$$LDAPIdentityProviderUserSearch$$]__ | UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
||||||
|
| *`groupSearch`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityprovidergroupsearch[$$LDAPIdentityProviderGroupSearch$$]__ | GroupSearch contains the configuration for searching for a user's group membership in the LDAP provider.
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
@ -791,8 +828,8 @@ Status of an LDAP identity provider.
|
|||||||
[cols="25a,75a", options="header"]
|
[cols="25a,75a", options="header"]
|
||||||
|===
|
|===
|
||||||
| Field | Description
|
| 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".
|
| *`base`* __string__ | Base is the dn (distinguished name) 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. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as the value from Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be explicitly specified, since the default value of "dn={}" would not work.
|
| *`filter`* __string__ | Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur in the filter at least once 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. Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used. Optional. When not specified, the default will act as if the Filter were specified as the value from Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be explicitly specified, since the default value of "dn={}" would not work.
|
||||||
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchattributes[$$LDAPIdentityProviderUserSearchAttributes$$]__ | Attributes specifies how the user's information should be read from the LDAP entry which was found as the result of the user search.
|
| *`attributes`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-supervisor-idp-v1alpha1-ldapidentityproviderusersearchattributes[$$LDAPIdentityProviderUserSearchAttributes$$]__ | Attributes specifies how the user's information should be read from the LDAP entry which was found as the result of the user search.
|
||||||
|===
|
|===
|
||||||
|
|
||||||
@ -810,7 +847,7 @@ Status of an LDAP identity provider.
|
|||||||
[cols="25a,75a", options="header"]
|
[cols="25a,75a", options="header"]
|
||||||
|===
|
|===
|
||||||
| Field | Description
|
| 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, although it can be different. E.g. "mail" or "uid" or "userPrincipalName". The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn". When this field is set to "dn" then the LDAPIdentityProviderUserSearch's Filter field cannot be blank, since the default value of "dn={}" would not work.
|
| *`username`* __string__ | Username specifies the name of the attribute in the LDAP entry 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, although it can be different. E.g. "mail" or "uid" or "userPrincipalName". The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn". When this field is set to "dn" then the LDAPIdentityProviderUserSearch's Filter field cannot be blank, since the default value of "dn={}" would not work.
|
||||||
| *`uid`* __string__ | UID 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". The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
| *`uid`* __string__ | UID 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". The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
@ -39,14 +39,14 @@ type LDAPIdentityProviderBind struct {
|
|||||||
// SecretName contains the name of a namespace-local Secret object that provides the username and
|
// 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
|
// 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
|
// 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".
|
// should be the full dn (distinguished name) of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com".
|
||||||
// The password must be non-empty.
|
// The password must be non-empty.
|
||||||
// +kubebuilder:validation:MinLength=1
|
// +kubebuilder:validation:MinLength=1
|
||||||
SecretName string `json:"secretName"`
|
SecretName string `json:"secretName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LDAPIdentityProviderUserSearchAttributes struct {
|
type LDAPIdentityProviderUserSearchAttributes struct {
|
||||||
// Username specifies the name of attribute in the LDAP entry which whose value shall become the username
|
// Username specifies the name of the attribute in the LDAP entry whose value shall become the username
|
||||||
// of the user after a successful authentication. This would typically be the same attribute name used in
|
// of the user after a successful authentication. This would typically be the same attribute name used in
|
||||||
// the user search filter, although it can be different. E.g. "mail" or "uid" or "userPrincipalName".
|
// the user search filter, although it can be different. E.g. "mail" or "uid" or "userPrincipalName".
|
||||||
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||||
@ -64,14 +64,26 @@ type LDAPIdentityProviderUserSearchAttributes struct {
|
|||||||
UID string `json:"uid,omitempty"`
|
UID string `json:"uid,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LDAPIdentityProviderGroupSearchAttributes struct {
|
||||||
|
// GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name
|
||||||
|
// in the user's list of groups after a successful authentication.
|
||||||
|
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||||
|
// server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
||||||
|
// Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||||
|
// +optional
|
||||||
|
GroupName string `json:"groupName,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type LDAPIdentityProviderUserSearch struct {
|
type LDAPIdentityProviderUserSearch 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".
|
// Base is the dn (distinguished name) that should be used as the search base when searching for users.
|
||||||
|
// E.g. "ou=users,dc=example,dc=com".
|
||||||
// +kubebuilder:validation:MinLength=1
|
// +kubebuilder:validation:MinLength=1
|
||||||
Base string `json:"base,omitempty"`
|
Base string `json:"base,omitempty"`
|
||||||
|
|
||||||
// Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur
|
// 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={}"
|
// in the filter at least once and will be dynamically replaced by the username for which the search is being run.
|
||||||
// or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters.
|
// E.g. "mail={}" or "&(objectClass=person)(uid={})". For more information about LDAP filters, see
|
||||||
|
// https://ldap.com/ldap-filters.
|
||||||
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
||||||
// Optional. When not specified, the default will act as if the Filter were specified as the value from
|
// Optional. When not specified, the default will act as if the Filter were specified as the value from
|
||||||
// Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be
|
// Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be
|
||||||
@ -85,6 +97,30 @@ type LDAPIdentityProviderUserSearch struct {
|
|||||||
Attributes LDAPIdentityProviderUserSearchAttributes `json:"attributes,omitempty"`
|
Attributes LDAPIdentityProviderUserSearchAttributes `json:"attributes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LDAPIdentityProviderGroupSearch struct {
|
||||||
|
// Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g.
|
||||||
|
// "ou=groups,dc=example,dc=com". When not specified, no group search will be performed and
|
||||||
|
// authenticated users will not belong to any groups from the LDAP provider. Also, when not specified,
|
||||||
|
// the values of Filter and Attributes are ignored.
|
||||||
|
// +optional
|
||||||
|
Base string `json:"base,omitempty"`
|
||||||
|
|
||||||
|
// Filter is the LDAP search filter which should be applied when searching for groups for a user.
|
||||||
|
// The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the
|
||||||
|
// dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or
|
||||||
|
// "&(objectClass=groupOfNames)(member={})". For more information about LDAP filters, see
|
||||||
|
// https://ldap.com/ldap-filters.
|
||||||
|
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
||||||
|
// Optional. When not specified, the default will act as if the Filter were specified as "member={}".
|
||||||
|
// +optional
|
||||||
|
Filter string `json:"filter,omitempty"`
|
||||||
|
|
||||||
|
// Attributes specifies how the group's information should be read from each LDAP entry which was found as
|
||||||
|
// the result of the group search.
|
||||||
|
// +optional
|
||||||
|
Attributes LDAPIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// Spec for configuring an LDAP identity provider.
|
// Spec for configuring an LDAP identity provider.
|
||||||
type LDAPIdentityProviderSpec struct {
|
type LDAPIdentityProviderSpec struct {
|
||||||
// Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636.
|
// Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636.
|
||||||
@ -100,6 +136,9 @@ type LDAPIdentityProviderSpec struct {
|
|||||||
|
|
||||||
// UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
// UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
||||||
UserSearch LDAPIdentityProviderUserSearch `json:"userSearch,omitempty"`
|
UserSearch LDAPIdentityProviderUserSearch `json:"userSearch,omitempty"`
|
||||||
|
|
||||||
|
// GroupSearch contains the configuration for searching for a user's group membership in the LDAP provider.
|
||||||
|
GroupSearch LDAPIdentityProviderGroupSearch `json:"groupSearch,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access
|
// LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access
|
||||||
|
@ -72,6 +72,39 @@ func (in *LDAPIdentityProviderBind) DeepCopy() *LDAPIdentityProviderBind {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearch) DeepCopyInto(out *LDAPIdentityProviderGroupSearch) {
|
||||||
|
*out = *in
|
||||||
|
out.Attributes = in.Attributes
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderGroupSearch.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearch) DeepCopy() *LDAPIdentityProviderGroupSearch {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(LDAPIdentityProviderGroupSearch)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearchAttributes) DeepCopyInto(out *LDAPIdentityProviderGroupSearchAttributes) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderGroupSearchAttributes.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearchAttributes) DeepCopy() *LDAPIdentityProviderGroupSearchAttributes {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(LDAPIdentityProviderGroupSearchAttributes)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *LDAPIdentityProviderList) DeepCopyInto(out *LDAPIdentityProviderList) {
|
func (in *LDAPIdentityProviderList) DeepCopyInto(out *LDAPIdentityProviderList) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@ -115,6 +148,7 @@ func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec)
|
|||||||
}
|
}
|
||||||
out.Bind = in.Bind
|
out.Bind = in.Bind
|
||||||
out.UserSearch = in.UserSearch
|
out.UserSearch = in.UserSearch
|
||||||
|
out.GroupSearch = in.GroupSearch
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,13 +63,55 @@ spec:
|
|||||||
LDAP bind user. This account will be used to perform LDAP searches.
|
LDAP bind user. This account will be used to perform LDAP searches.
|
||||||
The Secret should be of type "kubernetes.io/basic-auth" which
|
The Secret should be of type "kubernetes.io/basic-auth" which
|
||||||
includes "username" and "password" keys. The username value
|
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".
|
should be the full dn (distinguished name) of your bind account,
|
||||||
The password must be non-empty.
|
e.g. "cn=bind-account,ou=users,dc=example,dc=com". The password
|
||||||
|
must be non-empty.
|
||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- secretName
|
- secretName
|
||||||
type: object
|
type: object
|
||||||
|
groupSearch:
|
||||||
|
description: GroupSearch contains the configuration for searching
|
||||||
|
for a user's group membership in the LDAP provider.
|
||||||
|
properties:
|
||||||
|
attributes:
|
||||||
|
description: Attributes specifies how the group's information
|
||||||
|
should be read from each LDAP entry which was found as the result
|
||||||
|
of the group search.
|
||||||
|
properties:
|
||||||
|
groupName:
|
||||||
|
description: GroupName specifies the name of the attribute
|
||||||
|
in the LDAP entries whose value shall become a group name
|
||||||
|
in the user's list of groups after a successful authentication.
|
||||||
|
The value of this field is case-sensitive and must match
|
||||||
|
the case of the attribute name returned by the LDAP server
|
||||||
|
in the user's entry. Distinguished names can be used by
|
||||||
|
specifying lower-case "dn". Optional. When not specified,
|
||||||
|
the default will act as if the GroupName were specified
|
||||||
|
as "cn" (common name).
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
base:
|
||||||
|
description: Base is the dn (distinguished name) that should be
|
||||||
|
used as the search base when searching for groups. E.g. "ou=groups,dc=example,dc=com".
|
||||||
|
When not specified, no group search will be performed and authenticated
|
||||||
|
users will not belong to any groups from the LDAP provider.
|
||||||
|
Also, when not specified, the values of Filter and Attributes
|
||||||
|
are ignored.
|
||||||
|
type: string
|
||||||
|
filter:
|
||||||
|
description: Filter is the LDAP search filter which should be
|
||||||
|
applied when searching for groups for a user. The pattern "{}"
|
||||||
|
must occur in the filter at least once and will be dynamically
|
||||||
|
replaced by the dn (distinguished name) of the user entry found
|
||||||
|
as a result of the user search. E.g. "member={}" or "&(objectClass=groupOfNames)(member={})".
|
||||||
|
For more information about LDAP filters, see https://ldap.com/ldap-filters.
|
||||||
|
Note that the dn (distinguished name) is not an attribute of
|
||||||
|
an entry, so "dn={}" cannot be used. Optional. When not specified,
|
||||||
|
the default will act as if the Filter were specified as "member={}".
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
host:
|
host:
|
||||||
description: 'Host is the hostname of this LDAP identity provider,
|
description: 'Host is the hostname of this LDAP identity provider,
|
||||||
i.e., where to connect. For example: ldap.example.com:636.'
|
i.e., where to connect. For example: ldap.example.com:636.'
|
||||||
@ -104,37 +146,39 @@ spec:
|
|||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
username:
|
username:
|
||||||
description: Username specifies the name of attribute in the
|
description: Username specifies the name of the attribute
|
||||||
LDAP entry which whose value shall become the username of
|
in the LDAP entry whose value shall become the username
|
||||||
the user after a successful authentication. This would typically
|
of the user after a successful authentication. This would
|
||||||
be the same attribute name used in the user search filter,
|
typically be the same attribute name used in the user search
|
||||||
although it can be different. E.g. "mail" or "uid" or "userPrincipalName".
|
filter, although it can be different. E.g. "mail" or "uid"
|
||||||
The value of this field is case-sensitive and must match
|
or "userPrincipalName". The value of this field is case-sensitive
|
||||||
the case of the attribute name returned by the LDAP server
|
and must match the case of the attribute name returned by
|
||||||
in the user's entry. Distinguished names can be used by
|
the LDAP server in the user's entry. Distinguished names
|
||||||
specifying lower-case "dn". When this field is set to "dn"
|
can be used by specifying lower-case "dn". When this field
|
||||||
then the LDAPIdentityProviderUserSearch's Filter field cannot
|
is set to "dn" then the LDAPIdentityProviderUserSearch's
|
||||||
be blank, since the default value of "dn={}" would not work.
|
Filter field cannot be blank, since the default value of
|
||||||
|
"dn={}" would not work.
|
||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
base:
|
base:
|
||||||
description: Base is the DN that should be used as the search
|
description: Base is the dn (distinguished name) that should be
|
||||||
base when searching for users. E.g. "ou=users,dc=example,dc=com".
|
used as the search base when searching for users. E.g. "ou=users,dc=example,dc=com".
|
||||||
minLength: 1
|
minLength: 1
|
||||||
type: string
|
type: string
|
||||||
filter:
|
filter:
|
||||||
description: Filter is the LDAP search filter which should be
|
description: Filter is the LDAP search filter which should be
|
||||||
applied when searching for users. The pattern "{}" must occur
|
applied when searching for users. The pattern "{}" must occur
|
||||||
in the filter and will be dynamically replaced by the username
|
in the filter at least once and will be dynamically replaced
|
||||||
for which the search is being run. E.g. "mail={}" or "&(objectClass=person)(uid={})".
|
by the username for which the search is being run. E.g. "mail={}"
|
||||||
For more information about LDAP filters, see https://ldap.com/ldap-filters.
|
or "&(objectClass=person)(uid={})". For more information about
|
||||||
Note that the dn (distinguished name) is not an attribute of
|
LDAP filters, see https://ldap.com/ldap-filters. Note that the
|
||||||
an entry, so "dn={}" cannot be used. Optional. When not specified,
|
dn (distinguished name) is not an attribute of an entry, so
|
||||||
the default will act as if the Filter were specified as the
|
"dn={}" cannot be used. Optional. When not specified, the default
|
||||||
value from Attributes.Username appended by "={}". When the Attributes.Username
|
will act as if the Filter were specified as the value from Attributes.Username
|
||||||
is set to "dn" then the Filter must be explicitly specified,
|
appended by "={}". When the Attributes.Username is set to "dn"
|
||||||
since the default value of "dn={}" would not work.
|
then the Filter must be explicitly specified, since the default
|
||||||
|
value of "dn={}" would not work.
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
|
@ -39,14 +39,14 @@ type LDAPIdentityProviderBind struct {
|
|||||||
// SecretName contains the name of a namespace-local Secret object that provides the username and
|
// 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
|
// 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
|
// 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".
|
// should be the full dn (distinguished name) of your bind account, e.g. "cn=bind-account,ou=users,dc=example,dc=com".
|
||||||
// The password must be non-empty.
|
// The password must be non-empty.
|
||||||
// +kubebuilder:validation:MinLength=1
|
// +kubebuilder:validation:MinLength=1
|
||||||
SecretName string `json:"secretName"`
|
SecretName string `json:"secretName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LDAPIdentityProviderUserSearchAttributes struct {
|
type LDAPIdentityProviderUserSearchAttributes struct {
|
||||||
// Username specifies the name of attribute in the LDAP entry which whose value shall become the username
|
// Username specifies the name of the attribute in the LDAP entry whose value shall become the username
|
||||||
// of the user after a successful authentication. This would typically be the same attribute name used in
|
// of the user after a successful authentication. This would typically be the same attribute name used in
|
||||||
// the user search filter, although it can be different. E.g. "mail" or "uid" or "userPrincipalName".
|
// the user search filter, although it can be different. E.g. "mail" or "uid" or "userPrincipalName".
|
||||||
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||||
@ -64,14 +64,26 @@ type LDAPIdentityProviderUserSearchAttributes struct {
|
|||||||
UID string `json:"uid,omitempty"`
|
UID string `json:"uid,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LDAPIdentityProviderGroupSearchAttributes struct {
|
||||||
|
// GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name
|
||||||
|
// in the user's list of groups after a successful authentication.
|
||||||
|
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||||
|
// server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
||||||
|
// Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||||
|
// +optional
|
||||||
|
GroupName string `json:"groupName,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type LDAPIdentityProviderUserSearch struct {
|
type LDAPIdentityProviderUserSearch 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".
|
// Base is the dn (distinguished name) that should be used as the search base when searching for users.
|
||||||
|
// E.g. "ou=users,dc=example,dc=com".
|
||||||
// +kubebuilder:validation:MinLength=1
|
// +kubebuilder:validation:MinLength=1
|
||||||
Base string `json:"base,omitempty"`
|
Base string `json:"base,omitempty"`
|
||||||
|
|
||||||
// Filter is the LDAP search filter which should be applied when searching for users. The pattern "{}" must occur
|
// 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={}"
|
// in the filter at least once and will be dynamically replaced by the username for which the search is being run.
|
||||||
// or "&(objectClass=person)(uid={})". For more information about LDAP filters, see https://ldap.com/ldap-filters.
|
// E.g. "mail={}" or "&(objectClass=person)(uid={})". For more information about LDAP filters, see
|
||||||
|
// https://ldap.com/ldap-filters.
|
||||||
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
||||||
// Optional. When not specified, the default will act as if the Filter were specified as the value from
|
// Optional. When not specified, the default will act as if the Filter were specified as the value from
|
||||||
// Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be
|
// Attributes.Username appended by "={}". When the Attributes.Username is set to "dn" then the Filter must be
|
||||||
@ -85,6 +97,30 @@ type LDAPIdentityProviderUserSearch struct {
|
|||||||
Attributes LDAPIdentityProviderUserSearchAttributes `json:"attributes,omitempty"`
|
Attributes LDAPIdentityProviderUserSearchAttributes `json:"attributes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LDAPIdentityProviderGroupSearch struct {
|
||||||
|
// Base is the dn (distinguished name) that should be used as the search base when searching for groups. E.g.
|
||||||
|
// "ou=groups,dc=example,dc=com". When not specified, no group search will be performed and
|
||||||
|
// authenticated users will not belong to any groups from the LDAP provider. Also, when not specified,
|
||||||
|
// the values of Filter and Attributes are ignored.
|
||||||
|
// +optional
|
||||||
|
Base string `json:"base,omitempty"`
|
||||||
|
|
||||||
|
// Filter is the LDAP search filter which should be applied when searching for groups for a user.
|
||||||
|
// The pattern "{}" must occur in the filter at least once and will be dynamically replaced by the
|
||||||
|
// dn (distinguished name) of the user entry found as a result of the user search. E.g. "member={}" or
|
||||||
|
// "&(objectClass=groupOfNames)(member={})". For more information about LDAP filters, see
|
||||||
|
// https://ldap.com/ldap-filters.
|
||||||
|
// Note that the dn (distinguished name) is not an attribute of an entry, so "dn={}" cannot be used.
|
||||||
|
// Optional. When not specified, the default will act as if the Filter were specified as "member={}".
|
||||||
|
// +optional
|
||||||
|
Filter string `json:"filter,omitempty"`
|
||||||
|
|
||||||
|
// Attributes specifies how the group's information should be read from each LDAP entry which was found as
|
||||||
|
// the result of the group search.
|
||||||
|
// +optional
|
||||||
|
Attributes LDAPIdentityProviderGroupSearchAttributes `json:"attributes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// Spec for configuring an LDAP identity provider.
|
// Spec for configuring an LDAP identity provider.
|
||||||
type LDAPIdentityProviderSpec struct {
|
type LDAPIdentityProviderSpec struct {
|
||||||
// Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636.
|
// Host is the hostname of this LDAP identity provider, i.e., where to connect. For example: ldap.example.com:636.
|
||||||
@ -100,6 +136,9 @@ type LDAPIdentityProviderSpec struct {
|
|||||||
|
|
||||||
// UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
// UserSearch contains the configuration for searching for a user by name in the LDAP provider.
|
||||||
UserSearch LDAPIdentityProviderUserSearch `json:"userSearch,omitempty"`
|
UserSearch LDAPIdentityProviderUserSearch `json:"userSearch,omitempty"`
|
||||||
|
|
||||||
|
// GroupSearch contains the configuration for searching for a user's group membership in the LDAP provider.
|
||||||
|
GroupSearch LDAPIdentityProviderGroupSearch `json:"groupSearch,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access
|
// LDAPIdentityProvider describes the configuration of an upstream Lightweight Directory Access
|
||||||
|
@ -72,6 +72,39 @@ func (in *LDAPIdentityProviderBind) DeepCopy() *LDAPIdentityProviderBind {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearch) DeepCopyInto(out *LDAPIdentityProviderGroupSearch) {
|
||||||
|
*out = *in
|
||||||
|
out.Attributes = in.Attributes
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderGroupSearch.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearch) DeepCopy() *LDAPIdentityProviderGroupSearch {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(LDAPIdentityProviderGroupSearch)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearchAttributes) DeepCopyInto(out *LDAPIdentityProviderGroupSearchAttributes) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPIdentityProviderGroupSearchAttributes.
|
||||||
|
func (in *LDAPIdentityProviderGroupSearchAttributes) DeepCopy() *LDAPIdentityProviderGroupSearchAttributes {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(LDAPIdentityProviderGroupSearchAttributes)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *LDAPIdentityProviderList) DeepCopyInto(out *LDAPIdentityProviderList) {
|
func (in *LDAPIdentityProviderList) DeepCopyInto(out *LDAPIdentityProviderList) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@ -115,6 +148,7 @@ func (in *LDAPIdentityProviderSpec) DeepCopyInto(out *LDAPIdentityProviderSpec)
|
|||||||
}
|
}
|
||||||
out.Bind = in.Bind
|
out.Bind = in.Bind
|
||||||
out.UserSearch = in.UserSearch
|
out.UserSearch = in.UserSearch
|
||||||
|
out.GroupSearch = in.GroupSearch
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,6 +160,11 @@ func (c *ldapWatcherController) validateUpstream(ctx context.Context, upstream *
|
|||||||
UsernameAttribute: spec.UserSearch.Attributes.Username,
|
UsernameAttribute: spec.UserSearch.Attributes.Username,
|
||||||
UIDAttribute: spec.UserSearch.Attributes.UID,
|
UIDAttribute: spec.UserSearch.Attributes.UID,
|
||||||
},
|
},
|
||||||
|
GroupSearch: upstreamldap.GroupSearchConfig{
|
||||||
|
Base: spec.GroupSearch.Base,
|
||||||
|
Filter: spec.GroupSearch.Filter,
|
||||||
|
GroupNameAttribute: spec.GroupSearch.Attributes.GroupName,
|
||||||
|
},
|
||||||
Dialer: c.ldapDialer,
|
Dialer: c.ldapDialer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,16 +145,19 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
now := metav1.NewTime(time.Now().UTC())
|
now := metav1.NewTime(time.Now().UTC())
|
||||||
|
|
||||||
const (
|
const (
|
||||||
testNamespace = "test-namespace"
|
testNamespace = "test-namespace"
|
||||||
testName = "test-name"
|
testName = "test-name"
|
||||||
testSecretName = "test-bind-secret"
|
testSecretName = "test-bind-secret"
|
||||||
testBindUsername = "test-bind-username"
|
testBindUsername = "test-bind-username"
|
||||||
testBindPassword = "test-bind-password"
|
testBindPassword = "test-bind-password"
|
||||||
testHost = "ldap.example.com:123"
|
testHost = "ldap.example.com:123"
|
||||||
testUserSearchBase = "test-user-search-base"
|
testUserSearchBase = "test-user-search-base"
|
||||||
testUserSearchFilter = "test-user-search-filter"
|
testUserSearchFilter = "test-user-search-filter"
|
||||||
testUsernameAttrName = "test-username-attr"
|
testGroupSearchBase = "test-group-search-base"
|
||||||
testUIDAttrName = "test-uid-attr"
|
testGroupSearchFilter = "test-group-search-filter"
|
||||||
|
testUsernameAttrName = "test-username-attr"
|
||||||
|
testGroupNameAttrName = "test-group-name-attr"
|
||||||
|
testUIDAttrName = "test-uid-attr"
|
||||||
)
|
)
|
||||||
|
|
||||||
testValidSecretData := map[string][]byte{"username": []byte(testBindUsername), "password": []byte(testBindPassword)}
|
testValidSecretData := map[string][]byte{"username": []byte(testBindUsername), "password": []byte(testBindPassword)}
|
||||||
@ -178,6 +181,13 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
UID: testUIDAttrName,
|
UID: testUIDAttrName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
GroupSearch: v1alpha1.LDAPIdentityProviderGroupSearch{
|
||||||
|
Base: testGroupSearchBase,
|
||||||
|
Filter: testGroupSearchFilter,
|
||||||
|
Attributes: v1alpha1.LDAPIdentityProviderGroupSearchAttributes{
|
||||||
|
GroupName: testGroupNameAttrName,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
editedValidUpstream := func(editFunc func(*v1alpha1.LDAPIdentityProvider)) *v1alpha1.LDAPIdentityProvider {
|
editedValidUpstream := func(editFunc func(*v1alpha1.LDAPIdentityProvider)) *v1alpha1.LDAPIdentityProvider {
|
||||||
@ -198,6 +208,11 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
UsernameAttribute: testUsernameAttrName,
|
UsernameAttribute: testUsernameAttrName,
|
||||||
UIDAttribute: testUIDAttrName,
|
UIDAttribute: testUIDAttrName,
|
||||||
},
|
},
|
||||||
|
GroupSearch: upstreamldap.GroupSearchConfig{
|
||||||
|
Base: testGroupSearchBase,
|
||||||
|
Filter: testGroupSearchFilter,
|
||||||
|
GroupNameAttribute: testGroupNameAttrName,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
bindSecretValidTrueCondition := func(gen int64) v1alpha1.Condition {
|
bindSecretValidTrueCondition := func(gen int64) v1alpha1.Condition {
|
||||||
@ -438,6 +453,11 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
UsernameAttribute: testUsernameAttrName,
|
UsernameAttribute: testUsernameAttrName,
|
||||||
UIDAttribute: testUIDAttrName,
|
UIDAttribute: testUIDAttrName,
|
||||||
},
|
},
|
||||||
|
GroupSearch: upstreamldap.GroupSearchConfig{
|
||||||
|
Base: testGroupSearchBase,
|
||||||
|
Filter: testGroupSearchFilter,
|
||||||
|
GroupNameAttribute: testGroupNameAttrName,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantResultingUpstreams: []v1alpha1.LDAPIdentityProvider{{
|
wantResultingUpstreams: []v1alpha1.LDAPIdentityProvider{{
|
||||||
@ -484,6 +504,11 @@ func TestLDAPUpstreamWatcherControllerSync(t *testing.T) {
|
|||||||
UsernameAttribute: testUsernameAttrName,
|
UsernameAttribute: testUsernameAttrName,
|
||||||
UIDAttribute: testUIDAttrName,
|
UIDAttribute: testUIDAttrName,
|
||||||
},
|
},
|
||||||
|
GroupSearch: upstreamldap.GroupSearchConfig{
|
||||||
|
Base: testGroupSearchBase,
|
||||||
|
Filter: testGroupSearchFilter,
|
||||||
|
GroupNameAttribute: testGroupNameAttrName,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantResultingUpstreams: []v1alpha1.LDAPIdentityProvider{{
|
wantResultingUpstreams: []v1alpha1.LDAPIdentityProvider{{
|
||||||
|
@ -78,3 +78,18 @@ func (mr *MockConnMockRecorder) Search(arg0 interface{}) *gomock.Call {
|
|||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Search", reflect.TypeOf((*MockConn)(nil).Search), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Search", reflect.TypeOf((*MockConn)(nil).Search), arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SearchWithPaging mocks base method.
|
||||||
|
func (m *MockConn) SearchWithPaging(arg0 *ldap.SearchRequest, arg1 uint32) (*ldap.SearchResult, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SearchWithPaging", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(*ldap.SearchResult)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchWithPaging indicates an expected call of SearchWithPaging.
|
||||||
|
func (mr *MockConnMockRecorder) SearchWithPaging(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchWithPaging", reflect.TypeOf((*MockConn)(nil).SearchWithPaging), arg0, arg1)
|
||||||
|
}
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -24,9 +25,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ldapsScheme = "ldaps"
|
ldapsScheme = "ldaps"
|
||||||
distinguishedNameAttributeName = "dn"
|
distinguishedNameAttributeName = "dn"
|
||||||
userSearchFilterInterpolationLocationMarker = "{}"
|
commonNameAttributeName = "cn"
|
||||||
|
searchFilterInterpolationLocationMarker = "{}"
|
||||||
|
groupSearchPageSize = uint32(250)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Conn abstracts the upstream LDAP communication protocol (mostly for testing).
|
// Conn abstracts the upstream LDAP communication protocol (mostly for testing).
|
||||||
@ -35,6 +38,8 @@ type Conn interface {
|
|||||||
|
|
||||||
Search(searchRequest *ldap.SearchRequest) (*ldap.SearchResult, error)
|
Search(searchRequest *ldap.SearchRequest) (*ldap.SearchResult, error)
|
||||||
|
|
||||||
|
SearchWithPaging(searchRequest *ldap.SearchRequest, pagingSize uint32) (*ldap.SearchResult, error)
|
||||||
|
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +83,9 @@ type ProviderConfig struct {
|
|||||||
// UserSearch contains information about how to search for users in the upstream LDAP IDP.
|
// UserSearch contains information about how to search for users in the upstream LDAP IDP.
|
||||||
UserSearch UserSearchConfig
|
UserSearch UserSearchConfig
|
||||||
|
|
||||||
|
// GroupSearch contains information about how to search for group membership in the upstream LDAP IDP.
|
||||||
|
GroupSearch GroupSearchConfig
|
||||||
|
|
||||||
// Dialer exists to enable testing. When nil, will use a default appropriate for production use.
|
// Dialer exists to enable testing. When nil, will use a default appropriate for production use.
|
||||||
Dialer LDAPDialer
|
Dialer LDAPDialer
|
||||||
}
|
}
|
||||||
@ -99,6 +107,20 @@ type UserSearchConfig struct {
|
|||||||
UIDAttribute string
|
UIDAttribute string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GroupSearchConfig contains information about how to search for group membership for users in the upstream LDAP IDP.
|
||||||
|
type GroupSearchConfig struct {
|
||||||
|
// Base is the base DN to use for the group search in the upstream LDAP IDP. Empty means to skip group search
|
||||||
|
// entirely, in which case authenticated users will not belong to any groups from the upstream LDAP IDP.
|
||||||
|
Base string
|
||||||
|
|
||||||
|
// Filter is the filter to use for the group search in the upstream LDAP IDP. Empty means to use `member={}`.
|
||||||
|
Filter string
|
||||||
|
|
||||||
|
// GroupNameAttribute is the attribute in the LDAP group entry from which the group name should be
|
||||||
|
// retrieved. Empty means to use 'cn'.
|
||||||
|
GroupNameAttribute string
|
||||||
|
}
|
||||||
|
|
||||||
type Provider struct {
|
type Provider struct {
|
||||||
c ProviderConfig
|
c ProviderConfig
|
||||||
}
|
}
|
||||||
@ -257,7 +279,7 @@ func (p *Provider) authenticateUserImpl(ctx context.Context, username string, bi
|
|||||||
return nil, false, fmt.Errorf(`error binding as "%s" before user search: %w`, p.c.BindUsername, err)
|
return nil, false, fmt.Errorf(`error binding as "%s" before user search: %w`, p.c.BindUsername, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mappedUsername, mappedUID, err := p.searchAndBindUser(conn, username, bindFunc)
|
mappedUsername, mappedUID, mappedGroupNames, err := p.searchAndBindUser(conn, username, bindFunc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.traceAuthFailure(t, err)
|
p.traceAuthFailure(t, err)
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
@ -272,13 +294,39 @@ func (p *Provider) authenticateUserImpl(ctx context.Context, username string, bi
|
|||||||
User: &user.DefaultInfo{
|
User: &user.DefaultInfo{
|
||||||
Name: mappedUsername,
|
Name: mappedUsername,
|
||||||
UID: mappedUID,
|
UID: mappedUID,
|
||||||
Groups: []string{}, // Support for group search coming soon.
|
Groups: mappedGroupNames,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
p.traceAuthSuccess(t)
|
p.traceAuthSuccess(t)
|
||||||
return response, true, nil
|
return response, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Provider) searchGroupsForUserDN(conn Conn, userDN string) ([]string, error) {
|
||||||
|
searchResult, err := conn.SearchWithPaging(p.groupSearchRequest(userDN), groupSearchPageSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(`error searching for group memberships for user with DN %q: %w`, userDN, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
groupAttributeName := p.c.GroupSearch.GroupNameAttribute
|
||||||
|
if len(groupAttributeName) == 0 {
|
||||||
|
groupAttributeName = commonNameAttributeName
|
||||||
|
}
|
||||||
|
|
||||||
|
groups := []string{}
|
||||||
|
for _, groupEntry := range searchResult.Entries {
|
||||||
|
if len(groupEntry.DN) == 0 {
|
||||||
|
return nil, fmt.Errorf(`searching for group memberships for user with DN %q resulted in search result without DN`, userDN)
|
||||||
|
}
|
||||||
|
mappedGroupName, err := p.getSearchResultAttributeValue(groupAttributeName, groupEntry, userDN)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(`error searching for group memberships for user with DN %q: %w`, userDN, err)
|
||||||
|
}
|
||||||
|
groups = append(groups, mappedGroupName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Provider) validateConfig() error {
|
func (p *Provider) validateConfig() error {
|
||||||
if p.c.UserSearch.UsernameAttribute == distinguishedNameAttributeName && len(p.c.UserSearch.Filter) == 0 {
|
if p.c.UserSearch.UsernameAttribute == distinguishedNameAttributeName && len(p.c.UserSearch.Filter) == 0 {
|
||||||
// LDAP search filters do not allow searching by DN, so we would have no reasonable default for Filter.
|
// LDAP search filters do not allow searching by DN, so we would have no reasonable default for Filter.
|
||||||
@ -287,36 +335,45 @@ func (p *Provider) validateConfig() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) searchAndBindUser(conn Conn, username string, bindFunc func(conn Conn, foundUserDN string) error) (string, string, error) {
|
func (p *Provider) searchAndBindUser(conn Conn, username string, bindFunc func(conn Conn, foundUserDN string) error) (string, string, []string, error) {
|
||||||
searchResult, err := conn.Search(p.userSearchRequest(username))
|
searchResult, err := conn.Search(p.userSearchRequest(username))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", fmt.Errorf(`error searching for user "%s": %w`, username, err)
|
return "", "", nil, fmt.Errorf(`error searching for user "%s": %w`, username, err)
|
||||||
}
|
}
|
||||||
if len(searchResult.Entries) == 0 {
|
if len(searchResult.Entries) == 0 {
|
||||||
plog.Debug("error finding user: user not found (if this username is valid, please check the user search configuration)",
|
plog.Debug("error finding user: user not found (if this username is valid, please check the user search configuration)",
|
||||||
"upstreamName", p.GetName(), "username", username)
|
"upstreamName", p.GetName(), "username", username)
|
||||||
return "", "", nil
|
return "", "", nil, nil
|
||||||
}
|
}
|
||||||
if len(searchResult.Entries) > 1 {
|
if len(searchResult.Entries) > 1 {
|
||||||
return "", "", fmt.Errorf(`searching for user "%s" resulted in %d search results, but expected 1 result`,
|
return "", "", nil, fmt.Errorf(`searching for user "%s" resulted in %d search results, but expected 1 result`,
|
||||||
username, len(searchResult.Entries),
|
username, len(searchResult.Entries),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
userEntry := searchResult.Entries[0]
|
userEntry := searchResult.Entries[0]
|
||||||
if len(userEntry.DN) == 0 {
|
if len(userEntry.DN) == 0 {
|
||||||
return "", "", fmt.Errorf(`searching for user "%s" resulted in search result without DN`, username)
|
return "", "", nil, fmt.Errorf(`searching for user "%s" resulted in search result without DN`, username)
|
||||||
}
|
}
|
||||||
|
|
||||||
mappedUsername, err := p.getSearchResultAttributeValue(p.c.UserSearch.UsernameAttribute, userEntry, username)
|
mappedUsername, err := p.getSearchResultAttributeValue(p.c.UserSearch.UsernameAttribute, userEntry, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mappedUID, err := p.getSearchResultAttributeValue(p.c.UserSearch.UIDAttribute, userEntry, username)
|
mappedUID, err := p.getSearchResultAttributeValue(p.c.UserSearch.UIDAttribute, userEntry, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mappedGroupNames := []string{}
|
||||||
|
if len(p.c.GroupSearch.Base) > 0 {
|
||||||
|
mappedGroupNames, err = p.searchGroupsForUserDN(conn, userEntry.DN)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(mappedGroupNames)
|
||||||
|
|
||||||
// Caution: Note that any other LDAP commands after this bind will be run as this user instead of as the configured BindUsername!
|
// Caution: Note that any other LDAP commands after this bind will be run as this user instead of as the configured BindUsername!
|
||||||
err = bindFunc(conn, userEntry.DN)
|
err = bindFunc(conn, userEntry.DN)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -324,12 +381,12 @@ func (p *Provider) searchAndBindUser(conn Conn, username string, bindFunc func(c
|
|||||||
err, "upstreamName", p.GetName(), "username", username, "dn", userEntry.DN)
|
err, "upstreamName", p.GetName(), "username", username, "dn", userEntry.DN)
|
||||||
ldapErr := &ldap.Error{}
|
ldapErr := &ldap.Error{}
|
||||||
if errors.As(err, &ldapErr) && ldapErr.ResultCode == ldap.LDAPResultInvalidCredentials {
|
if errors.As(err, &ldapErr) && ldapErr.ResultCode == ldap.LDAPResultInvalidCredentials {
|
||||||
return "", "", nil
|
return "", "", nil, nil
|
||||||
}
|
}
|
||||||
return "", "", fmt.Errorf(`error binding for user "%s" using provided password against DN "%s": %w`, username, userEntry.DN, err)
|
return "", "", nil, fmt.Errorf(`error binding for user "%s" using provided password against DN "%s": %w`, username, userEntry.DN, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return mappedUsername, mappedUID, nil
|
return mappedUsername, mappedUID, mappedGroupNames, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) userSearchRequest(username string) *ldap.SearchRequest {
|
func (p *Provider) userSearchRequest(username string) *ldap.SearchRequest {
|
||||||
@ -347,6 +404,21 @@ func (p *Provider) userSearchRequest(username string) *ldap.SearchRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Provider) groupSearchRequest(userDN string) *ldap.SearchRequest {
|
||||||
|
// See https://ldap.com/the-ldap-search-operation for general documentation of LDAP search options.
|
||||||
|
return &ldap.SearchRequest{
|
||||||
|
BaseDN: p.c.GroupSearch.Base,
|
||||||
|
Scope: ldap.ScopeWholeSubtree,
|
||||||
|
DerefAliases: ldap.NeverDerefAliases,
|
||||||
|
SizeLimit: 0, // unlimited size because we will search with paging
|
||||||
|
TimeLimit: 90,
|
||||||
|
TypesOnly: false,
|
||||||
|
Filter: p.groupSearchFilter(userDN),
|
||||||
|
Attributes: p.groupSearchRequestedAttributes(),
|
||||||
|
Controls: nil, // nil because ldap.SearchWithPaging() will set the appropriate controls for us
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Provider) userSearchRequestedAttributes() []string {
|
func (p *Provider) userSearchRequestedAttributes() []string {
|
||||||
attributes := []string{}
|
attributes := []string{}
|
||||||
if p.c.UserSearch.UsernameAttribute != distinguishedNameAttributeName {
|
if p.c.UserSearch.UsernameAttribute != distinguishedNameAttributeName {
|
||||||
@ -358,12 +430,34 @@ func (p *Provider) userSearchRequestedAttributes() []string {
|
|||||||
return attributes
|
return attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Provider) groupSearchRequestedAttributes() []string {
|
||||||
|
switch p.c.GroupSearch.GroupNameAttribute {
|
||||||
|
case "":
|
||||||
|
return []string{commonNameAttributeName}
|
||||||
|
case distinguishedNameAttributeName:
|
||||||
|
return []string{}
|
||||||
|
default:
|
||||||
|
return []string{p.c.GroupSearch.GroupNameAttribute}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Provider) userSearchFilter(username string) string {
|
func (p *Provider) userSearchFilter(username string) string {
|
||||||
safeUsername := p.escapeUsernameForSearchFilter(username)
|
safeUsername := p.escapeUsernameForSearchFilter(username)
|
||||||
if len(p.c.UserSearch.Filter) == 0 {
|
if len(p.c.UserSearch.Filter) == 0 {
|
||||||
return fmt.Sprintf("(%s=%s)", p.c.UserSearch.UsernameAttribute, safeUsername)
|
return fmt.Sprintf("(%s=%s)", p.c.UserSearch.UsernameAttribute, safeUsername)
|
||||||
}
|
}
|
||||||
filter := strings.ReplaceAll(p.c.UserSearch.Filter, userSearchFilterInterpolationLocationMarker, safeUsername)
|
return interpolateSearchFilter(p.c.UserSearch.Filter, safeUsername)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Provider) groupSearchFilter(userDN string) string {
|
||||||
|
if len(p.c.GroupSearch.Filter) == 0 {
|
||||||
|
return fmt.Sprintf("(member=%s)", userDN)
|
||||||
|
}
|
||||||
|
return interpolateSearchFilter(p.c.GroupSearch.Filter, userDN)
|
||||||
|
}
|
||||||
|
|
||||||
|
func interpolateSearchFilter(filterFormat, valueToInterpolateIntoFilter string) string {
|
||||||
|
filter := strings.ReplaceAll(filterFormat, searchFilterInterpolationLocationMarker, valueToInterpolateIntoFilter)
|
||||||
if strings.HasPrefix(filter, "(") && strings.HasSuffix(filter, ")") {
|
if strings.HasPrefix(filter, "(") && strings.HasSuffix(filter, ")") {
|
||||||
return filter
|
return filter
|
||||||
}
|
}
|
||||||
@ -375,12 +469,12 @@ func (p *Provider) escapeUsernameForSearchFilter(username string) string {
|
|||||||
return ldap.EscapeFilter(username)
|
return ldap.EscapeFilter(username)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) getSearchResultAttributeValue(attributeName string, fromUserEntry *ldap.Entry, username string) (string, error) {
|
func (p *Provider) getSearchResultAttributeValue(attributeName string, entry *ldap.Entry, username string) (string, error) {
|
||||||
if attributeName == distinguishedNameAttributeName {
|
if attributeName == distinguishedNameAttributeName {
|
||||||
return fromUserEntry.DN, nil
|
return entry.DN, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
attributeValues := fromUserEntry.GetAttributeValues(attributeName)
|
attributeValues := entry.GetAttributeValues(attributeName)
|
||||||
|
|
||||||
if len(attributeValues) != 1 {
|
if len(attributeValues) != 1 {
|
||||||
return "", fmt.Errorf(`found %d values for attribute "%s" while searching for user "%s", but expected 1 result`,
|
return "", fmt.Errorf(`found %d values for attribute "%s" while searching for user "%s", but expected 1 result`,
|
||||||
|
@ -24,22 +24,32 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
testHost = "ldap.example.com:8443"
|
testHost = "ldap.example.com:8443"
|
||||||
testBindUsername = "cn=some-bind-username,dc=pinniped,dc=dev"
|
testBindUsername = "cn=some-bind-username,dc=pinniped,dc=dev"
|
||||||
testBindPassword = "some-bind-password"
|
testBindPassword = "some-bind-password"
|
||||||
testUpstreamUsername = "some-upstream-username"
|
testUpstreamUsername = "some-upstream-username"
|
||||||
testUpstreamPassword = "some-upstream-password"
|
testUpstreamPassword = "some-upstream-password"
|
||||||
testUserSearchBase = "some-upstream-base-dn"
|
testUserSearchBase = "some-upstream-user-base-dn"
|
||||||
testUserSearchFilter = "some-filter={}-and-more-filter={}"
|
testGroupSearchBase = "some-upstream-group-base-dn"
|
||||||
testUserSearchUsernameAttribute = "some-upstream-username-attribute"
|
testUserSearchFilter = "some-user-filter={}-and-more-filter={}"
|
||||||
testUserSearchUIDAttribute = "some-upstream-uid-attribute"
|
testGroupSearchFilter = "some-group-filter={}-and-more-filter={}"
|
||||||
testSearchResultDNValue = "some-upstream-user-dn"
|
testUserSearchUsernameAttribute = "some-upstream-username-attribute"
|
||||||
testSearchResultUsernameAttributeValue = "some-upstream-username-value"
|
testUserSearchUIDAttribute = "some-upstream-uid-attribute"
|
||||||
testSearchResultUIDAttributeValue = "some-upstream-uid-value"
|
testGroupSearchGroupNameAttribute = "some-upstream-group-name-attribute"
|
||||||
|
testUserSearchResultDNValue = "some-upstream-user-dn"
|
||||||
|
testGroupSearchResultDNValue1 = "some-upstream-group-dn1"
|
||||||
|
testGroupSearchResultDNValue2 = "some-upstream-group-dn2"
|
||||||
|
testUserSearchResultUsernameAttributeValue = "some-upstream-username-value"
|
||||||
|
testUserSearchResultUIDAttributeValue = "some-upstream-uid-value"
|
||||||
|
testGroupSearchResultGroupNameAttributeValue1 = "some-upstream-group-name-value1"
|
||||||
|
testGroupSearchResultGroupNameAttributeValue2 = "some-upstream-group-name-value2"
|
||||||
|
|
||||||
|
expectedGroupSearchPageSize = uint32(250)
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
testUserSearchFilterInterpolated = fmt.Sprintf("(some-filter=%s-and-more-filter=%s)", testUpstreamUsername, testUpstreamUsername)
|
testUserSearchFilterInterpolated = fmt.Sprintf("(some-user-filter=%s-and-more-filter=%s)", testUpstreamUsername, testUpstreamUsername)
|
||||||
|
testGroupSearchFilterInterpolated = fmt.Sprintf("(some-group-filter=%s-and-more-filter=%s)", testUserSearchResultDNValue, testUserSearchResultDNValue)
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEndUserAuthentication(t *testing.T) {
|
func TestEndUserAuthentication(t *testing.T) {
|
||||||
@ -56,6 +66,11 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
UsernameAttribute: testUserSearchUsernameAttribute,
|
UsernameAttribute: testUserSearchUsernameAttribute,
|
||||||
UIDAttribute: testUserSearchUIDAttribute,
|
UIDAttribute: testUserSearchUIDAttribute,
|
||||||
},
|
},
|
||||||
|
GroupSearch: GroupSearchConfig{
|
||||||
|
Base: testGroupSearchBase,
|
||||||
|
Filter: testGroupSearchFilter,
|
||||||
|
GroupNameAttribute: testGroupSearchGroupNameAttribute,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if editFunc != nil {
|
if editFunc != nil {
|
||||||
editFunc(config)
|
editFunc(config)
|
||||||
@ -63,7 +78,7 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedSearch := func(editFunc func(r *ldap.SearchRequest)) *ldap.SearchRequest {
|
expectedUserSearch := func(editFunc func(r *ldap.SearchRequest)) *ldap.SearchRequest {
|
||||||
request := &ldap.SearchRequest{
|
request := &ldap.SearchRequest{
|
||||||
BaseDN: testUserSearchBase,
|
BaseDN: testUserSearchBase,
|
||||||
Scope: ldap.ScopeWholeSubtree,
|
Scope: ldap.ScopeWholeSubtree,
|
||||||
@ -73,7 +88,7 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
TypesOnly: false,
|
TypesOnly: false,
|
||||||
Filter: testUserSearchFilterInterpolated,
|
Filter: testUserSearchFilterInterpolated,
|
||||||
Attributes: []string{testUserSearchUsernameAttribute, testUserSearchUIDAttribute},
|
Attributes: []string{testUserSearchUsernameAttribute, testUserSearchUIDAttribute},
|
||||||
Controls: nil,
|
Controls: nil, // don't need paging because we set the SizeLimit so small
|
||||||
}
|
}
|
||||||
if editFunc != nil {
|
if editFunc != nil {
|
||||||
editFunc(request)
|
editFunc(request)
|
||||||
@ -81,6 +96,68 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
return request
|
return request
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expectedGroupSearch := func(editFunc func(r *ldap.SearchRequest)) *ldap.SearchRequest {
|
||||||
|
request := &ldap.SearchRequest{
|
||||||
|
BaseDN: testGroupSearchBase,
|
||||||
|
Scope: ldap.ScopeWholeSubtree,
|
||||||
|
DerefAliases: ldap.NeverDerefAliases,
|
||||||
|
SizeLimit: 0, // unlimited size because we will search with paging
|
||||||
|
TimeLimit: 90,
|
||||||
|
TypesOnly: false,
|
||||||
|
Filter: testGroupSearchFilterInterpolated,
|
||||||
|
Attributes: []string{testGroupSearchGroupNameAttribute},
|
||||||
|
Controls: nil, // nil because ldap.SearchWithPaging() will set the appropriate controls for us
|
||||||
|
}
|
||||||
|
if editFunc != nil {
|
||||||
|
editFunc(request)
|
||||||
|
}
|
||||||
|
return request
|
||||||
|
}
|
||||||
|
|
||||||
|
exampleUserSearchResult := &ldap.SearchResult{
|
||||||
|
Entries: []*ldap.Entry{
|
||||||
|
{
|
||||||
|
DN: testUserSearchResultDNValue,
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{testUserSearchResultUsernameAttributeValue}),
|
||||||
|
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{testUserSearchResultUIDAttributeValue}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
exampleGroupSearchResult := &ldap.SearchResult{
|
||||||
|
Entries: []*ldap.Entry{
|
||||||
|
{
|
||||||
|
DN: testGroupSearchResultDNValue1,
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
ldap.NewEntryAttribute(testGroupSearchGroupNameAttribute, []string{testGroupSearchResultGroupNameAttributeValue1}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DN: testGroupSearchResultDNValue2,
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
ldap.NewEntryAttribute(testGroupSearchGroupNameAttribute, []string{testGroupSearchResultGroupNameAttributeValue2}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Referrals: []string{}, // note that we are not following referrals at this time
|
||||||
|
Controls: []ldap.Control{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// The auth response which matches the exampleUserSearchResult and exampleGroupSearchResult.
|
||||||
|
expectedAuthResponse := func(editFunc func(r *user.DefaultInfo)) *authenticator.Response {
|
||||||
|
u := &user.DefaultInfo{
|
||||||
|
Name: testUserSearchResultUsernameAttributeValue,
|
||||||
|
UID: testUserSearchResultUIDAttributeValue,
|
||||||
|
Groups: []string{testGroupSearchResultGroupNameAttributeValue1, testGroupSearchResultGroupNameAttributeValue2},
|
||||||
|
}
|
||||||
|
if editFunc != nil {
|
||||||
|
editFunc(u)
|
||||||
|
}
|
||||||
|
return &authenticator.Response{User: u}
|
||||||
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
username string
|
username string
|
||||||
@ -102,31 +179,15 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
providerConfig: providerConfig(nil),
|
providerConfig: providerConfig(nil),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(nil)).Return(&ldap.SearchResult{
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
Entries: []*ldap.Entry{
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(nil), expectedGroupSearchPageSize).
|
||||||
{
|
Return(exampleGroupSearchResult, nil).Times(1)
|
||||||
DN: testSearchResultDNValue,
|
|
||||||
Attributes: []*ldap.EntryAttribute{
|
|
||||||
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{testSearchResultUsernameAttributeValue}),
|
|
||||||
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{testSearchResultUIDAttributeValue}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Referrals: []string{}, // note that we are not following referrals at this time
|
|
||||||
Controls: []ldap.Control{}, // TODO are there any response controls that we need to be able to handle?
|
|
||||||
}, nil).Times(1)
|
|
||||||
conn.EXPECT().Close().Times(1)
|
conn.EXPECT().Close().Times(1)
|
||||||
},
|
},
|
||||||
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testSearchResultDNValue, testUpstreamPassword).Times(1)
|
conn.EXPECT().Bind(testUserSearchResultDNValue, testUpstreamPassword).Times(1)
|
||||||
},
|
|
||||||
wantAuthResponse: &authenticator.Response{
|
|
||||||
User: &user.DefaultInfo{
|
|
||||||
Name: testSearchResultUsernameAttributeValue,
|
|
||||||
UID: testSearchResultUIDAttributeValue,
|
|
||||||
Groups: []string{},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
wantAuthResponse: expectedAuthResponse(nil),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when the user search filter is already wrapped by parenthesis then it is not wrapped again",
|
name: "when the user search filter is already wrapped by parenthesis then it is not wrapped again",
|
||||||
@ -137,29 +198,53 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(nil)).Return(&ldap.SearchResult{
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
Entries: []*ldap.Entry{
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(nil), expectedGroupSearchPageSize).
|
||||||
{
|
Return(exampleGroupSearchResult, nil).Times(1)
|
||||||
DN: testSearchResultDNValue,
|
|
||||||
Attributes: []*ldap.EntryAttribute{
|
|
||||||
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{testSearchResultUsernameAttributeValue}),
|
|
||||||
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{testSearchResultUIDAttributeValue}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil).Times(1)
|
|
||||||
conn.EXPECT().Close().Times(1)
|
conn.EXPECT().Close().Times(1)
|
||||||
},
|
},
|
||||||
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testSearchResultDNValue, testUpstreamPassword).Times(1)
|
conn.EXPECT().Bind(testUserSearchResultDNValue, testUpstreamPassword).Times(1)
|
||||||
},
|
},
|
||||||
wantAuthResponse: &authenticator.Response{
|
wantAuthResponse: expectedAuthResponse(nil),
|
||||||
User: &user.DefaultInfo{
|
},
|
||||||
Name: testSearchResultUsernameAttributeValue,
|
{
|
||||||
UID: testSearchResultUIDAttributeValue,
|
name: "when the group search filter is already wrapped by parenthesis then it is not wrapped again",
|
||||||
Groups: []string{},
|
username: testUpstreamUsername,
|
||||||
},
|
password: testUpstreamPassword,
|
||||||
|
providerConfig: providerConfig(func(p *ProviderConfig) {
|
||||||
|
p.GroupSearch.Filter = "(" + testGroupSearchFilter + ")"
|
||||||
|
}),
|
||||||
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(nil), expectedGroupSearchPageSize).
|
||||||
|
Return(exampleGroupSearchResult, nil).Times(1)
|
||||||
|
conn.EXPECT().Close().Times(1)
|
||||||
},
|
},
|
||||||
|
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testUserSearchResultDNValue, testUpstreamPassword).Times(1)
|
||||||
|
},
|
||||||
|
wantAuthResponse: expectedAuthResponse(nil),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "when the group search base is empty then skip the group search entirely",
|
||||||
|
username: testUpstreamUsername,
|
||||||
|
password: testUpstreamPassword,
|
||||||
|
providerConfig: providerConfig(func(p *ProviderConfig) {
|
||||||
|
p.GroupSearch.Base = "" // this configuration means that the user does not want group search to happen
|
||||||
|
}),
|
||||||
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
|
conn.EXPECT().Close().Times(1)
|
||||||
|
},
|
||||||
|
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testUserSearchResultDNValue, testUpstreamPassword).Times(1)
|
||||||
|
},
|
||||||
|
wantAuthResponse: expectedAuthResponse(func(r *user.DefaultInfo) {
|
||||||
|
r.Groups = []string{}
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when the UsernameAttribute is dn and there is a user search filter provided",
|
name: "when the UsernameAttribute is dn and there is a user search filter provided",
|
||||||
@ -170,30 +255,28 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(func(r *ldap.SearchRequest) {
|
conn.EXPECT().Search(expectedUserSearch(func(r *ldap.SearchRequest) {
|
||||||
r.Attributes = []string{testUserSearchUIDAttribute}
|
r.Attributes = []string{testUserSearchUIDAttribute}
|
||||||
})).Return(&ldap.SearchResult{
|
})).Return(&ldap.SearchResult{
|
||||||
Entries: []*ldap.Entry{
|
Entries: []*ldap.Entry{
|
||||||
{
|
{
|
||||||
DN: testSearchResultDNValue,
|
DN: testUserSearchResultDNValue,
|
||||||
Attributes: []*ldap.EntryAttribute{
|
Attributes: []*ldap.EntryAttribute{
|
||||||
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{testSearchResultUIDAttributeValue}),
|
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{testUserSearchResultUIDAttributeValue}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil).Times(1)
|
}, nil).Times(1)
|
||||||
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(nil), expectedGroupSearchPageSize).
|
||||||
|
Return(exampleGroupSearchResult, nil).Times(1)
|
||||||
conn.EXPECT().Close().Times(1)
|
conn.EXPECT().Close().Times(1)
|
||||||
},
|
},
|
||||||
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testSearchResultDNValue, testUpstreamPassword).Times(1)
|
conn.EXPECT().Bind(testUserSearchResultDNValue, testUpstreamPassword).Times(1)
|
||||||
},
|
|
||||||
wantAuthResponse: &authenticator.Response{
|
|
||||||
User: &user.DefaultInfo{
|
|
||||||
Name: testSearchResultDNValue,
|
|
||||||
UID: testSearchResultUIDAttributeValue,
|
|
||||||
Groups: []string{},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
wantAuthResponse: expectedAuthResponse(func(r *user.DefaultInfo) {
|
||||||
|
r.Name = testUserSearchResultDNValue
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when the UIDAttribute is dn",
|
name: "when the UIDAttribute is dn",
|
||||||
@ -204,33 +287,92 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(func(r *ldap.SearchRequest) {
|
conn.EXPECT().Search(expectedUserSearch(func(r *ldap.SearchRequest) {
|
||||||
r.Attributes = []string{testUserSearchUsernameAttribute}
|
r.Attributes = []string{testUserSearchUsernameAttribute}
|
||||||
})).Return(&ldap.SearchResult{
|
})).Return(&ldap.SearchResult{
|
||||||
Entries: []*ldap.Entry{
|
Entries: []*ldap.Entry{
|
||||||
{
|
{
|
||||||
DN: testSearchResultDNValue,
|
DN: testUserSearchResultDNValue,
|
||||||
Attributes: []*ldap.EntryAttribute{
|
Attributes: []*ldap.EntryAttribute{
|
||||||
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{testSearchResultUsernameAttributeValue}),
|
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{testUserSearchResultUsernameAttributeValue}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil).Times(1)
|
}, nil).Times(1)
|
||||||
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(nil), expectedGroupSearchPageSize).
|
||||||
|
Return(exampleGroupSearchResult, nil).Times(1)
|
||||||
conn.EXPECT().Close().Times(1)
|
conn.EXPECT().Close().Times(1)
|
||||||
},
|
},
|
||||||
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testSearchResultDNValue, testUpstreamPassword).Times(1)
|
conn.EXPECT().Bind(testUserSearchResultDNValue, testUpstreamPassword).Times(1)
|
||||||
},
|
|
||||||
wantAuthResponse: &authenticator.Response{
|
|
||||||
User: &user.DefaultInfo{
|
|
||||||
Name: testSearchResultUsernameAttributeValue,
|
|
||||||
UID: testSearchResultDNValue,
|
|
||||||
Groups: []string{},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
wantAuthResponse: expectedAuthResponse(func(r *user.DefaultInfo) {
|
||||||
|
r.UID = testUserSearchResultDNValue
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when Filter is blank it derives a search filter from the UsernameAttribute",
|
name: "when the GroupNameAttribute is dn",
|
||||||
|
username: testUpstreamUsername,
|
||||||
|
password: testUpstreamPassword,
|
||||||
|
providerConfig: providerConfig(func(p *ProviderConfig) {
|
||||||
|
p.GroupSearch.GroupNameAttribute = "dn"
|
||||||
|
}),
|
||||||
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(func(r *ldap.SearchRequest) {
|
||||||
|
r.Attributes = []string{}
|
||||||
|
}), expectedGroupSearchPageSize).
|
||||||
|
Return(exampleGroupSearchResult, nil).Times(1)
|
||||||
|
conn.EXPECT().Close().Times(1)
|
||||||
|
},
|
||||||
|
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testUserSearchResultDNValue, testUpstreamPassword).Times(1)
|
||||||
|
},
|
||||||
|
wantAuthResponse: expectedAuthResponse(func(r *user.DefaultInfo) {
|
||||||
|
r.Groups = []string{testGroupSearchResultDNValue1, testGroupSearchResultDNValue2}
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "when the GroupNameAttribute is empty then it defaults to cn",
|
||||||
|
username: testUpstreamUsername,
|
||||||
|
password: testUpstreamPassword,
|
||||||
|
providerConfig: providerConfig(func(p *ProviderConfig) {
|
||||||
|
p.GroupSearch.GroupNameAttribute = "" // blank means to use cn
|
||||||
|
}),
|
||||||
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(func(r *ldap.SearchRequest) {
|
||||||
|
r.Attributes = []string{"cn"}
|
||||||
|
}), expectedGroupSearchPageSize).
|
||||||
|
Return(&ldap.SearchResult{
|
||||||
|
Entries: []*ldap.Entry{
|
||||||
|
{
|
||||||
|
DN: testGroupSearchResultDNValue1,
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
ldap.NewEntryAttribute("cn", []string{testGroupSearchResultGroupNameAttributeValue1}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DN: testGroupSearchResultDNValue2,
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
ldap.NewEntryAttribute("cn", []string{testGroupSearchResultGroupNameAttributeValue2}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Referrals: []string{}, // note that we are not following referrals at this time
|
||||||
|
Controls: []ldap.Control{},
|
||||||
|
}, nil).Times(1)
|
||||||
|
conn.EXPECT().Close().Times(1)
|
||||||
|
},
|
||||||
|
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testUserSearchResultDNValue, testUpstreamPassword).Times(1)
|
||||||
|
},
|
||||||
|
wantAuthResponse: expectedAuthResponse(nil),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "when user search Filter is blank it derives a search filter from the UsernameAttribute",
|
||||||
username: testUpstreamUsername,
|
username: testUpstreamUsername,
|
||||||
password: testUpstreamPassword,
|
password: testUpstreamPassword,
|
||||||
providerConfig: providerConfig(func(p *ProviderConfig) {
|
providerConfig: providerConfig(func(p *ProviderConfig) {
|
||||||
@ -238,62 +380,101 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(func(r *ldap.SearchRequest) {
|
conn.EXPECT().Search(expectedUserSearch(func(r *ldap.SearchRequest) {
|
||||||
r.Filter = "(" + testUserSearchUsernameAttribute + "=" + testUpstreamUsername + ")"
|
r.Filter = "(" + testUserSearchUsernameAttribute + "=" + testUpstreamUsername + ")"
|
||||||
})).Return(&ldap.SearchResult{
|
})).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
Entries: []*ldap.Entry{
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(nil), expectedGroupSearchPageSize).
|
||||||
{
|
Return(exampleGroupSearchResult, nil).Times(1)
|
||||||
DN: testSearchResultDNValue,
|
|
||||||
Attributes: []*ldap.EntryAttribute{
|
|
||||||
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{testSearchResultUsernameAttributeValue}),
|
|
||||||
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{testSearchResultUIDAttributeValue}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil).Times(1)
|
|
||||||
conn.EXPECT().Close().Times(1)
|
conn.EXPECT().Close().Times(1)
|
||||||
},
|
},
|
||||||
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testSearchResultDNValue, testUpstreamPassword).Times(1)
|
conn.EXPECT().Bind(testUserSearchResultDNValue, testUpstreamPassword).Times(1)
|
||||||
},
|
|
||||||
wantAuthResponse: &authenticator.Response{
|
|
||||||
User: &user.DefaultInfo{
|
|
||||||
Name: testSearchResultUsernameAttributeValue,
|
|
||||||
UID: testSearchResultUIDAttributeValue,
|
|
||||||
Groups: []string{},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
wantAuthResponse: expectedAuthResponse(nil),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when the username has special LDAP search filter characters then they must be properly escaped in the search filter",
|
name: "when group search Filter is blank it uses a default search filter of member={}",
|
||||||
|
username: testUpstreamUsername,
|
||||||
|
password: testUpstreamPassword,
|
||||||
|
providerConfig: providerConfig(func(p *ProviderConfig) {
|
||||||
|
p.GroupSearch.Filter = ""
|
||||||
|
}),
|
||||||
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(func(r *ldap.SearchRequest) {
|
||||||
|
r.Filter = "(member=" + testUserSearchResultDNValue + ")"
|
||||||
|
}), expectedGroupSearchPageSize).
|
||||||
|
Return(exampleGroupSearchResult, nil).Times(1)
|
||||||
|
conn.EXPECT().Close().Times(1)
|
||||||
|
},
|
||||||
|
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testUserSearchResultDNValue, testUpstreamPassword).Times(1)
|
||||||
|
},
|
||||||
|
wantAuthResponse: expectedAuthResponse(nil),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "when the username has special LDAP search filter characters then they must be properly escaped in the search filter, because the username is end-user input",
|
||||||
username: `a&b|c(d)e\f*g`,
|
username: `a&b|c(d)e\f*g`,
|
||||||
password: testUpstreamPassword,
|
password: testUpstreamPassword,
|
||||||
providerConfig: providerConfig(nil),
|
providerConfig: providerConfig(nil),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(func(r *ldap.SearchRequest) {
|
conn.EXPECT().Search(expectedUserSearch(func(r *ldap.SearchRequest) {
|
||||||
r.Filter = fmt.Sprintf("(some-filter=%s-and-more-filter=%s)", `a&b|c\28d\29e\5cf\2ag`, `a&b|c\28d\29e\5cf\2ag`)
|
r.Filter = fmt.Sprintf("(some-user-filter=%s-and-more-filter=%s)", `a&b|c\28d\29e\5cf\2ag`, `a&b|c\28d\29e\5cf\2ag`)
|
||||||
})).Return(&ldap.SearchResult{
|
})).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
Entries: []*ldap.Entry{
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(nil), expectedGroupSearchPageSize).
|
||||||
{
|
Return(exampleGroupSearchResult, nil).Times(1)
|
||||||
DN: testSearchResultDNValue,
|
|
||||||
Attributes: []*ldap.EntryAttribute{
|
|
||||||
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{testSearchResultUsernameAttributeValue}),
|
|
||||||
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{testSearchResultUIDAttributeValue}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil).Times(1)
|
|
||||||
conn.EXPECT().Close().Times(1)
|
conn.EXPECT().Close().Times(1)
|
||||||
},
|
},
|
||||||
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testSearchResultDNValue, testUpstreamPassword).Times(1)
|
conn.EXPECT().Bind(testUserSearchResultDNValue, testUpstreamPassword).Times(1)
|
||||||
|
},
|
||||||
|
wantAuthResponse: expectedAuthResponse(nil),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "group names are sorted to make the result more stable/predictable",
|
||||||
|
username: testUpstreamUsername,
|
||||||
|
password: testUpstreamPassword,
|
||||||
|
providerConfig: providerConfig(nil),
|
||||||
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(nil), expectedGroupSearchPageSize).
|
||||||
|
Return(&ldap.SearchResult{
|
||||||
|
Entries: []*ldap.Entry{
|
||||||
|
{
|
||||||
|
DN: testGroupSearchResultDNValue1,
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
ldap.NewEntryAttribute(testGroupSearchGroupNameAttribute, []string{"c"}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DN: testGroupSearchResultDNValue2,
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
ldap.NewEntryAttribute(testGroupSearchGroupNameAttribute, []string{"a"}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DN: testGroupSearchResultDNValue2,
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
ldap.NewEntryAttribute(testGroupSearchGroupNameAttribute, []string{"b"}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Referrals: []string{}, // note that we are not following referrals at this time
|
||||||
|
Controls: []ldap.Control{},
|
||||||
|
}, nil).Times(1)
|
||||||
|
conn.EXPECT().Close().Times(1)
|
||||||
|
},
|
||||||
|
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testUserSearchResultDNValue, testUpstreamPassword).Times(1)
|
||||||
},
|
},
|
||||||
wantAuthResponse: &authenticator.Response{
|
wantAuthResponse: &authenticator.Response{
|
||||||
User: &user.DefaultInfo{
|
User: &user.DefaultInfo{
|
||||||
Name: testSearchResultUsernameAttributeValue,
|
Name: testUserSearchResultUsernameAttributeValue,
|
||||||
UID: testSearchResultUIDAttributeValue,
|
UID: testUserSearchResultUIDAttributeValue,
|
||||||
Groups: []string{},
|
Groups: []string{"a", "b", "c"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -334,10 +515,24 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
providerConfig: providerConfig(nil),
|
providerConfig: providerConfig(nil),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(nil)).Return(nil, errors.New("some search error")).Times(1)
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(nil, errors.New("some user search error")).Times(1)
|
||||||
conn.EXPECT().Close().Times(1)
|
conn.EXPECT().Close().Times(1)
|
||||||
},
|
},
|
||||||
wantError: fmt.Sprintf(`error searching for user "%s": some search error`, testUpstreamUsername),
|
wantError: fmt.Sprintf(`error searching for user "%s": some user search error`, testUpstreamUsername),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "when searching for the user's groups returns an error",
|
||||||
|
username: testUpstreamUsername,
|
||||||
|
password: testUpstreamPassword,
|
||||||
|
providerConfig: providerConfig(nil),
|
||||||
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(nil), expectedGroupSearchPageSize).
|
||||||
|
Return(nil, errors.New("some group search error")).Times(1)
|
||||||
|
conn.EXPECT().Close().Times(1)
|
||||||
|
},
|
||||||
|
wantError: fmt.Sprintf(`error searching for group memberships for user with DN "%s": some group search error`, testUserSearchResultDNValue),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when searching for the user returns no results",
|
name: "when searching for the user returns no results",
|
||||||
@ -346,7 +541,7 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
providerConfig: providerConfig(nil),
|
providerConfig: providerConfig(nil),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(nil)).Return(&ldap.SearchResult{
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(&ldap.SearchResult{
|
||||||
Entries: []*ldap.Entry{},
|
Entries: []*ldap.Entry{},
|
||||||
}, nil).Times(1)
|
}, nil).Times(1)
|
||||||
conn.EXPECT().Close().Times(1)
|
conn.EXPECT().Close().Times(1)
|
||||||
@ -360,9 +555,9 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
providerConfig: providerConfig(nil),
|
providerConfig: providerConfig(nil),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(nil)).Return(&ldap.SearchResult{
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(&ldap.SearchResult{
|
||||||
Entries: []*ldap.Entry{
|
Entries: []*ldap.Entry{
|
||||||
{DN: testSearchResultDNValue},
|
{DN: testUserSearchResultDNValue},
|
||||||
{DN: "some-other-dn"},
|
{DN: "some-other-dn"},
|
||||||
},
|
},
|
||||||
}, nil).Times(1)
|
}, nil).Times(1)
|
||||||
@ -377,7 +572,7 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
providerConfig: providerConfig(nil),
|
providerConfig: providerConfig(nil),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(nil)).Return(&ldap.SearchResult{
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(&ldap.SearchResult{
|
||||||
Entries: []*ldap.Entry{
|
Entries: []*ldap.Entry{
|
||||||
{DN: ""},
|
{DN: ""},
|
||||||
},
|
},
|
||||||
@ -386,6 +581,39 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantError: fmt.Sprintf(`searching for user "%s" resulted in search result without DN`, testUpstreamUsername),
|
wantError: fmt.Sprintf(`searching for user "%s" resulted in search result without DN`, testUpstreamUsername),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "when searching for the user's groups returns a group without a DN",
|
||||||
|
username: testUpstreamUsername,
|
||||||
|
password: testUpstreamPassword,
|
||||||
|
providerConfig: providerConfig(nil),
|
||||||
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(nil), expectedGroupSearchPageSize).
|
||||||
|
Return(&ldap.SearchResult{
|
||||||
|
Entries: []*ldap.Entry{
|
||||||
|
{
|
||||||
|
DN: testGroupSearchResultDNValue1,
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
ldap.NewEntryAttribute(testGroupSearchGroupNameAttribute, []string{testGroupSearchResultGroupNameAttributeValue1}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DN: "",
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
ldap.NewEntryAttribute(testGroupSearchGroupNameAttribute, []string{testGroupSearchResultGroupNameAttributeValue2}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Referrals: []string{}, // note that we are not following referrals at this time
|
||||||
|
Controls: []ldap.Control{},
|
||||||
|
}, nil).Times(1)
|
||||||
|
conn.EXPECT().Close().Times(1)
|
||||||
|
},
|
||||||
|
wantError: fmt.Sprintf(
|
||||||
|
`searching for group memberships for user with DN "%s" resulted in search result without DN`,
|
||||||
|
testUserSearchResultDNValue),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "when searching for the user returns a user without an expected username attribute",
|
name: "when searching for the user returns a user without an expected username attribute",
|
||||||
username: testUpstreamUsername,
|
username: testUpstreamUsername,
|
||||||
@ -393,19 +621,54 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
providerConfig: providerConfig(nil),
|
providerConfig: providerConfig(nil),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(nil)).Return(&ldap.SearchResult{
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(&ldap.SearchResult{
|
||||||
Entries: []*ldap.Entry{
|
Entries: []*ldap.Entry{
|
||||||
{
|
{
|
||||||
DN: testSearchResultDNValue,
|
DN: testUserSearchResultDNValue,
|
||||||
Attributes: []*ldap.EntryAttribute{
|
Attributes: []*ldap.EntryAttribute{
|
||||||
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{testSearchResultUIDAttributeValue}),
|
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{testUserSearchResultUIDAttributeValue}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil).Times(1)
|
}, nil).Times(1)
|
||||||
conn.EXPECT().Close().Times(1)
|
conn.EXPECT().Close().Times(1)
|
||||||
},
|
},
|
||||||
wantError: fmt.Sprintf(`found 0 values for attribute "%s" while searching for user "%s", but expected 1 result`, testUserSearchUsernameAttribute, testUpstreamUsername),
|
wantError: fmt.Sprintf(
|
||||||
|
`found 0 values for attribute "%s" while searching for user "%s", but expected 1 result`,
|
||||||
|
testUserSearchUsernameAttribute, testUpstreamUsername),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "when searching for the group memberships returns a group without an expected group name attribute",
|
||||||
|
username: testUpstreamUsername,
|
||||||
|
password: testUpstreamPassword,
|
||||||
|
providerConfig: providerConfig(nil),
|
||||||
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(nil), expectedGroupSearchPageSize).
|
||||||
|
Return(&ldap.SearchResult{
|
||||||
|
Entries: []*ldap.Entry{
|
||||||
|
{
|
||||||
|
DN: testGroupSearchResultDNValue1,
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
ldap.NewEntryAttribute(testGroupSearchGroupNameAttribute, []string{testGroupSearchResultGroupNameAttributeValue1}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DN: testGroupSearchResultDNValue1,
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
ldap.NewEntryAttribute("unrelated attribute", []string{"anything"}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Referrals: []string{}, // note that we are not following referrals at this time
|
||||||
|
Controls: []ldap.Control{},
|
||||||
|
}, nil).Times(1)
|
||||||
|
conn.EXPECT().Close().Times(1)
|
||||||
|
},
|
||||||
|
wantError: fmt.Sprintf(
|
||||||
|
`error searching for group memberships for user with DN "%s": found 0 values for attribute "%s" while searching for user "%s", but expected 1 result`,
|
||||||
|
testUserSearchResultDNValue, testGroupSearchGroupNameAttribute, testUserSearchResultDNValue),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when searching for the user returns a user with too many values for the expected username attribute",
|
name: "when searching for the user returns a user with too many values for the expected username attribute",
|
||||||
@ -414,23 +677,61 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
providerConfig: providerConfig(nil),
|
providerConfig: providerConfig(nil),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(nil)).Return(&ldap.SearchResult{
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(&ldap.SearchResult{
|
||||||
Entries: []*ldap.Entry{
|
Entries: []*ldap.Entry{
|
||||||
{
|
{
|
||||||
DN: testSearchResultDNValue,
|
DN: testUserSearchResultDNValue,
|
||||||
Attributes: []*ldap.EntryAttribute{
|
Attributes: []*ldap.EntryAttribute{
|
||||||
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{
|
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{
|
||||||
testSearchResultUsernameAttributeValue,
|
testUserSearchResultUsernameAttributeValue,
|
||||||
"unexpected-additional-value",
|
"unexpected-additional-value",
|
||||||
}),
|
}),
|
||||||
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{testSearchResultUIDAttributeValue}),
|
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{testUserSearchResultUIDAttributeValue}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil).Times(1)
|
}, nil).Times(1)
|
||||||
conn.EXPECT().Close().Times(1)
|
conn.EXPECT().Close().Times(1)
|
||||||
},
|
},
|
||||||
wantError: fmt.Sprintf(`found 2 values for attribute "%s" while searching for user "%s", but expected 1 result`, testUserSearchUsernameAttribute, testUpstreamUsername),
|
wantError: fmt.Sprintf(
|
||||||
|
`found 2 values for attribute "%s" while searching for user "%s", but expected 1 result`,
|
||||||
|
testUserSearchUsernameAttribute, testUpstreamUsername),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "when searching for the group memberships returns a group with too many values for the expected group name attribute",
|
||||||
|
username: testUpstreamUsername,
|
||||||
|
password: testUpstreamPassword,
|
||||||
|
providerConfig: providerConfig(nil),
|
||||||
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(nil), expectedGroupSearchPageSize).
|
||||||
|
Return(&ldap.SearchResult{
|
||||||
|
Entries: []*ldap.Entry{
|
||||||
|
{
|
||||||
|
DN: testGroupSearchResultDNValue1,
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
ldap.NewEntryAttribute(testGroupSearchGroupNameAttribute, []string{testGroupSearchResultGroupNameAttributeValue1}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DN: testGroupSearchResultDNValue1,
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
ldap.NewEntryAttribute(testGroupSearchGroupNameAttribute, []string{
|
||||||
|
testGroupSearchResultGroupNameAttributeValue1,
|
||||||
|
"unexpected-additional-value",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Referrals: []string{}, // note that we are not following referrals at this time
|
||||||
|
Controls: []ldap.Control{},
|
||||||
|
}, nil).Times(1)
|
||||||
|
conn.EXPECT().Close().Times(1)
|
||||||
|
},
|
||||||
|
wantError: fmt.Sprintf(
|
||||||
|
`error searching for group memberships for user with DN "%s": found 2 values for attribute "%s" while searching for user "%s", but expected 1 result`,
|
||||||
|
testUserSearchResultDNValue, testGroupSearchGroupNameAttribute, testUserSearchResultDNValue),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when searching for the user returns a user with an empty value for the expected username attribute",
|
name: "when searching for the user returns a user with an empty value for the expected username attribute",
|
||||||
@ -439,20 +740,55 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
providerConfig: providerConfig(nil),
|
providerConfig: providerConfig(nil),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(nil)).Return(&ldap.SearchResult{
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(&ldap.SearchResult{
|
||||||
Entries: []*ldap.Entry{
|
Entries: []*ldap.Entry{
|
||||||
{
|
{
|
||||||
DN: testSearchResultDNValue,
|
DN: testUserSearchResultDNValue,
|
||||||
Attributes: []*ldap.EntryAttribute{
|
Attributes: []*ldap.EntryAttribute{
|
||||||
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{""}),
|
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{""}),
|
||||||
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{testSearchResultUIDAttributeValue}),
|
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{testUserSearchResultUIDAttributeValue}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil).Times(1)
|
}, nil).Times(1)
|
||||||
conn.EXPECT().Close().Times(1)
|
conn.EXPECT().Close().Times(1)
|
||||||
},
|
},
|
||||||
wantError: fmt.Sprintf(`found empty value for attribute "%s" while searching for user "%s", but expected value to be non-empty`, testUserSearchUsernameAttribute, testUpstreamUsername),
|
wantError: fmt.Sprintf(
|
||||||
|
`found empty value for attribute "%s" while searching for user "%s", but expected value to be non-empty`,
|
||||||
|
testUserSearchUsernameAttribute, testUpstreamUsername),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "when searching for the group memberships returns a group with an empty value for for the expected group name attribute",
|
||||||
|
username: testUpstreamUsername,
|
||||||
|
password: testUpstreamPassword,
|
||||||
|
providerConfig: providerConfig(nil),
|
||||||
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(nil), expectedGroupSearchPageSize).
|
||||||
|
Return(&ldap.SearchResult{
|
||||||
|
Entries: []*ldap.Entry{
|
||||||
|
{
|
||||||
|
DN: testGroupSearchResultDNValue1,
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
ldap.NewEntryAttribute(testGroupSearchGroupNameAttribute, []string{testGroupSearchResultGroupNameAttributeValue1}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DN: testGroupSearchResultDNValue1,
|
||||||
|
Attributes: []*ldap.EntryAttribute{
|
||||||
|
ldap.NewEntryAttribute(testGroupSearchGroupNameAttribute, []string{""}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Referrals: []string{}, // note that we are not following referrals at this time
|
||||||
|
Controls: []ldap.Control{},
|
||||||
|
}, nil).Times(1)
|
||||||
|
conn.EXPECT().Close().Times(1)
|
||||||
|
},
|
||||||
|
wantError: fmt.Sprintf(
|
||||||
|
`error searching for group memberships for user with DN "%s": found empty value for attribute "%s" while searching for user "%s", but expected value to be non-empty`,
|
||||||
|
testUserSearchResultDNValue, testGroupSearchGroupNameAttribute, testUserSearchResultDNValue),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when searching for the user returns a user without an expected UID attribute",
|
name: "when searching for the user returns a user without an expected UID attribute",
|
||||||
@ -461,12 +797,12 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
providerConfig: providerConfig(nil),
|
providerConfig: providerConfig(nil),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(nil)).Return(&ldap.SearchResult{
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(&ldap.SearchResult{
|
||||||
Entries: []*ldap.Entry{
|
Entries: []*ldap.Entry{
|
||||||
{
|
{
|
||||||
DN: testSearchResultDNValue,
|
DN: testUserSearchResultDNValue,
|
||||||
Attributes: []*ldap.EntryAttribute{
|
Attributes: []*ldap.EntryAttribute{
|
||||||
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{testSearchResultUsernameAttributeValue}),
|
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{testUserSearchResultUsernameAttributeValue}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -482,14 +818,14 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
providerConfig: providerConfig(nil),
|
providerConfig: providerConfig(nil),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(nil)).Return(&ldap.SearchResult{
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(&ldap.SearchResult{
|
||||||
Entries: []*ldap.Entry{
|
Entries: []*ldap.Entry{
|
||||||
{
|
{
|
||||||
DN: testSearchResultDNValue,
|
DN: testUserSearchResultDNValue,
|
||||||
Attributes: []*ldap.EntryAttribute{
|
Attributes: []*ldap.EntryAttribute{
|
||||||
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{testSearchResultUsernameAttributeValue}),
|
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{testUserSearchResultUsernameAttributeValue}),
|
||||||
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{
|
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{
|
||||||
testSearchResultUIDAttributeValue,
|
testUserSearchResultUIDAttributeValue,
|
||||||
"unexpected-additional-value",
|
"unexpected-additional-value",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -507,12 +843,12 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
providerConfig: providerConfig(nil),
|
providerConfig: providerConfig(nil),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(nil)).Return(&ldap.SearchResult{
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(&ldap.SearchResult{
|
||||||
Entries: []*ldap.Entry{
|
Entries: []*ldap.Entry{
|
||||||
{
|
{
|
||||||
DN: testSearchResultDNValue,
|
DN: testUserSearchResultDNValue,
|
||||||
Attributes: []*ldap.EntryAttribute{
|
Attributes: []*ldap.EntryAttribute{
|
||||||
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{testSearchResultUsernameAttributeValue}),
|
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{testUserSearchResultUsernameAttributeValue}),
|
||||||
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{""}),
|
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{""}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -529,24 +865,16 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
providerConfig: providerConfig(nil),
|
providerConfig: providerConfig(nil),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(nil)).Return(&ldap.SearchResult{
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
Entries: []*ldap.Entry{
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(nil), expectedGroupSearchPageSize).
|
||||||
{
|
Return(exampleGroupSearchResult, nil).Times(1)
|
||||||
DN: testSearchResultDNValue,
|
|
||||||
Attributes: []*ldap.EntryAttribute{
|
|
||||||
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{testSearchResultUsernameAttributeValue}),
|
|
||||||
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{testSearchResultUIDAttributeValue}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil).Times(1)
|
|
||||||
conn.EXPECT().Close().Times(1)
|
conn.EXPECT().Close().Times(1)
|
||||||
},
|
},
|
||||||
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testSearchResultDNValue, testUpstreamPassword).Return(errors.New("some bind error")).Times(1)
|
conn.EXPECT().Bind(testUserSearchResultDNValue, testUpstreamPassword).Return(errors.New("some bind error")).Times(1)
|
||||||
},
|
},
|
||||||
skipDryRunAuthenticateUser: true,
|
skipDryRunAuthenticateUser: true,
|
||||||
wantError: fmt.Sprintf(`error binding for user "%s" using provided password against DN "%s": some bind error`, testUpstreamUsername, testSearchResultDNValue),
|
wantError: fmt.Sprintf(`error binding for user "%s" using provided password against DN "%s": some bind error`, testUpstreamUsername, testUserSearchResultDNValue),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when binding as the found user returns a specific invalid credentials error",
|
name: "when binding as the found user returns a specific invalid credentials error",
|
||||||
@ -555,17 +883,9 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
providerConfig: providerConfig(nil),
|
providerConfig: providerConfig(nil),
|
||||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||||
conn.EXPECT().Search(expectedSearch(nil)).Return(&ldap.SearchResult{
|
conn.EXPECT().Search(expectedUserSearch(nil)).Return(exampleUserSearchResult, nil).Times(1)
|
||||||
Entries: []*ldap.Entry{
|
conn.EXPECT().SearchWithPaging(expectedGroupSearch(nil), expectedGroupSearchPageSize).
|
||||||
{
|
Return(exampleGroupSearchResult, nil).Times(1)
|
||||||
DN: testSearchResultDNValue,
|
|
||||||
Attributes: []*ldap.EntryAttribute{
|
|
||||||
ldap.NewEntryAttribute(testUserSearchUsernameAttribute, []string{testSearchResultUsernameAttributeValue}),
|
|
||||||
ldap.NewEntryAttribute(testUserSearchUIDAttribute, []string{testSearchResultUIDAttributeValue}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil).Times(1)
|
|
||||||
conn.EXPECT().Close().Times(1)
|
conn.EXPECT().Close().Times(1)
|
||||||
},
|
},
|
||||||
wantUnauthenticated: true,
|
wantUnauthenticated: true,
|
||||||
@ -575,7 +895,7 @@ func TestEndUserAuthentication(t *testing.T) {
|
|||||||
Err: errors.New("some bind error"),
|
Err: errors.New("some bind error"),
|
||||||
ResultCode: ldap.LDAPResultInvalidCredentials,
|
ResultCode: ldap.LDAPResultInvalidCredentials,
|
||||||
}
|
}
|
||||||
conn.EXPECT().Bind(testSearchResultDNValue, testUpstreamPassword).Return(err).Times(1)
|
conn.EXPECT().Bind(testUserSearchResultDNValue, testUpstreamPassword).Return(err).Times(1)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -278,7 +278,7 @@ func TestE2EFullIntegration(t *testing.T) {
|
|||||||
// Add an LDAP upstream IDP and try using it to authenticate during kubectl commands.
|
// Add an LDAP upstream IDP and try using it to authenticate during kubectl commands.
|
||||||
t.Run("with Supervisor LDAP upstream IDP", func(t *testing.T) {
|
t.Run("with Supervisor LDAP upstream IDP", func(t *testing.T) {
|
||||||
expectedUsername := env.SupervisorUpstreamLDAP.TestUserMailAttributeValue
|
expectedUsername := env.SupervisorUpstreamLDAP.TestUserMailAttributeValue
|
||||||
expectedGroups := []string{} // LDAP groups are not implemented yet
|
expectedGroups := env.SupervisorUpstreamLDAP.TestUserDirectGroupsCNs
|
||||||
|
|
||||||
// Create a ClusterRoleBinding to give our test user from the upstream read-only access to the cluster.
|
// Create a ClusterRoleBinding to give our test user from the upstream read-only access to the cluster.
|
||||||
library.CreateTestClusterRoleBinding(t,
|
library.CreateTestClusterRoleBinding(t,
|
||||||
@ -317,6 +317,13 @@ func TestE2EFullIntegration(t *testing.T) {
|
|||||||
UID: env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeName,
|
UID: env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
GroupSearch: idpv1alpha1.LDAPIdentityProviderGroupSearch{
|
||||||
|
Base: env.SupervisorUpstreamLDAP.GroupSearchBase,
|
||||||
|
Filter: "", // use the default value of "member={}"
|
||||||
|
Attributes: idpv1alpha1.LDAPIdentityProviderGroupSearchAttributes{
|
||||||
|
GroupName: "", // use the default value of "cn"
|
||||||
|
},
|
||||||
|
},
|
||||||
}, idpv1alpha1.LDAPPhaseReady)
|
}, idpv1alpha1.LDAPPhaseReady)
|
||||||
|
|
||||||
// Use a specific session cache for this test.
|
// Use a specific session cache for this test.
|
||||||
@ -438,17 +445,10 @@ func requireUserCanUseKubectlWithoutAuthenticatingAgain(
|
|||||||
for _, g := range expectedGroups {
|
for _, g := range expectedGroups {
|
||||||
expectedGroupsAsEmptyInterfaces = append(expectedGroupsAsEmptyInterfaces, g)
|
expectedGroupsAsEmptyInterfaces = append(expectedGroupsAsEmptyInterfaces, g)
|
||||||
}
|
}
|
||||||
require.Equal(t, expectedGroupsAsEmptyInterfaces, idTokenClaims[oidc.DownstreamGroupsClaim])
|
require.ElementsMatch(t, expectedGroupsAsEmptyInterfaces, idTokenClaims[oidc.DownstreamGroupsClaim])
|
||||||
|
|
||||||
expectedYAMLGroups := func() string {
|
expectedGroupsPlusAuthenticated := append([]string{}, expectedGroups...)
|
||||||
var b strings.Builder
|
expectedGroupsPlusAuthenticated = append(expectedGroupsPlusAuthenticated, "system:authenticated")
|
||||||
for _, g := range expectedGroups {
|
|
||||||
b.WriteString("\n")
|
|
||||||
b.WriteString(` - `)
|
|
||||||
b.WriteString(g)
|
|
||||||
}
|
|
||||||
return b.String()
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Confirm we are the right user according to Kube by calling the whoami API.
|
// Confirm we are the right user according to Kube by calling the whoami API.
|
||||||
kubectlCmd3 := exec.CommandContext(ctx, "kubectl", "create", "-f", "-", "-o", "yaml", "--kubeconfig", kubeconfigPath)
|
kubectlCmd3 := exec.CommandContext(ctx, "kubectl", "create", "-f", "-", "-o", "yaml", "--kubeconfig", kubeconfigPath)
|
||||||
@ -456,28 +456,15 @@ func requireUserCanUseKubectlWithoutAuthenticatingAgain(
|
|||||||
kubectlCmd3.Stdin = strings.NewReader(here.Docf(`
|
kubectlCmd3.Stdin = strings.NewReader(here.Docf(`
|
||||||
apiVersion: identity.concierge.%s/v1alpha1
|
apiVersion: identity.concierge.%s/v1alpha1
|
||||||
kind: WhoAmIRequest
|
kind: WhoAmIRequest
|
||||||
`, env.APIGroupSuffix))
|
`, env.APIGroupSuffix))
|
||||||
|
|
||||||
kubectlOutput3, err := kubectlCmd3.CombinedOutput()
|
kubectlOutput3, err := kubectlCmd3.CombinedOutput()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, here.Docf(`
|
whoAmI := deserializeWhoAmIRequest(t, string(kubectlOutput3), env.APIGroupSuffix)
|
||||||
apiVersion: identity.concierge.%s/v1alpha1
|
require.Equal(t, expectedUsername, whoAmI.Status.KubernetesUserInfo.User.Username)
|
||||||
kind: WhoAmIRequest
|
require.ElementsMatch(t, expectedGroupsPlusAuthenticated, whoAmI.Status.KubernetesUserInfo.User.Groups)
|
||||||
metadata:
|
|
||||||
creationTimestamp: null
|
|
||||||
spec: {}
|
|
||||||
status:
|
|
||||||
kubernetesUserInfo:
|
|
||||||
user:
|
|
||||||
groups:%s
|
|
||||||
- system:authenticated
|
|
||||||
username: %s
|
|
||||||
`, env.APIGroupSuffix, expectedYAMLGroups, expectedUsername),
|
|
||||||
string(kubectlOutput3))
|
|
||||||
|
|
||||||
expectedGroupsPlusAuthenticated := append([]string{}, expectedGroups...)
|
|
||||||
expectedGroupsPlusAuthenticated = append(expectedGroupsPlusAuthenticated, "system:authenticated")
|
|
||||||
// Validate that `pinniped whoami` returns the correct identity.
|
// Validate that `pinniped whoami` returns the correct identity.
|
||||||
assertWhoami(
|
assertWhoami(
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -15,7 +15,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
@ -70,7 +69,7 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
password: pinnyPassword,
|
password: pinnyPassword,
|
||||||
provider: upstreamldap.New(*providerConfig(nil)),
|
provider: upstreamldap.New(*providerConfig(nil)),
|
||||||
wantAuthResponse: &authenticator.Response{
|
wantAuthResponse: &authenticator.Response{
|
||||||
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{}},
|
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{"ball-game-players", "seals"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -79,7 +78,7 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
password: pinnyPassword,
|
password: pinnyPassword,
|
||||||
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.Base = "dc=pinniped,dc=dev" })),
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.Base = "dc=pinniped,dc=dev" })),
|
||||||
wantAuthResponse: &authenticator.Response{
|
wantAuthResponse: &authenticator.Response{
|
||||||
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{}},
|
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{"ball-game-players", "seals"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -88,7 +87,7 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
password: pinnyPassword,
|
password: pinnyPassword,
|
||||||
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.Filter = "(cn={})" })),
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.Filter = "(cn={})" })),
|
||||||
wantAuthResponse: &authenticator.Response{
|
wantAuthResponse: &authenticator.Response{
|
||||||
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{}},
|
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{"ball-game-players", "seals"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -100,7 +99,7 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
p.UserSearch.Filter = "cn={}"
|
p.UserSearch.Filter = "cn={}"
|
||||||
})),
|
})),
|
||||||
wantAuthResponse: &authenticator.Response{
|
wantAuthResponse: &authenticator.Response{
|
||||||
User: &user.DefaultInfo{Name: "cn=pinny,ou=users,dc=pinniped,dc=dev", UID: "1000", Groups: []string{}},
|
User: &user.DefaultInfo{Name: "cn=pinny,ou=users,dc=pinniped,dc=dev", UID: "1000", Groups: []string{"ball-game-players", "seals"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -111,7 +110,7 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
p.UserSearch.Filter = "(|(cn={})(mail={}))"
|
p.UserSearch.Filter = "(|(cn={})(mail={}))"
|
||||||
})),
|
})),
|
||||||
wantAuthResponse: &authenticator.Response{
|
wantAuthResponse: &authenticator.Response{
|
||||||
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{}},
|
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{"ball-game-players", "seals"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -122,7 +121,7 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
p.UserSearch.Filter = "(|(cn={})(mail={}))"
|
p.UserSearch.Filter = "(|(cn={})(mail={}))"
|
||||||
})),
|
})),
|
||||||
wantAuthResponse: &authenticator.Response{
|
wantAuthResponse: &authenticator.Response{
|
||||||
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{}},
|
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{"ball-game-players", "seals"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -131,7 +130,7 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
password: pinnyPassword,
|
password: pinnyPassword,
|
||||||
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.UIDAttribute = "dn" })),
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.UIDAttribute = "dn" })),
|
||||||
wantAuthResponse: &authenticator.Response{
|
wantAuthResponse: &authenticator.Response{
|
||||||
User: &user.DefaultInfo{Name: "pinny", UID: "cn=pinny,ou=users,dc=pinniped,dc=dev", Groups: []string{}},
|
User: &user.DefaultInfo{Name: "pinny", UID: "cn=pinny,ou=users,dc=pinniped,dc=dev", Groups: []string{"ball-game-players", "seals"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -140,7 +139,7 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
password: pinnyPassword,
|
password: pinnyPassword,
|
||||||
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.UIDAttribute = "sn" })),
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.UIDAttribute = "sn" })),
|
||||||
wantAuthResponse: &authenticator.Response{
|
wantAuthResponse: &authenticator.Response{
|
||||||
User: &user.DefaultInfo{Name: "pinny", UID: "Seal", Groups: []string{}},
|
User: &user.DefaultInfo{Name: "pinny", UID: "Seal", Groups: []string{"ball-game-players", "seals"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -149,7 +148,7 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
password: pinnyPassword,
|
password: pinnyPassword,
|
||||||
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.UsernameAttribute = "sn" })),
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.UsernameAttribute = "sn" })),
|
||||||
wantAuthResponse: &authenticator.Response{
|
wantAuthResponse: &authenticator.Response{
|
||||||
User: &user.DefaultInfo{Name: "Seal", UID: "1000", Groups: []string{}}, // note that the final answer has case preserved from the entry
|
User: &user.DefaultInfo{Name: "Seal", UID: "1000", Groups: []string{"ball-game-players", "seals"}}, // note that the final answer has case preserved from the entry
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -162,6 +161,75 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
})),
|
})),
|
||||||
wantError: `must specify UserSearch Filter when UserSearch UsernameAttribute is "dn"`,
|
wantError: `must specify UserSearch Filter when UserSearch UsernameAttribute is "dn"`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "group search disabled",
|
||||||
|
username: "pinny",
|
||||||
|
password: pinnyPassword,
|
||||||
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) {
|
||||||
|
p.GroupSearch.Base = ""
|
||||||
|
})),
|
||||||
|
wantAuthResponse: &authenticator.Response{
|
||||||
|
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "group search base causes no groups to be found for user",
|
||||||
|
username: "pinny",
|
||||||
|
password: pinnyPassword,
|
||||||
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) {
|
||||||
|
p.GroupSearch.Base = "ou=users,dc=pinniped,dc=dev" // there are no groups under this part of the tree
|
||||||
|
})),
|
||||||
|
wantAuthResponse: &authenticator.Response{
|
||||||
|
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "using dn as the group name attribute",
|
||||||
|
username: "pinny",
|
||||||
|
password: pinnyPassword,
|
||||||
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) {
|
||||||
|
p.GroupSearch.GroupNameAttribute = "dn"
|
||||||
|
})),
|
||||||
|
wantAuthResponse: &authenticator.Response{
|
||||||
|
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{
|
||||||
|
"cn=ball-game-players,ou=beach-groups,ou=groups,dc=pinniped,dc=dev",
|
||||||
|
"cn=seals,ou=groups,dc=pinniped,dc=dev",
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "using some other custom group name attribute",
|
||||||
|
username: "pinny",
|
||||||
|
password: pinnyPassword,
|
||||||
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) {
|
||||||
|
p.GroupSearch.GroupNameAttribute = "objectClass" // silly example, but still a meaningful test
|
||||||
|
})),
|
||||||
|
wantAuthResponse: &authenticator.Response{
|
||||||
|
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{"groupOfNames", "groupOfNames"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "using a more complex group search filter",
|
||||||
|
username: "pinny",
|
||||||
|
password: pinnyPassword,
|
||||||
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) {
|
||||||
|
p.GroupSearch.Filter = "(&(&(objectClass=groupOfNames)(member={}))(cn=seals))"
|
||||||
|
})),
|
||||||
|
wantAuthResponse: &authenticator.Response{
|
||||||
|
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{"seals"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "using a group filter which causes no groups to be found for the user",
|
||||||
|
username: "pinny",
|
||||||
|
password: pinnyPassword,
|
||||||
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) {
|
||||||
|
p.GroupSearch.Filter = "foobar={}" // foobar is not a valid attribute name for this LDAP server's schema
|
||||||
|
})),
|
||||||
|
wantAuthResponse: &authenticator.Response{
|
||||||
|
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{}},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "when the bind user username is not a valid DN",
|
name: "when the bind user username is not a valid DN",
|
||||||
username: "pinny",
|
username: "pinny",
|
||||||
@ -211,6 +279,13 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.Filter = "*" })),
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.Filter = "*" })),
|
||||||
wantError: `error searching for user "pinny": LDAP Result Code 201 "Filter Compile Error": ldap: error parsing filter`,
|
wantError: `error searching for user "pinny": LDAP Result Code 201 "Filter Compile Error": ldap: error parsing filter`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "when the group search filter does not compile",
|
||||||
|
username: "pinny",
|
||||||
|
password: pinnyPassword,
|
||||||
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.GroupSearch.Filter = "*" })),
|
||||||
|
wantError: `error searching for group memberships for user with DN "cn=pinny,ou=users,dc=pinniped,dc=dev": LDAP Result Code 201 "Filter Compile Error": ldap: error parsing filter`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "when there are too many search results for the user",
|
name: "when there are too many search results for the user",
|
||||||
username: "pinny",
|
username: "pinny",
|
||||||
@ -293,6 +368,13 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.UIDAttribute = "SN" })), // this is case-sensitive
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.UIDAttribute = "SN" })), // this is case-sensitive
|
||||||
wantError: `found 0 values for attribute "SN" while searching for user "pinny", but expected 1 result`,
|
wantError: `found 0 values for attribute "SN" while searching for user "pinny", but expected 1 result`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "when the GroupNameAttribute has the wrong case",
|
||||||
|
username: "pinny",
|
||||||
|
password: pinnyPassword,
|
||||||
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.GroupSearch.GroupNameAttribute = "CN" })), // this is case-sensitive
|
||||||
|
wantError: `error searching for group memberships for user with DN "cn=pinny,ou=users,dc=pinniped,dc=dev": found 0 values for attribute "CN" while searching for user "cn=pinny,ou=users,dc=pinniped,dc=dev", but expected 1 result`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "when the UsernameAttribute is DN and has the wrong case",
|
name: "when the UsernameAttribute is DN and has the wrong case",
|
||||||
username: "pinny",
|
username: "pinny",
|
||||||
@ -313,21 +395,44 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
wantError: `found 0 values for attribute "DN" while searching for user "pinny", but expected 1 result`,
|
wantError: `found 0 values for attribute "DN" while searching for user "pinny", but expected 1 result`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when the search base is invalid",
|
name: "when the GroupNameAttribute is DN and has the wrong case",
|
||||||
|
username: "pinny",
|
||||||
|
password: pinnyPassword,
|
||||||
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) {
|
||||||
|
p.GroupSearch.GroupNameAttribute = "DN" // dn must be lower-case
|
||||||
|
})),
|
||||||
|
wantError: `error searching for group memberships for user with DN "cn=pinny,ou=users,dc=pinniped,dc=dev": found 0 values for attribute "DN" while searching for user "cn=pinny,ou=users,dc=pinniped,dc=dev", but expected 1 result`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "when the user search base is invalid",
|
||||||
username: "pinny",
|
username: "pinny",
|
||||||
password: pinnyPassword,
|
password: pinnyPassword,
|
||||||
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.Base = "invalid-base" })),
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.Base = "invalid-base" })),
|
||||||
wantError: `error searching for user "pinny": LDAP Result Code 34 "Invalid DN Syntax": invalid DN`,
|
wantError: `error searching for user "pinny": LDAP Result Code 34 "Invalid DN Syntax": invalid DN`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when the search base does not exist",
|
name: "when the group search base is invalid",
|
||||||
|
username: "pinny",
|
||||||
|
password: pinnyPassword,
|
||||||
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.GroupSearch.Base = "invalid-base" })),
|
||||||
|
wantError: `error searching for group memberships for user with DN "cn=pinny,ou=users,dc=pinniped,dc=dev": LDAP Result Code 34 "Invalid DN Syntax": invalid DN`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "when the user search base does not exist",
|
||||||
username: "pinny",
|
username: "pinny",
|
||||||
password: pinnyPassword,
|
password: pinnyPassword,
|
||||||
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.Base = "ou=does-not-exist,dc=pinniped,dc=dev" })),
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.Base = "ou=does-not-exist,dc=pinniped,dc=dev" })),
|
||||||
wantError: `error searching for user "pinny": LDAP Result Code 32 "No Such Object": `,
|
wantError: `error searching for user "pinny": LDAP Result Code 32 "No Such Object": `,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when the search base causes no search results",
|
name: "when the group search base does not exist",
|
||||||
|
username: "pinny",
|
||||||
|
password: pinnyPassword,
|
||||||
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.GroupSearch.Base = "ou=does-not-exist,dc=pinniped,dc=dev" })),
|
||||||
|
wantError: `error searching for group memberships for user with DN "cn=pinny,ou=users,dc=pinniped,dc=dev": LDAP Result Code 32 "No Such Object": `,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "when the user search base causes no search results",
|
||||||
username: "pinny",
|
username: "pinny",
|
||||||
password: pinnyPassword,
|
password: pinnyPassword,
|
||||||
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.Base = "ou=groups,dc=pinniped,dc=dev" })),
|
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.Base = "ou=groups,dc=pinniped,dc=dev" })),
|
||||||
@ -425,7 +530,7 @@ func TestSimultaneousRequestsOnSingleProvider(t *testing.T) {
|
|||||||
assert.NoError(t, result.err)
|
assert.NoError(t, result.err)
|
||||||
assert.True(t, result.authenticated, "expected the user to be authenticated, but they were not")
|
assert.True(t, result.authenticated, "expected the user to be authenticated, but they were not")
|
||||||
assert.Equal(t, &authenticator.Response{
|
assert.Equal(t, &authenticator.Response{
|
||||||
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{}},
|
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{"ball-game-players", "seals"}},
|
||||||
}, result.response)
|
}, result.response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -449,6 +554,11 @@ func defaultProviderConfig(env *library.TestEnv, ldapHostPort string) *upstreaml
|
|||||||
UsernameAttribute: "cn",
|
UsernameAttribute: "cn",
|
||||||
UIDAttribute: "uidNumber",
|
UIDAttribute: "uidNumber",
|
||||||
},
|
},
|
||||||
|
GroupSearch: upstreamldap.GroupSearchConfig{
|
||||||
|
Base: "ou=groups,dc=pinniped,dc=dev",
|
||||||
|
Filter: "", // defaults to member={}
|
||||||
|
GroupNameAttribute: "", // defaults to cn
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
requestAuthorization func(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string, httpClient *http.Client)
|
requestAuthorization func(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string, httpClient *http.Client)
|
||||||
wantDownstreamIDTokenSubjectToMatch string
|
wantDownstreamIDTokenSubjectToMatch string
|
||||||
wantDownstreamIDTokenUsernameToMatch string
|
wantDownstreamIDTokenUsernameToMatch string
|
||||||
|
wantDownstreamIDTokenGroups []string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "oidc",
|
name: "oidc",
|
||||||
@ -65,9 +66,10 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
wantDownstreamIDTokenSubjectToMatch: regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
wantDownstreamIDTokenSubjectToMatch: regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||||
// the ID token Username should include the upstream user ID after the upstream issuer name
|
// the ID token Username should include the upstream user ID after the upstream issuer name
|
||||||
wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||||
|
wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ldap with email as username",
|
name: "ldap with email as username and groups names as DNs",
|
||||||
createIDP: func(t *testing.T) {
|
createIDP: func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
secret := library.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
|
secret := library.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
|
||||||
@ -92,6 +94,13 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
UID: env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeName,
|
UID: env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
GroupSearch: idpv1alpha1.LDAPIdentityProviderGroupSearch{
|
||||||
|
Base: env.SupervisorUpstreamLDAP.GroupSearchBase,
|
||||||
|
Filter: "",
|
||||||
|
Attributes: idpv1alpha1.LDAPIdentityProviderGroupSearchAttributes{
|
||||||
|
GroupName: "dn",
|
||||||
|
},
|
||||||
|
},
|
||||||
}, idpv1alpha1.LDAPPhaseReady)
|
}, idpv1alpha1.LDAPPhaseReady)
|
||||||
expectedMsg := fmt.Sprintf(
|
expectedMsg := fmt.Sprintf(
|
||||||
`successfully able to connect to "%s" and bind as user "%s" [validated with Secret "%s" at version "%s"]`,
|
`successfully able to connect to "%s" and bind as user "%s" [validated with Secret "%s" at version "%s"]`,
|
||||||
@ -114,9 +123,10 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
),
|
),
|
||||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||||
wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue),
|
wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserMailAttributeValue),
|
||||||
|
wantDownstreamIDTokenGroups: env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ldap with CN as username ", // try another variation of configuration options
|
name: "ldap with CN as username and group names as CNs", // try another variation of configuration options
|
||||||
createIDP: func(t *testing.T) {
|
createIDP: func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
secret := library.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
|
secret := library.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
|
||||||
@ -141,6 +151,13 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
UID: env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeName,
|
UID: env.SupervisorUpstreamLDAP.TestUserUniqueIDAttributeName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
GroupSearch: idpv1alpha1.LDAPIdentityProviderGroupSearch{
|
||||||
|
Base: env.SupervisorUpstreamLDAP.GroupSearchBase,
|
||||||
|
Filter: "",
|
||||||
|
Attributes: idpv1alpha1.LDAPIdentityProviderGroupSearchAttributes{
|
||||||
|
GroupName: "cn",
|
||||||
|
},
|
||||||
|
},
|
||||||
}, idpv1alpha1.LDAPPhaseReady)
|
}, idpv1alpha1.LDAPPhaseReady)
|
||||||
expectedMsg := fmt.Sprintf(
|
expectedMsg := fmt.Sprintf(
|
||||||
`successfully able to connect to "%s" and bind as user "%s" [validated with Secret "%s" at version "%s"]`,
|
`successfully able to connect to "%s" and bind as user "%s" [validated with Secret "%s" at version "%s"]`,
|
||||||
@ -163,6 +180,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
),
|
),
|
||||||
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
// the ID token Username should have been pulled from the requested UserSearch.Attributes.Username attribute
|
||||||
wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserDN),
|
wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamLDAP.TestUserDN),
|
||||||
|
wantDownstreamIDTokenGroups: env.SupervisorUpstreamLDAP.TestUserDirectGroupsCNs,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@ -173,6 +191,7 @@ func TestSupervisorLogin(t *testing.T) {
|
|||||||
test.requestAuthorization,
|
test.requestAuthorization,
|
||||||
test.wantDownstreamIDTokenSubjectToMatch,
|
test.wantDownstreamIDTokenSubjectToMatch,
|
||||||
test.wantDownstreamIDTokenUsernameToMatch,
|
test.wantDownstreamIDTokenUsernameToMatch,
|
||||||
|
test.wantDownstreamIDTokenGroups,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -207,7 +226,7 @@ func testSupervisorLogin(
|
|||||||
t *testing.T,
|
t *testing.T,
|
||||||
createIDP func(t *testing.T),
|
createIDP func(t *testing.T),
|
||||||
requestAuthorization func(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string, httpClient *http.Client),
|
requestAuthorization func(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string, httpClient *http.Client),
|
||||||
wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch string,
|
wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch string, wantDownstreamIDTokenGroups []string,
|
||||||
) {
|
) {
|
||||||
env := library.IntegrationEnv(t)
|
env := library.IntegrationEnv(t)
|
||||||
|
|
||||||
@ -350,7 +369,7 @@ func testSupervisorLogin(
|
|||||||
expectedIDTokenClaims := []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "nonce", "rat", "username", "groups"}
|
expectedIDTokenClaims := []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "nonce", "rat", "username", "groups"}
|
||||||
verifyTokenResponse(t,
|
verifyTokenResponse(t,
|
||||||
tokenResponse, discovery, downstreamOAuth2Config, nonceParam,
|
tokenResponse, discovery, downstreamOAuth2Config, nonceParam,
|
||||||
expectedIDTokenClaims, wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch)
|
expectedIDTokenClaims, wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch, wantDownstreamIDTokenGroups)
|
||||||
|
|
||||||
// token exchange on the original token
|
// token exchange on the original token
|
||||||
doTokenExchange(t, &downstreamOAuth2Config, tokenResponse, httpClient, discovery)
|
doTokenExchange(t, &downstreamOAuth2Config, tokenResponse, httpClient, discovery)
|
||||||
@ -363,7 +382,7 @@ func testSupervisorLogin(
|
|||||||
expectedIDTokenClaims = append(expectedIDTokenClaims, "at_hash")
|
expectedIDTokenClaims = append(expectedIDTokenClaims, "at_hash")
|
||||||
verifyTokenResponse(t,
|
verifyTokenResponse(t,
|
||||||
refreshedTokenResponse, discovery, downstreamOAuth2Config, "",
|
refreshedTokenResponse, discovery, downstreamOAuth2Config, "",
|
||||||
expectedIDTokenClaims, wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch)
|
expectedIDTokenClaims, wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch, wantDownstreamIDTokenGroups)
|
||||||
|
|
||||||
require.NotEqual(t, tokenResponse.AccessToken, refreshedTokenResponse.AccessToken)
|
require.NotEqual(t, tokenResponse.AccessToken, refreshedTokenResponse.AccessToken)
|
||||||
require.NotEqual(t, tokenResponse.RefreshToken, refreshedTokenResponse.RefreshToken)
|
require.NotEqual(t, tokenResponse.RefreshToken, refreshedTokenResponse.RefreshToken)
|
||||||
@ -380,7 +399,7 @@ func verifyTokenResponse(
|
|||||||
downstreamOAuth2Config oauth2.Config,
|
downstreamOAuth2Config oauth2.Config,
|
||||||
nonceParam nonce.Nonce,
|
nonceParam nonce.Nonce,
|
||||||
expectedIDTokenClaims []string,
|
expectedIDTokenClaims []string,
|
||||||
wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch string,
|
wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch string, wantDownstreamIDTokenGroups []string,
|
||||||
) {
|
) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@ -415,6 +434,9 @@ func verifyTokenResponse(
|
|||||||
// Check username claim of the ID token.
|
// Check username claim of the ID token.
|
||||||
require.Regexp(t, wantDownstreamIDTokenUsernameToMatch, idTokenClaims["username"].(string))
|
require.Regexp(t, wantDownstreamIDTokenUsernameToMatch, idTokenClaims["username"].(string))
|
||||||
|
|
||||||
|
// Check the groups claim.
|
||||||
|
require.ElementsMatch(t, wantDownstreamIDTokenGroups, idTokenClaims["groups"])
|
||||||
|
|
||||||
// Some light verification of the other tokens that were returned.
|
// Some light verification of the other tokens that were returned.
|
||||||
require.NotEmpty(t, tokenResponse.AccessToken)
|
require.NotEmpty(t, tokenResponse.AccessToken)
|
||||||
require.Equal(t, "bearer", tokenResponse.TokenType)
|
require.Equal(t, "bearer", tokenResponse.TokenType)
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
@ -71,18 +72,21 @@ type TestOIDCUpstream struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TestLDAPUpstream struct {
|
type TestLDAPUpstream struct {
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
CABundle string `json:"caBundle"`
|
CABundle string `json:"caBundle"`
|
||||||
BindUsername string `json:"bindUsername"`
|
BindUsername string `json:"bindUsername"`
|
||||||
BindPassword string `json:"bindPassword"`
|
BindPassword string `json:"bindPassword"`
|
||||||
UserSearchBase string `json:"userSearchBase"`
|
UserSearchBase string `json:"userSearchBase"`
|
||||||
TestUserDN string `json:"testUserDN"`
|
GroupSearchBase string `json:"groupSearchBase"`
|
||||||
TestUserCN string `json:"testUserCN"`
|
TestUserDN string `json:"testUserDN"`
|
||||||
TestUserPassword string `json:"testUserPassword"`
|
TestUserCN string `json:"testUserCN"`
|
||||||
TestUserMailAttributeName string `json:"testUserMailAttributeName"`
|
TestUserPassword string `json:"testUserPassword"`
|
||||||
TestUserMailAttributeValue string `json:"testUserMailAttributeValue"`
|
TestUserMailAttributeName string `json:"testUserMailAttributeName"`
|
||||||
TestUserUniqueIDAttributeName string `json:"testUserUniqueIDAttributeName"`
|
TestUserMailAttributeValue string `json:"testUserMailAttributeValue"`
|
||||||
TestUserUniqueIDAttributeValue string `json:"testUserUniqueIDAttributeValue"`
|
TestUserUniqueIDAttributeName string `json:"testUserUniqueIDAttributeName"`
|
||||||
|
TestUserUniqueIDAttributeValue string `json:"testUserUniqueIDAttributeValue"`
|
||||||
|
TestUserDirectGroupsCNs []string `json:"testUserDirectGroupsCNs"`
|
||||||
|
TestUserDirectGroupsDNs []string `json:"testUserDirectGroupsDNs"` //nolint:golint // this is "distinguished names", not "DNS"
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProxyEnv returns a set of environment variable strings (e.g., to combine with os.Environ()) which set up the configured test HTTP proxy.
|
// ProxyEnv returns a set of environment variable strings (e.g., to combine with os.Environ()) which set up the configured test HTTP proxy.
|
||||||
@ -240,14 +244,20 @@ func loadEnvVars(t *testing.T, result *TestEnv) {
|
|||||||
BindUsername: needEnv(t, "PINNIPED_TEST_LDAP_BIND_ACCOUNT_USERNAME"),
|
BindUsername: needEnv(t, "PINNIPED_TEST_LDAP_BIND_ACCOUNT_USERNAME"),
|
||||||
BindPassword: needEnv(t, "PINNIPED_TEST_LDAP_BIND_ACCOUNT_PASSWORD"),
|
BindPassword: needEnv(t, "PINNIPED_TEST_LDAP_BIND_ACCOUNT_PASSWORD"),
|
||||||
UserSearchBase: needEnv(t, "PINNIPED_TEST_LDAP_USERS_SEARCH_BASE"),
|
UserSearchBase: needEnv(t, "PINNIPED_TEST_LDAP_USERS_SEARCH_BASE"),
|
||||||
|
GroupSearchBase: needEnv(t, "PINNIPED_TEST_LDAP_GROUPS_SEARCH_BASE"),
|
||||||
TestUserDN: needEnv(t, "PINNIPED_TEST_LDAP_USER_DN"),
|
TestUserDN: needEnv(t, "PINNIPED_TEST_LDAP_USER_DN"),
|
||||||
TestUserCN: needEnv(t, "PINNIPED_TEST_LDAP_USER_CN"),
|
TestUserCN: needEnv(t, "PINNIPED_TEST_LDAP_USER_CN"),
|
||||||
TestUserUniqueIDAttributeName: needEnv(t, "PINNIPED_TEST_LDAP_USER_UNIQUE_ID_ATTRIBUTE_NAME"),
|
TestUserUniqueIDAttributeName: needEnv(t, "PINNIPED_TEST_LDAP_USER_UNIQUE_ID_ATTRIBUTE_NAME"),
|
||||||
TestUserUniqueIDAttributeValue: needEnv(t, "PINNIPED_TEST_LDAP_USER_UNIQUE_ID_ATTRIBUTE_VALUE"),
|
TestUserUniqueIDAttributeValue: needEnv(t, "PINNIPED_TEST_LDAP_USER_UNIQUE_ID_ATTRIBUTE_VALUE"),
|
||||||
TestUserMailAttributeName: needEnv(t, "PINNIPED_TEST_LDAP_USER_EMAIL_ATTRIBUTE_NAME"),
|
TestUserMailAttributeName: needEnv(t, "PINNIPED_TEST_LDAP_USER_EMAIL_ATTRIBUTE_NAME"),
|
||||||
TestUserMailAttributeValue: needEnv(t, "PINNIPED_TEST_LDAP_USER_EMAIL_ATTRIBUTE_VALUE"),
|
TestUserMailAttributeValue: needEnv(t, "PINNIPED_TEST_LDAP_USER_EMAIL_ATTRIBUTE_VALUE"),
|
||||||
|
TestUserDirectGroupsCNs: filterEmpty(strings.Split(needEnv(t, "PINNIPED_TEST_LDAP_EXPECTED_DIRECT_GROUPS_CN"), ";")),
|
||||||
|
TestUserDirectGroupsDNs: filterEmpty(strings.Split(needEnv(t, "PINNIPED_TEST_LDAP_EXPECTED_DIRECT_GROUPS_DN"), ";")),
|
||||||
TestUserPassword: needEnv(t, "PINNIPED_TEST_LDAP_USER_PASSWORD"),
|
TestUserPassword: needEnv(t, "PINNIPED_TEST_LDAP_USER_PASSWORD"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sort.Strings(result.SupervisorUpstreamLDAP.TestUserDirectGroupsCNs)
|
||||||
|
sort.Strings(result.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *TestEnv) HasCapability(cap Capability) bool {
|
func (e *TestEnv) HasCapability(cap Capability) bool {
|
||||||
|
Loading…
Reference in New Issue
Block a user