loading up the forgejo repo on tangled to test page performance
at forgejo 232 lines 7.7 kB view raw
1// Copyright 2021 The Gitea Authors. All rights reserved. 2// SPDX-License-Identifier: MIT 3 4package ldap 5 6import ( 7 "context" 8 "fmt" 9 "strings" 10 11 asymkey_model "forgejo.org/models/asymkey" 12 "forgejo.org/models/db" 13 "forgejo.org/models/organization" 14 user_model "forgejo.org/models/user" 15 auth_module "forgejo.org/modules/auth" 16 "forgejo.org/modules/container" 17 "forgejo.org/modules/log" 18 "forgejo.org/modules/optional" 19 source_service "forgejo.org/services/auth/source" 20 user_service "forgejo.org/services/user" 21) 22 23// Sync causes this ldap source to synchronize its users with the db 24func (source *Source) Sync(ctx context.Context, updateExisting bool) error { 25 log.Trace("Doing: SyncExternalUsers[%s]", source.authSource.Name) 26 27 isAttributeSSHPublicKeySet := len(strings.TrimSpace(source.AttributeSSHPublicKey)) > 0 28 var sshKeysNeedUpdate bool 29 30 // Find all users with this login type - FIXME: Should this be an iterator? 31 users, err := user_model.GetUsersBySource(ctx, source.authSource) 32 if err != nil { 33 log.Error("SyncExternalUsers: %v", err) 34 return err 35 } 36 select { 37 case <-ctx.Done(): 38 log.Warn("SyncExternalUsers: Cancelled before update of %s", source.authSource.Name) 39 return db.ErrCancelledf("Before update of %s", source.authSource.Name) 40 default: 41 } 42 43 usernameUsers := make(map[string]*user_model.User, len(users)) 44 mailUsers := make(map[string]*user_model.User, len(users)) 45 keepActiveUsers := make(container.Set[int64]) 46 47 for _, u := range users { 48 usernameUsers[u.LowerName] = u 49 mailUsers[strings.ToLower(u.Email)] = u 50 } 51 52 sr, err := source.SearchEntries() 53 if err != nil { 54 log.Error("SyncExternalUsers LDAP source failure [%s], skipped", source.authSource.Name) 55 return nil 56 } 57 58 if len(sr) == 0 { 59 if !source.AllowDeactivateAll { 60 log.Error("LDAP search found no entries but did not report an error. Refusing to deactivate all users") 61 return nil 62 } 63 log.Warn("LDAP search found no entries but did not report an error. All users will be deactivated as per settings") 64 } 65 66 orgCache := make(map[string]*organization.Organization) 67 teamCache := make(map[string]*organization.Team) 68 69 groupTeamMapping, err := auth_module.UnmarshalGroupTeamMapping(source.GroupTeamMap) 70 if err != nil { 71 return err 72 } 73 74 for _, su := range sr { 75 select { 76 case <-ctx.Done(): 77 log.Warn("SyncExternalUsers: Cancelled at update of %s before completed update of users", source.authSource.Name) 78 // Rewrite authorized_keys file if LDAP Public SSH Key attribute is set and any key was added or removed 79 if sshKeysNeedUpdate { 80 err = asymkey_model.RewriteAllPublicKeys(ctx) 81 if err != nil { 82 log.Error("RewriteAllPublicKeys: %v", err) 83 } 84 } 85 return db.ErrCancelledf("During update of %s before completed update of users", source.authSource.Name) 86 default: 87 } 88 if len(su.Username) == 0 && len(su.Mail) == 0 { 89 continue 90 } 91 92 var usr *user_model.User 93 if len(su.Username) > 0 { 94 usr = usernameUsers[su.LowerName] 95 } 96 if usr == nil && len(su.Mail) > 0 { 97 usr = mailUsers[strings.ToLower(su.Mail)] 98 } 99 100 if usr != nil { 101 keepActiveUsers.Add(usr.ID) 102 } else if len(su.Username) == 0 { 103 // we cannot create the user if su.Username is empty 104 continue 105 } 106 107 if len(su.Mail) == 0 { 108 domainName := source.DefaultDomainName 109 if len(domainName) == 0 { 110 domainName = "localhost.local" 111 } 112 su.Mail = fmt.Sprintf("%s@%s", su.Username, domainName) 113 } 114 115 fullName := composeFullName(su.Name, su.Surname, su.Username) 116 // If no existing user found, create one 117 if usr == nil { 118 log.Trace("SyncExternalUsers[%s]: Creating user %s", source.authSource.Name, su.Username) 119 120 usr = &user_model.User{ 121 LowerName: su.LowerName, 122 Name: su.Username, 123 FullName: fullName, 124 LoginType: source.authSource.Type, 125 LoginSource: source.authSource.ID, 126 LoginName: su.Username, 127 Email: su.Mail, 128 IsAdmin: su.IsAdmin, 129 } 130 overwriteDefault := &user_model.CreateUserOverwriteOptions{ 131 IsRestricted: optional.Some(su.IsRestricted), 132 IsActive: optional.Some(true), 133 } 134 135 err = user_model.CreateUser(ctx, usr, overwriteDefault) 136 if err != nil { 137 log.Error("SyncExternalUsers[%s]: Error creating user %s: %v", source.authSource.Name, su.Username, err) 138 } 139 140 if err == nil && isAttributeSSHPublicKeySet { 141 log.Trace("SyncExternalUsers[%s]: Adding LDAP Public SSH Keys for user %s", source.authSource.Name, usr.Name) 142 if asymkey_model.AddPublicKeysBySource(ctx, usr, source.authSource, su.SSHPublicKey) { 143 sshKeysNeedUpdate = true 144 } 145 } 146 147 if err == nil && len(source.AttributeAvatar) > 0 { 148 _ = user_service.UploadAvatar(ctx, usr, su.Avatar) 149 } 150 } else if updateExisting { 151 // Synchronize SSH Public Key if that attribute is set 152 if isAttributeSSHPublicKeySet && asymkey_model.SynchronizePublicKeys(ctx, usr, source.authSource, su.SSHPublicKey) { 153 sshKeysNeedUpdate = true 154 } 155 156 // Check if user data has changed 157 if (len(source.AdminFilter) > 0 && usr.IsAdmin != su.IsAdmin) || 158 (len(source.RestrictedFilter) > 0 && usr.IsRestricted != su.IsRestricted) || 159 !strings.EqualFold(usr.Email, su.Mail) || 160 usr.FullName != fullName || 161 !usr.IsActive { 162 log.Trace("SyncExternalUsers[%s]: Updating user %s", source.authSource.Name, usr.Name) 163 164 opts := &user_service.UpdateOptions{ 165 FullName: optional.Some(fullName), 166 IsActive: optional.Some(true), 167 } 168 if source.AdminFilter != "" { 169 opts.IsAdmin = optional.Some(su.IsAdmin) 170 } 171 // Change existing restricted flag only if RestrictedFilter option is set 172 if !su.IsAdmin && source.RestrictedFilter != "" { 173 opts.IsRestricted = optional.Some(su.IsRestricted) 174 } 175 176 if err := user_service.UpdateUser(ctx, usr, opts); err != nil { 177 log.Error("SyncExternalUsers[%s]: Error updating user %s: %v", source.authSource.Name, usr.Name, err) 178 } 179 180 if err := user_service.ReplacePrimaryEmailAddress(ctx, usr, su.Mail); err != nil { 181 log.Error("SyncExternalUsers[%s]: Error updating user %s primary email %s: %v", source.authSource.Name, usr.Name, su.Mail, err) 182 } 183 } 184 185 if usr.IsUploadAvatarChanged(su.Avatar) { 186 if err == nil && len(source.AttributeAvatar) > 0 { 187 _ = user_service.UploadAvatar(ctx, usr, su.Avatar) 188 } 189 } 190 } 191 // Synchronize LDAP groups with organization and team memberships 192 if source.GroupsEnabled && (source.GroupTeamMap != "" || source.GroupTeamMapRemoval) { 193 if err := source_service.SyncGroupsToTeamsCached(ctx, usr, su.Groups, groupTeamMapping, source.GroupTeamMapRemoval, orgCache, teamCache); err != nil { 194 log.Error("SyncGroupsToTeamsCached: %v", err) 195 } 196 } 197 } 198 199 // Rewrite authorized_keys file if LDAP Public SSH Key attribute is set and any key was added or removed 200 if sshKeysNeedUpdate { 201 err = asymkey_model.RewriteAllPublicKeys(ctx) 202 if err != nil { 203 log.Error("RewriteAllPublicKeys: %v", err) 204 } 205 } 206 207 select { 208 case <-ctx.Done(): 209 log.Warn("SyncExternalUsers: Cancelled during update of %s before delete users", source.authSource.Name) 210 return db.ErrCancelledf("During update of %s before delete users", source.authSource.Name) 211 default: 212 } 213 214 // Deactivate users not present in LDAP 215 if updateExisting { 216 for _, usr := range users { 217 if keepActiveUsers.Contains(usr.ID) { 218 continue 219 } 220 221 log.Trace("SyncExternalUsers[%s]: Deactivating user %s", source.authSource.Name, usr.Name) 222 223 opts := &user_service.UpdateOptions{ 224 IsActive: optional.Some(false), 225 } 226 if err := user_service.UpdateUser(ctx, usr, opts); err != nil { 227 log.Error("SyncExternalUsers[%s]: Error deactivating user %s: %v", source.authSource.Name, usr.Name, err) 228 } 229 } 230 } 231 return nil 232}