fix: list bot users in gitea admin user list and admin users API (#40060)

Fixes https://github.com/go-gitea/gitea/issues/40056

Signed-off-by: Soujas K <kudchadkarsoujas@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
authored and GitHub committed 2026-10-31 12:30:07 +00:00
1 parent 6782d0a37d
commit 405ba258a8
5 files changed
+28 -19

No files matched your search

+5 -5
View File
@@ -33,7 +33,7 @@ func runListUsers(ctx context.Context, c *cli.Command) error {
return err
}
users, err := user_model.GetAllUsers(ctx)
users, err := user_model.GetAllUsersForAdminListing(ctx)
if err != nil {
return err
}
@@ -41,17 +41,17 @@ func runListUsers(ctx context.Context, c *cli.Command) error {
w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0)
if c.IsSet("admin") {
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\n")
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tType\n")
for _, u := range users {
if u.IsAdmin {
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", u.ID, u.Name, u.Email, u.IsActive)
fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%s\n", u.ID, u.Name, u.Email, u.IsActive, u.Type.DisplayName())
}
}
} else {
twofa := user_model.UserList(users).GetTwoFaStatus(ctx)
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n")
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\tType\n")
for _, u := range users {
fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\t%t\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin, twofa[u.ID])
fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\t%t\t%s\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin, twofa[u.ID], u.Type.DisplayName())
}
}
+11 -4
View File
@@ -68,13 +68,20 @@ const (
UserTypeRemoteUser // 5
)
// AdminListedUserTypes HINT: USER-TYPE-GLOSSARY: administrators list every user type except organizations
var AdminListedUserTypes = []UserType{UserTypeIndividual, UserTypeUserReserved, UserTypeBot, UserTypeRemoteUser}
// DisplayName returns the English name of the user type for logs and the CLI, the UI translates "concept_user_*" instead
func (t UserType) DisplayName() string {
switch t {
case UserTypeOrganization, UserTypeOrganizationReserved:
return "Organization"
case UserTypeUserReserved:
return "Reserved"
case UserTypeBot:
return "Bot"
case UserTypeRemoteUser:
return "Remote"
default:
return "User"
}
@@ -251,10 +258,10 @@ func (u *User) GetEmail() string {
return u.Email
}
// GetAllUsers returns a slice of all individual users found in DB.
func GetAllUsers(ctx context.Context) ([]*User, error) {
users := make([]*User, 0)
return users, db.GetEngine(ctx).OrderBy("id").Where("type = ?", UserTypeIndividual).Find(&users)
// GetAllUsersForAdminListing returns a slice of all users of AdminListedUserTypes found in DB.
func GetAllUsersForAdminListing(ctx context.Context) (users []*User, err error) {
err = db.GetEngine(ctx).OrderBy("id").In("type", AdminListedUserTypes).Find(&users)
return users, err
}
// IsLocal returns true if user login type is LoginPlain.
+1 -1
View File
@@ -459,7 +459,7 @@ func SearchUsers(ctx *context.APIContext) {
searchOpts := user_model.SearchUserOptions{
Actor: ctx.Doer,
Types: []user_model.UserType{user_model.UserTypeIndividual},
Types: user_model.AdminListedUserTypes,
LoginName: ctx.FormTrim("login_name"),
SourceID: ctx.FormOptionalInt64("source_id"),
Keyword: ctx.FormTrim("q"),
+1 -2
View File
@@ -70,8 +70,7 @@ func Users(ctx *context.Context) {
sortType := ctx.FormString("sort", UserSearchDefaultAdminSort)
// unfiltered, an administrator needs to list every account kind
types := []user_model.UserType{user_model.UserTypeIndividual, user_model.UserTypeUserReserved, user_model.UserTypeBot, user_model.UserTypeRemoteUser}
types := user_model.AdminListedUserTypes
userTypeFilter := api.UserTypeString(ctx.FormString("user_type"))
ctx.Data["UserTypeFilter"] = ""
if t, err := convert.UserTypeFromString(userTypeFilter); err == nil {
+10 -7
View File
@@ -22,6 +22,8 @@ import (
"gitea.dev/tests"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"xorm.io/builder"
)
func TestAPIAdminListPackages(t *testing.T) {
@@ -55,20 +57,21 @@ func TestAPIAdminAccessAndSSHKeys(t *testing.T) {
t.Run("ListUsers", func(t *testing.T) {
adminUsername := "user1"
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeReadAdmin)
bot := &user_model.User{Name: "list-bot", Email: "list-bot@example.com", Type: user_model.UserTypeBot}
require.NoError(t, user_model.CreateUser(t.Context(), bot, &user_model.Meta{}))
req := NewRequest(t, "GET", "/api/v1/admin/users").
req := NewRequest(t, "GET", "/api/v1/admin/users?limit=50").
AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
users := DecodeJSON(t, resp, []api.User{})
found := false
userTypes := map[string]api.UserTypeString{}
for _, user := range users {
if user.UserName == adminUsername {
found = true
}
userTypes[user.UserName] = user.Type
}
assert.True(t, found)
numberOfUsers := unittest.GetCount(t, &user_model.User{}, "type = 0")
assert.Equal(t, api.UserTypeStringUser, userTypes[adminUsername])
assert.Equal(t, api.UserTypeStringBot, userTypes[bot.Name])
numberOfUsers := unittest.GetCount(t, &user_model.User{}, builder.In("type", user_model.AdminListedUserTypes))
assert.Len(t, users, numberOfUsers)
})