diff options
Diffstat (limited to 'services/auth/source/ldap/source_sync.go')
-rw-r--r-- | services/auth/source/ldap/source_sync.go | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/services/auth/source/ldap/source_sync.go b/services/auth/source/ldap/source_sync.go new file mode 100644 index 0000000000..7e4088e571 --- /dev/null +++ b/services/auth/source/ldap/source_sync.go @@ -0,0 +1,184 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package ldap + +import ( + "context" + "fmt" + "strings" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" +) + +// Sync causes this ldap source to synchronize its users with the db +func (source *Source) Sync(ctx context.Context, updateExisting bool) error { + log.Trace("Doing: SyncExternalUsers[%s]", source.loginSource.Name) + + var existingUsers []int64 + isAttributeSSHPublicKeySet := len(strings.TrimSpace(source.AttributeSSHPublicKey)) > 0 + var sshKeysNeedUpdate bool + + // Find all users with this login type - FIXME: Should this be an iterator? + users, err := models.GetUsersBySource(source.loginSource) + if err != nil { + log.Error("SyncExternalUsers: %v", err) + return err + } + select { + case <-ctx.Done(): + log.Warn("SyncExternalUsers: Cancelled before update of %s", source.loginSource.Name) + return models.ErrCancelledf("Before update of %s", source.loginSource.Name) + default: + } + + sr, err := source.SearchEntries() + if err != nil { + log.Error("SyncExternalUsers LDAP source failure [%s], skipped", source.loginSource.Name) + return nil + } + + if len(sr) == 0 { + if !source.AllowDeactivateAll { + log.Error("LDAP search found no entries but did not report an error. Refusing to deactivate all users") + return nil + } + log.Warn("LDAP search found no entries but did not report an error. All users will be deactivated as per settings") + } + + for _, su := range sr { + select { + case <-ctx.Done(): + log.Warn("SyncExternalUsers: Cancelled at update of %s before completed update of users", source.loginSource.Name) + // Rewrite authorized_keys file if LDAP Public SSH Key attribute is set and any key was added or removed + if sshKeysNeedUpdate { + err = models.RewriteAllPublicKeys() + if err != nil { + log.Error("RewriteAllPublicKeys: %v", err) + } + } + return models.ErrCancelledf("During update of %s before completed update of users", source.loginSource.Name) + default: + } + if len(su.Username) == 0 { + continue + } + + if len(su.Mail) == 0 { + su.Mail = fmt.Sprintf("%s@localhost", su.Username) + } + + var usr *models.User + // Search for existing user + for _, du := range users { + if du.LowerName == strings.ToLower(su.Username) { + usr = du + break + } + } + + fullName := composeFullName(su.Name, su.Surname, su.Username) + // If no existing user found, create one + if usr == nil { + log.Trace("SyncExternalUsers[%s]: Creating user %s", source.loginSource.Name, su.Username) + + usr = &models.User{ + LowerName: strings.ToLower(su.Username), + Name: su.Username, + FullName: fullName, + LoginType: source.loginSource.Type, + LoginSource: source.loginSource.ID, + LoginName: su.Username, + Email: su.Mail, + IsAdmin: su.IsAdmin, + IsRestricted: su.IsRestricted, + IsActive: true, + } + + err = models.CreateUser(usr) + + if err != nil { + log.Error("SyncExternalUsers[%s]: Error creating user %s: %v", source.loginSource.Name, su.Username, err) + } else if isAttributeSSHPublicKeySet { + log.Trace("SyncExternalUsers[%s]: Adding LDAP Public SSH Keys for user %s", source.loginSource.Name, usr.Name) + if models.AddPublicKeysBySource(usr, source.loginSource, su.SSHPublicKey) { + sshKeysNeedUpdate = true + } + } + } else if updateExisting { + existingUsers = append(existingUsers, usr.ID) + + // Synchronize SSH Public Key if that attribute is set + if isAttributeSSHPublicKeySet && models.SynchronizePublicKeys(usr, source.loginSource, su.SSHPublicKey) { + sshKeysNeedUpdate = true + } + + // Check if user data has changed + if (len(source.AdminFilter) > 0 && usr.IsAdmin != su.IsAdmin) || + (len(source.RestrictedFilter) > 0 && usr.IsRestricted != su.IsRestricted) || + !strings.EqualFold(usr.Email, su.Mail) || + usr.FullName != fullName || + !usr.IsActive { + + log.Trace("SyncExternalUsers[%s]: Updating user %s", source.loginSource.Name, usr.Name) + + usr.FullName = fullName + usr.Email = su.Mail + // Change existing admin flag only if AdminFilter option is set + if len(source.AdminFilter) > 0 { + usr.IsAdmin = su.IsAdmin + } + // Change existing restricted flag only if RestrictedFilter option is set + if !usr.IsAdmin && len(source.RestrictedFilter) > 0 { + usr.IsRestricted = su.IsRestricted + } + usr.IsActive = true + + err = models.UpdateUserCols(usr, "full_name", "email", "is_admin", "is_restricted", "is_active") + if err != nil { + log.Error("SyncExternalUsers[%s]: Error updating user %s: %v", source.loginSource.Name, usr.Name, err) + } + } + } + } + + // Rewrite authorized_keys file if LDAP Public SSH Key attribute is set and any key was added or removed + if sshKeysNeedUpdate { + err = models.RewriteAllPublicKeys() + if err != nil { + log.Error("RewriteAllPublicKeys: %v", err) + } + } + + select { + case <-ctx.Done(): + log.Warn("SyncExternalUsers: Cancelled during update of %s before delete users", source.loginSource.Name) + return models.ErrCancelledf("During update of %s before delete users", source.loginSource.Name) + default: + } + + // Deactivate users not present in LDAP + if updateExisting { + for _, usr := range users { + found := false + for _, uid := range existingUsers { + if usr.ID == uid { + found = true + break + } + } + if !found { + log.Trace("SyncExternalUsers[%s]: Deactivating user %s", source.loginSource.Name, usr.Name) + + usr.IsActive = false + err = models.UpdateUserCols(usr, "is_active") + if err != nil { + log.Error("SyncExternalUsers[%s]: Error deactivating user %s: %v", source.loginSource.Name, usr.Name, err) + } + } + } + } + return nil +} |