Skip to content

👨‍👨‍👦‍👦 feat: Admin Users API Endpoints#12446

Merged
danny-avila merged 16 commits into
devfrom
feat/admin-people-picker-v2
Mar 31, 2026
Merged

👨‍👨‍👦‍👦 feat: Admin Users API Endpoints#12446
danny-avila merged 16 commits into
devfrom
feat/admin-people-picker-v2

Conversation

@dustinhealy

@dustinhealy dustinhealy commented Mar 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

This pull request introduces a new set of admin user management endpoints to the API, enabling listing and searching users via the /api/admin/users route. It also adds support for paginated and sorted user queries at the data layer, and includes comprehensive tests for these features. Additionally, there are minor improvements to capability checks and logging.

Admin user management API:

  • Added new admin user routes at /api/admin/users with endpoints for listing and searching users, protected by capability-based access control. The delete endpoint is scaffolded but currently commented out. (api/server/routes/admin/users.js, api/server/index.js, api/server/routes/index.js) [1] [2] [3] [4]
  • Implemented createAdminUsersHandlers in packages/api/src/admin/users.ts to provide handlers for listing, searching, and deleting users, including logic for pagination, search validation, and safe deletion (e.g., preventing self-deletion and last-admin deletion). (packages/api/src/admin/users.ts)
  • Exported the new handlers and types from the API package for integration. (packages/api/src/admin/index.ts)

User data layer enhancements:

  • Extended the findUsers method to support limit, offset, and sort options for efficient pagination and sorting, and added corresponding tests to ensure correct behavior. (packages/data-schemas/src/methods/user.ts, packages/data-schemas/src/methods/user.methods.spec.ts) [1] [2]
  • Updated the AdminUserSearchResult type to include username for richer search results. (packages/data-schemas/src/types/admin.ts)

Other improvements:

  • Improved capability middleware to log warnings when a user is forbidden due to missing capabilities. (packages/api/src/middleware/capabilities.ts)
  • Optimized capability checks in admin grants handler for efficiency by batching checks. (packages/api/src/admin/grants.ts)

Change Type

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

Testing

  • 28 unit tests for user handlers covering:
    • listUsers: paginated response, pagination params forwarded, empty list, error handling
    • searchUsers: regex search across name/email/username, special character escaping, input validation (missing/empty/short/long query), limit enforcement and capping, error handling
    • deleteUser: successful deletion, self-deletion guard (403), last-admin guard (400), non-admin skip of admin check, cascade cleanup (Config/AclEntries/SystemGrants with ObjectId cast), partial cascade failure resilience, invalid ObjectId (400), user not found (404), error handling
  • 6 data-layer integration tests (mongodb-memory-server) for findUsers options: limit, offset, sort, pagination, limit=0 behavior, no-options default

Checklist

  • My code adheres to this project's style guidelines
  • I have performed a self-review of my own code
  • I have commented in any complex areas of my code
  • I have made pertinent documentation changes
  • My changes do not introduce new warnings
  • I have written tests demonstrating that my changes are effective or that my feature works
  • Local unit tests pass with my changes
  • A pull request for updating the documentation has been submitted.

@dustinhealy dustinhealy mentioned this pull request Mar 28, 2026
10 tasks
@dustinhealy dustinhealy marked this pull request as ready for review March 28, 2026 18:09
@dustinhealy dustinhealy requested a review from Copilot March 28, 2026 18:23
@dustinhealy dustinhealy marked this pull request as draft March 28, 2026 18:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces new admin user management endpoints (/api/admin/users) to support listing, searching, and deleting users, with capability-based protection and supporting data-layer pagination/sorting.

Changes:

  • Added createAdminUsersHandlers (list/search/delete) and wired new /api/admin/users Express routes with admin/capability middleware.
  • Extended findUsers data-schema method to support { sort, offset, limit } options to enable pagination/sorting from API handlers.
  • Added Jest unit tests for the new admin users handlers; also includes a small performance refactor in admin grants capability checks.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/data-schemas/src/methods/user.ts Adds optional sort/offset/limit support to findUsers() to enable paginated user listing/search.
