mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-09-19 06:31:47 +00:00
Use base64 binary-encoded value as UID for LDAP
This is to allow the use of binary LDAP entry attributes as the UID. For example, a user might like to configure AD’s objectGUID or maybe objectSid attributes as the UID attribute. This negatively impacts the readability of the UID when it did not come from a binary value, but we're considering this an okay trade-off to keep things simple for now. In the future, we may offer more customizable encoding options for binary attributes. These UIDs are currently only used in the downstream OIDC `sub` claim. They do not effect the user's identity on the Kubernetes cluster, which is only based on their mapped username and group memberships from the upstream identity provider. We are not currently supporting any special encoding for those username and group name LDAP attributes, so their values in the LDAP entry must be ASCII or UTF-8 in order for them to be interpreted correctly.
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
@@ -417,7 +418,9 @@ func (p *Provider) searchAndBindUser(conn Conn, username string, bindFunc func(c
|
||||
return "", "", nil, err
|
||||
}
|
||||
|
||||
mappedUID, err := p.getSearchResultAttributeValue(p.c.UserSearch.UIDAttribute, userEntry, username)
|
||||
// We would like to support binary typed attributes for UIDs, so always read them as binary and encode them,
|
||||
// even when the attribute may not be binary.
|
||||
mappedUID, err := p.getSearchResultAttributeRawValueEncoded(p.c.UserSearch.UIDAttribute, userEntry, username)
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
@@ -526,6 +529,30 @@ func (p *Provider) escapeUsernameForSearchFilter(username string) string {
|
||||
return ldap.EscapeFilter(username)
|
||||
}
|
||||
|
||||
// Returns the (potentially) binary data of the attribute's value, base64 URL encoded.
|
||||
func (p *Provider) getSearchResultAttributeRawValueEncoded(attributeName string, entry *ldap.Entry, username string) (string, error) {
|
||||
if attributeName == distinguishedNameAttributeName {
|
||||
return base64.RawURLEncoding.EncodeToString([]byte(entry.DN)), nil
|
||||
}
|
||||
|
||||
attributeValues := entry.GetRawAttributeValues(attributeName)
|
||||
|
||||
if len(attributeValues) != 1 {
|
||||
return "", fmt.Errorf(`found %d values for attribute "%s" while searching for user "%s", but expected 1 result`,
|
||||
len(attributeValues), attributeName, username,
|
||||
)
|
||||
}
|
||||
|
||||
attributeValue := attributeValues[0]
|
||||
if len(attributeValue) == 0 {
|
||||
return "", fmt.Errorf(`found empty value for attribute "%s" while searching for user "%s", but expected value to be non-empty`,
|
||||
attributeName, username,
|
||||
)
|
||||
}
|
||||
|
||||
return base64.RawURLEncoding.EncodeToString(attributeValue), nil
|
||||
}
|
||||
|
||||
func (p *Provider) getSearchResultAttributeValue(attributeName string, entry *ldap.Entry, username string) (string, error) {
|
||||
if attributeName == distinguishedNameAttributeName {
|
||||
return entry.DN, nil
|
||||
|
||||
@@ -6,6 +6,7 @@ package upstreamldap
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
@@ -153,7 +154,7 @@ func TestEndUserAuthentication(t *testing.T) {
|
||||
expectedAuthResponse := func(editFunc func(r *user.DefaultInfo)) *authenticator.Response {
|
||||
u := &user.DefaultInfo{
|
||||
Name: testUserSearchResultUsernameAttributeValue,
|
||||
UID: testUserSearchResultUIDAttributeValue,
|
||||
UID: base64.RawURLEncoding.EncodeToString([]byte(testUserSearchResultUIDAttributeValue)),
|
||||
Groups: []string{testGroupSearchResultGroupNameAttributeValue1, testGroupSearchResultGroupNameAttributeValue2},
|
||||
}
|
||||
if editFunc != nil {
|
||||
@@ -311,7 +312,7 @@ func TestEndUserAuthentication(t *testing.T) {
|
||||
conn.EXPECT().Bind(testUserSearchResultDNValue, testUpstreamPassword).Times(1)
|
||||
},
|
||||
wantAuthResponse: expectedAuthResponse(func(r *user.DefaultInfo) {
|
||||
r.UID = testUserSearchResultDNValue
|
||||
r.UID = base64.RawURLEncoding.EncodeToString([]byte(testUserSearchResultDNValue))
|
||||
}),
|
||||
},
|
||||
{
|
||||
@@ -477,7 +478,7 @@ func TestEndUserAuthentication(t *testing.T) {
|
||||
wantAuthResponse: &authenticator.Response{
|
||||
User: &user.DefaultInfo{
|
||||
Name: testUserSearchResultUsernameAttributeValue,
|
||||
UID: testUserSearchResultUIDAttributeValue,
|
||||
UID: base64.RawURLEncoding.EncodeToString([]byte(testUserSearchResultUIDAttributeValue)),
|
||||
Groups: []string{"a", "b", "c"},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user