feat: allow for getting 2fa enabled users via /api/v1/admin/users (#12091)

Allow for filtering users with 2fa enabled as admin. So that it is easy to audit users' settings compliance with iso27001, etc.

Resolves #11800

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/12091
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Dominik Zyla <zylad@noreply.codeberg.org>
Co-committed-by: Dominik Zyla <zylad@noreply.codeberg.org>
This commit is contained in:
Dominik Zyla 2026-04-16 19:52:56 +02:00 committed by Gusted
parent 39f677c0db
commit 6236a4cc99
3 changed files with 49 additions and 6 deletions

View file

@ -416,6 +416,10 @@ func SearchUsers(ctx *context.APIContext) {
// in: query // in: query
// description: user's login name to search for // description: user's login name to search for
// type: string // type: string
// - name: is_2fa_enabled
// in: query
// description: whether or not to filter users with the 2fa enabled
// type: boolean
// - name: sort // - name: sort
// in: query // in: query
// description: sort order of results // description: sort order of results
@ -449,6 +453,7 @@ func SearchUsers(ctx *context.APIContext) {
Actor: ctx.Doer, Actor: ctx.Doer,
Type: user_model.UserTypeIndividual, Type: user_model.UserTypeIndividual,
LoginName: ctx.FormTrim("login_name"), LoginName: ctx.FormTrim("login_name"),
IsTwoFactorEnabled: ctx.FormOptionalBool("is_2fa_enabled"),
SourceID: sourceID, SourceID: sourceID,
OrderBy: utils.GetDbSearchOrder(ctx), OrderBy: utils.GetDbSearchOrder(ctx),
ListOptions: listOptions, ListOptions: listOptions,

View file

@ -1509,6 +1509,12 @@
"name": "login_name", "name": "login_name",
"in": "query" "in": "query"
}, },
{
"type": "boolean",
"description": "whether or not to filter users with the 2fa enabled",
"name": "is_2fa_enabled",
"in": "query"
},
{ {
"enum": [ "enum": [
"oldest", "oldest",

View file

@ -169,6 +169,38 @@ func TestAPIListUsers(t *testing.T) {
assert.Len(t, users, numberOfUsers) assert.Len(t, users, numberOfUsers)
} }
func TestAPIListUsersNo2FA(t *testing.T) {
defer tests.PrepareTestEnv(t)()
adminUsername := "user1"
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeReadAdmin)
req := NewRequest(t, "GET", "/api/v1/admin/users?is_2fa_enabled=0").
AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
total := resp.Header().Get("X-Total-Count")
numberOfUsers := "28"
assert.Equal(t, numberOfUsers, total)
}
func TestAPIListUsers2FA(t *testing.T) {
defer tests.PrepareTestEnv(t)()
adminUsername := "user1"
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeReadAdmin)
req := NewRequest(t, "GET", "/api/v1/admin/users?is_2fa_enabled=1").
AddTokenAuth(token)
resp := MakeRequest(t, req, http.StatusOK)
total := resp.Header().Get("X-Total-Count")
numberOfUsers := "2"
assert.Equal(t, numberOfUsers, total)
}
func TestAPIListUsersNotLoggedIn(t *testing.T) { func TestAPIListUsersNotLoggedIn(t *testing.T) {
defer tests.PrepareTestEnv(t)() defer tests.PrepareTestEnv(t)()
req := NewRequest(t, "GET", "/api/v1/admin/users") req := NewRequest(t, "GET", "/api/v1/admin/users")