packages/api/src/middleware/capabilities.ts Changes 403 response message to include missing capability string.
packages/api/src/admin/users.ts Implements admin user list/search/delete handlers and cascade cleanup on delete.
packages/api/src/admin/users.spec.ts Adds unit tests covering success/error/validation paths for the new handlers.
packages/api/src/admin/index.ts Exports the new handler factory + deps types from the admin module.
packages/api/src/admin/grants.ts Parallelizes capability checks when computing allowed types.
api/server/routes/index.js Registers new adminUsers route module export.
api/server/routes/admin/users.js Wires /api/admin/users endpoints with JWT + capability middleware and injected db methods.
api/server/index.js Mounts the new /api/admin/users router on the Express app.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/api/src/admin/users.ts
Comment thread packages/api/src/admin/users.ts Outdated
Comment thread packages/api/src/middleware/capabilities.ts Outdated
Comment thread packages/api/src/admin/users.spec.ts Outdated
@dustinhealy dustinhealy force-pushed the feat/admin-people-picker-v2 branch from f20a888 to b1173b5 Compare March 28, 2026 18:51
@dustinhealy dustinhealy changed the title WIP: Admin Users API Endpoints 👨‍👨‍👦‍👦 feat: Admin Users API Endpoints Mar 28, 2026
@dustinhealy dustinhealy marked this pull request as ready for review March 28, 2026 19:27
Base automatically changed from feat/admin-grants to dev March 30, 2026 20:49
Add /api/admin/users with list, search, and delete handlers gated by
ACCESS_ADMIN + READ_USERS/MANAGE_USERS system grants. Handler factory
in packages/api uses findUsers, countUsers, and deleteUserById from
data-schemas.
- listUsers now uses parsePagination + countUsers for proper pagination
  matching the roles/groups pattern
- findUsers extended with optional limit/offset options
- deleteUser returns 403 when caller tries to delete own account
- searchUsers passes limit to DB query instead of fetching all and
  slicing in JS
- Fix import ordering per CLAUDE.md, complete logger mock
- Replace fabricated date fallback with undefined
- Add sort option to findUsers; listUsers sorts by createdAt desc for
  deterministic pagination
- Use != null guards for offset/limit to handle zero values correctly
- Remove username from search filter since it is not in the projection
  or AdminUserSearchResult response type
- Prevent deleting the last admin user (look up target role, count
  admins, reject with 400 if count <= 1)
- Cap search query at 200 characters to prevent regex DoS
- Add tests for both guards
…ity checks

- Cascade Config, AclEntry, and SystemGrant cleanup on user deletion
  (matching the pattern in roles/groups handlers)
- Add username to admin search $or filter for parity with searchUsers
- Parallelize READ_* capability checks in listAllGrants with Promise.all
…-layer tests

- Add post-delete admin recount with CRITICAL log if race leaves 0 admins
- Revert capability name from 403 response to server-side log only
- Document thin deleteUserById limitation (full cascade is a future task)
- DRY: extract query.trim() to local variable in searchUsersHandler
- Add username to search projection, response type, and AdminUserSearchResult
- Functional filter/map in grants.ts parallel capability check
- Consistent null guards and limit>0 guard in findUsers options
- Fallback for empty result.message on delete response
- Fix mockUser() to generate unique _id per call
- Break long destructuring across multiple lines
- Assert countUsers filter and non-admin skip in delete tests
- Add data-layer tests for findUsers limit, offset, sort, and pagination
ACL entries store USER principalId as ObjectId (via grantPermission casting),
but deleteAclEntries is a raw deleteMany that passes the filter through.
Passing a string won't match stored ObjectIds, leaving orphaned entries.
@dustinhealy dustinhealy force-pushed the feat/admin-people-picker-v2 branch from 5e3997a to b334cad Compare March 30, 2026 21:51
@dustinhealy dustinhealy marked this pull request as draft March 30, 2026 21:57
… test coverage

- Unify response shape: AdminUserSearchResult.userId → id, add AdminUserListItem type
- Fix unsafe req.query type assertion in searchUsersHandler (typeof guards)
- Anchor search regex with ^ for prefix matching (enables index usage)
- Add total/capped to search response for truncation signaling
- Add parseInt radix, remove redundant new Date() wrap
- Add tests: countUsers throw, countUsers call args, array query param, capped flag
@dustinhealy dustinhealy marked this pull request as ready for review March 31, 2026 00:23
@danny-avila

Copy link
Copy Markdown
Owner

@codex review

…ort, align test mocks

- Add tenantId option to AdminUsersDeps.deleteGrantsForPrincipal and
  pass req.user.tenantId at the call site, matching the pattern already
  used by the roles and groups handlers
- Add sort: { name: 1 } to searchUsersHandler for deterministic results
- Align test mock deleteUserById messages with production output
  ('User was deleted successfully.')
