diff --git a/.codex-history.md b/.codex-history.md index 285f950a6e..39485d37d1 100644 --- a/.codex-history.md +++ b/.codex-history.md @@ -286,10 +286,10 @@ History search guidance: - 2 - I modified the admin users list creator badge so it shows `by ` and exposes the stored admin email as a hover tooltip. - 3 - I updated the focused admin list tests to prove the badge data still renders from stored name/email even without looking up the admin account, then regenerated local ignored template bindata. -53 - [2026-04-29 02:30:55] - v1.27.0-dev-52-gcd96c721e0 - Type: Modified - [auth] [admin-created-user] [badges] Added admin activation badges and first-admin GOOD badge. +53 - [2026-04-29 02:30:55] - v1.27.0-dev-52-gcd96c721e0 - Type: Modified - [auth] [admin-created-user] [badges] Added admin activation badges and first-admin GOD badge. - 1 - I modified the admin users list so the Activated column can show a `by ` badge with the stored admin email as a hover tooltip. - 2 - I stored admin activation ID, username, and email address when account requests are approved or inactive users are manually activated by an admin. -- 3 - I added the special `by GOOD` Created-column badge for the first admin account, updated focused admin list tests, and regenerated local ignored template bindata. +- 3 - I added the special `by GOD` Created-column badge for the first admin account, updated focused admin list tests, and regenerated local ignored template bindata. 54 - [2026-04-29 08:25:36] - v1.27.0-dev-53-ga3e09bb819 - Type: Fixed - [admin-users] [badges] Prevented empty `by` badges in the admin users list. - 1 - I changed the admin users list badge maps to use pointer values so missing entries render as empty instead of truthy zero-value structs. @@ -362,14 +362,14 @@ History search guidance: 68 - [2026-04-30 01:44:17] - v1.27.0-dev-64-gcff1b46f50 - Type: Added - [admin-users] [super-admin] Added persistent super administrator protection. - 1 - I added `SUPER_ADMIN_ENABLED` and `ADMIN_MANAGEMENT_POLICY` configuration options for administrator permission management. -- 2 - I stored the super administrator role and grant/revoke metadata in persistent user settings, with automatic `by GOOD` bootstrap for the first active admin. +- 2 - I stored the super administrator role and grant/revoke metadata in persistent user settings, with automatic `by GOD` bootstrap for the first active admin. - 3 - I protected administrator and super administrator changes so regular admins cannot alter existing admins unless the selected policy allows a regular-user promotion. - 4 - I added super administrator badges, reason tracking, and status-change emails, then regenerated template and options bindata. 69 - [2026-04-30 02:20:26] - v1.27.0-dev-64-gcff1b46f50 - Type: Fixed - [admin-users] [super-admin] Enabled super administrator bootstrap by default. - 1 - I changed `SUPER_ADMIN_ENABLED` to default to `true` so existing installations without the new app.ini key bootstrap the first active administrator automatically. - 2 - I updated the app.ini example to document the enabled default and kept `SUPER_ADMIN_ENABLED = false` available as the explicit opt-out. -- 3 - I made the super administrator list badge fall back to `by GOOD` for the bootstrapped first administrator and regenerated options bindata. +- 3 - I made the super administrator list badge fall back to `by GOD` for the bootstrapped first administrator and regenerated options bindata. 70 - [2026-04-30 04:08:23] - v1.27.0-dev-66-g35b9fa65d3 - Type: Fixed - [admin-users] [super-admin] Blocked regular administrators from editing super administrator accounts. - 1 - I modified `routers/web/admin/users.go` so non-super-admins are redirected away from super-admin edit actions, including profile edits and avatar changes. @@ -381,21 +381,21 @@ History search guidance: - 1 - I modified `templates/admin/user/list.tmpl` so super-admin rows still show the `Edit` and `Delete` icons for regular admins, but as disabled muted controls with the super-admin-required tooltip. - 2 - I modified `templates/admin/user/view.tmpl` so the `Edit` button remains visible on the super-admin user details page for regular admins, but in a disabled state. -72 - [2026-04-30 04:58:16] - v1.27.0-dev-68-g5da24d2c7b - Type: Modified - [admin-users] [admin-policy] Protected GOOD-granted admin accounts and blocked deleting the direct grantor. -- 1 - I added `models/user/admin_grant.go` with helpers to read admin and super-admin grantor metadata, detect `by GOOD` bootstrap grants, and check whether a target user is the direct grantor of another admin. -- 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. +72 - [2026-04-30 04:58:16] - v1.27.0-dev-68-g5da24d2c7b - Type: Modified - [admin-users] [admin-policy] Protected GOD-granted admin accounts and blocked deleting the direct grantor. +- 1 - I added `models/user/admin_grant.go` with helpers to read admin and super-admin grantor metadata, detect `by GOD` bootstrap grants, and check whether a target user is the direct grantor of another admin. +- 2 - I modified `routers/web/admin/users.go` so GOD-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 GOD-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 GOD-protected admin API edits. 73 - [2026-04-30 16:51:42] - v1.27.0-dev-71-g80497e4194 - Type: Modified - [admin-users] 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. +- 1 - I modified `routers/web/admin/users.go` so a GOD-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 GOD-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. 74 - [2026-04-30 20:11:48] - v1.27.0-dev-72-g43161732e3 - Type: Modified - [installer] [locale] [en] Added install-time admin management policy choices with direct-grantor and inherited-grantor enforcement. - 1 - I modified `modules/setting/admin.go`, `services/forms/user_form.go`, `routers/install/install.go`, `templates/install.tmpl`, and `options/locale/locale_en-US.json` so installation now exposes three administrator-management policies: `super_admin_only`, `grantor_only`, and `grantor_inheritance`, while normalizing the old legacy policy names. - 2 - I modified `models/user/admin_grant.go` so the code can resolve the effective admin grantor by walking the admin-grant chain until it finds an active administrator who still has sign-in enabled. -- 3 - I modified `routers/web/admin/users.go` so regular admins can edit or delete only the administrator accounts allowed by the selected policy, while keeping super-admin protections, GOOD protections, self-edit exceptions, and disabled forbidden actions intact in the admin UI. +- 3 - I modified `routers/web/admin/users.go` so regular admins can edit or delete only the administrator accounts allowed by the selected policy, while keeping super-admin protections, GOD protections, self-edit exceptions, and disabled forbidden actions intact in the admin UI. - 4 - I modified `routers/api/v1/admin/user.go` so the admin API now applies the same grantor-based restrictions for editing and deleting administrator accounts. - 5 - I added focused integration coverage in `tests/integration/admin_user_test.go` and `tests/integration/api_admin_test.go` for direct-grantor edits, inherited-grantor edits, and API denial for unrelated admins. @@ -778,3 +778,8 @@ History search guidance: 154 - [2026-05-18 17:33:14] - v1.27.0-dev-165-gee30675569 - Type: Modified - [docs] [changelog] [github-links] Converted remaining bare pull-request references in `CHANGELOG.md` and `CHANGELOG-archived.md` to explicit GitHub links. - 1 - I updated `CHANGELOG.md` and `CHANGELOG-archived.md` so bare changelog references of the form `#123` now use explicit Markdown links to `https://github.com/go-gitea/gitea/pull/123`, while preserving the existing local changelog edits already present in the working tree. + +155 - [2026-05-17 00:18:43] - v1.26.0-rc0-154-gfc98a14014 - Type: Fixed - [admin-users] [terminology] [locale] [en] [ro] Corrected the bootstrap-admin label from `GOOD` to `GOD`. +- 1 - I renamed the bootstrap admin actor label and the related immutable-account locale key from `good` to `god` in the admin grant helpers and admin users UI flow. +- 2 - I updated the admin API and admin-panel restriction messages, tests, and both English and Romanian locale strings so bootstrap-protected accounts are consistently labeled as `GOD`-granted. +- 3 - I updated the matching historical task descriptions in `.codex-history.md` so the earlier admin-policy entries now use the corrected `GOD` terminology. diff --git a/models/user/admin_grant.go b/models/user/admin_grant.go index 032577c614..893ada7c0c 100644 --- a/models/user/admin_grant.go +++ b/models/user/admin_grant.go @@ -8,7 +8,7 @@ import ( "strconv" ) -const BootstrapAdminActorName = "GOOD" +const BootstrapAdminActorName = "GOD" func getUserSettingInt64(ctx context.Context, userID int64, key string) (int64, bool, error) { value, err := GetUserSetting(ctx, userID, key) diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index ee257ab5ad..f8c3bcbf7c 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -3191,7 +3191,7 @@ "admin.users.cannot_delete_self": "You cannot delete yourself", "admin.users.cannot_delete_grantor": "You cannot delete the administrator who granted your privileges.", "admin.users.admin_grantor.required": "You can only manage administrator accounts that belong to your grant chain.", - "admin.users.good_immutable": "Accounts granted by GOOD cannot be modified or deleted.", + "admin.users.god_immutable": "Accounts granted by GOD cannot be modified or deleted.", "admin.users.still_own_repo": "This user still owns one or more repositories. Delete or transfer these repositories first.", "admin.users.still_has_org": "This user is a member of an organization. Remove the user from any organizations first.", "admin.users.purge": "Purge User", diff --git a/options/locale/locale_ro-RO.json b/options/locale/locale_ro-RO.json index 4038fdb392..551284c1c5 100644 --- a/options/locale/locale_ro-RO.json +++ b/options/locale/locale_ro-RO.json @@ -3191,7 +3191,7 @@ "admin.users.cannot_delete_self": "Nu te poți auto-șterge", "admin.users.cannot_delete_grantor": "Nu poți șterge administratorul care ți-a acordat privilegiile administrative.", "admin.users.admin_grantor.required": "Poți gestiona doar conturile de administratori care aparțin lanțului tău de granturi.", - "admin.users.good_immutable": "Conturile acordate de GOOD nu pot fi modificate sau șterse.", + "admin.users.god_immutable": "Conturile acordate de GOD nu pot fi modificate sau șterse.", "admin.users.still_own_repo": "Acest utilizator încă deține unul sau mai multe repozitorii. Șterge sau transferă mai întâi aceste proiecte.", "admin.users.still_has_org": "Acest utilizator este membru al unei organizații. Elimină mai întâi utilizatorul din orice organizație.", "admin.users.purge": "Elimină utilizatorul", diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 58b4a95f17..981393b4bc 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -188,7 +188,7 @@ func canDeleteUserFromAdminAPI(ctx *context.APIContext, doer, target *user_model return false, errors.New("you cannot delete yourself") } if isBootstrapProtectedUser(ctx, target.ID) { - return false, errors.New("GOOD-granted accounts cannot be modified or deleted") + return false, errors.New("GOD-granted accounts cannot be modified or deleted") } if target.IsAdmin { canManage, err := canManageAdminUser(ctx, doer, target) @@ -377,7 +377,7 @@ func EditUser(ctx *context.APIContext) { form := web.GetForm(ctx).(*api.EditUserOption) 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")) + ctx.APIError(http.StatusForbidden, errors.New("GOD-granted accounts cannot be modified or deleted")) return } if isSuperAdmin(ctx, ctx.ContextUser.ID) && !isSuperAdmin(ctx, ctx.Doer.ID) { diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index 4e2b7045bb..9df74bab60 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -158,7 +158,7 @@ func loadAdminUserListSuperAdminBy(ctx *context.Context, users user_model.UserLi } actor := loadStoredAdminActor(ctx, u.ID, user_model.SettingsKeySuperAdminGrantedBy, user_model.SettingsKeySuperAdminGrantedByName, user_model.SettingsKeySuperAdminGrantedByEmail, "super admin grant") if actor == nil { - actor = &adminActionActorInfo{Name: "GOOD"} + actor = &adminActionActorInfo{Name: "GOD"} } usersSuperAdminByAdmin[u.ID] = &superAdminInfo{ At: timeutil.TimeStamp(grantedUnix), @@ -181,7 +181,7 @@ func loadAdminUserListCreatedBy(ctx *context.Context, users user_model.UserList) } if adminIDValue == "" { if u.IsAdmin && u.ID == firstAdminID { - usersCreatedByAdmin[u.ID] = &adminInviteCreatorInfo{Name: "GOOD"} + usersCreatedByAdmin[u.ID] = &adminInviteCreatorInfo{Name: "GOD"} } continue } @@ -489,7 +489,7 @@ func storeSuperAdminGrantedInfo(ctx *context.Context, userID int64, admin *user_ return storeAdminTimedActionInfo(ctx, userID, admin, user_model.SettingsKeySuperAdminGrantedUnix, user_model.SettingsKeySuperAdminGrantedBy, user_model.SettingsKeySuperAdminGrantedByName, user_model.SettingsKeySuperAdminGrantedByEmail) } -func storeGoodSuperAdminGrantedInfo(ctx *context.Context, userID int64) bool { +func storeGodSuperAdminGrantedInfo(ctx *context.Context, userID int64) bool { if err := user_model.SetUserSetting(ctx, userID, user_model.SettingsKeySuperAdminEnabled, "true"); err != nil { ctx.ServerError("SetUserSetting", err) return false @@ -498,7 +498,7 @@ func storeGoodSuperAdminGrantedInfo(ctx *context.Context, userID int64) bool { ctx.ServerError("SetUserSetting", err) return false } - if err := user_model.SetUserSetting(ctx, userID, user_model.SettingsKeySuperAdminGrantedByName, "GOOD"); err != nil { + if err := user_model.SetUserSetting(ctx, userID, user_model.SettingsKeySuperAdminGrantedByName, "GOD"); err != nil { ctx.ServerError("SetUserSetting", err) return false } @@ -549,7 +549,7 @@ func ensureSuperAdminBootstrap(ctx *context.Context) bool { if !has { return true } - return storeGoodSuperAdminGrantedInfo(ctx, firstAdmin.ID) + return storeGodSuperAdminGrantedInfo(ctx, firstAdmin.ID) } func isSuperAdmin(ctx *context.Context, userID int64) bool { @@ -696,7 +696,7 @@ func isBootstrapProtectedUser(ctx *context.Context, userID int64) bool { func canEditUserFromAdminPanel(ctx *context.Context, doer, target *user_model.User) (bool, string) { if isBootstrapProtectedUser(ctx, target.ID) && doer.ID != target.ID { - return false, ctx.Locale.TrString("admin.users.good_immutable") + return false, ctx.Locale.TrString("admin.users.god_immutable") } if !canEditSuperAdminUser(ctx, isSuperAdmin(ctx, target.ID)) { return false, ctx.Locale.TrString("admin.users.super_admin.required") @@ -711,7 +711,7 @@ func denyBootstrapProtectedUserAccess(ctx *context.Context, u *user_model.User) if !isBootstrapProtectedUser(ctx, u.ID) || ctx.Doer.ID == u.ID { return false } - ctx.Flash.Error(ctx.Tr("admin.users.good_immutable")) + ctx.Flash.Error(ctx.Tr("admin.users.god_immutable")) ctx.Redirect(setting.AppSubURL + "/-/admin/users/" + strconv.FormatInt(u.ID, 10)) return true } @@ -731,7 +731,7 @@ func canDeleteUserFromAdminPanel(ctx *context.Context, doer, target *user_model. } if isBootstrapProtectedUser(ctx, target.ID) { - return false, ctx.Locale.TrString("admin.users.good_immutable") + return false, ctx.Locale.TrString("admin.users.god_immutable") } if target.IsAdmin { diff --git a/routers/web/admin/users_test.go b/routers/web/admin/users_test.go index 40539430dd..fff6cc968d 100644 --- a/routers/web/admin/users_test.go +++ b/routers/web/admin/users_test.go @@ -334,7 +334,7 @@ func TestLoadAdminUserListCreatedBy(t *testing.T) { loadAdminUserListCreatedBy(ctx, user_model.UserList{firstAdmin, createdUser}) usersCreatedByAdmin := ctx.Data["UsersCreatedByAdmin"].(map[int64]*adminInviteCreatorInfo) - assert.Equal(t, "GOOD", usersCreatedByAdmin[firstAdmin.ID].Name) + assert.Equal(t, "GOD", usersCreatedByAdmin[firstAdmin.ID].Name) assert.Empty(t, usersCreatedByAdmin[firstAdmin.ID].Email) assert.Equal(t, "deleted-admin", usersCreatedByAdmin[createdUser.ID].Name) assert.Equal(t, "deleted-admin@example.com", usersCreatedByAdmin[createdUser.ID].Email)