Skip to content

Commit 7421947

Browse files
committed
fix & improve
1 parent e39acb0 commit 7421947

File tree

6 files changed

+50
-12
lines changed

6 files changed

+50
-12
lines changed

models/error.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,21 @@ func (err ErrUserOwnPackages) Error() string {
5757
return fmt.Sprintf("user still has ownership of packages [uid: %d]", err.UID)
5858
}
5959

60+
// ErrDeleteLastAdminUser represents a "DeleteLastAdminUser" kind of error.
61+
type ErrDeleteLastAdminUser struct {
62+
UID int64
63+
}
64+
65+
// IsErrDeleteLastAdminUser checks if an error is a ErrDeleteLastAdminUser.
66+
func IsErrDeleteLastAdminUser(err error) bool {
67+
_, ok := err.(ErrDeleteLastAdminUser)
68+
return ok
69+
}
70+
71+
func (err ErrDeleteLastAdminUser) Error() string {
72+
return fmt.Sprintf("can not delete the last admin user [uid: %d]", err.UID)
73+
}
74+
6075
// ErrNoPendingRepoTransfer is an error type for repositories without a pending
6176
// transfer request
6277
type ErrNoPendingRepoTransfer struct {

models/user/user.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,14 @@ func CreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUserOve
705705
return committer.Commit()
706706
}
707707

708+
// IsLastAdminUser check whether user is the last admin
709+
func IsLastAdminUser(ctx context.Context, user *User) bool {
710+
if user.IsAdmin && CountUsers(ctx, &CountUserFilter{IsAdmin: util.OptionalBoolTrue}) <= 1 {
711+
return true
712+
}
713+
return false
714+
}
715+
708716
// CountUserFilter represent optional filters for CountUsers
709717
type CountUserFilter struct {
710718
LastLoginSince *int64

routers/api/v1/admin/user.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -255,13 +255,10 @@ func EditUser(ctx *context.APIContext) {
255255
if len(form.Visibility) != 0 {
256256
ctx.ContextUser.Visibility = api.VisibilityModes[form.Visibility]
257257
}
258-
if form.Admin != nil {
259-
// Check whether user is the last admin
260-
if ctx.ContextUser.IsAdmin && !*form.Admin &&
261-
user_model.CountUsers(ctx, &user_model.CountUserFilter{IsAdmin: util.OptionalBoolTrue}) <= 1 {
262-
ctx.Error(http.StatusBadRequest, "LastAdmin", ctx.Tr("auth.last_admin"))
263-
return
264-
}
258+
if form.Admin != nil && !*form.Admin && user_model.IsLastAdminUser(ctx, ctx.ContextUser) {
259+
ctx.Error(http.StatusBadRequest, "LastAdmin", ctx.Tr("auth.last_admin"))
260+
return
261+
} else {
265262
ctx.ContextUser.IsAdmin = *form.Admin
266263
}
267264
if form.AllowGitHook != nil {
@@ -339,7 +336,8 @@ func DeleteUser(ctx *context.APIContext) {
339336
if err := user_service.DeleteUser(ctx, ctx.ContextUser, ctx.FormBool("purge")); err != nil {
340337
if models.IsErrUserOwnRepos(err) ||
341338
models.IsErrUserHasOrgs(err) ||
342-
models.IsErrUserOwnPackages(err) {
339+
models.IsErrUserOwnPackages(err) ||
340+
models.IsErrDeleteLastAdminUser(err) {
343341
ctx.Error(http.StatusUnprocessableEntity, "", err)
344342
} else {
345343
ctx.Error(http.StatusInternalServerError, "DeleteUser", err)

routers/web/admin/users.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -437,8 +437,7 @@ func EditUserPost(ctx *context.Context) {
437437
}
438438

439439
// Check whether user is the last admin
440-
if u.IsAdmin && !form.Admin &&
441-
user_model.CountUsers(ctx, &user_model.CountUserFilter{IsAdmin: util.OptionalBoolTrue}) <= 1 {
440+
if !form.Admin && user_model.IsLastAdminUser(ctx, u) {
442441
ctx.RenderWithErr(ctx.Tr("auth.last_admin"), tplUserEdit, &form)
443442
return
444443
}
@@ -510,7 +509,10 @@ func DeleteUser(ctx *context.Context) {
510509
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
511510
case models.IsErrUserOwnPackages(err):
512511
ctx.Flash.Error(ctx.Tr("admin.users.still_own_packages"))
513-
ctx.Redirect(setting.AppSubURL + "/admin/users/" + ctx.Params(":userid"))
512+
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
513+
case models.IsErrDeleteLastAdminUser(err):
514+
ctx.Flash.Error(ctx.Tr("auth.last_admin"))
515+
ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
514516
default:
515517
ctx.ServerError("DeleteUser", err)
516518
}

routers/web/user/setting/account.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,13 @@ func DeleteAccount(ctx *context.Context) {
246246
return
247247
}
248248

249+
// admin should not delete themself
250+
if ctx.ContextUser.ID == ctx.Doer.ID {
251+
ctx.Flash.Error(ctx.Tr("admin.users.cannot_delete_self"))
252+
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
253+
return
254+
}
255+
249256
if err := user.DeleteUser(ctx, ctx.Doer, false); err != nil {
250257
switch {
251258
case models.IsErrUserOwnRepos(err):
@@ -257,6 +264,9 @@ func DeleteAccount(ctx *context.Context) {
257264
case models.IsErrUserOwnPackages(err):
258265
ctx.Flash.Error(ctx.Tr("form.still_own_packages"))
259266
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
267+
case models.IsErrDeleteLastAdminUser(err):
268+
ctx.Flash.Error(ctx.Tr("auth.last_admin"))
269+
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
260270
default:
261271
ctx.ServerError("DeleteUser", err)
262272
}

services/user/user.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error {
129129
return fmt.Errorf("%s is an organization not a user", u.Name)
130130
}
131131

132+
if user_model.IsLastAdminUser(ctx, u) {
133+
return models.ErrDeleteLastAdminUser{UID: u.ID}
134+
}
135+
132136
if purge {
133137
// Disable the user first
134138
// NOTE: This is deliberately not within a transaction as it must disable the user immediately to prevent any further action by the user to be purged.
@@ -295,7 +299,8 @@ func DeleteInactiveUsers(ctx context.Context, olderThan time.Duration) error {
295299
}
296300
if err := DeleteUser(ctx, u, false); err != nil {
297301
// Ignore users that were set inactive by admin.
298-
if models.IsErrUserOwnRepos(err) || models.IsErrUserHasOrgs(err) || models.IsErrUserOwnPackages(err) {
302+
if models.IsErrUserOwnRepos(err) || models.IsErrUserHasOrgs(err) ||
303+
models.IsErrUserOwnPackages(err) || models.IsErrDeleteLastAdminUser(err) {
299304
continue
300305
}
301306
return err

0 commit comments

Comments
 (0)