mirror of
https://github.com/go-gitea/gitea.git
synced 2026-11-02 13:44:16 +09:00
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:
5 files changed
+28
-19
No files matched your search
@@ -33,7 +33,7 @@ func runListUsers(ctx context.Context, c *cli.Command) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
users, err := user_model.GetAllUsers(ctx)
|
users, err := user_model.GetAllUsersForAdminListing(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -41,17 +41,17 @@ func runListUsers(ctx context.Context, c *cli.Command) error {
|
|||||||
w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0)
|
w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0)
|
||||||
|
|
||||||
if c.IsSet("admin") {
|
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 {
|
for _, u := range users {
|
||||||
if u.IsAdmin {
|
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 {
|
} else {
|
||||||
twofa := user_model.UserList(users).GetTwoFaStatus(ctx)
|
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 {
|
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
@@ -68,13 +68,20 @@ const (
|
|||||||
UserTypeRemoteUser // 5
|
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
|
// 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 {
|
func (t UserType) DisplayName() string {
|
||||||
switch t {
|
switch t {
|
||||||
case UserTypeOrganization, UserTypeOrganizationReserved:
|
case UserTypeOrganization, UserTypeOrganizationReserved:
|
||||||
return "Organization"
|
return "Organization"
|
||||||
|
case UserTypeUserReserved:
|
||||||
|
return "Reserved"
|
||||||
case UserTypeBot:
|
case UserTypeBot:
|
||||||
return "Bot"
|
return "Bot"
|
||||||
|
case UserTypeRemoteUser:
|
||||||
|
return "Remote"
|
||||||
default:
|
default:
|
||||||
return "User"
|
return "User"
|
||||||
}
|
}
|
||||||
@@ -251,10 +258,10 @@ func (u *User) GetEmail() string {
|
|||||||
return u.Email
|
return u.Email
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllUsers returns a slice of all individual users found in DB.
|
// GetAllUsersForAdminListing returns a slice of all users of AdminListedUserTypes found in DB.
|
||||||
func GetAllUsers(ctx context.Context) ([]*User, error) {
|
func GetAllUsersForAdminListing(ctx context.Context) (users []*User, err error) {
|
||||||
users := make([]*User, 0)
|
err = db.GetEngine(ctx).OrderBy("id").In("type", AdminListedUserTypes).Find(&users)
|
||||||
return users, db.GetEngine(ctx).OrderBy("id").Where("type = ?", UserTypeIndividual).Find(&users)
|
return users, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsLocal returns true if user login type is LoginPlain.
|
// IsLocal returns true if user login type is LoginPlain.
|
||||||
|
|||||||
@@ -459,7 +459,7 @@ func SearchUsers(ctx *context.APIContext) {
|
|||||||
|
|
||||||
searchOpts := user_model.SearchUserOptions{
|
searchOpts := user_model.SearchUserOptions{
|
||||||
Actor: ctx.Doer,
|
Actor: ctx.Doer,
|
||||||
Types: []user_model.UserType{user_model.UserTypeIndividual},
|
Types: user_model.AdminListedUserTypes,
|
||||||
LoginName: ctx.FormTrim("login_name"),
|
LoginName: ctx.FormTrim("login_name"),
|
||||||
SourceID: ctx.FormOptionalInt64("source_id"),
|
SourceID: ctx.FormOptionalInt64("source_id"),
|
||||||
Keyword: ctx.FormTrim("q"),
|
Keyword: ctx.FormTrim("q"),
|
||||||
|
|||||||
@@ -70,8 +70,7 @@ func Users(ctx *context.Context) {
|
|||||||
|
|
||||||
sortType := ctx.FormString("sort", UserSearchDefaultAdminSort)
|
sortType := ctx.FormString("sort", UserSearchDefaultAdminSort)
|
||||||
|
|
||||||
// unfiltered, an administrator needs to list every account kind
|
types := user_model.AdminListedUserTypes
|
||||||
types := []user_model.UserType{user_model.UserTypeIndividual, user_model.UserTypeUserReserved, user_model.UserTypeBot, user_model.UserTypeRemoteUser}
|
|
||||||
userTypeFilter := api.UserTypeString(ctx.FormString("user_type"))
|
userTypeFilter := api.UserTypeString(ctx.FormString("user_type"))
|
||||||
ctx.Data["UserTypeFilter"] = ""
|
ctx.Data["UserTypeFilter"] = ""
|
||||||
if t, err := convert.UserTypeFromString(userTypeFilter); err == nil {
|
if t, err := convert.UserTypeFromString(userTypeFilter); err == nil {
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import (
|
|||||||
"gitea.dev/tests"
|
"gitea.dev/tests"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAPIAdminListPackages(t *testing.T) {
|
func TestAPIAdminListPackages(t *testing.T) {
|
||||||
@@ -55,20 +57,21 @@ func TestAPIAdminAccessAndSSHKeys(t *testing.T) {
|
|||||||
t.Run("ListUsers", func(t *testing.T) {
|
t.Run("ListUsers", func(t *testing.T) {
|
||||||
adminUsername := "user1"
|
adminUsername := "user1"
|
||||||
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeReadAdmin)
|
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)
|
AddTokenAuth(token)
|
||||||
resp := MakeRequest(t, req, http.StatusOK)
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
users := DecodeJSON(t, resp, []api.User{})
|
users := DecodeJSON(t, resp, []api.User{})
|
||||||
|
|
||||||
found := false
|
userTypes := map[string]api.UserTypeString{}
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
if user.UserName == adminUsername {
|
userTypes[user.UserName] = user.Type
|
||||||
found = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
assert.True(t, found)
|
assert.Equal(t, api.UserTypeStringUser, userTypes[adminUsername])
|
||||||
numberOfUsers := unittest.GetCount(t, &user_model.User{}, "type = 0")
|
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)
|
assert.Len(t, users, numberOfUsers)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user