Skip to content

Commit

Permalink
Add empty anonymous group search configs
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonodonnell committed Mar 28, 2024
1 parent 83a4092 commit f5e355f
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 12 deletions.
12 changes: 10 additions & 2 deletions ldap/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ type Attribute struct {
// the WithGroups option is specified, it will also return the user's groups
// from the directory.
//
// Supported options: WithUserAttributes, WithGroups, WithDialer, WithURLs, WithLowerUserAttributeKeys
// Supported options: WithUserAttributes, WithGroups, WithDialer, WithURLs,
// WithLowerUserAttributeKeys, WithEmptyAnonymousGroupSearch
func (c *Client) Authenticate(ctx context.Context, username, password string, opt ...Option) (*AuthResult, error) {
const op = "ldap.(Client).Authenticate"
if username == "" {
Expand Down Expand Up @@ -289,7 +290,14 @@ func (c *Client) Authenticate(ctx context.Context, username, password string, op
}

if c.conf.AnonymousGroupSearch {
if err := c.conn.UnauthenticatedBind(userDN); err != nil {
// Some LDAP servers will reject anonymous group searches if userDN is
// included in the query.
dn := userDN
if c.conf.AllowEmptyAnonymousGroupSearch || opts.withEmptyAnonymousGroupSearch {
dn = ""
}

if err := c.conn.UnauthenticatedBind(dn); err != nil {
return nil, fmt.Errorf("%s: group search anonymous bind failed: %w", op, err)
}
}
Expand Down
67 changes: 67 additions & 0 deletions ldap/client_exported_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,73 @@ func TestClient_Authenticate(t *testing.T) {
opts: []ldap.Option{ldap.WithGroups()},
wantGroups: []string{groups[0].DN},
},
{
name: "success-with-anon-bind-groups-empty-userdn",
username: "alice",
password: "password",
clientConfig: &ldap.ClientConfig{
URLs: []string{fmt.Sprintf("ldaps://127.0.0.1:%d", td.Port())},
Certificates: []string{td.Cert()},
DiscoverDN: true,
UserDN: testdirectory.DefaultUserDN,
GroupDN: testdirectory.DefaultGroupDN,
UseTokenGroups: true,
AnonymousGroupSearch: true,
AllowEmptyAnonymousGroupSearch: true,
},
opts: []ldap.Option{ldap.WithGroups()},
wantGroups: []string{groups[0].DN},
},
{
name: "success-with-anon-bind-groups-empty-userdn-opt",
username: "alice",
password: "password",
clientConfig: &ldap.ClientConfig{
URLs: []string{fmt.Sprintf("ldaps://127.0.0.1:%d", td.Port())},
Certificates: []string{td.Cert()},
DiscoverDN: true,
UserDN: testdirectory.DefaultUserDN,
GroupDN: testdirectory.DefaultGroupDN,
UseTokenGroups: true,
AnonymousGroupSearch: true,
},
opts: []ldap.Option{ldap.WithGroups(), ldap.WithEmptyAnonymousGroupSearch()},
wantGroups: []string{groups[0].DN},
},
{
name: "success-with-anon-bind-upn-domain-empty-userdn",
username: "eve",
password: "password",
clientConfig: &ldap.ClientConfig{
URLs: []string{fmt.Sprintf("ldaps://127.0.0.1:%d", td.Port())},
Certificates: []string{td.Cert()},
DiscoverDN: true,
UserDN: testdirectory.DefaultUserDN,
GroupDN: testdirectory.DefaultGroupDN,
UPNDomain: "example.com",
AnonymousGroupSearch: true,
AllowEmptyAnonymousGroupSearch: true,
},
opts: []ldap.Option{ldap.WithGroups()},
wantGroups: []string{groups[0].DN},
},
{
name: "success-with-anon-bind-upn-domain-empty-userdn-opt",
username: "eve",
password: "password",
clientConfig: &ldap.ClientConfig{
URLs: []string{fmt.Sprintf("ldaps://127.0.0.1:%d", td.Port())},
Certificates: []string{td.Cert()},
DiscoverDN: true,
UserDN: testdirectory.DefaultUserDN,
GroupDN: testdirectory.DefaultGroupDN,
UPNDomain: "example.com",
AnonymousGroupSearch: true,
AllowEmptyAnonymousGroupSearch: true,
},
opts: []ldap.Option{ldap.WithGroups(), ldap.WithEmptyAnonymousGroupSearch()},
wantGroups: []string{groups[0].DN},
},
{
name: "success-with-binddn",
username: "alice",
Expand Down
4 changes: 4 additions & 0 deletions ldap/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ type ClientConfig struct {
// for the initial connection test).
AnonymousGroupSearch bool `json:"anonymous_group_search"`

// AllowEmptyAnonymousGroupSearches: if true it removes the userDN from
// unauthenticated group searches (optional).
AllowEmptyAnonymousGroupSearch bool `json:"allow_empty_anonymous_group_search"`

// GroupDN is the distinguished name to use as base when searching for group
// membership (eg: ou=Groups,dc=example,dc=org)
GroupDN string `json:"groupdn"`
Expand Down
31 changes: 21 additions & 10 deletions ldap/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ package ldap
type Option func(interface{})

type configOptions struct {
withURLs []string
withInsecureTLS bool
withTLSMinVersion string
withTLSMaxVersion string
withCertificates []string
withClientTLSCert string
withClientTLSKey string
withGroups bool
withUserAttributes bool
withLowerUserAttributeKeys bool
withURLs []string
withInsecureTLS bool
withTLSMinVersion string
withTLSMaxVersion string
withCertificates []string
withClientTLSCert string
withClientTLSKey string
withGroups bool
withUserAttributes bool
withLowerUserAttributeKeys bool
withEmptyAnonymousGroupSearch bool
}

func configDefaults() configOptions {
Expand Down Expand Up @@ -88,6 +89,16 @@ func WithLowerUserAttributeKeys() Option {
}
}

// WithEmptyAnonymousGroupSearch removes userDN from anonymous group searches.
func WithEmptyAnonymousGroupSearch() Option {
return func(o interface{}) {
switch v := o.(type) {
case *configOptions:
v.withEmptyAnonymousGroupSearch = true
}
}
}

func withTLSMinVersion(version string) Option {
return func(o interface{}) {
switch v := o.(type) {
Expand Down

0 comments on commit f5e355f

Please sign in to comment.