- Make capped-results test explicitly set limit: '20' instead of
  relying on the implicit default
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Add tenantId to createReqRes user type and test that a non-undefined
tenantId is threaded through to deleteGrantsForPrincipal.
@danny-avila danny-avila merged commit 3d1b883 into dev Mar 31, 2026
10 checks passed
@danny-avila danny-avila deleted the feat/admin-people-picker-v2 branch March 31, 2026 03:06
jcbartle pushed a commit to jcbartle/LibreChat that referenced this pull request May 11, 2026
* feat: add admin user management endpoints

Add /api/admin/users with list, search, and delete handlers gated by
ACCESS_ADMIN + READ_USERS/MANAGE_USERS system grants. Handler factory
in packages/api uses findUsers, countUsers, and deleteUserById from
data-schemas.

* fix: address convention violations in admin users handlers

* fix: add pagination, self-deletion guard, and DB-level search limit

- listUsers now uses parsePagination + countUsers for proper pagination
  matching the roles/groups pattern
- findUsers extended with optional limit/offset options
- deleteUser returns 403 when caller tries to delete own account
- searchUsers passes limit to DB query instead of fetching all and
  slicing in JS
- Fix import ordering per CLAUDE.md, complete logger mock
- Replace fabricated date fallback with undefined

* fix: deterministic sort, null-safe pagination, consistent search filter

- Add sort option to findUsers; listUsers sorts by createdAt desc for
  deterministic pagination
- Use != null guards for offset/limit to handle zero values correctly
- Remove username from search filter since it is not in the projection
  or AdminUserSearchResult response type

* fix: last-admin deletion guard and search query max-length

- Prevent deleting the last admin user (look up target role, count
  admins, reject with 400 if count <= 1)
- Cap search query at 200 characters to prevent regex DoS
- Add tests for both guards

* fix: include missing capability name in 403 Forbidden response

* fix: cascade user deletion cleanup, search username, parallel capability checks

- Cascade Config, AclEntry, and SystemGrant cleanup on user deletion
  (matching the pattern in roles/groups handlers)
- Add username to admin search $or filter for parity with searchUsers
- Parallelize READ_* capability checks in listAllGrants with Promise.all

* fix: TOCTOU safety net, capability info leak, DRY/style cleanup, data-layer tests

- Add post-delete admin recount with CRITICAL log if race leaves 0 admins
- Revert capability name from 403 response to server-side log only
- Document thin deleteUserById limitation (full cascade is a future task)
- DRY: extract query.trim() to local variable in searchUsersHandler
- Add username to search projection, response type, and AdminUserSearchResult
- Functional filter/map in grants.ts parallel capability check
- Consistent null guards and limit>0 guard in findUsers options
- Fallback for empty result.message on delete response
- Fix mockUser() to generate unique _id per call
- Break long destructuring across multiple lines
- Assert countUsers filter and non-admin skip in delete tests
- Add data-layer tests for findUsers limit, offset, sort, and pagination

* chore: comment out admin delete user endpoint (out of scope)

* fix: cast USER principalId to ObjectId for ACL entry cleanup

ACL entries store USER principalId as ObjectId (via grantPermission casting),
but deleteAclEntries is a raw deleteMany that passes the filter through.
Passing a string won't match stored ObjectIds, leaving orphaned entries.

* chore: comment out unused requireManageUsers alongside disabled delete route

* fix: add missing logger.warn mock in capabilities test

* fix: harden admin users handlers — type safety, response consistency, test coverage

- Unify response shape: AdminUserSearchResult.userId → id, add AdminUserListItem type
- Fix unsafe req.query type assertion in searchUsersHandler (typeof guards)
- Anchor search regex with ^ for prefix matching (enables index usage)
- Add total/capped to search response for truncation signaling
- Add parseInt radix, remove redundant new Date() wrap
- Add tests: countUsers throw, countUsers call args, array query param, capped flag

* fix: scope deleteGrantsForPrincipal to tenant, deterministic search sort, align test mocks

- Add tenantId option to AdminUsersDeps.deleteGrantsForPrincipal and
  pass req.user.tenantId at the call site, matching the pattern already
  used by the roles and groups handlers
- Add sort: { name: 1 } to searchUsersHandler for deterministic results
- Align test mock deleteUserById messages with production output
  ('User was deleted successfully.')
- Make capped-results test explicitly set limit: '20' instead of
  relying on the implicit default

* test: add tenantId propagation test for deleteGrantsForPrincipal

Add tenantId to createReqRes user type and test that a non-undefined
tenantId is threaded through to deleteGrantsForPrincipal.

* test: remove redundant deleteUserById override in tenantId test

---------

Co-authored-by: Danny Avila <danny@librechat.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants