first-commit
This commit is contained in:
229
services/auth/source/ldap/source_sync.go
Normal file
229
services/auth/source/ldap/source_sync.go
Normal file
@@ -0,0 +1,229 @@
|
||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
auth_module "code.gitea.io/gitea/modules/auth"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
||||
source_service "code.gitea.io/gitea/services/auth/source"
|
||||
user_service "code.gitea.io/gitea/services/user"
|
||||
)
|
||||
|
||||
// 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.AuthSource.Name)
|
||||
|
||||
isAttributeSSHPublicKeySet := strings.TrimSpace(source.AttributeSSHPublicKey) != ""
|
||||
var sshKeysNeedUpdate bool
|
||||
|
||||
// Find all users with this login type - FIXME: Should this be an iterator?
|
||||
users, err := user_model.GetUsersBySource(ctx, source.AuthSource)
|
||||
if err != nil {
|
||||
log.Error("SyncExternalUsers: %v", err)
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Warn("SyncExternalUsers: Cancelled before update of %s", source.AuthSource.Name)
|
||||
return db.ErrCancelledf("Before update of %s", source.AuthSource.Name)
|
||||
default:
|
||||
}
|
||||
|
||||
usernameUsers := make(map[string]*user_model.User, len(users))
|
||||
mailUsers := make(map[string]*user_model.User, len(users))
|
||||
keepActiveUsers := make(container.Set[int64])
|
||||
|
||||
for _, u := range users {
|
||||
usernameUsers[u.LowerName] = u
|
||||
mailUsers[strings.ToLower(u.Email)] = u
|
||||
}
|
||||
|
||||
sr, err := source.SearchEntries()
|
||||
if err != nil {
|
||||
log.Error("SyncExternalUsers LDAP source failure [%s], skipped", source.AuthSource.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")
|
||||
}
|
||||
|
||||
orgCache := make(map[string]*organization.Organization)
|
||||
teamCache := make(map[string]*organization.Team)
|
||||
|
||||
groupTeamMapping, err := auth_module.UnmarshalGroupTeamMapping(source.GroupTeamMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, su := range sr {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Warn("SyncExternalUsers: Cancelled at update of %s before completed update of users", source.AuthSource.Name)
|
||||
// Rewrite authorized_keys file if LDAP Public SSH Key attribute is set and any key was added or removed
|
||||
if sshKeysNeedUpdate {
|
||||
err = asymkey_service.RewriteAllPublicKeys(ctx)
|
||||
if err != nil {
|
||||
log.Error("RewriteAllPublicKeys: %v", err)
|
||||
}
|
||||
}
|
||||
return db.ErrCancelledf("During update of %s before completed update of users", source.AuthSource.Name)
|
||||
default:
|
||||
}
|
||||
if su.Username == "" && su.Mail == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
var usr *user_model.User
|
||||
if su.Username != "" {
|
||||
usr = usernameUsers[su.LowerName]
|
||||
}
|
||||
if usr == nil && su.Mail != "" {
|
||||
usr = mailUsers[strings.ToLower(su.Mail)]
|
||||
}
|
||||
|
||||
if usr != nil {
|
||||
keepActiveUsers.Add(usr.ID)
|
||||
} else if su.Username == "" {
|
||||
// we cannot create the user if su.Username is empty
|
||||
continue
|
||||
}
|
||||
|
||||
if su.Mail == "" {
|
||||
su.Mail = su.Username + "@localhost.local"
|
||||
}
|
||||
|
||||
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.AuthSource.Name, su.Username)
|
||||
|
||||
usr = &user_model.User{
|
||||
LowerName: su.LowerName,
|
||||
Name: su.Username,
|
||||
FullName: fullName,
|
||||
LoginType: source.AuthSource.Type,
|
||||
LoginSource: source.AuthSource.ID,
|
||||
LoginName: su.Username,
|
||||
Email: su.Mail,
|
||||
IsAdmin: su.IsAdmin,
|
||||
}
|
||||
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
||||
IsRestricted: optional.Some(su.IsRestricted),
|
||||
IsActive: optional.Some(true),
|
||||
}
|
||||
|
||||
err = user_model.CreateUser(ctx, usr, &user_model.Meta{}, overwriteDefault)
|
||||
if err != nil {
|
||||
log.Error("SyncExternalUsers[%s]: Error creating user %s: %v", source.AuthSource.Name, su.Username, err)
|
||||
}
|
||||
|
||||
if err == nil && isAttributeSSHPublicKeySet {
|
||||
log.Trace("SyncExternalUsers[%s]: Adding LDAP Public SSH Keys for user %s", source.AuthSource.Name, usr.Name)
|
||||
if asymkey_model.AddPublicKeysBySource(ctx, usr, source.AuthSource, su.SSHPublicKey) {
|
||||
sshKeysNeedUpdate = true
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil && source.AttributeAvatar != "" {
|
||||
_ = user_service.UploadAvatar(ctx, usr, su.Avatar)
|
||||
}
|
||||
} else if updateExisting {
|
||||
// Synchronize SSH Public Key if that attribute is set
|
||||
if isAttributeSSHPublicKeySet && asymkey_model.SynchronizePublicKeys(ctx, usr, source.AuthSource, su.SSHPublicKey) {
|
||||
sshKeysNeedUpdate = true
|
||||
}
|
||||
|
||||
// Check if user data has changed
|
||||
if (source.AdminFilter != "" && usr.IsAdmin != su.IsAdmin) ||
|
||||
(source.RestrictedFilter != "" && usr.IsRestricted != su.IsRestricted) ||
|
||||
!strings.EqualFold(usr.Email, su.Mail) ||
|
||||
usr.FullName != fullName ||
|
||||
!usr.IsActive {
|
||||
log.Trace("SyncExternalUsers[%s]: Updating user %s", source.AuthSource.Name, usr.Name)
|
||||
|
||||
opts := &user_service.UpdateOptions{
|
||||
FullName: optional.Some(fullName),
|
||||
IsActive: optional.Some(true),
|
||||
}
|
||||
if source.AdminFilter != "" {
|
||||
opts.IsAdmin = user_service.UpdateOptionFieldFromSync(su.IsAdmin)
|
||||
}
|
||||
// Change existing restricted flag only if RestrictedFilter option is set
|
||||
if !su.IsAdmin && source.RestrictedFilter != "" {
|
||||
opts.IsRestricted = optional.Some(su.IsRestricted)
|
||||
}
|
||||
|
||||
if err := user_service.UpdateUser(ctx, usr, opts); err != nil {
|
||||
log.Error("SyncExternalUsers[%s]: Error updating user %s: %v", source.AuthSource.Name, usr.Name, err)
|
||||
}
|
||||
|
||||
if err := user_service.ReplacePrimaryEmailAddress(ctx, usr, su.Mail); err != nil {
|
||||
log.Error("SyncExternalUsers[%s]: Error updating user %s primary email %s: %v", source.AuthSource.Name, usr.Name, su.Mail, err)
|
||||
}
|
||||
}
|
||||
|
||||
if source.AttributeAvatar != "" {
|
||||
if len(su.Avatar) > 0 && usr.IsUploadAvatarChanged(su.Avatar) {
|
||||
log.Trace("SyncExternalUsers[%s]: Uploading new avatar for %s", source.AuthSource.Name, usr.Name)
|
||||
_ = user_service.UploadAvatar(ctx, usr, su.Avatar)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Synchronize LDAP groups with organization and team memberships
|
||||
if source.GroupsEnabled && (source.GroupTeamMap != "" || source.GroupTeamMapRemoval) {
|
||||
if err := source_service.SyncGroupsToTeamsCached(ctx, usr, su.Groups, groupTeamMapping, source.GroupTeamMapRemoval, orgCache, teamCache); err != nil {
|
||||
log.Error("SyncGroupsToTeamsCached: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite authorized_keys file if LDAP Public SSH Key attribute is set and any key was added or removed
|
||||
if sshKeysNeedUpdate {
|
||||
err = asymkey_service.RewriteAllPublicKeys(ctx)
|
||||
if err != nil {
|
||||
log.Error("RewriteAllPublicKeys: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Warn("SyncExternalUsers: Cancelled during update of %s before delete users", source.AuthSource.Name)
|
||||
return db.ErrCancelledf("During update of %s before delete users", source.AuthSource.Name)
|
||||
default:
|
||||
}
|
||||
|
||||
// Deactivate users not present in LDAP
|
||||
if updateExisting {
|
||||
for _, usr := range users {
|
||||
if keepActiveUsers.Contains(usr.ID) {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Trace("SyncExternalUsers[%s]: Deactivating user %s", source.AuthSource.Name, usr.Name)
|
||||
|
||||
opts := &user_service.UpdateOptions{
|
||||
IsActive: optional.Some(false),
|
||||
}
|
||||
if err := user_service.UpdateUser(ctx, usr, opts); err != nil {
|
||||
log.Error("SyncExternalUsers[%s]: Error deactivating user %s: %v", source.AuthSource.Name, usr.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user