215 lines
6.6 KiB
Go
215 lines
6.6 KiB
Go
package admin
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"code.gitea.io/gitea/models/auth"
|
|
"code.gitea.io/gitea/models/db"
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
"code.gitea.io/gitea/modules/auth/password"
|
|
"code.gitea.io/gitea/modules/context"
|
|
"code.gitea.io/gitea/modules/convert"
|
|
"code.gitea.io/gitea/modules/log"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
api "code.gitea.io/gitea/modules/structs"
|
|
"code.gitea.io/gitea/modules/web"
|
|
"code.gitea.io/gitea/services/agit"
|
|
container_service "code.gitea.io/gitea/services/packages/container"
|
|
hat_api "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/structs"
|
|
)
|
|
|
|
func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64, loginName string) {
|
|
if sourceID == 0 {
|
|
return
|
|
}
|
|
|
|
source, err := auth.GetSourceByID(sourceID)
|
|
if err != nil {
|
|
if auth.IsErrSourceNotExist(err) {
|
|
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
|
} else {
|
|
ctx.Error(http.StatusInternalServerError, "auth.GetSourceByID", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
u.LoginType = source.Type
|
|
u.LoginSource = source.ID
|
|
u.LoginName = loginName
|
|
}
|
|
|
|
func EditUser(ctx *context.APIContext) {
|
|
form := web.GetForm(ctx).(*hat_api.HatEditUserOption)
|
|
parseAuthSource(ctx, ctx.ContextUser, form.SourceID, form.LoginName)
|
|
if ctx.Written() {
|
|
return
|
|
}
|
|
|
|
if len(form.Password) != 0 {
|
|
if len(form.Password) < setting.MinPasswordLength {
|
|
ctx.Error(http.StatusBadRequest, "PasswordTooShort", fmt.Errorf("password must be at least %d characters", setting.MinPasswordLength))
|
|
return
|
|
}
|
|
if !password.IsComplexEnough(form.Password) {
|
|
err := errors.New("PasswordComplexity")
|
|
ctx.Error(http.StatusBadRequest, "PasswordComplexity", err)
|
|
return
|
|
}
|
|
pwned, err := password.IsPwned(ctx, form.Password)
|
|
if pwned {
|
|
if err != nil {
|
|
log.Error(err.Error())
|
|
}
|
|
ctx.Data["Err_Password"] = true
|
|
ctx.Error(http.StatusBadRequest, "PasswordPwned", errors.New("PasswordPwned"))
|
|
return
|
|
}
|
|
if ctx.ContextUser.Salt, err = user_model.GetUserSalt(); err != nil {
|
|
ctx.Error(http.StatusInternalServerError, "UpdateUser", err)
|
|
return
|
|
}
|
|
if err = ctx.ContextUser.SetPassword(form.Password); err != nil {
|
|
ctx.InternalServerError(err)
|
|
return
|
|
}
|
|
}
|
|
|
|
if form.MustChangePassword != nil {
|
|
ctx.ContextUser.MustChangePassword = *form.MustChangePassword
|
|
}
|
|
|
|
if len(form.NewName) != 0 && ctx.ContextUser.Name != form.NewName {
|
|
if err := handleUsernameChange(ctx, ctx.ContextUser, form.NewName); err != nil {
|
|
ctx.Error(http.StatusInternalServerError, "UpdateUser", err)
|
|
return
|
|
}
|
|
ctx.ContextUser.Name = form.NewName
|
|
ctx.ContextUser.LowerName = strings.ToLower(form.NewName)
|
|
}
|
|
|
|
ctx.ContextUser.LoginName = form.LoginName
|
|
|
|
if form.FullName != nil {
|
|
ctx.ContextUser.FullName = *form.FullName
|
|
}
|
|
var emailChanged bool
|
|
if form.Email != nil {
|
|
email := strings.TrimSpace(*form.Email)
|
|
if len(email) == 0 {
|
|
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("email is not allowed to be empty string"))
|
|
return
|
|
}
|
|
|
|
if err := user_model.ValidateEmail(email); err != nil {
|
|
ctx.InternalServerError(err)
|
|
return
|
|
}
|
|
|
|
emailChanged = !strings.EqualFold(ctx.ContextUser.Email, email)
|
|
ctx.ContextUser.Email = email
|
|
}
|
|
if form.Website != nil {
|
|
ctx.ContextUser.Website = *form.Website
|
|
}
|
|
if form.Location != nil {
|
|
ctx.ContextUser.Location = *form.Location
|
|
}
|
|
if form.Description != nil {
|
|
ctx.ContextUser.Description = *form.Description
|
|
}
|
|
if form.Active != nil {
|
|
ctx.ContextUser.IsActive = *form.Active
|
|
}
|
|
if len(form.Visibility) != 0 {
|
|
ctx.ContextUser.Visibility = api.VisibilityModes[form.Visibility]
|
|
}
|
|
if form.Admin != nil {
|
|
ctx.ContextUser.IsAdmin = *form.Admin
|
|
}
|
|
if form.AllowGitHook != nil {
|
|
ctx.ContextUser.AllowGitHook = *form.AllowGitHook
|
|
}
|
|
if form.AllowImportLocal != nil {
|
|
ctx.ContextUser.AllowImportLocal = *form.AllowImportLocal
|
|
}
|
|
if form.MaxRepoCreation != nil {
|
|
ctx.ContextUser.MaxRepoCreation = *form.MaxRepoCreation
|
|
}
|
|
if form.AllowCreateOrganization != nil {
|
|
ctx.ContextUser.AllowCreateOrganization = *form.AllowCreateOrganization
|
|
}
|
|
if form.ProhibitLogin != nil {
|
|
ctx.ContextUser.ProhibitLogin = *form.ProhibitLogin
|
|
}
|
|
if form.Restricted != nil {
|
|
ctx.ContextUser.IsRestricted = *form.Restricted
|
|
}
|
|
|
|
if err := user_model.UpdateUser(ctx, ctx.ContextUser, emailChanged); err != nil {
|
|
if user_model.IsErrEmailAlreadyUsed(err) ||
|
|
user_model.IsErrEmailCharIsNotSupported(err) ||
|
|
user_model.IsErrEmailInvalid(err) {
|
|
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
|
} else {
|
|
ctx.Error(http.StatusInternalServerError, "UpdateUser", err)
|
|
}
|
|
return
|
|
}
|
|
log.Trace("Account profile updated by admin (%s): %s", ctx.Doer.Name, ctx.ContextUser.Name)
|
|
|
|
ctx.JSON(http.StatusOK, convert.ToUser(ctx.ContextUser, ctx.Doer))
|
|
}
|
|
|
|
func handleUsernameChange(ctx *context.APIContext, user *user_model.User, newName string) error {
|
|
// Non-local users are not allowed to change their username.
|
|
if !user.IsLocal() {
|
|
ctx.Flash.Error(ctx.Tr("form.username_change_not_local_user"))
|
|
return fmt.Errorf(ctx.Tr("form.username_change_not_local_user"))
|
|
}
|
|
|
|
// Check if user name has been changed
|
|
if user.LowerName != strings.ToLower(newName) {
|
|
if err := user_model.ChangeUserName(user, newName); err != nil {
|
|
switch {
|
|
case user_model.IsErrUserAlreadyExist(err):
|
|
ctx.Error(http.StatusInternalServerError, "ChangeUserName", ctx.Tr("form.username_been_taken"))
|
|
case user_model.IsErrEmailAlreadyUsed(err):
|
|
ctx.Error(http.StatusInternalServerError, "ChangeUserName", ctx.Tr("form.email_been_used"))
|
|
case db.IsErrNameReserved(err):
|
|
ctx.Error(http.StatusInternalServerError, "ChangeUserName", ctx.Tr("user.form.name_reserved", newName))
|
|
case db.IsErrNamePatternNotAllowed(err):
|
|
ctx.Error(http.StatusInternalServerError, "ChangeUserName", ctx.Tr("user.form.name_pattern_not_allowed", newName))
|
|
case db.IsErrNameCharsNotAllowed(err):
|
|
ctx.Error(http.StatusInternalServerError, "ChangeUserName", ctx.Tr("user.form.name_chars_not_allowed", newName))
|
|
default:
|
|
ctx.Error(http.StatusInternalServerError, "ChangeUserName", err)
|
|
}
|
|
return err
|
|
}
|
|
} else {
|
|
if err := repo_model.UpdateRepositoryOwnerNames(user.ID, newName); err != nil {
|
|
ctx.Error(http.StatusInternalServerError, "UpdateRepository", err)
|
|
return err
|
|
}
|
|
}
|
|
|
|
// update all agit flow pull request header
|
|
err := agit.UserNameChanged(user, newName)
|
|
if err != nil {
|
|
ctx.Error(http.StatusInternalServerError, "agit.UserNameChanged", err)
|
|
return err
|
|
}
|
|
|
|
if err := container_service.UpdateRepositoryNames(ctx, user, newName); err != nil {
|
|
ctx.Error(http.StatusInternalServerError, "UpdateRepositoryNames", err)
|
|
return err
|
|
}
|
|
|
|
log.Trace("User name changed: %s -> %s", user.Name, newName)
|
|
return nil
|
|
}
|