Modified - Restored normal self-edit access for super admins and disabled only the admin actions that are truly forbidden.
This commit is contained in:
@@ -382,3 +382,8 @@ Project Change ID[date-time] - application-version - Type - Summary:
|
||||
- 2 - I modified `routers/web/admin/users.go` so GOOD-granted accounts cannot be edited or deleted through admin actions, and regular admins may delete other admin accounts except their direct grantor and protected super-admin cases.
|
||||
- 3 - I modified `routers/api/v1/admin/user.go` so the admin API now enforces the same GOOD-protection and direct-grantor deletion rule.
|
||||
- 4 - I added locale messages for the new admin restrictions and added focused integration coverage for deleting another admin versus the direct grantor, plus GOOD-protected admin API edits.
|
||||
|
||||
73 - [2026-04-30 16:51:42] - v1.27.0-dev-71-g80497e4194 - Type: Modified - Restored normal self-edit access for super admins and disabled only the admin actions that are truly forbidden.
|
||||
- 1 - I modified `routers/web/admin/users.go` so a GOOD-granted or super-admin user can still edit their own ordinary account fields, while forbidden cross-user edits remain blocked and table action states are computed per target user.
|
||||
- 2 - I modified `routers/api/v1/admin/user.go` so GOOD-protection no longer blocks a user from editing their own account through the admin API.
|
||||
- 3 - I modified `templates/admin/user/list.tmpl`, `templates/admin/user/view.tmpl`, and `templates/admin/user/edit.tmpl` so `Edit` stays enabled for allowed self-edits, while forbidden `Edit` and `Delete` actions are shown disabled with the specific reason tooltip or warning message.
|
||||
|
||||
@@ -262,7 +262,7 @@ func EditUser(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/validationError"
|
||||
|
||||
form := web.GetForm(ctx).(*api.EditUserOption)
|
||||
if isBootstrapProtectedUser(ctx, ctx.ContextUser.ID) {
|
||||
if isBootstrapProtectedUser(ctx, ctx.ContextUser.ID) && ctx.ContextUser.ID != ctx.Doer.ID {
|
||||
ctx.APIError(http.StatusForbidden, errors.New("GOOD-granted accounts cannot be modified or deleted"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ func Users(ctx *context.Context) {
|
||||
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, loadAdminUserListDeleteState, loadAdminUserListAdminBy, loadAdminUserListSuperAdminBy, loadAdminUserListCreatedBy, loadAdminUserListActivatedBy, loadAdminUserListProhibitLoginBy, loadAdminUserListRestrictedBy)
|
||||
}, tplUsers, loadAdminUserListDeleteState, loadAdminUserListAdminBy, loadAdminUserListSuperAdminBy, loadAdminUserListCreatedBy, loadAdminUserListActivatedBy, loadAdminUserListProhibitLoginBy, loadAdminUserListRestrictedBy, loadAdminUserListActionState)
|
||||
}
|
||||
|
||||
func loadAdminUserListDeleteState(ctx *context.Context, users user_model.UserList) {
|
||||
@@ -905,7 +905,9 @@ func ViewUser(ctx *context.Context) {
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
ctx.Data["CanEditUser"] = canEditSuperAdminUser(ctx, isSuperAdmin(ctx, u.ID))
|
||||
canEditUser, editDisabledReason := canEditUserFromAdminPanel(ctx, ctx.Doer, u)
|
||||
ctx.Data["CanEditUser"] = canEditUser
|
||||
ctx.Data["EditUserDisabledReason"] = editDisabledReason
|
||||
|
||||
repos, count, err := repo_model.SearchRepository(ctx, repo_model.SearchRepoOptions{
|
||||
ListOptions: db.ListOptionsAll,
|
||||
@@ -978,6 +980,9 @@ func EditUser(ctx *context.Context) {
|
||||
}
|
||||
loadAdminStatusReasonData(ctx, u)
|
||||
ctx.Data["IsLastAdminUser"] = user_model.IsLastAdminUser(ctx, u)
|
||||
canDeleteUser, deleteDisabledReason := canDeleteUserFromAdminPanel(ctx, ctx.Doer, u)
|
||||
ctx.Data["CanDeleteUser"] = canDeleteUser
|
||||
ctx.Data["DeleteUserDisabledReason"] = deleteDisabledReason
|
||||
targetIsSuperAdmin := isSuperAdmin(ctx, u.ID)
|
||||
ctx.Data["SuperAdminEnabled"] = setting.Admin.SuperAdminEnabled
|
||||
ctx.Data["IsUserSuperAdmin"] = targetIsSuperAdmin
|
||||
@@ -1024,6 +1029,9 @@ func renderAdminUserEditReasonErr(ctx *context.Context, u *user_model.User, form
|
||||
ctx.Data["CanEditAdminStatus"] = !setting.Admin.SuperAdminEnabled || isSuperAdmin(ctx, ctx.Doer.ID) || (setting.Admin.AdminManagementPolicy == setting.AdminManagementPolicyAdminsCanPromote && !u.IsAdmin && !targetIsSuperAdmin)
|
||||
ctx.Data["IsLastSuperAdminUser"] = targetIsSuperAdmin && countSuperAdmins(ctx) <= 1
|
||||
ctx.Data["IsLastAdminUser"] = user_model.IsLastAdminUser(ctx, u)
|
||||
canDeleteUser, deleteDisabledReason := canDeleteUserFromAdminPanel(ctx, ctx.Doer, u)
|
||||
ctx.Data["CanDeleteUser"] = canDeleteUser
|
||||
ctx.Data["DeleteUserDisabledReason"] = deleteDisabledReason
|
||||
ctx.Data["admin_reason"] = form.AdminReason
|
||||
ctx.Data["super_admin_reason"] = form.SuperAdminReason
|
||||
ctx.Data["active_reason"] = form.ActiveReason
|
||||
|
||||
@@ -242,13 +242,13 @@
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
{{if .IsLastAdminUser}}
|
||||
<p class="text left tw-mb-3">{{svg "octicon-alert"}} {{ctx.Locale.Tr "auth.last_admin"}}</p>
|
||||
{{if not .CanDeleteUser}}
|
||||
<p class="text left tw-mb-3">{{svg "octicon-alert"}} {{.DeleteUserDisabledReason}}</p>
|
||||
{{end}}
|
||||
<div class="field">
|
||||
<button class="ui primary button">{{ctx.Locale.Tr "admin.users.update_profile"}}</button>
|
||||
{{if .IsLastAdminUser}}
|
||||
<button class="ui red button disabled" disabled>{{ctx.Locale.Tr "admin.users.delete_account"}}</button>
|
||||
{{if not .CanDeleteUser}}
|
||||
<button class="ui red button disabled" disabled data-tooltip-content="{{.DeleteUserDisabledReason}}">{{ctx.Locale.Tr "admin.users.delete_account"}}</button>
|
||||
{{else}}
|
||||
<button class="ui red button show-modal" data-modal="#delete-user-modal">{{ctx.Locale.Tr "admin.users.delete_account"}}</button>
|
||||
{{end}}
|
||||
@@ -293,7 +293,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{if not .IsLastAdminUser}}
|
||||
{{if .CanDeleteUser}}
|
||||
<div class="ui g-modal-confirm delete modal" id="delete-user-modal">
|
||||
<div class="header">
|
||||
{{svg "octicon-trash"}}
|
||||
|
||||
@@ -161,20 +161,18 @@
|
||||
<td>
|
||||
<div class="tw-flex tw-gap-2">
|
||||
<a href="{{$.Link}}/{{.ID}}" data-tooltip-content="{{ctx.Locale.Tr "admin.users.details"}}">{{svg "octicon-person"}}</a>
|
||||
{{if or $.CanEditSuperAdmins (not (index $.UsersSuperAdminByAdmin .ID))}}
|
||||
{{if index $.UsersCanEdit .ID}}
|
||||
<a href="{{$.Link}}/{{.ID}}/edit" data-tooltip-content="{{ctx.Locale.Tr "edit"}}">{{svg "octicon-pencil"}}</a>
|
||||
{{else}}
|
||||
<span class="tw-text-muted" aria-disabled="true" data-tooltip-content="{{ctx.Locale.Tr "admin.users.super_admin.required"}}">{{svg "octicon-pencil"}}</span>
|
||||
<span class="tw-text-muted" aria-disabled="true" data-tooltip-content="{{index $.UsersEditDisabledReason .ID}}">{{svg "octicon-pencil"}}</span>
|
||||
{{end}}
|
||||
{{if and $.UsersIsLastAdmin (index $.UsersIsLastAdmin .ID)}}
|
||||
<span class="tw-text-muted" aria-disabled="true" data-tooltip-content="{{ctx.Locale.Tr "auth.last_admin"}}">{{svg "octicon-trash"}}</span>
|
||||
{{else if and (not $.CanEditSuperAdmins) (index $.UsersSuperAdminByAdmin .ID)}}
|
||||
<span class="tw-text-muted" aria-disabled="true" data-tooltip-content="{{ctx.Locale.Tr "admin.users.super_admin.required"}}">{{svg "octicon-trash"}}</span>
|
||||
{{else}}
|
||||
{{if index $.UsersCanDelete .ID}}
|
||||
<a class="tw-text-red show-modal" href data-modal="#admin-user-delete-modal"
|
||||
data-modal-form.action="{{$.Link}}/{{.ID}}/delete"
|
||||
data-tooltip-content="{{ctx.Locale.Tr "admin.users.delete_account"}}"
|
||||
>{{svg "octicon-trash"}}</a>
|
||||
{{else}}
|
||||
<span class="tw-text-muted" aria-disabled="true" data-tooltip-content="{{index $.UsersDeleteDisabledReason .ID}}">{{svg "octicon-trash"}}</span>
|
||||
{{end}}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
{{if .CanEditUser}}
|
||||
<a class="ui primary tiny button" href="{{.Link}}/edit">{{ctx.Locale.Tr "admin.users.edit"}}</a>
|
||||
{{else}}
|
||||
<button class="ui tiny button" type="button" disabled data-tooltip-content="{{ctx.Locale.Tr "admin.users.super_admin.required"}}">{{ctx.Locale.Tr "admin.users.edit"}}</button>
|
||||
<button class="ui tiny button" type="button" disabled data-tooltip-content="{{.EditUserDisabledReason}}">{{ctx.Locale.Tr "admin.users.edit"}}</button>
|
||||
{{end}}
|
||||
</div>
|
||||
</h4>
|
||||
|
||||
Reference in New Issue
Block a user