Files

554 lines
18 KiB
Go
Raw Permalink Normal View History

2014-08-29 15:32:52 +08:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2020-01-24 19:00:29 +00:00
// Copyright 2020 The Gitea Authors.
// SPDX-License-Identifier: MIT
2014-08-29 15:32:52 +08:00
package admin
import (
2024-02-04 14:29:09 +01:00
"errors"
"net/http"
2021-11-16 18:18:25 +00:00
"net/url"
2020-12-25 09:59:32 +00:00
"strconv"
2014-08-29 15:32:52 +08:00
"strings"
2022-01-02 21:12:35 +08:00
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
2023-08-31 11:21:18 +02:00
org_model "code.gitea.io/gitea/models/organization"
packages_model "code.gitea.io/gitea/models/packages"
2023-08-31 11:21:18 +02:00
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/log"
2024-02-04 14:29:09 +01:00
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/templates"
2021-01-26 23:36:53 +08:00
"code.gitea.io/gitea/modules/web"
2021-06-09 07:33:54 +08:00
"code.gitea.io/gitea/routers/web/explore"
user_setting "code.gitea.io/gitea/routers/web/user/setting"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/mailer"
user_service "code.gitea.io/gitea/services/user"
2014-08-29 15:32:52 +08:00
)
const (
tplUsers templates.TplName = "admin/user/list"
tplUserNew templates.TplName = "admin/user/new"
tplUserView templates.TplName = "admin/user/view"
tplUserEdit templates.TplName = "admin/user/edit"
2014-08-29 15:32:52 +08:00
)
// UserSearchDefaultAdminSort is the default sort type for admin view
const UserSearchDefaultAdminSort = "alphabetically"
2016-11-21 11:21:24 +08:00
// Users show all the users
2016-03-11 11:56:52 -05:00
func Users(ctx *context.Context) {
2014-08-29 20:50:43 +08:00
ctx.Data["Title"] = ctx.Tr("admin.users")
ctx.Data["PageIsAdminUsers"] = true
statusFilterKeys := []string{"is_active", "is_admin", "is_restricted", "is_2fa_enabled", "is_prohibit_login"}
statusFilterMap := map[string]string{}
for _, filterKey := range statusFilterKeys {
paramKey := "status_filter[" + filterKey + "]"
paramVal := ctx.FormString(paramKey)
statusFilterMap[filterKey] = paramVal
}
sortType := ctx.FormString("sort")
if sortType == "" {
sortType = UserSearchDefaultAdminSort
ctx.SetFormString("sort", sortType)
}
2023-07-04 20:36:08 +02:00
ctx.PageData["adminUserListSearchForm"] = map[string]any{
"StatusFilterMap": statusFilterMap,
"SortType": sortType,
}
explore.RenderUserSearch(ctx, user_model.SearchUserOptions{
2022-03-22 08:03:22 +01:00
Actor: ctx.Doer,
Types: []user_model.UserType{user_model.UserTypeIndividual},
ListOptions: db.ListOptions{
2020-01-24 19:00:29 +00:00
PageSize: setting.UI.Admin.UserPagingNum,
},
SearchByEmail: true,
2025-03-23 20:53:30 -07:00
IsActive: optional.ParseBool(statusFilterMap["is_active"]),
IsAdmin: optional.ParseBool(statusFilterMap["is_admin"]),
IsRestricted: optional.ParseBool(statusFilterMap["is_restricted"]),
IsTwoFactorEnabled: optional.ParseBool(statusFilterMap["is_2fa_enabled"]),
IsProhibitLogin: optional.ParseBool(statusFilterMap["is_prohibit_login"]),
IncludeReserved: true, // administrator needs to list all accounts include reserved, bot, remote ones
}, tplUsers)
2014-08-29 15:32:52 +08:00
}
2016-11-21 11:21:24 +08:00
// NewUser render adding a new user page
2016-03-11 11:56:52 -05:00
func NewUser(ctx *context.Context) {
2014-08-29 15:32:52 +08:00
ctx.Data["Title"] = ctx.Tr("admin.users.new_account")
ctx.Data["PageIsAdminUsers"] = true
ctx.Data["DefaultUserVisibilityMode"] = setting.Service.DefaultUserVisibilityMode
ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
2014-08-29 15:32:52 +08:00
ctx.Data["login_type"] = "0-0"
sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{
IsActive: optional.Some(true),
})
2014-08-29 15:32:52 +08:00
if err != nil {
2022-01-02 21:12:35 +08:00
ctx.ServerError("auth.Sources", err)
2014-08-29 15:32:52 +08:00
return
}
ctx.Data["Sources"] = sources
ctx.Data["CanSendEmail"] = setting.MailService != nil
ctx.HTML(http.StatusOK, tplUserNew)
2014-08-29 15:32:52 +08:00
}
2016-11-21 11:21:24 +08:00
// NewUserPost response for adding a new user
2021-01-26 23:36:53 +08:00
func NewUserPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.AdminCreateUserForm)
2014-08-29 15:32:52 +08:00
ctx.Data["Title"] = ctx.Tr("admin.users.new_account")
ctx.Data["PageIsAdminUsers"] = true
ctx.Data["DefaultUserVisibilityMode"] = setting.Service.DefaultUserVisibilityMode
ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
2014-08-29 15:32:52 +08:00
sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{
IsActive: optional.Some(true),
})
if err != nil {
2022-01-02 21:12:35 +08:00
ctx.ServerError("auth.Sources", err)
2014-08-29 15:32:52 +08:00
return
}
ctx.Data["Sources"] = sources
2014-08-29 15:32:52 +08:00
ctx.Data["CanSendEmail"] = setting.MailService != nil
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplUserNew)
2014-08-29 15:32:52 +08:00
return
}
u := &user_model.User{
Name: form.UserName,
Email: form.Email,
Passwd: form.Password,
2022-01-02 21:12:35 +08:00
LoginType: auth.Plain,
2014-08-29 15:32:52 +08:00
}
overwriteDefault := &user_model.CreateUserOverwriteOptions{
IsActive: optional.Some(true),
Visibility: &form.Visibility,
}
2014-08-29 15:32:52 +08:00
if len(form.LoginType) > 0 {
fields := strings.Split(form.LoginType, "-")
if len(fields) == 2 {
2020-12-25 09:59:32 +00:00
lType, _ := strconv.ParseInt(fields[0], 10, 0)
2022-01-02 21:12:35 +08:00
u.LoginType = auth.Type(lType)
2020-12-25 09:59:32 +00:00
u.LoginSource, _ = strconv.ParseInt(fields[1], 10, 64)
u.LoginName = form.LoginName
}
2014-08-29 15:32:52 +08:00
}
2022-01-02 21:12:35 +08:00
if u.LoginType == auth.NoType || u.LoginType == auth.Plain {
if len(form.Password) < setting.MinPasswordLength {
ctx.Data["Err_Password"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplUserNew, &form)
return
}
if !password.IsComplexEnough(form.Password) {
ctx.Data["Err_Password"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(password.BuildComplexityError(ctx.Locale), tplUserNew, &form)
return
}
2024-02-04 14:29:09 +01:00
if err := password.IsPwned(ctx, form.Password); err != nil {
ctx.Data["Err_Password"] = true
2024-09-02 20:36:24 +02:00
errMsg := ctx.Tr("auth.password_pwned", "https://haveibeenpwned.com/Passwords")
2024-02-04 14:29:09 +01:00
if password.IsErrIsPwnedRequest(err) {
log.Error(err.Error())
errMsg = ctx.Tr("auth.password_pwned_err")
}
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(errMsg, tplUserNew, &form)
return
}
u.MustChangePassword = form.MustChangePassword
2019-10-14 22:24:26 +07:00
}
if err := user_model.AdminCreateUser(ctx, u, &user_model.Meta{}, overwriteDefault); err != nil {
switch {
case user_model.IsErrUserAlreadyExist(err):
2014-08-29 15:32:52 +08:00
ctx.Data["Err_UserName"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("form.username_been_taken"), tplUserNew, &form)
case user_model.IsErrEmailAlreadyUsed(err):
2014-08-29 15:32:52 +08:00
ctx.Data["Err_Email"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("form.email_been_used"), tplUserNew, &form)
2024-02-04 14:29:09 +01:00
case user_model.IsErrEmailInvalid(err), user_model.IsErrEmailCharIsNotSupported(err):
2020-11-15 00:53:43 +08:00
ctx.Data["Err_Email"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("form.email_invalid"), tplUserNew, &form)
case db.IsErrNameReserved(err):
2014-08-29 15:32:52 +08:00
ctx.Data["Err_UserName"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("user.form.name_reserved", err.(db.ErrNameReserved).Name), tplUserNew, &form)
case db.IsErrNamePatternNotAllowed(err):
ctx.Data["Err_UserName"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("user.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplUserNew, &form)
case db.IsErrNameCharsNotAllowed(err):
2020-02-23 16:52:05 -03:00
ctx.Data["Err_UserName"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("user.form.name_chars_not_allowed", err.(db.ErrNameCharsNotAllowed).Name), tplUserNew, &form)
2014-08-29 15:32:52 +08:00
default:
2018-01-10 22:34:17 +01:00
ctx.ServerError("CreateUser", err)
2014-08-29 15:32:52 +08:00
}
return
}
if !user_model.IsEmailDomainAllowed(u.Email) {
ctx.Flash.Warning(ctx.Tr("form.email_domain_is_not_allowed", u.Email))
}
2022-03-22 08:03:22 +01:00
log.Trace("Account created by admin (%s): %s", ctx.Doer.Name, u.Name)
// Send email notification.
if form.SendNotify {
2021-04-02 12:25:13 +02:00
mailer.SendRegisterNotifyMail(u)
}
ctx.Flash.Success(ctx.Tr("admin.users.new_success", u.Name))
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + strconv.FormatInt(u.ID, 10))
2014-08-29 15:32:52 +08:00
}
func prepareUserInfo(ctx *context.Context) *user_model.User {
2024-12-24 21:47:45 +08:00
u, err := user_model.GetUserByID(ctx, ctx.PathParamInt64("userid"))
2014-08-29 15:32:52 +08:00
if err != nil {
2022-08-29 15:44:39 +02:00
if user_model.IsErrUserNotExist(err) {
ctx.Redirect(setting.AppSubURL + "/-/admin/users")
2022-08-29 15:44:39 +02:00
} else {
ctx.ServerError("GetUserByID", err)
}
2015-09-13 11:07:21 -04:00
return nil
2014-08-29 15:32:52 +08:00
}
ctx.Data["User"] = u
2015-09-13 11:07:21 -04:00
if u.LoginSource > 0 {
ctx.Data["LoginSource"], err = auth.GetSourceByID(ctx, u.LoginSource)
2015-09-13 11:07:21 -04:00
if err != nil {
2022-01-02 21:12:35 +08:00
ctx.ServerError("auth.GetSourceByID", err)
2015-09-13 11:07:21 -04:00
return nil
}
} else {
2022-01-02 21:12:35 +08:00
ctx.Data["LoginSource"] = &auth.Source{}
2015-09-13 11:07:21 -04:00
}
sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{})
2014-08-29 15:32:52 +08:00
if err != nil {
2022-01-02 21:12:35 +08:00
ctx.ServerError("auth.Sources", err)
2015-09-13 11:07:21 -04:00
return nil
2014-08-29 15:32:52 +08:00
}
2015-09-13 11:07:21 -04:00
ctx.Data["Sources"] = sources
hasTOTP, err := auth.HasTwoFactorByUID(ctx, u.ID)
if err != nil {
ctx.ServerError("auth.HasTwoFactorByUID", err)
return nil
}
hasWebAuthn, err := auth.HasWebAuthnRegistrationsByUID(ctx, u.ID)
if err != nil {
ctx.ServerError("auth.HasWebAuthnRegistrationsByUID", err)
return nil
}
ctx.Data["TwoFactorEnabled"] = hasTOTP || hasWebAuthn
2015-09-13 11:07:21 -04:00
return u
2014-08-29 15:32:52 +08:00
}
2023-08-31 11:21:18 +02:00
func ViewUser(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.users.details")
ctx.Data["PageIsAdminUsers"] = true
ctx.Data["DisableRegularOrgCreation"] = setting.Admin.DisableRegularOrgCreation
ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
u := prepareUserInfo(ctx)
if ctx.Written() {
return
}
repos, count, err := repo_model.SearchRepository(ctx, repo_model.SearchRepoOptions{
ListOptions: db.ListOptionsAll,
2023-08-31 11:21:18 +02:00
OwnerID: u.ID,
OrderBy: db.SearchOrderByAlphabetically,
Private: true,
Collaborate: optional.Some(false),
2023-08-31 11:21:18 +02:00
})
if err != nil {
ctx.ServerError("SearchRepository", err)
return
}
ctx.Data["Repos"] = repos
ctx.Data["ReposTotal"] = int(count)
2023-09-14 19:09:32 +02:00
emails, err := user_model.GetEmailAddresses(ctx, u.ID)
2023-08-31 11:21:18 +02:00
if err != nil {
ctx.ServerError("GetEmailAddresses", err)
return
}
ctx.Data["Emails"] = emails
ctx.Data["EmailsTotal"] = len(emails)
orgs, err := db.Find[org_model.Organization](ctx, org_model.FindOrgOptions{
ListOptions: db.ListOptionsAll,
UserID: u.ID,
IncludeVisibility: structs.VisibleTypePrivate,
2023-08-31 11:21:18 +02:00
})
if err != nil {
ctx.ServerError("FindOrgs", err)
return
}
ctx.Data["Users"] = orgs // needed to be able to use explore/user_list template
ctx.Data["OrgsTotal"] = len(orgs)
ctx.HTML(http.StatusOK, tplUserView)
}
2023-10-05 09:08:19 +08:00
func editUserCommon(ctx *context.Context) {
2014-08-29 15:32:52 +08:00
ctx.Data["Title"] = ctx.Tr("admin.users.edit_account")
ctx.Data["PageIsAdminUsers"] = true
ctx.Data["DisableRegularOrgCreation"] = setting.Admin.DisableRegularOrgCreation
2020-12-21 15:39:41 +01:00
ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
ctx.Data["DisableGitHooks"] = setting.DisableGitHooks
ctx.Data["DisableImportLocal"] = !setting.ImportLocalPaths
ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice()
2023-10-05 09:08:19 +08:00
ctx.Data["DisableGravatar"] = setting.Config().Picture.DisableGravatar.Value(ctx)
}
2014-08-29 15:32:52 +08:00
2023-10-05 09:08:19 +08:00
// EditUser show editing user page
func EditUser(ctx *context.Context) {
editUserCommon(ctx)
2015-09-13 11:07:21 -04:00
prepareUserInfo(ctx)
if ctx.Written() {
2014-08-29 15:32:52 +08:00
return
}
ctx.HTML(http.StatusOK, tplUserEdit)
2015-09-13 11:07:21 -04:00
}
// EditUserPost response for editing user
2021-01-26 23:36:53 +08:00
func EditUserPost(ctx *context.Context) {
2023-10-05 09:08:19 +08:00
editUserCommon(ctx)
2015-09-13 11:07:21 -04:00
u := prepareUserInfo(ctx)
if ctx.Written() {
2014-08-29 15:32:52 +08:00
return
}
2023-10-05 09:08:19 +08:00
form := web.GetForm(ctx).(*forms.AdminEditUserForm)
2014-08-29 15:32:52 +08:00
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplUserEdit)
2014-08-29 15:32:52 +08:00
return
}
2024-02-04 14:29:09 +01:00
if form.UserName != "" {
if err := user_service.RenameUser(ctx, u, form.UserName, ctx.Doer); err != nil {
2024-02-04 14:29:09 +01:00
switch {
case user_model.IsErrUserIsNotLocal(err):
ctx.Data["Err_UserName"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("form.username_change_not_local_user"), tplUserEdit, &form)
2024-02-04 14:29:09 +01:00
case user_model.IsErrUserAlreadyExist(err):
ctx.Data["Err_UserName"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("form.username_been_taken"), tplUserEdit, &form)
2024-02-04 14:29:09 +01:00
case db.IsErrNameReserved(err):
ctx.Data["Err_UserName"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("user.form.name_reserved", form.UserName), tplUserEdit, &form)
2024-02-04 14:29:09 +01:00
case db.IsErrNamePatternNotAllowed(err):
ctx.Data["Err_UserName"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("user.form.name_pattern_not_allowed", form.UserName), tplUserEdit, &form)
2024-02-04 14:29:09 +01:00
case db.IsErrNameCharsNotAllowed(err):
ctx.Data["Err_UserName"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("user.form.name_chars_not_allowed", form.UserName), tplUserEdit, &form)
2024-02-04 14:29:09 +01:00
default:
ctx.ServerError("RenameUser", err)
}
return
}
}
authOpts := &user_service.UpdateAuthOptions{
Password: optional.FromNonDefault(form.Password),
LoginName: optional.Some(form.LoginName),
}
// skip self Prohibit Login
if ctx.Doer.ID == u.ID {
authOpts.ProhibitLogin = optional.Some(false)
} else {
authOpts.ProhibitLogin = optional.Some(form.ProhibitLogin)
}
2015-09-13 11:07:21 -04:00
fields := strings.Split(form.LoginType, "-")
if len(fields) == 2 {
2022-01-02 21:12:35 +08:00
authSource, _ := strconv.ParseInt(fields[1], 10, 64)
2015-09-13 11:07:21 -04:00
2024-02-04 14:29:09 +01:00
authOpts.LoginSource = optional.Some(authSource)
2015-09-13 11:07:21 -04:00
}
2024-02-04 14:29:09 +01:00
if err := user_service.UpdateAuth(ctx, u, authOpts); err != nil {
switch {
case errors.Is(err, password.ErrMinLength):
ctx.Data["Err_Password"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplUserEdit, &form)
2024-02-04 14:29:09 +01:00
case errors.Is(err, password.ErrComplexity):
ctx.Data["Err_Password"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(password.BuildComplexityError(ctx.Locale), tplUserEdit, &form)
2024-02-04 14:29:09 +01:00
case errors.Is(err, password.ErrIsPwned):
ctx.Data["Err_Password"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("auth.password_pwned", "https://haveibeenpwned.com/Passwords"), tplUserEdit, &form)
2024-02-04 14:29:09 +01:00
case password.IsErrIsPwnedRequest(err):
ctx.Data["Err_Password"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("auth.password_pwned_err"), tplUserEdit, &form)
2024-02-04 14:29:09 +01:00
default:
ctx.ServerError("UpdateUser", err)
}
2024-02-04 14:29:09 +01:00
return
}
2024-02-04 14:29:09 +01:00
if form.Email != "" {
2025-12-01 15:50:10 -08:00
if err := user_service.ReplacePrimaryEmailAddress(ctx, u, form.Email); err != nil {
2024-02-04 14:29:09 +01:00
switch {
case user_model.IsErrEmailCharIsNotSupported(err), user_model.IsErrEmailInvalid(err):
ctx.Data["Err_Email"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("form.email_invalid"), tplUserEdit, &form)
2024-02-04 14:29:09 +01:00
case user_model.IsErrEmailAlreadyUsed(err):
ctx.Data["Err_Email"] = true
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("form.email_been_used"), tplUserEdit, &form)
2024-02-04 14:29:09 +01:00
default:
ctx.ServerError("AddOrSetPrimaryEmailAddress", err)
}
return
}
if !user_model.IsEmailDomainAllowed(form.Email) {
ctx.Flash.Warning(ctx.Tr("form.email_domain_is_not_allowed", form.Email))
}
2024-02-04 14:29:09 +01:00
}
2024-02-04 14:29:09 +01:00
opts := &user_service.UpdateOptions{
FullName: optional.Some(form.FullName),
Website: optional.Some(form.Website),
Location: optional.Some(form.Location),
IsActive: optional.Some(form.Active),
IsAdmin: user_service.UpdateOptionFieldFromValue(form.Admin),
2024-02-04 14:29:09 +01:00
AllowGitHook: optional.Some(form.AllowGitHook),
AllowImportLocal: optional.Some(form.AllowImportLocal),
MaxRepoCreation: optional.Some(form.MaxRepoCreation),
AllowCreateOrganization: optional.Some(form.AllowCreateOrganization),
IsRestricted: optional.Some(form.Restricted),
Visibility: optional.Some(form.Visibility),
Language: optional.Some(form.Language),
2014-08-29 15:32:52 +08:00
}
2024-02-04 14:29:09 +01:00
if err := user_service.UpdateUser(ctx, u, opts); err != nil {
if user_model.IsErrDeleteLastAdminUser(err) {
2026-02-27 20:38:44 +08:00
ctx.RenderWithErrDeprecated(ctx.Tr("auth.last_admin"), tplUserEdit, &form)
2024-02-04 14:29:09 +01:00
} else {
ctx.ServerError("UpdateUser", err)
}
2024-02-04 14:29:09 +01:00
return
}
2024-02-04 14:29:09 +01:00
log.Trace("Account profile updated by admin (%s): %s", ctx.Doer.Name, u.Name)
if form.Reset2FA {
tf, err := auth.GetTwoFactorByUID(ctx, u.ID)
2022-01-02 21:12:35 +08:00
if err != nil && !auth.IsErrTwoFactorNotEnrolled(err) {
ctx.ServerError("auth.GetTwoFactorByUID", err)
return
} else if tf != nil {
if err := auth.DeleteTwoFactorByID(ctx, tf.ID, u.ID); err != nil {
ctx.ServerError("auth.DeleteTwoFactorByID", err)
return
}
}
wn, err := auth.GetWebAuthnCredentialsByUID(ctx, u.ID)
if err != nil {
ctx.ServerError("auth.GetTwoFactorByUID", err)
return
}
for _, cred := range wn {
if _, err := auth.DeleteCredential(ctx, cred.ID, u.ID); err != nil {
ctx.ServerError("auth.DeleteCredential", err)
return
}
}
2014-08-29 15:32:52 +08:00
}
2015-09-13 11:07:21 -04:00
2014-08-29 15:32:52 +08:00
ctx.Flash.Success(ctx.Tr("admin.users.update_profile_success"))
2024-12-24 21:47:45 +08:00
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam("userid")))
2014-08-29 15:32:52 +08:00
}
2016-11-21 11:21:24 +08:00
// DeleteUser response for deleting a user
2016-03-11 11:56:52 -05:00
func DeleteUser(ctx *context.Context) {
2024-12-24 21:47:45 +08:00
u, err := user_model.GetUserByID(ctx, ctx.PathParamInt64("userid"))
2014-08-29 15:32:52 +08:00
if err != nil {
2018-01-10 22:34:17 +01:00
ctx.ServerError("GetUserByID", err)
2014-08-29 15:32:52 +08:00
return
}
2022-05-09 04:22:55 +08:00
// admin should not delete themself
if u.ID == ctx.Doer.ID {
ctx.Flash.Error(ctx.Tr("admin.users.cannot_delete_self"))
2024-12-24 21:47:45 +08:00
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam("userid")))
2022-05-09 04:22:55 +08:00
return
}
2022-07-14 08:22:09 +01:00
if err = user_service.DeleteUser(ctx, u, ctx.FormBool("purge")); err != nil {
switch {
case repo_model.IsErrUserOwnRepos(err):
2014-08-29 15:32:52 +08:00
ctx.Flash.Error(ctx.Tr("admin.users.still_own_repo"))
2024-12-24 21:47:45 +08:00
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam("userid")))
case org_model.IsErrUserHasOrgs(err):
2014-11-13 05:27:01 -05:00
ctx.Flash.Error(ctx.Tr("admin.users.still_has_org"))
2024-12-24 21:47:45 +08:00
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam("userid")))
case packages_model.IsErrUserOwnPackages(err):
2022-03-30 10:42:47 +02:00
ctx.Flash.Error(ctx.Tr("admin.users.still_own_packages"))
2024-12-24 21:47:45 +08:00
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam("userid")))
case user_model.IsErrDeleteLastAdminUser(err):
ctx.Flash.Error(ctx.Tr("auth.last_admin"))
2024-12-24 21:47:45 +08:00
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + url.PathEscape(ctx.PathParam("userid")))
2014-08-29 15:32:52 +08:00
default:
2018-01-10 22:34:17 +01:00
ctx.ServerError("DeleteUser", err)
2014-08-29 15:32:52 +08:00
}
return
}
2022-03-22 08:03:22 +01:00
log.Trace("Account deleted by admin (%s): %s", ctx.Doer.Name, u.Name)
2015-09-13 13:26:20 -04:00
ctx.Flash.Success(ctx.Tr("admin.users.deletion_success"))
ctx.Redirect(setting.AppSubURL + "/-/admin/users")
2014-08-29 15:32:52 +08:00
}
// AvatarPost response for change user's avatar request
func AvatarPost(ctx *context.Context) {
u := prepareUserInfo(ctx)
if ctx.Written() {
return
}
form := web.GetForm(ctx).(*forms.AvatarForm)
if err := user_setting.UpdateAvatarSetting(ctx, form, u); err != nil {
ctx.Flash.Error(err.Error())
} else {
ctx.Flash.Success(ctx.Tr("settings.update_user_avatar_success"))
}
ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + strconv.FormatInt(u.ID, 10))
}
// DeleteAvatar render delete avatar page
func DeleteAvatar(ctx *context.Context) {
u := prepareUserInfo(ctx)
if ctx.Written() {
return
}
if err := user_service.DeleteAvatar(ctx, u); err != nil {
ctx.Flash.Error(err.Error())
}
ctx.JSONRedirect(setting.AppSubURL + "/-/admin/users/" + strconv.FormatInt(u.ID, 10))
}