Skip to content

Commit 84db9f7

Browse files
committed
Fix group filter for ldap source sync
Unified ldap group membership lookup for authentication and source sync flows Replaced custom group membership lookup (used for authentication flow) with an existing listLdapGroupMemberships method (used for source sync flow) Modified listLdapGroupMemberships and getUserAttributeListedInGroup in a way group lookup could be called separately Added user filtering based on a group membership for a source sync Added tests to cover this logic
1 parent b5b3e07 commit 84db9f7

File tree

2 files changed

+159
-77
lines changed

2 files changed

+159
-77
lines changed

services/auth/source/ldap/source_search.go

+71-66
Original file line numberDiff line numberDiff line change
@@ -196,22 +196,39 @@ func checkRestricted(l *ldap.Conn, ls *Source, userDN string) bool {
196196
}
197197

198198
// List all group memberships of a user
199-
func (source *Source) listLdapGroupMemberships(l *ldap.Conn, uid string) []string {
199+
func (source *Source) listLdapGroupMemberships(l *ldap.Conn, uid string, applyGroupFilter bool) []string {
200200
var ldapGroups []string
201-
groupFilter := fmt.Sprintf("(%s=%s)", source.GroupMemberUID, ldap.EscapeFilter(uid))
201+
var searchFilter string
202+
203+
groupFilter, ok := source.sanitizedGroupFilter(source.GroupFilter)
204+
if !ok {
205+
return ldapGroups
206+
}
207+
208+
groupDN, ok := source.sanitizedGroupDN(source.GroupDN)
209+
if !ok {
210+
return ldapGroups
211+
}
212+
213+
if applyGroupFilter {
214+
searchFilter = fmt.Sprintf("(&(%s)(%s=%s))", groupFilter, source.GroupMemberUID, ldap.EscapeFilter(uid))
215+
} else {
216+
searchFilter = fmt.Sprintf("(%s=%s)", source.GroupMemberUID, ldap.EscapeFilter(uid))
217+
}
218+
202219
result, err := l.Search(ldap.NewSearchRequest(
203-
source.GroupDN,
220+
groupDN,
204221
ldap.ScopeWholeSubtree,
205222
ldap.NeverDerefAliases,
206223
0,
207224
0,
208225
false,
209-
groupFilter,
226+
searchFilter,
210227
[]string{},
211228
nil,
212229
))
213230
if err != nil {
214-
log.Error("Failed group search using filter[%s]: %v", groupFilter, err)
231+
log.Error("Failed group search in LDAP with filter [%s]: %v", searchFilter, err)
215232
return ldapGroups
216233
}
217234

@@ -238,9 +255,7 @@ func (source *Source) mapLdapGroupsToTeams() map[string]map[string][]string {
238255
}
239256

240257
// getMappedMemberships : returns the organizations and teams to modify the users membership
241-
func (source *Source) getMappedMemberships(l *ldap.Conn, uid string) (map[string][]string, map[string][]string) {
242-
// get all LDAP group memberships for user
243-
usersLdapGroups := source.listLdapGroupMemberships(l, uid)
258+
func (source *Source) getMappedMemberships(usersLdapGroups []string, uid string) (map[string][]string, map[string][]string) {
244259
// unmarshall LDAP group team map from configs
245260
ldapGroupsToTeams := source.mapLdapGroupsToTeams()
246261
membershipsToAdd := map[string][]string{}
@@ -260,6 +275,14 @@ func (source *Source) getMappedMemberships(l *ldap.Conn, uid string) (map[string
260275
return membershipsToAdd, membershipsToRemove
261276
}
262277

278+
func (source *Source) getUserAttributeListedInGroup(entry *ldap.Entry) string {
279+
if strings.ToLower(source.UserUID) == "dn" {
280+
return entry.DN
281+
}
282+
283+
return entry.GetAttributeValue(source.UserUID)
284+
}
285+
263286
// SearchEntry : search an LDAP source if an entry (name, passwd) is valid and in the specific filter
264287
func (source *Source) SearchEntry(name, passwd string, directBind bool) *SearchResult {
265288
// See https://tools.ietf.org/search/rfc4513#section-5.1.2
@@ -375,58 +398,30 @@ func (source *Source) SearchEntry(name, passwd string, directBind bool) *SearchR
375398
firstname := sr.Entries[0].GetAttributeValue(source.AttributeName)
376399
surname := sr.Entries[0].GetAttributeValue(source.AttributeSurname)
377400
mail := sr.Entries[0].GetAttributeValue(source.AttributeMail)
378-
uid := sr.Entries[0].GetAttributeValue(source.UserUID)
379-
if source.UserUID == "dn" || source.UserUID == "DN" {
380-
uid = sr.Entries[0].DN
381-
}
382401

383-
// Check group membership
384-
if source.GroupsEnabled && source.GroupFilter != "" {
385-
groupFilter, ok := source.sanitizedGroupFilter(source.GroupFilter)
386-
if !ok {
387-
return nil
388-
}
389-
groupDN, ok := source.sanitizedGroupDN(source.GroupDN)
390-
if !ok {
391-
return nil
392-
}
402+
teamsToAdd := make(map[string][]string)
403+
teamsToRemove := make(map[string][]string)
393404

394-
log.Trace("Fetching groups '%v' with filter '%s' and base '%s'", source.GroupMemberUID, groupFilter, groupDN)
395-
groupSearch := ldap.NewSearchRequest(
396-
groupDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, groupFilter,
397-
[]string{source.GroupMemberUID},
398-
nil)
405+
// Check group membership
406+
if source.GroupsEnabled {
407+
userAttributeListedInGroup := source.getUserAttributeListedInGroup(sr.Entries[0])
408+
usersLdapGroups := source.listLdapGroupMemberships(l, userAttributeListedInGroup, true)
399409

400-
srg, err := l.Search(groupSearch)
401-
if err != nil {
402-
log.Error("LDAP group search failed: %v", err)
403-
return nil
404-
} else if len(srg.Entries) < 1 {
405-
log.Error("LDAP group search failed: 0 entries")
410+
if source.GroupFilter != "" && len(usersLdapGroups) == 0 {
406411
return nil
407412
}
408413

409-
isMember := false
410-
Entries:
411-
for _, group := range srg.Entries {
412-
for _, member := range group.GetAttributeValues(source.GroupMemberUID) {
413-
if (source.UserUID == "dn" && member == sr.Entries[0].DN) || member == uid {
414-
isMember = true
415-
break Entries
416-
}
417-
}
418-
}
419-
420-
if !isMember {
421-
log.Error("LDAP group membership test failed")
422-
return nil
414+
if source.GroupTeamMap != "" || source.GroupTeamMapRemoval {
415+
teamsToAdd, teamsToRemove = source.getMappedMemberships(usersLdapGroups, userAttributeListedInGroup)
423416
}
424417
}
425418

426419
if isAttributeSSHPublicKeySet {
427420
sshPublicKey = sr.Entries[0].GetAttributeValues(source.AttributeSSHPublicKey)
428421
}
422+
429423
isAdmin := checkAdmin(l, source, userDN)
424+
430425
var isRestricted bool
431426
if !isAdmin {
432427
isRestricted = checkRestricted(l, source, userDN)
@@ -436,12 +431,6 @@ func (source *Source) SearchEntry(name, passwd string, directBind bool) *SearchR
436431
Avatar = sr.Entries[0].GetRawAttributeValue(source.AttributeAvatar)
437432
}
438433

439-
teamsToAdd := make(map[string][]string)
440-
teamsToRemove := make(map[string][]string)
441-
if source.GroupsEnabled && (source.GroupTeamMap != "" || source.GroupTeamMapRemoval) {
442-
teamsToAdd, teamsToRemove = source.getMappedMemberships(l, uid)
443-
}
444-
445434
if !directBind && source.AttributesInBind {
446435
// binds user (checking password) after looking-up attributes in BindDN context
447436
err = bindUser(l, userDN, passwd)
@@ -520,19 +509,29 @@ func (source *Source) SearchEntries() ([]*SearchResult, error) {
520509
return nil, err
521510
}
522511

523-
result := make([]*SearchResult, len(sr.Entries))
512+
result := make([]*SearchResult, 0, len(sr.Entries))
524513

525-
for i, v := range sr.Entries {
514+
for _, v := range sr.Entries {
526515
teamsToAdd := make(map[string][]string)
527516
teamsToRemove := make(map[string][]string)
528-
if source.GroupsEnabled && (source.GroupTeamMap != "" || source.GroupTeamMapRemoval) {
529-
userAttributeListedInGroup := v.GetAttributeValue(source.UserUID)
530-
if source.UserUID == "dn" || source.UserUID == "DN" {
531-
userAttributeListedInGroup = v.DN
517+
518+
if source.GroupsEnabled {
519+
userAttributeListedInGroup := source.getUserAttributeListedInGroup(v)
520+
521+
if source.GroupFilter != "" {
522+
usersLdapGroups := source.listLdapGroupMemberships(l, userAttributeListedInGroup, true)
523+
if len(usersLdapGroups) == 0 {
524+
continue
525+
}
526+
}
527+
528+
if source.GroupTeamMap != "" || source.GroupTeamMapRemoval {
529+
usersLdapGroups := source.listLdapGroupMemberships(l, userAttributeListedInGroup, false)
530+
teamsToAdd, teamsToRemove = source.getMappedMemberships(usersLdapGroups, userAttributeListedInGroup)
532531
}
533-
teamsToAdd, teamsToRemove = source.getMappedMemberships(l, userAttributeListedInGroup)
534532
}
535-
result[i] = &SearchResult{
533+
534+
user := &SearchResult{
536535
Username: v.GetAttributeValue(source.AttributeUsername),
537536
Name: v.GetAttributeValue(source.AttributeName),
538537
Surname: v.GetAttributeValue(source.AttributeSurname),
@@ -541,16 +540,22 @@ func (source *Source) SearchEntries() ([]*SearchResult, error) {
541540
LdapTeamAdd: teamsToAdd,
542541
LdapTeamRemove: teamsToRemove,
543542
}
544-
if !result[i].IsAdmin {
545-
result[i].IsRestricted = checkRestricted(l, source, v.DN)
543+
544+
if !user.IsAdmin {
545+
user.IsRestricted = checkRestricted(l, source, v.DN)
546546
}
547+
547548
if isAttributeSSHPublicKeySet {
548-
result[i].SSHPublicKey = v.GetAttributeValues(source.AttributeSSHPublicKey)
549+
user.SSHPublicKey = v.GetAttributeValues(source.AttributeSSHPublicKey)
549550
}
551+
550552
if isAtributeAvatarSet {
551-
result[i].Avatar = v.GetRawAttributeValue(source.AttributeAvatar)
553+
user.Avatar = v.GetRawAttributeValue(source.AttributeAvatar)
552554
}
553-
result[i].LowerName = strings.ToLower(result[i].Username)
555+
556+
user.LowerName = strings.ToLower(user.Username)
557+
558+
result = append(result, user)
554559
}
555560

556561
return result, nil

0 commit comments

Comments
 (0)