diff --git a/modules/structs/admin_user.go b/modules/structs/admin_user.go index facf16a39..7f68e7f0c 100644 --- a/modules/structs/admin_user.go +++ b/modules/structs/admin_user.go @@ -24,6 +24,7 @@ type CreateUserOption struct { // EditUserOption edit user options type EditUserOption struct { + NewName string `json:"new_name" binding:"MaxSize(40)"` // required: true SourceID int64 `json:"source_id"` // required: true diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 6bc9b849b..8e7a3625e 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "net/http" + "strings" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" @@ -154,12 +155,13 @@ func EditUser(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" + + log.Info("user setting begin :%s", ctx.User.Name) form := web.GetForm(ctx).(*api.EditUserOption) u := user.GetUserByParams(ctx) if ctx.Written() { return } - parseLoginSource(ctx, u, form.SourceID, form.LoginName) if ctx.Written() { return @@ -193,6 +195,28 @@ func EditUser(ctx *context.APIContext) { if form.MustChangePassword != nil { u.MustChangePassword = *form.MustChangePassword } + // Non-local users are not allowed to change their username. + if len(form.NewName) != 0 && u.IsLocal() { + + // Check if user name has been changed + if err := models.ChangeUserName(u, form.NewName); err != nil { + switch { + case models.IsErrUserAlreadyExist(err): + ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("user name is already taken [name: %s]", form.NewName), err) + case models.IsErrNameReserved(err): + ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("user name is reserved [name:%s]", form.NewName), err) + case models.IsErrNamePatternNotAllowed(err): + ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("user name's pattern is not allowed [name:%s]", form.NewName), err) + case models.IsErrNameCharsNotAllowed(err): + ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("user name's chars is not allowed [name:%s]", form.NewName), err) + default: + ctx.Error(http.StatusUnprocessableEntity, "ChangeUserName", err) + } + return + } + u.Name = form.NewName + u.LowerName = strings.ToLower(form.NewName) + } u.LoginName = form.LoginName @@ -243,14 +267,15 @@ func EditUser(ctx *context.APIContext) { u.IsRestricted = *form.Restricted } - if err := models.UpdateUser(u); err != nil { + if err := models.UpdateUserSetting(u); err != nil { if models.IsErrEmailAlreadyUsed(err) || models.IsErrEmailInvalid(err) { - ctx.Error(http.StatusUnprocessableEntity, "", err) + ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("the e-mail is not valid [name %s]", u.Email), err) } else { ctx.Error(http.StatusInternalServerError, "UpdateUser", err) } return } + log.Trace("Account profile updated by admin (%s): %s", ctx.User.Name, u.Name) ctx.JSON(http.StatusOK, convert.ToUser(u, ctx.User)) @@ -405,7 +430,7 @@ func GetAllUsers(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" listOptions := utils.GetListOptions(ctx) - + log.Info("user setting begin :%s", ctx.User.Name) users, maxResults, err := models.SearchUsers(&models.SearchUserOptions{ Actor: ctx.User, Type: models.UserTypeIndividual, diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 037f4f6e7..5d0efc88c 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -15214,6 +15214,10 @@ "type": "boolean", "x-go-name": "MustChangePassword" }, + "new_name": { + "type": "string", + "x-go-name": "NewName" + }, "password": { "type": "string", "x-go-name": "Password"