feat: add filtering by service account (#23468)

You can now filter by/out service accounts using
`service_account:true/false` or using the filter dropdown.
This commit is contained in:
Asher
2026-03-24 10:13:25 -08:00
committed by GitHub
parent 5544a60b6e
commit 81188b9ac9
19 changed files with 284 additions and 178 deletions
+3
View File
@@ -17426,6 +17426,9 @@ const docTemplate = `{
"$ref": "#/definitions/codersdk.SlimRole"
}
},
"is_service_account": {
"type": "boolean"
},
"last_seen_at": {
"type": "string",
"format": "date-time"
+3
View File
@@ -15851,6 +15851,9 @@
"$ref": "#/definitions/codersdk.SlimRole"
}
},
"is_service_account": {
"type": "boolean"
},
"last_seen_at": {
"type": "string",
"format": "date-time"
+26
View File
@@ -210,6 +210,14 @@ func UsersFilter(
users = append(users, user)
}
// Add some service accounts.
for range 3 {
_, user := CreateAnotherUserMutators(t, client, orgID, nil, func(r *codersdk.CreateUserRequestWithOrgs) {
r.ServiceAccount = true
})
users = append(users, user)
}
hashedPassword, err := userpassword.Hash("SomeStrongPassword!")
require.NoError(t, err)
@@ -560,6 +568,24 @@ func UsersFilter(
return u.Status == codersdk.UserStatusSuspended && u.LoginType == codersdk.LoginTypeNone
},
},
{
Name: "IsServiceAccount",
Filter: codersdk.UsersRequest{
Search: "service_account:true",
},
FilterF: func(_ codersdk.UsersRequest, u codersdk.User) bool {
return u.IsServiceAccount
},
},
{
Name: "IsNotServiceAccount",
Filter: codersdk.UsersRequest{
Search: "service_account:false",
},
FilterF: func(_ codersdk.UsersRequest, u codersdk.User) bool {
return !u.IsServiceAccount
},
},
}
for _, c := range testCases {
+1
View File
@@ -422,6 +422,7 @@ func (q *sqlQuerier) GetAuthorizedUsers(ctx context.Context, arg GetUsersParams,
arg.IncludeSystem,
arg.GithubComUserID,
pq.Array(arg.LoginType),
arg.IsServiceAccount,
arg.OffsetOpt,
arg.LimitOpt,
)
+87 -57
View File
@@ -7797,11 +7797,12 @@ WHERE
user_created_at >= $10
ELSE true
END
-- Filter by system type
-- Filter by system type
AND CASE
WHEN $11::bool THEN TRUE
ELSE user_is_system = false
END
-- Filter by github.com user ID
AND CASE
WHEN $12 :: bigint != 0 THEN
user_github_com_user_id = $12
@@ -7813,31 +7814,38 @@ WHERE
user_login_type = ANY($13 :: login_type[])
ELSE true
END
-- Filter by service account.
AND CASE
WHEN $14 :: boolean IS NOT NULL THEN
user_is_service_account = $14 :: boolean
ELSE true
END
-- End of filters
ORDER BY
-- Deterministic and consistent ordering of all users. This is to ensure consistent pagination.
LOWER(user_username) ASC OFFSET $14
LOWER(user_username) ASC OFFSET $15
LIMIT
-- A null limit means "no limit", so 0 means return all
NULLIF($15 :: int, 0)
NULLIF($16 :: int, 0)
`
type GetGroupMembersByGroupIDPaginatedParams struct {
GroupID uuid.UUID `db:"group_id" json:"group_id"`
AfterID uuid.UUID `db:"after_id" json:"after_id"`
Search string `db:"search" json:"search"`
Name string `db:"name" json:"name"`
Status []UserStatus `db:"status" json:"status"`
RbacRole []string `db:"rbac_role" json:"rbac_role"`
LastSeenBefore time.Time `db:"last_seen_before" json:"last_seen_before"`
LastSeenAfter time.Time `db:"last_seen_after" json:"last_seen_after"`
CreatedBefore time.Time `db:"created_before" json:"created_before"`
CreatedAfter time.Time `db:"created_after" json:"created_after"`
IncludeSystem bool `db:"include_system" json:"include_system"`
GithubComUserID int64 `db:"github_com_user_id" json:"github_com_user_id"`
LoginType []LoginType `db:"login_type" json:"login_type"`
OffsetOpt int32 `db:"offset_opt" json:"offset_opt"`
LimitOpt int32 `db:"limit_opt" json:"limit_opt"`
GroupID uuid.UUID `db:"group_id" json:"group_id"`
AfterID uuid.UUID `db:"after_id" json:"after_id"`
Search string `db:"search" json:"search"`
Name string `db:"name" json:"name"`
Status []UserStatus `db:"status" json:"status"`
RbacRole []string `db:"rbac_role" json:"rbac_role"`
LastSeenBefore time.Time `db:"last_seen_before" json:"last_seen_before"`
LastSeenAfter time.Time `db:"last_seen_after" json:"last_seen_after"`
CreatedBefore time.Time `db:"created_before" json:"created_before"`
CreatedAfter time.Time `db:"created_after" json:"created_after"`
IncludeSystem bool `db:"include_system" json:"include_system"`
GithubComUserID int64 `db:"github_com_user_id" json:"github_com_user_id"`
LoginType []LoginType `db:"login_type" json:"login_type"`
IsServiceAccount sql.NullBool `db:"is_service_account" json:"is_service_account"`
OffsetOpt int32 `db:"offset_opt" json:"offset_opt"`
LimitOpt int32 `db:"limit_opt" json:"limit_opt"`
}
type GetGroupMembersByGroupIDPaginatedRow struct {
@@ -7879,6 +7887,7 @@ func (q *sqlQuerier) GetGroupMembersByGroupIDPaginated(ctx context.Context, arg
arg.IncludeSystem,
arg.GithubComUserID,
pq.Array(arg.LoginType),
arg.IsServiceAccount,
arg.OffsetOpt,
arg.LimitOpt,
)
@@ -12745,7 +12754,7 @@ const organizationMembers = `-- name: OrganizationMembers :many
SELECT
organization_members.user_id, organization_members.organization_id, organization_members.created_at, organization_members.updated_at, organization_members.roles,
users.username, users.avatar_url, users.name, users.email, users.rbac_roles as "global_roles",
users.last_seen_at, users.status, users.login_type,
users.last_seen_at, users.status, users.login_type, users.is_service_account,
users.created_at as user_created_at, users.updated_at as user_updated_at
FROM
organization_members
@@ -12795,6 +12804,7 @@ type OrganizationMembersRow struct {
LastSeenAt time.Time `db:"last_seen_at" json:"last_seen_at"`
Status UserStatus `db:"status" json:"status"`
LoginType LoginType `db:"login_type" json:"login_type"`
IsServiceAccount bool `db:"is_service_account" json:"is_service_account"`
UserCreatedAt time.Time `db:"user_created_at" json:"user_created_at"`
UserUpdatedAt time.Time `db:"user_updated_at" json:"user_updated_at"`
}
@@ -12831,6 +12841,7 @@ func (q *sqlQuerier) OrganizationMembers(ctx context.Context, arg OrganizationMe
&i.LastSeenAt,
&i.Status,
&i.LoginType,
&i.IsServiceAccount,
&i.UserCreatedAt,
&i.UserUpdatedAt,
); err != nil {
@@ -12851,7 +12862,7 @@ const paginatedOrganizationMembers = `-- name: PaginatedOrganizationMembers :man
SELECT
organization_members.user_id, organization_members.organization_id, organization_members.created_at, organization_members.updated_at, organization_members.roles,
users.username, users.avatar_url, users.name, users.email, users.rbac_roles as "global_roles",
users.last_seen_at, users.status, users.login_type,
users.last_seen_at, users.status, users.login_type, users.is_service_account,
users.created_at as user_created_at, users.updated_at as user_updated_at,
COUNT(*) OVER() AS count
FROM
@@ -12956,31 +12967,38 @@ WHERE
users.login_type = ANY($13 :: login_type[])
ELSE true
END
-- Filter by service account.
AND CASE
WHEN $14 :: boolean IS NOT NULL THEN
users.is_service_account = $14 :: boolean
ELSE true
END
-- End of filters
ORDER BY
-- Deterministic and consistent ordering of all users. This is to ensure consistent pagination.
LOWER(users.username) ASC OFFSET $14
LOWER(users.username) ASC OFFSET $15
LIMIT
-- A null limit means "no limit", so 0 means return all
NULLIF($15 :: int, 0)
NULLIF($16 :: int, 0)
`
type PaginatedOrganizationMembersParams struct {
AfterID uuid.UUID `db:"after_id" json:"after_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
Search string `db:"search" json:"search"`
Name string `db:"name" json:"name"`
Status []UserStatus `db:"status" json:"status"`
RbacRole []string `db:"rbac_role" json:"rbac_role"`
LastSeenBefore time.Time `db:"last_seen_before" json:"last_seen_before"`
LastSeenAfter time.Time `db:"last_seen_after" json:"last_seen_after"`
CreatedBefore time.Time `db:"created_before" json:"created_before"`
CreatedAfter time.Time `db:"created_after" json:"created_after"`
IncludeSystem bool `db:"include_system" json:"include_system"`
GithubComUserID int64 `db:"github_com_user_id" json:"github_com_user_id"`
LoginType []LoginType `db:"login_type" json:"login_type"`
OffsetOpt int32 `db:"offset_opt" json:"offset_opt"`
LimitOpt int32 `db:"limit_opt" json:"limit_opt"`
AfterID uuid.UUID `db:"after_id" json:"after_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
Search string `db:"search" json:"search"`
Name string `db:"name" json:"name"`
Status []UserStatus `db:"status" json:"status"`
RbacRole []string `db:"rbac_role" json:"rbac_role"`
LastSeenBefore time.Time `db:"last_seen_before" json:"last_seen_before"`
LastSeenAfter time.Time `db:"last_seen_after" json:"last_seen_after"`
CreatedBefore time.Time `db:"created_before" json:"created_before"`
CreatedAfter time.Time `db:"created_after" json:"created_after"`
IncludeSystem bool `db:"include_system" json:"include_system"`
GithubComUserID int64 `db:"github_com_user_id" json:"github_com_user_id"`
LoginType []LoginType `db:"login_type" json:"login_type"`
IsServiceAccount sql.NullBool `db:"is_service_account" json:"is_service_account"`
OffsetOpt int32 `db:"offset_opt" json:"offset_opt"`
LimitOpt int32 `db:"limit_opt" json:"limit_opt"`
}
type PaginatedOrganizationMembersRow struct {
@@ -12993,6 +13011,7 @@ type PaginatedOrganizationMembersRow struct {
LastSeenAt time.Time `db:"last_seen_at" json:"last_seen_at"`
Status UserStatus `db:"status" json:"status"`
LoginType LoginType `db:"login_type" json:"login_type"`
IsServiceAccount bool `db:"is_service_account" json:"is_service_account"`
UserCreatedAt time.Time `db:"user_created_at" json:"user_created_at"`
UserUpdatedAt time.Time `db:"user_updated_at" json:"user_updated_at"`
Count int64 `db:"count" json:"count"`
@@ -13013,6 +13032,7 @@ func (q *sqlQuerier) PaginatedOrganizationMembers(ctx context.Context, arg Pagin
arg.IncludeSystem,
arg.GithubComUserID,
pq.Array(arg.LoginType),
arg.IsServiceAccount,
arg.OffsetOpt,
arg.LimitOpt,
)
@@ -13037,6 +13057,7 @@ func (q *sqlQuerier) PaginatedOrganizationMembers(ctx context.Context, arg Pagin
&i.LastSeenAt,
&i.Status,
&i.LoginType,
&i.IsServiceAccount,
&i.UserCreatedAt,
&i.UserUpdatedAt,
&i.Count,
@@ -21843,11 +21864,12 @@ WHERE
created_at >= $9
ELSE true
END
AND CASE
WHEN $10::bool THEN TRUE
ELSE
is_system = false
-- Filter by system type
AND CASE
WHEN $10::bool THEN TRUE
ELSE is_system = false
END
-- Filter by github.com user ID
AND CASE
WHEN $11 :: bigint != 0 THEN
github_com_user_id = $11
@@ -21859,33 +21881,40 @@ WHERE
login_type = ANY($12 :: login_type[])
ELSE true
END
-- Filter by service account.
AND CASE
WHEN $13 :: boolean IS NOT NULL THEN
is_service_account = $13 :: boolean
ELSE true
END
-- End of filters
-- Authorize Filter clause will be injected below in GetAuthorizedUsers
-- @authorize_filter
ORDER BY
-- Deterministic and consistent ordering of all users. This is to ensure consistent pagination.
LOWER(username) ASC OFFSET $13
LOWER(username) ASC OFFSET $14
LIMIT
-- A null limit means "no limit", so 0 means return all
NULLIF($14 :: int, 0)
NULLIF($15 :: int, 0)
`
type GetUsersParams struct {
AfterID uuid.UUID `db:"after_id" json:"after_id"`
Search string `db:"search" json:"search"`
Name string `db:"name" json:"name"`
Status []UserStatus `db:"status" json:"status"`
RbacRole []string `db:"rbac_role" json:"rbac_role"`
LastSeenBefore time.Time `db:"last_seen_before" json:"last_seen_before"`
LastSeenAfter time.Time `db:"last_seen_after" json:"last_seen_after"`
CreatedBefore time.Time `db:"created_before" json:"created_before"`
CreatedAfter time.Time `db:"created_after" json:"created_after"`
IncludeSystem bool `db:"include_system" json:"include_system"`
GithubComUserID int64 `db:"github_com_user_id" json:"github_com_user_id"`
LoginType []LoginType `db:"login_type" json:"login_type"`
OffsetOpt int32 `db:"offset_opt" json:"offset_opt"`
LimitOpt int32 `db:"limit_opt" json:"limit_opt"`
AfterID uuid.UUID `db:"after_id" json:"after_id"`
Search string `db:"search" json:"search"`
Name string `db:"name" json:"name"`
Status []UserStatus `db:"status" json:"status"`
RbacRole []string `db:"rbac_role" json:"rbac_role"`
LastSeenBefore time.Time `db:"last_seen_before" json:"last_seen_before"`
LastSeenAfter time.Time `db:"last_seen_after" json:"last_seen_after"`
CreatedBefore time.Time `db:"created_before" json:"created_before"`
CreatedAfter time.Time `db:"created_after" json:"created_after"`
IncludeSystem bool `db:"include_system" json:"include_system"`
GithubComUserID int64 `db:"github_com_user_id" json:"github_com_user_id"`
LoginType []LoginType `db:"login_type" json:"login_type"`
IsServiceAccount sql.NullBool `db:"is_service_account" json:"is_service_account"`
OffsetOpt int32 `db:"offset_opt" json:"offset_opt"`
LimitOpt int32 `db:"limit_opt" json:"limit_opt"`
}
type GetUsersRow struct {
@@ -21927,6 +21956,7 @@ func (q *sqlQuerier) GetUsers(ctx context.Context, arg GetUsersParams) ([]GetUse
arg.IncludeSystem,
arg.GithubComUserID,
pq.Array(arg.LoginType),
arg.IsServiceAccount,
arg.OffsetOpt,
arg.LimitOpt,
)
+8 -1
View File
@@ -97,11 +97,12 @@ WHERE
user_created_at >= @created_after
ELSE true
END
-- Filter by system type
-- Filter by system type
AND CASE
WHEN @include_system::bool THEN TRUE
ELSE user_is_system = false
END
-- Filter by github.com user ID
AND CASE
WHEN @github_com_user_id :: bigint != 0 THEN
user_github_com_user_id = @github_com_user_id
@@ -113,6 +114,12 @@ WHERE
user_login_type = ANY(@login_type :: login_type[])
ELSE true
END
-- Filter by service account.
AND CASE
WHEN sqlc.narg('is_service_account') :: boolean IS NOT NULL THEN
user_is_service_account = sqlc.narg('is_service_account') :: boolean
ELSE true
END
-- End of filters
ORDER BY
-- Deterministic and consistent ordering of all users. This is to ensure consistent pagination.
@@ -6,7 +6,7 @@
SELECT
sqlc.embed(organization_members),
users.username, users.avatar_url, users.name, users.email, users.rbac_roles as "global_roles",
users.last_seen_at, users.status, users.login_type,
users.last_seen_at, users.status, users.login_type, users.is_service_account,
users.created_at as user_created_at, users.updated_at as user_updated_at
FROM
organization_members
@@ -85,7 +85,7 @@ RETURNING *;
SELECT
sqlc.embed(organization_members),
users.username, users.avatar_url, users.name, users.email, users.rbac_roles as "global_roles",
users.last_seen_at, users.status, users.login_type,
users.last_seen_at, users.status, users.login_type, users.is_service_account,
users.created_at as user_created_at, users.updated_at as user_updated_at,
COUNT(*) OVER() AS count
FROM
@@ -190,6 +190,12 @@ WHERE
users.login_type = ANY(@login_type :: login_type[])
ELSE true
END
-- Filter by service account.
AND CASE
WHEN sqlc.narg('is_service_account') :: boolean IS NOT NULL THEN
users.is_service_account = sqlc.narg('is_service_account') :: boolean
ELSE true
END
-- End of filters
ORDER BY
-- Deterministic and consistent ordering of all users. This is to ensure consistent pagination.
+11 -4
View File
@@ -344,11 +344,12 @@ WHERE
created_at >= @created_after
ELSE true
END
AND CASE
WHEN @include_system::bool THEN TRUE
ELSE
is_system = false
-- Filter by system type
AND CASE
WHEN @include_system::bool THEN TRUE
ELSE is_system = false
END
-- Filter by github.com user ID
AND CASE
WHEN @github_com_user_id :: bigint != 0 THEN
github_com_user_id = @github_com_user_id
@@ -360,6 +361,12 @@ WHERE
login_type = ANY(@login_type :: login_type[])
ELSE true
END
-- Filter by service account.
AND CASE
WHEN sqlc.narg('is_service_account') :: boolean IS NOT NULL THEN
is_service_account = sqlc.narg('is_service_account') :: boolean
ELSE true
END
-- End of filters
-- Authorize Filter clause will be injected below in GetAuthorizedUsers
+16 -13
View File
@@ -270,19 +270,20 @@ func (api *API) paginatedMembers(rw http.ResponseWriter, r *http.Request) {
}
paginatedMemberRows, err := api.Database.PaginatedOrganizationMembers(ctx, database.PaginatedOrganizationMembersParams{
AfterID: paginationParams.AfterID,
OrganizationID: organization.ID,
IncludeSystem: false,
Search: userFilterParams.Search,
Name: userFilterParams.Name,
Status: userFilterParams.Status,
RbacRole: userFilterParams.RbacRole,
LastSeenBefore: userFilterParams.LastSeenBefore,
LastSeenAfter: userFilterParams.LastSeenAfter,
CreatedAfter: userFilterParams.CreatedAfter,
CreatedBefore: userFilterParams.CreatedBefore,
GithubComUserID: userFilterParams.GithubComUserID,
LoginType: userFilterParams.LoginType,
AfterID: paginationParams.AfterID,
OrganizationID: organization.ID,
IncludeSystem: false,
Search: userFilterParams.Search,
Name: userFilterParams.Name,
Status: userFilterParams.Status,
IsServiceAccount: userFilterParams.IsServiceAccount,
RbacRole: userFilterParams.RbacRole,
LastSeenBefore: userFilterParams.LastSeenBefore,
LastSeenAfter: userFilterParams.LastSeenAfter,
CreatedAfter: userFilterParams.CreatedAfter,
CreatedBefore: userFilterParams.CreatedBefore,
GithubComUserID: userFilterParams.GithubComUserID,
LoginType: userFilterParams.LoginType,
// #nosec G115 - Pagination offsets are small and fit in int32
OffsetOpt: int32(paginationParams.Offset),
// #nosec G115 - Pagination limits are small and fit in int32
@@ -308,6 +309,7 @@ func (api *API) paginatedMembers(rw http.ResponseWriter, r *http.Request) {
GlobalRoles: pRow.GlobalRoles,
LastSeenAt: pRow.LastSeenAt,
Status: pRow.Status,
IsServiceAccount: pRow.IsServiceAccount,
LoginType: pRow.LoginType,
UserCreatedAt: pRow.UserCreatedAt,
UserUpdatedAt: pRow.UserUpdatedAt,
@@ -530,6 +532,7 @@ func convertOrganizationMembersWithUserData(ctx context.Context, db database.Sto
GlobalRoles: db2sdk.SlimRolesFromNames(rows[i].GlobalRoles),
LastSeenAt: rows[i].LastSeenAt,
Status: codersdk.UserStatus(rows[i].Status),
IsServiceAccount: rows[i].IsServiceAccount,
LoginType: codersdk.LoginType(rows[i].LoginType),
UserCreatedAt: rows[i].UserCreatedAt,
UserUpdatedAt: rows[i].UserUpdatedAt,
+7 -6
View File
@@ -190,11 +190,12 @@ func orgMemberToReducedUser(user codersdk.OrganizationMemberWithUserData) coders
Name: user.Name,
AvatarURL: user.AvatarURL,
},
Email: user.Email,
CreatedAt: user.UserCreatedAt,
UpdatedAt: user.UserUpdatedAt,
LastSeenAt: user.LastSeenAt,
Status: user.Status,
LoginType: user.LoginType,
Email: user.Email,
CreatedAt: user.UserCreatedAt,
UpdatedAt: user.UserUpdatedAt,
LastSeenAt: user.LastSeenAt,
Status: user.Status,
IsServiceAccount: user.IsServiceAccount,
LoginType: user.LoginType,
}
}
+11 -10
View File
@@ -155,16 +155,17 @@ func Users(query string) (database.GetUsersParams, []codersdk.ValidationError) {
parser := httpapi.NewQueryParamParser()
filter := database.GetUsersParams{
Search: parser.String(values, "", "search"),
Name: parser.String(values, "", "name"),
Status: httpapi.ParseCustomList(parser, values, []database.UserStatus{}, "status", httpapi.ParseEnum[database.UserStatus]),
RbacRole: parser.Strings(values, []string{}, "role"),
LastSeenAfter: parser.Time3339Nano(values, time.Time{}, "last_seen_after"),
LastSeenBefore: parser.Time3339Nano(values, time.Time{}, "last_seen_before"),
CreatedAfter: parser.Time3339Nano(values, time.Time{}, "created_after"),
CreatedBefore: parser.Time3339Nano(values, time.Time{}, "created_before"),
GithubComUserID: parser.Int64(values, 0, "github_com_user_id"),
LoginType: httpapi.ParseCustomList(parser, values, []database.LoginType{}, "login_type", httpapi.ParseEnum[database.LoginType]),
Search: parser.String(values, "", "search"),
Name: parser.String(values, "", "name"),
Status: httpapi.ParseCustomList(parser, values, []database.UserStatus{}, "status", httpapi.ParseEnum[database.UserStatus]),
IsServiceAccount: parser.NullableBoolean(values, sql.NullBool{}, "service_account"),
RbacRole: parser.Strings(values, []string{}, "role"),
LastSeenAfter: parser.Time3339Nano(values, time.Time{}, "last_seen_after"),
LastSeenBefore: parser.Time3339Nano(values, time.Time{}, "last_seen_before"),
CreatedAfter: parser.Time3339Nano(values, time.Time{}, "created_after"),
CreatedBefore: parser.Time3339Nano(values, time.Time{}, "created_before"),
GithubComUserID: parser.Int64(values, 0, "github_com_user_id"),
LoginType: httpapi.ParseCustomList(parser, values, []database.LoginType{}, "login_type", httpapi.ParseEnum[database.LoginType]),
}
parser.ErrorExcessParams(values)
return filter, parser.Errors
+12 -11
View File
@@ -353,17 +353,18 @@ func (api *API) GetUsers(rw http.ResponseWriter, r *http.Request) ([]database.Us
}
userRows, err := api.Database.GetUsers(ctx, database.GetUsersParams{
AfterID: paginationParams.AfterID,
Search: params.Search,
Name: params.Name,
Status: params.Status,
RbacRole: params.RbacRole,
LastSeenBefore: params.LastSeenBefore,
LastSeenAfter: params.LastSeenAfter,
CreatedAfter: params.CreatedAfter,
CreatedBefore: params.CreatedBefore,
GithubComUserID: params.GithubComUserID,
LoginType: params.LoginType,
AfterID: paginationParams.AfterID,
Search: params.Search,
Name: params.Name,
Status: params.Status,
IsServiceAccount: params.IsServiceAccount,
RbacRole: params.RbacRole,
LastSeenBefore: params.LastSeenBefore,
LastSeenAfter: params.LastSeenAfter,
CreatedAfter: params.CreatedAfter,
CreatedBefore: params.CreatedBefore,
GithubComUserID: params.GithubComUserID,
LoginType: params.LoginType,
// #nosec G115 - Pagination offsets are small and fit in int32
OffsetOpt: int32(paginationParams.Offset),
// #nosec G115 - Pagination limits are small and fit in int32
+1
View File
@@ -82,6 +82,7 @@ type OrganizationMemberWithUserData struct {
LastSeenAt time.Time `table:"last seen at" json:"last_seen_at,omitempty" format:"date-time"`
UserCreatedAt time.Time `table:"user created at" json:"user_created_at" format:"date-time"`
UserUpdatedAt time.Time `table:"user updated at" json:"user_updated_at" format:"date-time"`
IsServiceAccount bool `json:"is_service_account,omitempty"`
GlobalRoles []SlimRole `json:"global_roles"`
OrganizationMember `table:"m,recursive_inline"`
}
+4
View File
@@ -192,6 +192,7 @@ to use the Coder's filter query:
`created_before:"2023-01-18T00:00:00Z" created_after:"2023-01-01T23:59:59Z"`
- To find users who login using Github:
`login_type:github`
- To find service accounts: `service_account:true`.
The following filters are supported:
@@ -206,6 +207,9 @@ The following filters are supported:
- `created_before` and `created_after` - The time a user was created. Uses the
RFC3339Nano format.
- `login_type` - Represents the login type of the user. Refer to the [LoginType documentation](https://pkg.go.dev/github.com/coder/coder/v2/codersdk#LoginType) for a list of supported values
- `service_account` - Can be either `true` to only include service accounts or
`false` to filter them out. If omitted, both service and regular accounts and
are returned.
## Edit a user's profile
+49 -44
View File
@@ -36,6 +36,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/members
"organization_id": "string"
}
],
"is_service_account": true,
"last_seen_at": "2019-08-24T14:15:22Z",
"login_type": "",
"name": "string",
@@ -67,27 +68,28 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/members
Status Code **200**
| Name | Type | Required | Restrictions | Description |
|----------------------|------------------------------------------------------|----------|--------------|-------------|
| `[array item]` | array | false | | |
| `» avatar_url` | string | false | | |
| `» created_at` | string(date-time) | false | | |
| `» email` | string | false | | |
| `» global_roles` | array | false | | |
| `»» display_name` | string | false | | |
| `»» name` | string | false | | |
| `»» organization_id` | string | false | | |
| `» last_seen_at` | string(date-time) | false | | |
| `» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | |
| `» name` | string | false | | |
| `» organization_id` | string(uuid) | false | | |
| `» roles` | array | false | | |
| `» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | |
| `» updated_at` | string(date-time) | false | | |
| `» user_created_at` | string(date-time) | false | | |
| `» user_id` | string(uuid) | false | | |
| `» user_updated_at` | string(date-time) | false | | |
| `» username` | string | false | | |
| Name | Type | Required | Restrictions | Description |
|------------------------|------------------------------------------------------|----------|--------------|-------------|
| `[array item]` | array | false | | |
| `» avatar_url` | string | false | | |
| `» created_at` | string(date-time) | false | | |
| `» email` | string | false | | |
| `» global_roles` | array | false | | |
| `»» display_name` | string | false | | |
| `»» name` | string | false | | |
| `»» organization_id` | string | false | | |
| `» is_service_account` | boolean | false | | |
| `» last_seen_at` | string(date-time) | false | | |
| `» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | |
| `» name` | string | false | | |
| `» organization_id` | string(uuid) | false | | |
| `» roles` | array | false | | |
| `» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | |
| `» updated_at` | string(date-time) | false | | |
| `» user_created_at` | string(date-time) | false | | |
| `» user_id` | string(uuid) | false | | |
| `» user_updated_at` | string(date-time) | false | | |
| `» username` | string | false | | |
#### Enumerated Values
@@ -593,6 +595,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/members
"organization_id": "string"
}
],
"is_service_account": true,
"last_seen_at": "2019-08-24T14:15:22Z",
"login_type": "",
"name": "string",
@@ -799,6 +802,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/paginat
"organization_id": "string"
}
],
"is_service_account": true,
"last_seen_at": "2019-08-24T14:15:22Z",
"login_type": "",
"name": "string",
@@ -832,29 +836,30 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/paginat
Status Code **200**
| Name | Type | Required | Restrictions | Description |
|-----------------------|------------------------------------------------------|----------|--------------|-------------|
| `[array item]` | array | false | | |
| `» count` | integer | false | | |
| `» members` | array | false | | |
| `»» avatar_url` | string | false | | |
| `»» created_at` | string(date-time) | false | | |
| `»» email` | string | false | | |
| `»» global_roles` | array | false | | |
| `»»» display_name` | string | false | | |
| `»»» name` | string | false | | |
| `»»» organization_id` | string | false | | |
| `»» last_seen_at` | string(date-time) | false | | |
| `»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | |
| `»» name` | string | false | | |
| `»» organization_id` | string(uuid) | false | | |
| `»» roles` | array | false | | |
| `»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | |
| `»» updated_at` | string(date-time) | false | | |
| `»» user_created_at` | string(date-time) | false | | |
| `»» user_id` | string(uuid) | false | | |
| `»» user_updated_at` | string(date-time) | false | | |
| `»» username` | string | false | | |
| Name | Type | Required | Restrictions | Description |
|-------------------------|------------------------------------------------------|----------|--------------|-------------|
| `[array item]` | array | false | | |
| `» count` | integer | false | | |
| `» members` | array | false | | |
| `»» avatar_url` | string | false | | |
| `»» created_at` | string(date-time) | false | | |
| `»» email` | string | false | | |
| `»» global_roles` | array | false | | |
| `»»» display_name` | string | false | | |
| `»»» name` | string | false | | |
| `»»» organization_id` | string | false | | |
| `»» is_service_account` | boolean | false | | |
| `»» last_seen_at` | string(date-time) | false | | |
| `»» login_type` | [codersdk.LoginType](schemas.md#codersdklogintype) | false | | |
| `»» name` | string | false | | |
| `»» organization_id` | string(uuid) | false | | |
| `»» roles` | array | false | | |
| `»» status` | [codersdk.UserStatus](schemas.md#codersdkuserstatus) | false | | |
| `»» updated_at` | string(date-time) | false | | |
| `»» user_created_at` | string(date-time) | false | | |
| `»» user_id` | string(uuid) | false | | |
| `»» user_updated_at` | string(date-time) | false | | |
| `»» username` | string | false | | |
#### Enumerated Values
+20 -17
View File
@@ -6127,6 +6127,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit|
"organization_id": "string"
}
],
"is_service_account": true,
"last_seen_at": "2019-08-24T14:15:22Z",
"login_type": "",
"name": "string",
@@ -6149,23 +6150,24 @@ Only certain features set these fields: - FeatureManagedAgentLimit|
### Properties
| Name | Type | Required | Restrictions | Description |
|-------------------|-------------------------------------------------|----------|--------------|-------------|
| `avatar_url` | string | false | | |
| `created_at` | string | false | | |
| `email` | string | false | | |
| `global_roles` | array of [codersdk.SlimRole](#codersdkslimrole) | false | | |
| `last_seen_at` | string | false | | |
| `login_type` | [codersdk.LoginType](#codersdklogintype) | false | | |
| `name` | string | false | | |
| `organization_id` | string | false | | |
| `roles` | array of [codersdk.SlimRole](#codersdkslimrole) | false | | |
| `status` | [codersdk.UserStatus](#codersdkuserstatus) | false | | |
| `updated_at` | string | false | | |
| `user_created_at` | string | false | | |
| `user_id` | string | false | | |
| `user_updated_at` | string | false | | |
| `username` | string | false | | |
| Name | Type | Required | Restrictions | Description |
|----------------------|-------------------------------------------------|----------|--------------|-------------|
| `avatar_url` | string | false | | |
| `created_at` | string | false | | |
| `email` | string | false | | |
| `global_roles` | array of [codersdk.SlimRole](#codersdkslimrole) | false | | |
| `is_service_account` | boolean | false | | |
| `last_seen_at` | string | false | | |
| `login_type` | [codersdk.LoginType](#codersdklogintype) | false | | |
| `name` | string | false | | |
| `organization_id` | string | false | | |
| `roles` | array of [codersdk.SlimRole](#codersdkslimrole) | false | | |
| `status` | [codersdk.UserStatus](#codersdkuserstatus) | false | | |
| `updated_at` | string | false | | |
| `user_created_at` | string | false | | |
| `user_id` | string | false | | |
| `user_updated_at` | string | false | | |
| `username` | string | false | | |
#### Enumerated Values
@@ -6429,6 +6431,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit|
"organization_id": "string"
}
],
"is_service_account": true,
"last_seen_at": "2019-08-24T14:15:22Z",
"login_type": "",
"name": "string",
+14 -13
View File
@@ -491,19 +491,20 @@ func (api *API) groupMembers(rw http.ResponseWriter, r *http.Request) {
}
members, err := api.Database.GetGroupMembersByGroupIDPaginated(ctx, database.GetGroupMembersByGroupIDPaginatedParams{
AfterID: paginationParams.AfterID,
GroupID: group.ID,
IncludeSystem: false,
Search: userFilterParams.Search,
Name: userFilterParams.Name,
Status: userFilterParams.Status,
RbacRole: userFilterParams.RbacRole,
LastSeenBefore: userFilterParams.LastSeenBefore,
LastSeenAfter: userFilterParams.LastSeenAfter,
CreatedAfter: userFilterParams.CreatedAfter,
CreatedBefore: userFilterParams.CreatedBefore,
GithubComUserID: userFilterParams.GithubComUserID,
LoginType: userFilterParams.LoginType,
AfterID: paginationParams.AfterID,
GroupID: group.ID,
IncludeSystem: false,
Search: userFilterParams.Search,
Name: userFilterParams.Name,
Status: userFilterParams.Status,
IsServiceAccount: userFilterParams.IsServiceAccount,
RbacRole: userFilterParams.RbacRole,
LastSeenBefore: userFilterParams.LastSeenBefore,
LastSeenAfter: userFilterParams.LastSeenAfter,
CreatedAfter: userFilterParams.CreatedAfter,
CreatedBefore: userFilterParams.CreatedBefore,
GithubComUserID: userFilterParams.GithubComUserID,
LoginType: userFilterParams.LoginType,
// #nosec G115 - Pagination offsets are small and fit in int32
OffsetOpt: int32(paginationParams.Offset),
// #nosec G115 - Pagination limits are small and fit in int32
+1
View File
@@ -4592,6 +4592,7 @@ export interface OrganizationMemberWithUserData extends OrganizationMember {
readonly last_seen_at?: string;
readonly user_created_at: string;
readonly user_updated_at: string;
readonly is_service_account?: boolean;
readonly global_roles: readonly SlimRole[];
}
@@ -13,6 +13,7 @@ import { docs } from "utils/docs";
const userFilterQuery = {
active: "status:active",
serviceAccount: "service_account:true",
all: "",
};
@@ -51,6 +52,7 @@ type StatusFilterMenu = ReturnType<typeof useStatusFilterMenu>;
const PRESET_FILTERS = [
{ query: userFilterQuery.active, name: "Active users" },
{ query: userFilterQuery.serviceAccount, name: "Service accounts" },
{ query: userFilterQuery.all, name: "All users" },
];