Skip to content

Commit db6b7db

Browse files
authoredSep 15, 2021
Improve LDAP synchronization efficiency (#16994)
The current LDAP sync routine has order n^2 efficiency. This change reduces this to order n.log n. Signed-off-by: Andrew Thornton <art27@cantab.net>
1 parent 976db2a commit db6b7db

File tree

2 files changed

+28
-18
lines changed

2 files changed

+28
-18
lines changed
 

‎services/auth/source/ldap/source_search.go

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type SearchResult struct {
2626
SSHPublicKey []string // SSH Public Key
2727
IsAdmin bool // if user is administrator
2828
IsRestricted bool // if user is restricted
29+
LowerName string // Lowername
2930
}
3031

3132
func (ls *Source) sanitizedUserQuery(username string) (string, bool) {
@@ -363,6 +364,7 @@ func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResul
363364
}
364365

365366
return &SearchResult{
367+
LowerName: strings.ToLower(username),
366368
Username: username,
367369
Name: firstname,
368370
Surname: surname,
@@ -440,6 +442,8 @@ func (ls *Source) SearchEntries() ([]*SearchResult, error) {
440442
if isAttributeSSHPublicKeySet {
441443
result[i].SSHPublicKey = v.GetAttributeValues(ls.AttributeSSHPublicKey)
442444
}
445+
result[i].LowerName = strings.ToLower(result[i].Username)
446+
443447
}
444448

445449
return result, nil

‎services/auth/source/ldap/source_sync.go

+24-18
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package ldap
77
import (
88
"context"
99
"fmt"
10+
"sort"
1011
"strings"
1112

1213
"code.gitea.io/gitea/models"
@@ -17,7 +18,7 @@ import (
1718
func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
1819
log.Trace("Doing: SyncExternalUsers[%s]", source.loginSource.Name)
1920

20-
var existingUsers []int64
21+
var existingUsers []int
2122
isAttributeSSHPublicKeySet := len(strings.TrimSpace(source.AttributeSSHPublicKey)) > 0
2223
var sshKeysNeedUpdate bool
2324

@@ -34,6 +35,10 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
3435
default:
3536
}
3637

38+
sort.Slice(users, func(i, j int) bool {
39+
return users[i].LowerName < users[j].LowerName
40+
})
41+
3742
sr, err := source.SearchEntries()
3843
if err != nil {
3944
log.Error("SyncExternalUsers LDAP source failure [%s], skipped", source.loginSource.Name)
@@ -48,6 +53,12 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
4853
log.Warn("LDAP search found no entries but did not report an error. All users will be deactivated as per settings")
4954
}
5055

56+
sort.Slice(sr, func(i, j int) bool {
57+
return sr[i].LowerName < sr[j].LowerName
58+
})
59+
60+
userPos := 0
61+
5162
for _, su := range sr {
5263
select {
5364
case <-ctx.Done():
@@ -71,12 +82,12 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
7182
}
7283

7384
var usr *models.User
74-
// Search for existing user
75-
for _, du := range users {
76-
if du.LowerName == strings.ToLower(su.Username) {
77-
usr = du
78-
break
79-
}
85+
for userPos < len(users) && users[userPos].LowerName < su.LowerName {
86+
userPos++
87+
}
88+
if userPos < len(users) && users[userPos].LowerName == su.LowerName {
89+
usr = users[userPos]
90+
existingUsers = append(existingUsers, userPos)
8091
}
8192

8293
fullName := composeFullName(su.Name, su.Surname, su.Username)
@@ -85,7 +96,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
8596
log.Trace("SyncExternalUsers[%s]: Creating user %s", source.loginSource.Name, su.Username)
8697

8798
usr = &models.User{
88-
LowerName: strings.ToLower(su.Username),
99+
LowerName: su.LowerName,
89100
Name: su.Username,
90101
FullName: fullName,
91102
LoginType: source.loginSource.Type,
@@ -108,8 +119,6 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
108119
}
109120
}
110121
} else if updateExisting {
111-
existingUsers = append(existingUsers, usr.ID)
112-
113122
// Synchronize SSH Public Key if that attribute is set
114123
if isAttributeSSHPublicKeySet && models.SynchronizePublicKeys(usr, source.loginSource, su.SSHPublicKey) {
115124
sshKeysNeedUpdate = true
@@ -161,15 +170,12 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
161170

162171
// Deactivate users not present in LDAP
163172
if updateExisting {
164-
for _, usr := range users {
165-
found := false
166-
for _, uid := range existingUsers {
167-
if usr.ID == uid {
168-
found = true
169-
break
170-
}
173+
existPos := 0
174+
for i, usr := range users {
175+
for existPos < len(existingUsers) && i > existingUsers[existPos] {
176+
existPos++
171177
}
172-
if !found {
178+
if usr.IsActive && (existPos >= len(existingUsers) || i < existingUsers[existPos]) {
173179
log.Trace("SyncExternalUsers[%s]: Deactivating user %s", source.loginSource.Name, usr.Name)
174180

175181
usr.IsActive = false

0 commit comments

Comments
 (0)