You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

source_sync.go 7.5KB

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