code.gitea.io/gitea@v1.22.3/services/user/update.go (about)

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