gitea源码

update.go 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. // Copyright 2024 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package user
  4. import (
  5. "context"
  6. "fmt"
  7. auth_model "code.gitea.io/gitea/models/auth"
  8. user_model "code.gitea.io/gitea/models/user"
  9. password_module "code.gitea.io/gitea/modules/auth/password"
  10. "code.gitea.io/gitea/modules/optional"
  11. "code.gitea.io/gitea/modules/setting"
  12. "code.gitea.io/gitea/modules/structs"
  13. )
  14. type UpdateOptionField[T any] struct {
  15. FieldValue T
  16. FromSync bool
  17. }
  18. func UpdateOptionFieldFromValue[T any](value T) optional.Option[UpdateOptionField[T]] {
  19. return optional.Some(UpdateOptionField[T]{FieldValue: value})
  20. }
  21. func UpdateOptionFieldFromSync[T any](value T) optional.Option[UpdateOptionField[T]] {
  22. return optional.Some(UpdateOptionField[T]{FieldValue: value, FromSync: true})
  23. }
  24. func UpdateOptionFieldFromPtr[T any](value *T) optional.Option[UpdateOptionField[T]] {
  25. if value == nil {
  26. return optional.None[UpdateOptionField[T]]()
  27. }
  28. return UpdateOptionFieldFromValue(*value)
  29. }
  30. type UpdateOptions struct {
  31. KeepEmailPrivate optional.Option[bool]
  32. FullName optional.Option[string]
  33. Website optional.Option[string]
  34. Location optional.Option[string]
  35. Description optional.Option[string]
  36. AllowGitHook optional.Option[bool]
  37. AllowImportLocal optional.Option[bool]
  38. MaxRepoCreation optional.Option[int]
  39. IsRestricted optional.Option[bool]
  40. Visibility optional.Option[structs.VisibleType]
  41. KeepActivityPrivate optional.Option[bool]
  42. Language optional.Option[string]
  43. Theme optional.Option[string]
  44. DiffViewStyle optional.Option[string]
  45. AllowCreateOrganization optional.Option[bool]
  46. IsActive optional.Option[bool]
  47. IsAdmin optional.Option[UpdateOptionField[bool]]
  48. EmailNotificationsPreference optional.Option[string]
  49. SetLastLogin bool
  50. RepoAdminChangeTeamAccess optional.Option[bool]
  51. }
  52. func UpdateUser(ctx context.Context, u *user_model.User, opts *UpdateOptions) error {
  53. cols := make([]string, 0, 20)
  54. if opts.KeepEmailPrivate.Has() {
  55. u.KeepEmailPrivate = opts.KeepEmailPrivate.Value()
  56. cols = append(cols, "keep_email_private")
  57. }
  58. if opts.FullName.Has() {
  59. u.FullName = opts.FullName.Value()
  60. cols = append(cols, "full_name")
  61. }
  62. if opts.Website.Has() {
  63. u.Website = opts.Website.Value()
  64. cols = append(cols, "website")
  65. }
  66. if opts.Location.Has() {
  67. u.Location = opts.Location.Value()
  68. cols = append(cols, "location")
  69. }
  70. if opts.Description.Has() {
  71. u.Description = opts.Description.Value()
  72. cols = append(cols, "description")
  73. }
  74. if opts.Language.Has() {
  75. u.Language = opts.Language.Value()
  76. cols = append(cols, "language")
  77. }
  78. if opts.Theme.Has() {
  79. u.Theme = opts.Theme.Value()
  80. cols = append(cols, "theme")
  81. }
  82. if opts.DiffViewStyle.Has() {
  83. u.DiffViewStyle = opts.DiffViewStyle.Value()
  84. cols = append(cols, "diff_view_style")
  85. }
  86. if opts.AllowGitHook.Has() {
  87. u.AllowGitHook = opts.AllowGitHook.Value()
  88. cols = append(cols, "allow_git_hook")
  89. }
  90. if opts.AllowImportLocal.Has() {
  91. u.AllowImportLocal = opts.AllowImportLocal.Value()
  92. cols = append(cols, "allow_import_local")
  93. }
  94. if opts.MaxRepoCreation.Has() {
  95. u.MaxRepoCreation = opts.MaxRepoCreation.Value()
  96. cols = append(cols, "max_repo_creation")
  97. }
  98. if opts.IsActive.Has() {
  99. u.IsActive = opts.IsActive.Value()
  100. cols = append(cols, "is_active")
  101. }
  102. if opts.IsRestricted.Has() {
  103. u.IsRestricted = opts.IsRestricted.Value()
  104. cols = append(cols, "is_restricted")
  105. }
  106. if opts.IsAdmin.Has() {
  107. if opts.IsAdmin.Value().FieldValue /* true */ {
  108. u.IsAdmin = opts.IsAdmin.Value().FieldValue // set IsAdmin=true
  109. cols = append(cols, "is_admin")
  110. } else if !user_model.IsLastAdminUser(ctx, u) /* not the last admin */ {
  111. u.IsAdmin = opts.IsAdmin.Value().FieldValue // it's safe to change it from false to true (not the last admin)
  112. cols = append(cols, "is_admin")
  113. } else /* IsAdmin=false but this is the last admin user */ { //nolint:gocritic // make it easier to read
  114. if !opts.IsAdmin.Value().FromSync {
  115. return user_model.ErrDeleteLastAdminUser{UID: u.ID}
  116. }
  117. // else: syncing from external-source, this user is the last admin, so skip the "IsAdmin=false" change
  118. }
  119. }
  120. if opts.Visibility.Has() {
  121. if !u.IsOrganization() && !setting.Service.AllowedUserVisibilityModesSlice.IsAllowedVisibility(opts.Visibility.Value()) {
  122. return fmt.Errorf("visibility mode not allowed: %s", opts.Visibility.Value().String())
  123. }
  124. u.Visibility = opts.Visibility.Value()
  125. cols = append(cols, "visibility")
  126. }
  127. if opts.KeepActivityPrivate.Has() {
  128. u.KeepActivityPrivate = opts.KeepActivityPrivate.Value()
  129. cols = append(cols, "keep_activity_private")
  130. }
  131. if opts.AllowCreateOrganization.Has() {
  132. u.AllowCreateOrganization = opts.AllowCreateOrganization.Value()
  133. cols = append(cols, "allow_create_organization")
  134. }
  135. if opts.RepoAdminChangeTeamAccess.Has() {
  136. u.RepoAdminChangeTeamAccess = opts.RepoAdminChangeTeamAccess.Value()
  137. cols = append(cols, "repo_admin_change_team_access")
  138. }
  139. if opts.EmailNotificationsPreference.Has() {
  140. u.EmailNotificationsPreference = opts.EmailNotificationsPreference.Value()
  141. cols = append(cols, "email_notifications_preference")
  142. }
  143. if opts.SetLastLogin {
  144. u.SetLastLogin()
  145. cols = append(cols, "last_login_unix")
  146. }
  147. return user_model.UpdateUserCols(ctx, u, cols...)
  148. }
  149. type UpdateAuthOptions struct {
  150. LoginSource optional.Option[int64]
  151. LoginName optional.Option[string]
  152. Password optional.Option[string]
  153. MustChangePassword optional.Option[bool]
  154. ProhibitLogin optional.Option[bool]
  155. }
  156. func UpdateAuth(ctx context.Context, u *user_model.User, opts *UpdateAuthOptions) error {
  157. if opts.LoginSource.Has() {
  158. source, err := auth_model.GetSourceByID(ctx, opts.LoginSource.Value())
  159. if err != nil {
  160. return err
  161. }
  162. u.LoginType = source.Type
  163. u.LoginSource = source.ID
  164. }
  165. if opts.LoginName.Has() {
  166. u.LoginName = opts.LoginName.Value()
  167. }
  168. deleteAuthTokens := false
  169. if opts.Password.Has() && (u.IsLocal() || u.IsOAuth2()) {
  170. password := opts.Password.Value()
  171. if len(password) < setting.MinPasswordLength {
  172. return password_module.ErrMinLength
  173. }
  174. if !password_module.IsComplexEnough(password) {
  175. return password_module.ErrComplexity
  176. }
  177. if err := password_module.IsPwned(ctx, password); err != nil {
  178. return err
  179. }
  180. if err := u.SetPassword(password); err != nil {
  181. return err
  182. }
  183. deleteAuthTokens = true
  184. }
  185. if opts.MustChangePassword.Has() {
  186. u.MustChangePassword = opts.MustChangePassword.Value()
  187. }
  188. if opts.ProhibitLogin.Has() {
  189. u.ProhibitLogin = opts.ProhibitLogin.Value()
  190. }
  191. if err := user_model.UpdateUserCols(ctx, u, "login_type", "login_source", "login_name", "passwd", "passwd_hash_algo", "salt", "must_change_password", "prohibit_login"); err != nil {
  192. return err
  193. }
  194. if deleteAuthTokens {
  195. return auth_model.DeleteAuthTokensByUserID(ctx, u.ID)
  196. }
  197. return nil
  198. }