2025-07-26 12:28:31 -04:00
|
|
|
const express = require('express');
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
const request = require('supertest');
|
2025-07-26 12:28:31 -04:00
|
|
|
const mongoose = require('mongoose');
|
|
|
|
|
const { ObjectId } = require('mongodb');
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
const { MongoMemoryServer } = require('mongodb-memory-server');
|
|
|
|
|
const {
|
|
|
|
|
SystemRoles,
|
|
|
|
|
ResourceType,
|
|
|
|
|
AccessRoleIds,
|
2025-08-02 16:02:56 -04:00
|
|
|
PrincipalType,
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
PermissionBits,
|
|
|
|
|
} = require('librechat-data-provider');
|
📜 feat: Implement System Grants for Capability-Based Authorization (#11896)
* feat: Implement System Grants for Role-Based Capabilities
- Added a new `systemGrant` model and associated methods to manage role-based capabilities within the application.
- Introduced middleware functions `hasCapability` and `requireCapability` to check user permissions based on their roles.
- Updated the database seeding process to include system grants for the ADMIN role, ensuring all necessary capabilities are assigned on startup.
- Enhanced type definitions and schemas to support the new system grant functionality, improving overall type safety and clarity in the codebase.
* test: Add unit tests for capabilities middleware and system grant methods
- Introduced comprehensive unit tests for the capabilities middleware, including `hasCapability` and `requireCapability`, ensuring proper permission checks based on user roles.
- Added tests for the `SystemGrant` methods, verifying the seeding of system grants, capability granting, and revocation processes.
- Enhanced test coverage for edge cases, including idempotency of grant operations and handling of unexpected errors in middleware.
- Utilized mocks for database interactions to isolate tests and improve reliability.
* refactor: Transition to Capability-Based Access Control
- Replaced role-based access checks with capability-based checks across various middleware and routes, enhancing permission management.
- Introduced `hasCapability` and `requireCapability` functions to streamline capability verification for user actions.
- Updated relevant routes and middleware to utilize the new capability system, ensuring consistent permission enforcement.
- Enhanced type definitions and added tests for the new capability functions, improving overall code reliability and maintainability.
* test: Enhance capability-based access tests for ADMIN role
- Updated tests to reflect the new capability-based access control, specifically for the ADMIN role.
- Modified test descriptions to clarify that users with the MANAGE_AGENTS capability can bypass permission checks.
- Seeded capabilities for the ADMIN role in multiple test files to ensure consistent permission checks across different routes and middleware.
- Improved overall test coverage for capability verification, ensuring robust permission management.
* test: Update capability tests for MCP server access
- Renamed test to reflect the correct capability for bypassing permission checks, changing from MANAGE_AGENTS to MANAGE_MCP_SERVERS.
- Updated seeding of capabilities for the ADMIN role to align with the new capability structure.
- Ensured consistency in capability definitions across tests and middleware for improved permission management.
* feat: Add hasConfigCapability for enhanced config access control
- Introduced `hasConfigCapability` function to check user permissions for managing or reading specific config sections.
- Updated middleware to export the new capability function, ensuring consistent access control across the application.
- Enhanced unit tests to cover various scenarios for the new capability, improving overall test coverage and reliability.
* fix: Update tenantId filter in createSystemGrantMethods
- Added a condition to set tenantId filter to { $exists: false } when tenantId is null, ensuring proper handling of cases where tenantId is not provided.
- This change improves the robustness of the system grant methods by explicitly managing the absence of tenantId in the filter logic.
* fix: account deletion capability check
- Updated the `canDeleteAccount` middleware to ensure that the `hasManageUsers` capability check only occurs if a user is present, preventing potential errors when the user object is undefined.
- This change improves the robustness of the account deletion logic by ensuring proper handling of user permissions.
* refactor: Optimize seeding of system grants for ADMIN role
- Replaced sequential capability granting with parallel execution using Promise.all in the seedSystemGrants function.
- This change improves performance and efficiency during the initialization of system grants, ensuring all capabilities are granted concurrently.
* refactor: Simplify systemGrantSchema index definition
- Removed the sparse option from the unique index on principalType, principalId, capability, and tenantId in the systemGrantSchema.
- This change streamlines the index definition, potentially improving query performance and clarity in the schema design.
* refactor: Reorganize role capability check in roles route
- Moved the capability check for reading roles to occur after parsing the roleName, improving code clarity and structure.
- This change ensures that the authorization logic is consistently applied before fetching role details, enhancing overall permission management.
* refactor: Remove unused ISystemGrant interface from systemCapabilities.ts
- Deleted the ISystemGrant interface as it was no longer needed, streamlining the code and improving clarity.
- This change helps reduce clutter in the file and focuses on relevant capabilities for the system.
* refactor: Migrate SystemCapabilities to data-schemas
- Replaced imports of SystemCapabilities from 'librechat-data-provider' with imports from '@librechat/data-schemas' across multiple files.
- This change centralizes the management of system capabilities, improving code organization and maintainability.
* refactor: Update account deletion middleware and capability checks
- Modified the `canDeleteAccount` middleware to ensure that the account deletion permission is only granted to users with the `MANAGE_USERS` capability, improving security and clarity in permission management.
- Enhanced error logging for unauthorized account deletion attempts, providing better insights into permission issues.
- Updated the `capabilities.ts` file to ensure consistent handling of user authentication checks, improving robustness in capability verification.
- Refined type definitions in `systemGrant.ts` and `systemGrantMethods.ts` to utilize the `PrincipalType` enum, enhancing type safety and code clarity.
* refactor: Extract principal ID normalization into a separate function
- Introduced `normalizePrincipalId` function to streamline the normalization of principal IDs based on their type, enhancing code clarity and reusability.
- Updated references in `createSystemGrantMethods` to utilize the new normalization function, improving maintainability and reducing code duplication.
* test: Add unit tests for principalId normalization in systemGrant
- Introduced tests for the `grantCapability`, `revokeCapability`, and `getCapabilitiesForPrincipal` methods to verify correct handling of principalId normalization between string and ObjectId formats.
- Enhanced the `capabilities.ts` middleware to utilize the `PrincipalType` enum for improved type safety.
- Added a new utility function `normalizePrincipalId` to streamline principal ID normalization logic, ensuring consistent behavior across the application.
* feat: Introduce capability implications and enhance system grant methods
- Added `CapabilityImplications` to define relationships between broader and implied capabilities, allowing for more intuitive permission checks.
- Updated `createSystemGrantMethods` to expand capability queries to include implied capabilities, improving authorization logic.
- Enhanced `systemGrantSchema` to include an `expiresAt` field for future TTL enforcement of grants, and added validation to ensure `tenantId` is not set to null.
- Documented authorization requirements for prompt group and prompt deletion methods to clarify access control expectations.
* test: Add unit tests for canDeleteAccount middleware
- Introduced unit tests for the `canDeleteAccount` middleware to verify account deletion permissions based on user roles and capabilities.
- Covered scenarios for both allowed and blocked account deletions, including checks for ADMIN users with the `MANAGE_USERS` capability and handling of undefined user cases.
- Enhanced test structure to ensure clarity and maintainability of permission checks in the middleware.
* fix: Add principalType enum validation to SystemGrant schema
Without enum validation, any string value was accepted for principalType
and silently stored. Invalid documents would never match capability
queries, creating phantom grants impossible to diagnose without raw DB
inspection. All other ACL models in the codebase validate this field.
* fix: Replace seedSystemGrants Promise.all with bulkWrite for concurrency safety
When two server instances start simultaneously (K8s rolling deploy, PM2
cluster), both call seedSystemGrants. With Promise.all + findOneAndUpdate
upsert, both instances may attempt to insert the same documents, causing
E11000 duplicate key errors that crash server startup.
bulkWrite with ordered:false handles concurrent upserts gracefully and
reduces 17 individual round trips to a single network call. The returned
documents (previously discarded) are no longer fetched.
* perf: Add AsyncLocalStorage per-request cache for capability checks
Every hasCapability call previously required 2 DB round trips
(getUserPrincipals + SystemGrant.exists) — replacing what were O(1)
string comparisons. Routes like patchPromptGroup triggered this twice,
and hasConfigCapability's fallback path resolved principals twice.
This adds a per-request AsyncLocalStorage cache that:
- Caches resolved principals (same for all checks within one request)
- Caches capability check results (same user+cap = same answer)
- Automatically scoped to request lifetime (no stale grants)
- Falls through to DB when no store exists (background jobs, tests)
- Requires no signature changes to hasCapability
The capabilityContextMiddleware is registered at the app level before
all routes, initializing a fresh store per request.
* fix: Add error handling for inline hasCapability calls
canDeleteAccount, fetchAssistants, and validateAuthor all call
hasCapability without try-catch. These were previously O(1) string
comparisons that could never throw. Now they hit the database and can
fail on connection timeout or transient errors.
Wrap each call in try-catch, defaulting to deny (false) on error.
This ensures a DB hiccup returns a clean 403 instead of an unhandled
500 with a stack trace.
* test: Add canDeleteAccount DB-error resilience test
Tests that hasCapability rejection (e.g., DB timeout) results in a clean
403 rather than an unhandled exception. Validates the error handling
added in the previous commit.
* refactor: Use barrel import for hasCapability in validateAuthor
Import from ~/server/middleware barrel instead of directly from
~/server/middleware/roles/capabilities for consistency with other
non-middleware consumers. Files within the middleware barrel itself
must continue using direct imports to avoid circular requires.
* refactor: Remove misleading pre('save') hook from SystemGrant schema
The pre('save') hook normalized principalId for USER/GROUP principals,
but the primary write path (grantCapability) uses findOneAndUpdate —
which does not trigger save hooks. The normalization was already handled
explicitly in grantCapability itself. The hook created a false impression
of schema-level enforcement that only covered save()/create() paths.
Replace with a comment documenting that all writes must go through
grantCapability.
* feat: Add READ_ASSISTANTS capability to complete manage/read pair
Every other managed resource had a paired READ_X / MANAGE_X capability
except assistants. This adds READ_ASSISTANTS and registers the
MANAGE_ASSISTANTS → READ_ASSISTANTS implication in CapabilityImplications,
enabling future read-only assistant visibility grants.
* chore: Reorder systemGrant methods for clarity
Moved hasCapabilityForPrincipals to a more logical position in the returned object of createSystemGrantMethods, improving code readability. This change also maintains the inclusion of seedSystemGrants in the export, ensuring all necessary methods are available.
* fix: Wrap seedSystemGrants in try-catch to avoid blocking startup
Seeding capabilities is idempotent and will succeed on the next restart.
A transient DB error during seeding should not prevent the server from
starting — log the error and continue.
* refactor: Improve capability check efficiency and add audit logging
Move hasCapability calls after cheap early-exits in validateAuthor and
fetchAssistants so the DB check only runs when its result matters. Add
logger.debug on every capability bypass grant across all 7 call sites
for auditability, and log errors in catch blocks instead of silently
swallowing them.
* test: Add integration tests for AsyncLocalStorage capability caching
Exercises the full vertical — ALS context, generateCapabilityCheck,
real getUserPrincipals, real hasCapabilityForPrincipals, real MongoDB
via MongoMemoryServer. Covers per-request caching, cross-context
isolation, concurrent request isolation, negative caching, capability
implications, tenant scoping, group-based grants, and requireCapability
middleware.
* test: Add systemGrant data-layer and ALS edge-case integration tests
systemGrant.spec.ts (51 tests): Full integration tests for all
systemGrant methods against real MongoDB — grant/revoke lifecycle,
principalId normalization (string→ObjectId for USER/GROUP, string for
ROLE), capability implications (both directions), tenant scoping,
schema validation (null tenantId, invalid enum, required fields,
unique compound index).
capabilities.integration.spec.ts (27 tests): Adds ALS edge cases —
missing context degrades gracefully with no caching (background jobs,
child processes), nested middleware creates independent inner context,
optional-chaining safety when store is undefined, mid-request grant
changes are invisible due to result caching, requireCapability works
without ALS, and interleaved concurrent contexts maintain isolation.
* fix: Add worker thread guards to capability ALS usage
Detect when hasCapability or capabilityContextMiddleware is called from
a worker thread (where ALS context does not propagate from the parent).
hasCapability logs a warn-once per factory instance; the middleware logs
an error since mounting Express middleware in a worker is likely a
misconfiguration. Both continue to function correctly — the guard is
observability, not a hard block.
* fix: Include tenantId in ALS principal cache key for tenant isolation
The principal cache key was user.id:user.role, which would reuse
cached principals across tenants for the same user within a request.
When getUserPrincipals gains tenant-scoped group resolution, principals
from tenant-a would incorrectly serve tenant-b checks. Changed to
user.id:user.role:user.tenantId to prevent cross-tenant cache hits.
Adds integration test proving separate principal lookups per tenantId.
* test: Remove redundant mocked capabilities.spec.js
The JS wrapper test (7 tests, all mocked) is a strict subset of
capabilities.integration.spec.ts (28 tests, real MongoDB). Every
scenario it covered — hasCapability true/false, tenantId passthrough,
requireCapability 403/500, error handling — is tested with higher
fidelity in the integration suite.
* test: Replace mocked canDeleteAccount tests with real MongoDB integration
Remove hasCapability mock — tests now exercise the full capability
chain against real MongoDB (getUserPrincipals, hasCapabilityForPrincipals,
SystemGrant collection). Only mocks remaining are logger and cache.
Adds new coverage: admin role without grant is blocked, user-level
grant bypasses deletion restriction, null user handling.
* test: Add comprehensive tests for ACL entry management and user group methods
Introduces new tests for `deleteAclEntries`, `bulkWriteAclEntries`, and `findPublicResourceIds` in `aclEntry.spec.ts`, ensuring proper functionality for deleting and bulk managing ACL entries. Additionally, enhances `userGroup.spec.ts` with tests for finding groups by ID and name pattern, including external ID matching and source filtering. These changes improve coverage and validate the integrity of ACL and user group operations against real MongoDB interactions.
* refactor: Update capability checks and logging for better clarity and error handling
Replaced `MANAGE_USERS` with `ACCESS_ADMIN` in the `canDeleteAccount` middleware and related tests to align with updated permission structure. Enhanced logging in various middleware functions to use `logger.warn` for capability check failures, providing clearer error messages. Additionally, refactored capability checks in the `patchPromptGroup` and `validateAuthor` functions to improve readability and maintainability. This commit also includes adjustments to the `systemGrant` methods to implement retry logic for transient failures during capability seeding, ensuring robustness in the face of database errors.
* refactor: Enhance logging and retry logic in seedSystemGrants method
Updated the logging format in the seedSystemGrants method to include error messages for better clarity. Improved the retry mechanism by explicitly mocking multiple failures in tests, ensuring robust error handling during transient database issues. Additionally, refined imports in the systemGrant schema for better type management.
* refactor: Consolidate imports in canDeleteAccount middleware
Merged logger and SystemCapabilities imports from the data-schemas module into a single line for improved readability and maintainability of the code. This change streamlines the import statements in the canDeleteAccount middleware.
* test: Enhance systemGrant tests for error handling and capability validation
Added tests to the systemGrant methods to handle various error scenarios, including E11000 race conditions, invalid ObjectId strings for USER and GROUP principals, and invalid capability strings. These enhancements improve the robustness of the capability granting and revoking logic, ensuring proper error propagation and validation of inputs.
* fix: Wrap hasCapability calls in deny-by-default try-catch at remaining sites
canAccessResource, files.js, and roles.js all had hasCapability inside
outer try-catch blocks that returned 500 on DB failure instead of
falling through to the regular ACL check. This contradicts the
deny-by-default pattern used everywhere else.
Also removes raw error.message from the roles.js 500 response to
prevent internal host/connection info leaking to clients.
* fix: Normalize user ID in canDeleteAccount before passing to hasCapability
requireCapability normalizes req.user.id via _id?.toString() fallback,
but canDeleteAccount passed raw req.user directly. If req.user.id is
absent (some auth layers only populate _id), getUserPrincipals received
undefined, silently returning empty principals and blocking the bypass.
* fix: Harden systemGrant schema and type safety
- Reject empty string tenantId in schema validator (was only blocking
null; empty string silently orphaned documents)
- Fix reverseImplications to use BaseSystemCapability[] instead of
string[], preserving the narrow discriminated type
- Document READ_ASSISTANTS as reserved/unenforced
* test: Use fake timers for seedSystemGrants retry tests and add tenantId validation
- Switch retry tests to jest.useFakeTimers() to eliminate 3+ seconds
of real setTimeout delays per test run
- Add regression test for empty-string tenantId rejection
* docs: Add TODO(#12091) comments for tenant-scoped capability gaps
In multi-tenant mode, platform-level grants (no tenantId) won't match
tenant-scoped queries, breaking admin access. getUserPrincipals also
returns cross-tenant group memberships. Both need fixes in #12091.
2026-03-07 13:56:32 -05:00
|
|
|
const { SystemCapabilities } = require('@librechat/data-schemas');
|
2025-07-26 12:28:31 -04:00
|
|
|
|
|
|
|
|
// Mock modules before importing
|
|
|
|
|
jest.mock('~/server/services/Config', () => ({
|
|
|
|
|
getCachedTools: jest.fn().mockResolvedValue({}),
|
|
|
|
|
}));
|
|
|
|
|
|
📦 refactor: Consolidate DB models, encapsulating Mongoose usage in `data-schemas` (#11830)
* chore: move database model methods to /packages/data-schemas
* chore: add TypeScript ESLint rule to warn on unused variables
* refactor: model imports to streamline access
- Consolidated model imports across various files to improve code organization and reduce redundancy.
- Updated imports for models such as Assistant, Message, Conversation, and others to a unified import path.
- Adjusted middleware and service files to reflect the new import structure, ensuring functionality remains intact.
- Enhanced test files to align with the new import paths, maintaining test coverage and integrity.
* chore: migrate database models to packages/data-schemas and refactor all direct Mongoose Model usage outside of data-schemas
* test: update agent model mocks in unit tests
- Added `getAgent` mock to `client.test.js` to enhance test coverage for agent-related functionality.
- Removed redundant `getAgent` and `getAgents` mocks from `openai.spec.js` and `responses.unit.spec.js` to streamline test setup and reduce duplication.
- Ensured consistency in agent mock implementations across test files.
* fix: update types in data-schemas
* refactor: enhance type definitions in transaction and spending methods
- Updated type definitions in `checkBalance.ts` to use specific request and response types.
- Refined `spendTokens.ts` to utilize a new `SpendTxData` interface for better clarity and type safety.
- Improved transaction handling in `transaction.ts` by introducing `TransactionResult` and `TxData` interfaces, ensuring consistent data structures across methods.
- Adjusted unit tests in `transaction.spec.ts` to accommodate new type definitions and enhance robustness.
* refactor: streamline model imports and enhance code organization
- Consolidated model imports across various controllers and services to a unified import path, improving code clarity and reducing redundancy.
- Updated multiple files to reflect the new import structure, ensuring all functionalities remain intact.
- Enhanced overall code organization by removing duplicate import statements and optimizing the usage of model methods.
* feat: implement loadAddedAgent and refactor agent loading logic
- Introduced `loadAddedAgent` function to handle loading agents from added conversations, supporting multi-convo parallel execution.
- Created a new `load.ts` file to encapsulate agent loading functionalities, including `loadEphemeralAgent` and `loadAgent`.
- Updated the `index.ts` file to export the new `load` module instead of the deprecated `loadAgent`.
- Enhanced type definitions and improved error handling in the agent loading process.
- Adjusted unit tests to reflect changes in the agent loading structure and ensure comprehensive coverage.
* refactor: enhance balance handling with new update interface
- Introduced `IBalanceUpdate` interface to streamline balance update operations across the codebase.
- Updated `upsertBalanceFields` method signatures in `balance.ts`, `transaction.ts`, and related tests to utilize the new interface for improved type safety.
- Adjusted type imports in `balance.spec.ts` to include `IBalanceUpdate`, ensuring consistency in balance management functionalities.
- Enhanced overall code clarity and maintainability by refining type definitions related to balance operations.
* feat: add unit tests for loadAgent functionality and enhance agent loading logic
- Introduced comprehensive unit tests for the `loadAgent` function, covering various scenarios including null and empty agent IDs, loading of ephemeral agents, and permission checks.
- Enhanced the `initializeClient` function by moving `getConvoFiles` to the correct position in the database method exports, ensuring proper functionality.
- Improved test coverage for agent loading, including handling of non-existent agents and user permissions.
* chore: reorder memory method exports for consistency
- Moved `deleteAllUserMemories` to the correct position in the exported memory methods, ensuring a consistent and logical order of method exports in `memory.ts`.
2026-02-17 18:23:44 -05:00
|
|
|
jest.mock('~/models', () => {
|
|
|
|
|
const mongoose = require('mongoose');
|
|
|
|
|
const { createMethods } = require('@librechat/data-schemas');
|
|
|
|
|
const methods = createMethods(mongoose, {
|
|
|
|
|
removeAllPermissions: async ({ resourceType, resourceId }) => {
|
|
|
|
|
const AclEntry = mongoose.models.AclEntry;
|
|
|
|
|
if (AclEntry) {
|
|
|
|
|
await AclEntry.deleteMany({ resourceType, resourceId });
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
return {
|
|
|
|
|
...methods,
|
|
|
|
|
getRoleByName: jest.fn(),
|
|
|
|
|
};
|
|
|
|
|
});
|
2025-07-26 12:28:31 -04:00
|
|
|
|
|
|
|
|
jest.mock('~/server/middleware', () => ({
|
|
|
|
|
requireJwtAuth: (req, res, next) => next(),
|
📁 refactor: Prompts UI (#11570)
* style: enhance prompts UI with new components and improved structure; add CreatePromptButton and AutoSendPrompt; refactor GroupSidePanel and PromptsAccordion
* refactor(Prompts): move button components to buttons/ subdirectory
* refactor(Prompts): move dialog components to dialogs/ subdirectory
* refactor(Prompts): move display components to display/ subdirectory
* refactor(Prompts): move editor components to editor/ subdirectory
* refactor(Prompts): move field components to fields/ subdirectory
* refactor(Prompts): move form components to forms/ subdirectory
* refactor(Prompts): move layout components to layouts/ subdirectory
* refactor(Prompts): move list components to lists/ subdirectory
* refactor(Prompts): move sidebar components to sidebar/ subdirectory
* refactor(Prompts): move utility components to utils/ subdirectory
* refactor(Prompts): update main exports and external imports
* refactor(Prompts): fix class name typo in AutoSendPrompt
* refactor(Prompts): reorganize exports and imports order across components
* refactor(Prompts): reorder exports for better organization and clarity
* refactor(Buttons): enhance prompts accessibility with aria-labels and update translations
* refactor(AdminSettings): reorganize imports and improve form structure for clarity
* refactor(Dialogs): reorganize imports for consistency and clarity across DeleteVersion, SharePrompt, and VariableDialog components
* refactor(Dialogs): enhance prompts accessibility with aria-labels
* refactor(Display): enhance prompt components and accessibility features
* refactor(.gitignore): add Playwright MCP directory
* refactor(Preview): enhance prompt components, improve layout, and add accessibility features
* refactor(Prompts): enhance variable handling, improve accessibility, and update UI components
* refactor(Prompts): enhance loading state handling and improve accessibility in PromptName component
* refactor(Prompts): streamline special variable handling, improve icon management, and enhance UI components
* refactor(Prompts): update AdvancedSwitch component to use Radio for mode selection, enhance PromptName with tooltips, and improve layout in PromptForm
* refactor(Prompts): enhance VersionCard and VersionBadge components for improved UI and accessibility, update loading state handling in VersionsPanel
* refactor(Prompts): improve layout and styling of VersionCard component for better visual alignment and clarity
* refactor(DeleteVersion): update text color for confirmation prompt in DeleteConfirmDialog
* refactor(Prompts): add configurations for always make production and auto-send prompts, update localization strings for clarity
* refactor(Prompts): enhance layout and styling in CategorySelector, CreatePromptForm, and List components for improved responsiveness and clarity
* refactor(Prompts): enhance PromptDetailHeader and ChatGroupItem components, add shared prompt indication, and remove unused PromptMetadata component
* refactor(Prompts): implement prompt group usage tracking, update sorting logic, and enhance related components
* fix(Prompts): security, performance, and pagination fixes
- Fix cursor pagination skipping/duplicating items by including
numberOfGenerations in cursor condition to match sort order
- Close NoSQL injection vector via otherFilters rest spread in
GET /all, GET /groups, and buildPromptGroupFilter
- Validate groupId as ObjectId before passing to query (GET /)
- Add prompt body validation in addPromptToGroup (type + text)
- Return 404 instead of 500 for missing group in POST /use
- Combine data + count into single $facet aggregation
- Add compound index {numberOfGenerations, updatedAt, _id}
- Add index on prompt.author for deleteUserPrompts
- Update useRecordPromptUsage to refresh client caches
- Replace console.error with logger.error
* refactor(PromptForm): remove console warning for unselected prompt in VersionsPanel
* refactor(Prompts): improve error handling for groupId and streamline usage tracking
* refactor(.gitignore): add CLAUDE.md to ignore list
* refactor(Prompts): streamline prompt components by removing unused variables and enhancing props structure
* refactor(Prompts): fix sort stability, keyboard handling, and remove dead code
Add _id tiebreaker to prompt group sort pipelines for deterministic
pagination ordering. Prevent default browser scroll on Space key in
PromptEditor preview mode. Remove unused blurTimeoutRef and its
onMutate callback from DashGroupItem.
* refactor(Prompts): enhance groupId validation and improve prompt group aggregation handling
* fix: aria-hidden, API fixes, accessibility improvements
* fix: ACL author filter, mobile guard, semantic HTML, and add useFocusTrap hook
- Remove author filter from patchPromptGroup so ACL-granted editors
can update prompt groups (aligns with deletePromptGroupController)
- Add missing group guard to mobile HeaderActions in PromptForm
- Replace div with article in DashGroupItem, remove redundant
stopPropagation and onClick on outer container
- Add useFocusTrap hook for keyboard focus management
- Add numberOfGenerations to default projection
- Deduplicate ObjectId validation, remove console.warn,
fix aria-labelledby, localize search announcements
* refactor(Prompts): adjust UI and improve a11y
* refactor(Prompts): reorder imports for consistency and clarity
* refactor(Prompts): implement updateFieldsInPlace for efficient data updates and add related tests
* refactor(Prompts): reorder imports to include updateFieldsInPlace for better organization
* refactor(Prompts): enhance DashGroupItem with toast notifications for prompt updates and add click-to-edit functionality in PromptEditor
* style: use self-closing TooltipAnchor in CreatePromptButton
Replace ></TooltipAnchor> with /> for consistency with the rest of the Prompts directory.
* fix(i18n): replace placeholder text for com_ui_global_group translation key
The value was left as 'something needs to go here. was empty' which
would be visible to users as an aria-label in DashGroupItem.
* fix(DashGroupItem): sync rename input with group.name on external changes
nameInputValue was initialized via useState(group.name) but never
synced when group.name changed from a background refetch. Added
useEffect that updates the input when the dialog is closed.
* perf(useFocusTrap): store onEscape in ref to avoid listener churn
onEscape was in the useEffect dependency array, causing the keydown
listener to be torn down and re-attached on every render when callers
passed an inline function. Now stored in a ref so the effect only
re-runs when active or containerRef changes.
* fix(a11y): replace role=button div with layered button overlay in ListCard
The card used role='button' on a div that contained nested Button
elements — an invalid ARIA pattern. Replaced with a hidden button
at z-0 for the card action while child interactive elements sit
at z-10, eliminating nested interactive element violations.
* fix(PromptForm): reset selectionIndex on route change, guard auto-save, and fix a11y
- Reset selectionIndex to 0 and isEditing to false when promptId
changes, preventing out-of-bounds index when navigating between
groups with different version counts.
- Track selectedPrompt in a ref so the auto-save effect doesn't
fire against a stale prompt when the selection changed mid-edit.
- Stabilize useFocusTrap onEscape via useCallback to avoid
unnecessary listener re-attachment.
- Conditionally render mobile overlay instead of always-present
button with aria-hidden/pointer-events toggling.
* refactor: extract isValidObjectIdString to shared utility in data-schemas
The same regex helper was duplicated in api/server/routes/prompts.js
and packages/data-schemas/src/methods/prompt.ts. Moved to
packages/data-schemas/src/utils/objectId.ts and imported from both
consumers. Also removed a duplicate router.use block introduced
during the extraction.
* perf(updateFieldsInPlace): replace JSON deep clone with targeted spread
Instead of JSON.parse(JSON.stringify(data)) which serializes the
entire paginated data structure, use targeted immutable spreads
that only copy the affected page and collection array. Returns the
original data reference unchanged when the item is not found.
* perf(VariablesDropdown): memoize items array and stabilize handleAddVariable
The items array containing JSX elements was rebuilt on every render.
Wrapped in useMemo keyed on usedVariables and localize. Also wrapped
handleAddVariable in useCallback and memoized usedCount to avoid
redundant array filtering.
* perf(DashGroupItem): stabilize mutation callbacks via refs
handleSaveRename and handleDelete had updateGroup/deleteGroup mutation
objects in their useCallback dependency arrays. Since mutation objects
are new references each render, the callbacks were recreated every
render, defeating memoization. Now store mutation objects in refs and
call via ref.current in the callbacks.
* fix(security): validate groupId in incrementPromptGroupUsage
The data-schema method passed the groupId string directly to
findByIdAndUpdate without validation. If called from a different
entrypoint without the route-level check, Mongoose would throw a
CastError. Now validates with isValidObjectIdString before the
DB call and throws a clean 'Invalid groupId' error.
* fix(security): add rate limiter to prompt usage tracking endpoint
POST /groups/:groupId/use had no rate limiting — a user could spam
it to inflate numberOfGenerations, which controls sort order for all
users. Added promptUsageLimiter (30 req/user/min) following the same
pattern as toolCallLimiter. Also handle 'Invalid groupId' error from
the data layer in the route error handler.
* fix(updateFieldsInPlace): guard against undefined identifier value
If updatedItem[identifierField] is null/undefined, findIndex could
match unintended items where that field is also undefined. Added
early return when the identifier value is nullish.
* fix(a11y): use React useId for stable unique IDs in ListCard
aria-describedby/id values were derived from prompt name which can
contain spaces and special characters, producing invalid HTML IDs
and potential collisions. Now uses React.useId() for guaranteed
unique, valid IDs per component instance.
* fix: Align prompts panel styling with other sidebar panels and fix test
- Match FilterPrompts first row to Memory/Bookmark pattern (items-center gap-2)
- Remove items-stretch override from PromptsAccordion
- Add missing promptUsageLimiter mock to prompts route test
* fix: Address code review findings for prompts refactor PR
- Fix #5: Gate DeletePrompt in HeaderActions behind canDelete permission
- Fix #8: BackToChat navigates to last conversation instead of /c/new
- Fix #7: Restore useLiveAnnouncer for screen reader feedback on delete/rename
- Fix #1: Use isPublic (set by API) instead of deprecated projectIds for globe icon
- Fix #4: Optimistic cache update in useRecordPromptUsage instead of full invalidation
- Fix #6: Add migration to drop superseded { createdAt, updatedAt } compound index
- Fix #9: Single-pass reduce in PromptVariables instead of triple filter
- Fix #10: Rename PromptLabelsForm internal component to avoid collision with PromptForm
- Fix #14: Remove redundant aria-label from aria-hidden Checkbox in AutoSendPrompt
* fix: Align prompts panel filter row element sizes with other panels
- Override Dropdown trigger to size-9 (36px) to match FilterInput height
- Set CreatePromptButton to size-9 shrink-0 bg-transparent matching
Memory/Bookmark panel button pattern
* fix(prompts): Shared Prompts filter ignores direct shares, only returns PUBLIC
Folds fix from PR #11882 into the refactored codebase.
Bug A: filterAccessibleIdsBySharedLogic now accepts ownedPromptGroupIds:
- MY_PROMPTS: accessible intersect owned
- SHARED_PROMPTS: (accessible union public) minus owned
- ALL: accessible union public (deduplicated)
Legacy fallback preserved when ownedPromptGroupIds is omitted.
Bug B: getPromptGroup uses $lookup aggregation to populate productionPrompt,
fixing empty text on direct URL navigation to shared prompts.
Also adds getOwnedPromptGroupIds to data-schemas methods and passes it
from both /all and /groups route handlers.
* fix: Add missing canDelete to mobile HeaderActions, remove dead instanceProjectId prop
- Pass canDelete to mobile HeaderActions row (was only on desktop)
- Remove instanceProjectId prop from ChatGroupItem and DashGroupItem
since global check now uses group.isPublic
- Remove useGetStartupConfig from List.tsx (no longer needed)
* fix: Use runtime ObjectId instead of type-only Types.ObjectId, fix i18next interpolation
- getPromptGroup and getOwnedPromptGroupIds were using Types.ObjectId
(imported as type-only), which is erased at compile time. Use the
runtime ObjectId from mongoose.Types (already destructured at line 20).
This fixes the 404s in PATCH /groups/:groupId tests.
- Fix com_ui_prompt_deleted_group translation to use {{0}} (i18next
double-brace syntax) instead of {0}.
* chore: Fix translation key ordering, add sideEffects: false to data-provider
- Reorder new translation keys to maintain alphabetical order:
com_ui_click_to_edit, com_ui_labels, com_ui_live, com_ui_prompt_delete_confirm,
com_ui_prompt_deleted_group, com_ui_prompt_details, com_ui_prompt_renamed,
com_ui_prompt_update_error, com_ui_prompt_variables_list
- Add "sideEffects": false to librechat-data-provider package.json to
enable tree-shaking of unused exports (types, constants, pure functions)
* fix: Reduce prompts panel spacing, align memory toggle with checkbox pattern
- Remove unnecessary wrapper div around AutoSendPrompt in PromptsAccordion,
reducing vertical space between the toggle and the first prompt item
- Replace Memory panel's Switch toggle with Checkbox+Button pattern
matching the prompts panel's AutoSendPrompt for visual consistency
* fix: Reduce gap between AutoSendPrompt and first prompt item
Change ChatGroupItem margin from my-2 to mb-2 to eliminate the
doubled spacing (gap-2 from parent + top margin from first item).
Restore wrapper div around AutoSendPrompt for right-alignment.
* fix: Restore prompt name on empty save, remove dead bodyProps from checkGlobalPromptShare
- PromptName: reset newName to name when save is cancelled due to empty
or unchanged input, preventing blank title in read mode
- checkGlobalPromptShare: remove dead bodyProps config — Permissions.SHARE
was not in the permissions array so the bodyProps rule was never evaluated.
Per-resource share checks are handled by canAccessPromptGroupResource.
---------
Co-authored-by: Danny Avila <danny@librechat.ai>
2026-03-22 21:56:22 +01:00
|
|
|
promptUsageLimiter: (req, res, next) => next(),
|
2025-07-26 12:28:31 -04:00
|
|
|
canAccessPromptViaGroup: jest.requireActual('~/server/middleware').canAccessPromptViaGroup,
|
|
|
|
|
canAccessPromptGroupResource:
|
|
|
|
|
jest.requireActual('~/server/middleware').canAccessPromptGroupResource,
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
let app;
|
|
|
|
|
let mongoServer;
|
|
|
|
|
let promptRoutes;
|
📜 feat: Implement System Grants for Capability-Based Authorization (#11896)
* feat: Implement System Grants for Role-Based Capabilities
- Added a new `systemGrant` model and associated methods to manage role-based capabilities within the application.
- Introduced middleware functions `hasCapability` and `requireCapability` to check user permissions based on their roles.
- Updated the database seeding process to include system grants for the ADMIN role, ensuring all necessary capabilities are assigned on startup.
- Enhanced type definitions and schemas to support the new system grant functionality, improving overall type safety and clarity in the codebase.
* test: Add unit tests for capabilities middleware and system grant methods
- Introduced comprehensive unit tests for the capabilities middleware, including `hasCapability` and `requireCapability`, ensuring proper permission checks based on user roles.
- Added tests for the `SystemGrant` methods, verifying the seeding of system grants, capability granting, and revocation processes.
- Enhanced test coverage for edge cases, including idempotency of grant operations and handling of unexpected errors in middleware.
- Utilized mocks for database interactions to isolate tests and improve reliability.
* refactor: Transition to Capability-Based Access Control
- Replaced role-based access checks with capability-based checks across various middleware and routes, enhancing permission management.
- Introduced `hasCapability` and `requireCapability` functions to streamline capability verification for user actions.
- Updated relevant routes and middleware to utilize the new capability system, ensuring consistent permission enforcement.
- Enhanced type definitions and added tests for the new capability functions, improving overall code reliability and maintainability.
* test: Enhance capability-based access tests for ADMIN role
- Updated tests to reflect the new capability-based access control, specifically for the ADMIN role.
- Modified test descriptions to clarify that users with the MANAGE_AGENTS capability can bypass permission checks.
- Seeded capabilities for the ADMIN role in multiple test files to ensure consistent permission checks across different routes and middleware.
- Improved overall test coverage for capability verification, ensuring robust permission management.
* test: Update capability tests for MCP server access
- Renamed test to reflect the correct capability for bypassing permission checks, changing from MANAGE_AGENTS to MANAGE_MCP_SERVERS.
- Updated seeding of capabilities for the ADMIN role to align with the new capability structure.
- Ensured consistency in capability definitions across tests and middleware for improved permission management.
* feat: Add hasConfigCapability for enhanced config access control
- Introduced `hasConfigCapability` function to check user permissions for managing or reading specific config sections.
- Updated middleware to export the new capability function, ensuring consistent access control across the application.
- Enhanced unit tests to cover various scenarios for the new capability, improving overall test coverage and reliability.
* fix: Update tenantId filter in createSystemGrantMethods
- Added a condition to set tenantId filter to { $exists: false } when tenantId is null, ensuring proper handling of cases where tenantId is not provided.
- This change improves the robustness of the system grant methods by explicitly managing the absence of tenantId in the filter logic.
* fix: account deletion capability check
- Updated the `canDeleteAccount` middleware to ensure that the `hasManageUsers` capability check only occurs if a user is present, preventing potential errors when the user object is undefined.
- This change improves the robustness of the account deletion logic by ensuring proper handling of user permissions.
* refactor: Optimize seeding of system grants for ADMIN role
- Replaced sequential capability granting with parallel execution using Promise.all in the seedSystemGrants function.
- This change improves performance and efficiency during the initialization of system grants, ensuring all capabilities are granted concurrently.
* refactor: Simplify systemGrantSchema index definition
- Removed the sparse option from the unique index on principalType, principalId, capability, and tenantId in the systemGrantSchema.
- This change streamlines the index definition, potentially improving query performance and clarity in the schema design.
* refactor: Reorganize role capability check in roles route
- Moved the capability check for reading roles to occur after parsing the roleName, improving code clarity and structure.
- This change ensures that the authorization logic is consistently applied before fetching role details, enhancing overall permission management.
* refactor: Remove unused ISystemGrant interface from systemCapabilities.ts
- Deleted the ISystemGrant interface as it was no longer needed, streamlining the code and improving clarity.
- This change helps reduce clutter in the file and focuses on relevant capabilities for the system.
* refactor: Migrate SystemCapabilities to data-schemas
- Replaced imports of SystemCapabilities from 'librechat-data-provider' with imports from '@librechat/data-schemas' across multiple files.
- This change centralizes the management of system capabilities, improving code organization and maintainability.
* refactor: Update account deletion middleware and capability checks
- Modified the `canDeleteAccount` middleware to ensure that the account deletion permission is only granted to users with the `MANAGE_USERS` capability, improving security and clarity in permission management.
- Enhanced error logging for unauthorized account deletion attempts, providing better insights into permission issues.
- Updated the `capabilities.ts` file to ensure consistent handling of user authentication checks, improving robustness in capability verification.
- Refined type definitions in `systemGrant.ts` and `systemGrantMethods.ts` to utilize the `PrincipalType` enum, enhancing type safety and code clarity.
* refactor: Extract principal ID normalization into a separate function
- Introduced `normalizePrincipalId` function to streamline the normalization of principal IDs based on their type, enhancing code clarity and reusability.
- Updated references in `createSystemGrantMethods` to utilize the new normalization function, improving maintainability and reducing code duplication.
* test: Add unit tests for principalId normalization in systemGrant
- Introduced tests for the `grantCapability`, `revokeCapability`, and `getCapabilitiesForPrincipal` methods to verify correct handling of principalId normalization between string and ObjectId formats.
- Enhanced the `capabilities.ts` middleware to utilize the `PrincipalType` enum for improved type safety.
- Added a new utility function `normalizePrincipalId` to streamline principal ID normalization logic, ensuring consistent behavior across the application.
* feat: Introduce capability implications and enhance system grant methods
- Added `CapabilityImplications` to define relationships between broader and implied capabilities, allowing for more intuitive permission checks.
- Updated `createSystemGrantMethods` to expand capability queries to include implied capabilities, improving authorization logic.
- Enhanced `systemGrantSchema` to include an `expiresAt` field for future TTL enforcement of grants, and added validation to ensure `tenantId` is not set to null.
- Documented authorization requirements for prompt group and prompt deletion methods to clarify access control expectations.
* test: Add unit tests for canDeleteAccount middleware
- Introduced unit tests for the `canDeleteAccount` middleware to verify account deletion permissions based on user roles and capabilities.
- Covered scenarios for both allowed and blocked account deletions, including checks for ADMIN users with the `MANAGE_USERS` capability and handling of undefined user cases.
- Enhanced test structure to ensure clarity and maintainability of permission checks in the middleware.
* fix: Add principalType enum validation to SystemGrant schema
Without enum validation, any string value was accepted for principalType
and silently stored. Invalid documents would never match capability
queries, creating phantom grants impossible to diagnose without raw DB
inspection. All other ACL models in the codebase validate this field.
* fix: Replace seedSystemGrants Promise.all with bulkWrite for concurrency safety
When two server instances start simultaneously (K8s rolling deploy, PM2
cluster), both call seedSystemGrants. With Promise.all + findOneAndUpdate
upsert, both instances may attempt to insert the same documents, causing
E11000 duplicate key errors that crash server startup.
bulkWrite with ordered:false handles concurrent upserts gracefully and
reduces 17 individual round trips to a single network call. The returned
documents (previously discarded) are no longer fetched.
* perf: Add AsyncLocalStorage per-request cache for capability checks
Every hasCapability call previously required 2 DB round trips
(getUserPrincipals + SystemGrant.exists) — replacing what were O(1)
string comparisons. Routes like patchPromptGroup triggered this twice,
and hasConfigCapability's fallback path resolved principals twice.
This adds a per-request AsyncLocalStorage cache that:
- Caches resolved principals (same for all checks within one request)
- Caches capability check results (same user+cap = same answer)
- Automatically scoped to request lifetime (no stale grants)
- Falls through to DB when no store exists (background jobs, tests)
- Requires no signature changes to hasCapability
The capabilityContextMiddleware is registered at the app level before
all routes, initializing a fresh store per request.
* fix: Add error handling for inline hasCapability calls
canDeleteAccount, fetchAssistants, and validateAuthor all call
hasCapability without try-catch. These were previously O(1) string
comparisons that could never throw. Now they hit the database and can
fail on connection timeout or transient errors.
Wrap each call in try-catch, defaulting to deny (false) on error.
This ensures a DB hiccup returns a clean 403 instead of an unhandled
500 with a stack trace.
* test: Add canDeleteAccount DB-error resilience test
Tests that hasCapability rejection (e.g., DB timeout) results in a clean
403 rather than an unhandled exception. Validates the error handling
added in the previous commit.
* refactor: Use barrel import for hasCapability in validateAuthor
Import from ~/server/middleware barrel instead of directly from
~/server/middleware/roles/capabilities for consistency with other
non-middleware consumers. Files within the middleware barrel itself
must continue using direct imports to avoid circular requires.
* refactor: Remove misleading pre('save') hook from SystemGrant schema
The pre('save') hook normalized principalId for USER/GROUP principals,
but the primary write path (grantCapability) uses findOneAndUpdate —
which does not trigger save hooks. The normalization was already handled
explicitly in grantCapability itself. The hook created a false impression
of schema-level enforcement that only covered save()/create() paths.
Replace with a comment documenting that all writes must go through
grantCapability.
* feat: Add READ_ASSISTANTS capability to complete manage/read pair
Every other managed resource had a paired READ_X / MANAGE_X capability
except assistants. This adds READ_ASSISTANTS and registers the
MANAGE_ASSISTANTS → READ_ASSISTANTS implication in CapabilityImplications,
enabling future read-only assistant visibility grants.
* chore: Reorder systemGrant methods for clarity
Moved hasCapabilityForPrincipals to a more logical position in the returned object of createSystemGrantMethods, improving code readability. This change also maintains the inclusion of seedSystemGrants in the export, ensuring all necessary methods are available.
* fix: Wrap seedSystemGrants in try-catch to avoid blocking startup
Seeding capabilities is idempotent and will succeed on the next restart.
A transient DB error during seeding should not prevent the server from
starting — log the error and continue.
* refactor: Improve capability check efficiency and add audit logging
Move hasCapability calls after cheap early-exits in validateAuthor and
fetchAssistants so the DB check only runs when its result matters. Add
logger.debug on every capability bypass grant across all 7 call sites
for auditability, and log errors in catch blocks instead of silently
swallowing them.
* test: Add integration tests for AsyncLocalStorage capability caching
Exercises the full vertical — ALS context, generateCapabilityCheck,
real getUserPrincipals, real hasCapabilityForPrincipals, real MongoDB
via MongoMemoryServer. Covers per-request caching, cross-context
isolation, concurrent request isolation, negative caching, capability
implications, tenant scoping, group-based grants, and requireCapability
middleware.
* test: Add systemGrant data-layer and ALS edge-case integration tests
systemGrant.spec.ts (51 tests): Full integration tests for all
systemGrant methods against real MongoDB — grant/revoke lifecycle,
principalId normalization (string→ObjectId for USER/GROUP, string for
ROLE), capability implications (both directions), tenant scoping,
schema validation (null tenantId, invalid enum, required fields,
unique compound index).
capabilities.integration.spec.ts (27 tests): Adds ALS edge cases —
missing context degrades gracefully with no caching (background jobs,
child processes), nested middleware creates independent inner context,
optional-chaining safety when store is undefined, mid-request grant
changes are invisible due to result caching, requireCapability works
without ALS, and interleaved concurrent contexts maintain isolation.
* fix: Add worker thread guards to capability ALS usage
Detect when hasCapability or capabilityContextMiddleware is called from
a worker thread (where ALS context does not propagate from the parent).
hasCapability logs a warn-once per factory instance; the middleware logs
an error since mounting Express middleware in a worker is likely a
misconfiguration. Both continue to function correctly — the guard is
observability, not a hard block.
* fix: Include tenantId in ALS principal cache key for tenant isolation
The principal cache key was user.id:user.role, which would reuse
cached principals across tenants for the same user within a request.
When getUserPrincipals gains tenant-scoped group resolution, principals
from tenant-a would incorrectly serve tenant-b checks. Changed to
user.id:user.role:user.tenantId to prevent cross-tenant cache hits.
Adds integration test proving separate principal lookups per tenantId.
* test: Remove redundant mocked capabilities.spec.js
The JS wrapper test (7 tests, all mocked) is a strict subset of
capabilities.integration.spec.ts (28 tests, real MongoDB). Every
scenario it covered — hasCapability true/false, tenantId passthrough,
requireCapability 403/500, error handling — is tested with higher
fidelity in the integration suite.
* test: Replace mocked canDeleteAccount tests with real MongoDB integration
Remove hasCapability mock — tests now exercise the full capability
chain against real MongoDB (getUserPrincipals, hasCapabilityForPrincipals,
SystemGrant collection). Only mocks remaining are logger and cache.
Adds new coverage: admin role without grant is blocked, user-level
grant bypasses deletion restriction, null user handling.
* test: Add comprehensive tests for ACL entry management and user group methods
Introduces new tests for `deleteAclEntries`, `bulkWriteAclEntries`, and `findPublicResourceIds` in `aclEntry.spec.ts`, ensuring proper functionality for deleting and bulk managing ACL entries. Additionally, enhances `userGroup.spec.ts` with tests for finding groups by ID and name pattern, including external ID matching and source filtering. These changes improve coverage and validate the integrity of ACL and user group operations against real MongoDB interactions.
* refactor: Update capability checks and logging for better clarity and error handling
Replaced `MANAGE_USERS` with `ACCESS_ADMIN` in the `canDeleteAccount` middleware and related tests to align with updated permission structure. Enhanced logging in various middleware functions to use `logger.warn` for capability check failures, providing clearer error messages. Additionally, refactored capability checks in the `patchPromptGroup` and `validateAuthor` functions to improve readability and maintainability. This commit also includes adjustments to the `systemGrant` methods to implement retry logic for transient failures during capability seeding, ensuring robustness in the face of database errors.
* refactor: Enhance logging and retry logic in seedSystemGrants method
Updated the logging format in the seedSystemGrants method to include error messages for better clarity. Improved the retry mechanism by explicitly mocking multiple failures in tests, ensuring robust error handling during transient database issues. Additionally, refined imports in the systemGrant schema for better type management.
* refactor: Consolidate imports in canDeleteAccount middleware
Merged logger and SystemCapabilities imports from the data-schemas module into a single line for improved readability and maintainability of the code. This change streamlines the import statements in the canDeleteAccount middleware.
* test: Enhance systemGrant tests for error handling and capability validation
Added tests to the systemGrant methods to handle various error scenarios, including E11000 race conditions, invalid ObjectId strings for USER and GROUP principals, and invalid capability strings. These enhancements improve the robustness of the capability granting and revoking logic, ensuring proper error propagation and validation of inputs.
* fix: Wrap hasCapability calls in deny-by-default try-catch at remaining sites
canAccessResource, files.js, and roles.js all had hasCapability inside
outer try-catch blocks that returned 500 on DB failure instead of
falling through to the regular ACL check. This contradicts the
deny-by-default pattern used everywhere else.
Also removes raw error.message from the roles.js 500 response to
prevent internal host/connection info leaking to clients.
* fix: Normalize user ID in canDeleteAccount before passing to hasCapability
requireCapability normalizes req.user.id via _id?.toString() fallback,
but canDeleteAccount passed raw req.user directly. If req.user.id is
absent (some auth layers only populate _id), getUserPrincipals received
undefined, silently returning empty principals and blocking the bypass.
* fix: Harden systemGrant schema and type safety
- Reject empty string tenantId in schema validator (was only blocking
null; empty string silently orphaned documents)
- Fix reverseImplications to use BaseSystemCapability[] instead of
string[], preserving the narrow discriminated type
- Document READ_ASSISTANTS as reserved/unenforced
* test: Use fake timers for seedSystemGrants retry tests and add tenantId validation
- Switch retry tests to jest.useFakeTimers() to eliminate 3+ seconds
of real setTimeout delays per test run
- Add regression test for empty-string tenantId rejection
* docs: Add TODO(#12091) comments for tenant-scoped capability gaps
In multi-tenant mode, platform-level grants (no tenantId) won't match
tenant-scoped queries, breaking admin access. getUserPrincipals also
returns cross-tenant group memberships. Both need fixes in #12091.
2026-03-07 13:56:32 -05:00
|
|
|
let Prompt, PromptGroup, AclEntry, AccessRole, User, SystemGrant;
|
2025-07-26 12:28:31 -04:00
|
|
|
let testUsers, testRoles;
|
|
|
|
|
let grantPermission;
|
2025-08-30 15:58:49 -04:00
|
|
|
let currentTestUser; // Track current user for middleware
|
2025-07-26 12:28:31 -04:00
|
|
|
|
|
|
|
|
// Helper function to set user in middleware
|
|
|
|
|
function setTestUser(app, user) {
|
2025-08-30 15:58:49 -04:00
|
|
|
currentTestUser = user;
|
2025-07-26 12:28:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
beforeAll(async () => {
|
|
|
|
|
mongoServer = await MongoMemoryServer.create();
|
|
|
|
|
const mongoUri = mongoServer.getUri();
|
|
|
|
|
await mongoose.connect(mongoUri);
|
|
|
|
|
|
|
|
|
|
// Initialize models
|
|
|
|
|
const dbModels = require('~/db/models');
|
|
|
|
|
Prompt = dbModels.Prompt;
|
|
|
|
|
PromptGroup = dbModels.PromptGroup;
|
|
|
|
|
AclEntry = dbModels.AclEntry;
|
|
|
|
|
AccessRole = dbModels.AccessRole;
|
|
|
|
|
User = dbModels.User;
|
📜 feat: Implement System Grants for Capability-Based Authorization (#11896)
* feat: Implement System Grants for Role-Based Capabilities
- Added a new `systemGrant` model and associated methods to manage role-based capabilities within the application.
- Introduced middleware functions `hasCapability` and `requireCapability` to check user permissions based on their roles.
- Updated the database seeding process to include system grants for the ADMIN role, ensuring all necessary capabilities are assigned on startup.
- Enhanced type definitions and schemas to support the new system grant functionality, improving overall type safety and clarity in the codebase.
* test: Add unit tests for capabilities middleware and system grant methods
- Introduced comprehensive unit tests for the capabilities middleware, including `hasCapability` and `requireCapability`, ensuring proper permission checks based on user roles.
- Added tests for the `SystemGrant` methods, verifying the seeding of system grants, capability granting, and revocation processes.
- Enhanced test coverage for edge cases, including idempotency of grant operations and handling of unexpected errors in middleware.
- Utilized mocks for database interactions to isolate tests and improve reliability.
* refactor: Transition to Capability-Based Access Control
- Replaced role-based access checks with capability-based checks across various middleware and routes, enhancing permission management.
- Introduced `hasCapability` and `requireCapability` functions to streamline capability verification for user actions.
- Updated relevant routes and middleware to utilize the new capability system, ensuring consistent permission enforcement.
- Enhanced type definitions and added tests for the new capability functions, improving overall code reliability and maintainability.
* test: Enhance capability-based access tests for ADMIN role
- Updated tests to reflect the new capability-based access control, specifically for the ADMIN role.
- Modified test descriptions to clarify that users with the MANAGE_AGENTS capability can bypass permission checks.
- Seeded capabilities for the ADMIN role in multiple test files to ensure consistent permission checks across different routes and middleware.
- Improved overall test coverage for capability verification, ensuring robust permission management.
* test: Update capability tests for MCP server access
- Renamed test to reflect the correct capability for bypassing permission checks, changing from MANAGE_AGENTS to MANAGE_MCP_SERVERS.
- Updated seeding of capabilities for the ADMIN role to align with the new capability structure.
- Ensured consistency in capability definitions across tests and middleware for improved permission management.
* feat: Add hasConfigCapability for enhanced config access control
- Introduced `hasConfigCapability` function to check user permissions for managing or reading specific config sections.
- Updated middleware to export the new capability function, ensuring consistent access control across the application.
- Enhanced unit tests to cover various scenarios for the new capability, improving overall test coverage and reliability.
* fix: Update tenantId filter in createSystemGrantMethods
- Added a condition to set tenantId filter to { $exists: false } when tenantId is null, ensuring proper handling of cases where tenantId is not provided.
- This change improves the robustness of the system grant methods by explicitly managing the absence of tenantId in the filter logic.
* fix: account deletion capability check
- Updated the `canDeleteAccount` middleware to ensure that the `hasManageUsers` capability check only occurs if a user is present, preventing potential errors when the user object is undefined.
- This change improves the robustness of the account deletion logic by ensuring proper handling of user permissions.
* refactor: Optimize seeding of system grants for ADMIN role
- Replaced sequential capability granting with parallel execution using Promise.all in the seedSystemGrants function.
- This change improves performance and efficiency during the initialization of system grants, ensuring all capabilities are granted concurrently.
* refactor: Simplify systemGrantSchema index definition
- Removed the sparse option from the unique index on principalType, principalId, capability, and tenantId in the systemGrantSchema.
- This change streamlines the index definition, potentially improving query performance and clarity in the schema design.
* refactor: Reorganize role capability check in roles route
- Moved the capability check for reading roles to occur after parsing the roleName, improving code clarity and structure.
- This change ensures that the authorization logic is consistently applied before fetching role details, enhancing overall permission management.
* refactor: Remove unused ISystemGrant interface from systemCapabilities.ts
- Deleted the ISystemGrant interface as it was no longer needed, streamlining the code and improving clarity.
- This change helps reduce clutter in the file and focuses on relevant capabilities for the system.
* refactor: Migrate SystemCapabilities to data-schemas
- Replaced imports of SystemCapabilities from 'librechat-data-provider' with imports from '@librechat/data-schemas' across multiple files.
- This change centralizes the management of system capabilities, improving code organization and maintainability.
* refactor: Update account deletion middleware and capability checks
- Modified the `canDeleteAccount` middleware to ensure that the account deletion permission is only granted to users with the `MANAGE_USERS` capability, improving security and clarity in permission management.
- Enhanced error logging for unauthorized account deletion attempts, providing better insights into permission issues.
- Updated the `capabilities.ts` file to ensure consistent handling of user authentication checks, improving robustness in capability verification.
- Refined type definitions in `systemGrant.ts` and `systemGrantMethods.ts` to utilize the `PrincipalType` enum, enhancing type safety and code clarity.
* refactor: Extract principal ID normalization into a separate function
- Introduced `normalizePrincipalId` function to streamline the normalization of principal IDs based on their type, enhancing code clarity and reusability.
- Updated references in `createSystemGrantMethods` to utilize the new normalization function, improving maintainability and reducing code duplication.
* test: Add unit tests for principalId normalization in systemGrant
- Introduced tests for the `grantCapability`, `revokeCapability`, and `getCapabilitiesForPrincipal` methods to verify correct handling of principalId normalization between string and ObjectId formats.
- Enhanced the `capabilities.ts` middleware to utilize the `PrincipalType` enum for improved type safety.
- Added a new utility function `normalizePrincipalId` to streamline principal ID normalization logic, ensuring consistent behavior across the application.
* feat: Introduce capability implications and enhance system grant methods
- Added `CapabilityImplications` to define relationships between broader and implied capabilities, allowing for more intuitive permission checks.
- Updated `createSystemGrantMethods` to expand capability queries to include implied capabilities, improving authorization logic.
- Enhanced `systemGrantSchema` to include an `expiresAt` field for future TTL enforcement of grants, and added validation to ensure `tenantId` is not set to null.
- Documented authorization requirements for prompt group and prompt deletion methods to clarify access control expectations.
* test: Add unit tests for canDeleteAccount middleware
- Introduced unit tests for the `canDeleteAccount` middleware to verify account deletion permissions based on user roles and capabilities.
- Covered scenarios for both allowed and blocked account deletions, including checks for ADMIN users with the `MANAGE_USERS` capability and handling of undefined user cases.
- Enhanced test structure to ensure clarity and maintainability of permission checks in the middleware.
* fix: Add principalType enum validation to SystemGrant schema
Without enum validation, any string value was accepted for principalType
and silently stored. Invalid documents would never match capability
queries, creating phantom grants impossible to diagnose without raw DB
inspection. All other ACL models in the codebase validate this field.
* fix: Replace seedSystemGrants Promise.all with bulkWrite for concurrency safety
When two server instances start simultaneously (K8s rolling deploy, PM2
cluster), both call seedSystemGrants. With Promise.all + findOneAndUpdate
upsert, both instances may attempt to insert the same documents, causing
E11000 duplicate key errors that crash server startup.
bulkWrite with ordered:false handles concurrent upserts gracefully and
reduces 17 individual round trips to a single network call. The returned
documents (previously discarded) are no longer fetched.
* perf: Add AsyncLocalStorage per-request cache for capability checks
Every hasCapability call previously required 2 DB round trips
(getUserPrincipals + SystemGrant.exists) — replacing what were O(1)
string comparisons. Routes like patchPromptGroup triggered this twice,
and hasConfigCapability's fallback path resolved principals twice.
This adds a per-request AsyncLocalStorage cache that:
- Caches resolved principals (same for all checks within one request)
- Caches capability check results (same user+cap = same answer)
- Automatically scoped to request lifetime (no stale grants)
- Falls through to DB when no store exists (background jobs, tests)
- Requires no signature changes to hasCapability
The capabilityContextMiddleware is registered at the app level before
all routes, initializing a fresh store per request.
* fix: Add error handling for inline hasCapability calls
canDeleteAccount, fetchAssistants, and validateAuthor all call
hasCapability without try-catch. These were previously O(1) string
comparisons that could never throw. Now they hit the database and can
fail on connection timeout or transient errors.
Wrap each call in try-catch, defaulting to deny (false) on error.
This ensures a DB hiccup returns a clean 403 instead of an unhandled
500 with a stack trace.
* test: Add canDeleteAccount DB-error resilience test
Tests that hasCapability rejection (e.g., DB timeout) results in a clean
403 rather than an unhandled exception. Validates the error handling
added in the previous commit.
* refactor: Use barrel import for hasCapability in validateAuthor
Import from ~/server/middleware barrel instead of directly from
~/server/middleware/roles/capabilities for consistency with other
non-middleware consumers. Files within the middleware barrel itself
must continue using direct imports to avoid circular requires.
* refactor: Remove misleading pre('save') hook from SystemGrant schema
The pre('save') hook normalized principalId for USER/GROUP principals,
but the primary write path (grantCapability) uses findOneAndUpdate —
which does not trigger save hooks. The normalization was already handled
explicitly in grantCapability itself. The hook created a false impression
of schema-level enforcement that only covered save()/create() paths.
Replace with a comment documenting that all writes must go through
grantCapability.
* feat: Add READ_ASSISTANTS capability to complete manage/read pair
Every other managed resource had a paired READ_X / MANAGE_X capability
except assistants. This adds READ_ASSISTANTS and registers the
MANAGE_ASSISTANTS → READ_ASSISTANTS implication in CapabilityImplications,
enabling future read-only assistant visibility grants.
* chore: Reorder systemGrant methods for clarity
Moved hasCapabilityForPrincipals to a more logical position in the returned object of createSystemGrantMethods, improving code readability. This change also maintains the inclusion of seedSystemGrants in the export, ensuring all necessary methods are available.
* fix: Wrap seedSystemGrants in try-catch to avoid blocking startup
Seeding capabilities is idempotent and will succeed on the next restart.
A transient DB error during seeding should not prevent the server from
starting — log the error and continue.
* refactor: Improve capability check efficiency and add audit logging
Move hasCapability calls after cheap early-exits in validateAuthor and
fetchAssistants so the DB check only runs when its result matters. Add
logger.debug on every capability bypass grant across all 7 call sites
for auditability, and log errors in catch blocks instead of silently
swallowing them.
* test: Add integration tests for AsyncLocalStorage capability caching
Exercises the full vertical — ALS context, generateCapabilityCheck,
real getUserPrincipals, real hasCapabilityForPrincipals, real MongoDB
via MongoMemoryServer. Covers per-request caching, cross-context
isolation, concurrent request isolation, negative caching, capability
implications, tenant scoping, group-based grants, and requireCapability
middleware.
* test: Add systemGrant data-layer and ALS edge-case integration tests
systemGrant.spec.ts (51 tests): Full integration tests for all
systemGrant methods against real MongoDB — grant/revoke lifecycle,
principalId normalization (string→ObjectId for USER/GROUP, string for
ROLE), capability implications (both directions), tenant scoping,
schema validation (null tenantId, invalid enum, required fields,
unique compound index).
capabilities.integration.spec.ts (27 tests): Adds ALS edge cases —
missing context degrades gracefully with no caching (background jobs,
child processes), nested middleware creates independent inner context,
optional-chaining safety when store is undefined, mid-request grant
changes are invisible due to result caching, requireCapability works
without ALS, and interleaved concurrent contexts maintain isolation.
* fix: Add worker thread guards to capability ALS usage
Detect when hasCapability or capabilityContextMiddleware is called from
a worker thread (where ALS context does not propagate from the parent).
hasCapability logs a warn-once per factory instance; the middleware logs
an error since mounting Express middleware in a worker is likely a
misconfiguration. Both continue to function correctly — the guard is
observability, not a hard block.
* fix: Include tenantId in ALS principal cache key for tenant isolation
The principal cache key was user.id:user.role, which would reuse
cached principals across tenants for the same user within a request.
When getUserPrincipals gains tenant-scoped group resolution, principals
from tenant-a would incorrectly serve tenant-b checks. Changed to
user.id:user.role:user.tenantId to prevent cross-tenant cache hits.
Adds integration test proving separate principal lookups per tenantId.
* test: Remove redundant mocked capabilities.spec.js
The JS wrapper test (7 tests, all mocked) is a strict subset of
capabilities.integration.spec.ts (28 tests, real MongoDB). Every
scenario it covered — hasCapability true/false, tenantId passthrough,
requireCapability 403/500, error handling — is tested with higher
fidelity in the integration suite.
* test: Replace mocked canDeleteAccount tests with real MongoDB integration
Remove hasCapability mock — tests now exercise the full capability
chain against real MongoDB (getUserPrincipals, hasCapabilityForPrincipals,
SystemGrant collection). Only mocks remaining are logger and cache.
Adds new coverage: admin role without grant is blocked, user-level
grant bypasses deletion restriction, null user handling.
* test: Add comprehensive tests for ACL entry management and user group methods
Introduces new tests for `deleteAclEntries`, `bulkWriteAclEntries`, and `findPublicResourceIds` in `aclEntry.spec.ts`, ensuring proper functionality for deleting and bulk managing ACL entries. Additionally, enhances `userGroup.spec.ts` with tests for finding groups by ID and name pattern, including external ID matching and source filtering. These changes improve coverage and validate the integrity of ACL and user group operations against real MongoDB interactions.
* refactor: Update capability checks and logging for better clarity and error handling
Replaced `MANAGE_USERS` with `ACCESS_ADMIN` in the `canDeleteAccount` middleware and related tests to align with updated permission structure. Enhanced logging in various middleware functions to use `logger.warn` for capability check failures, providing clearer error messages. Additionally, refactored capability checks in the `patchPromptGroup` and `validateAuthor` functions to improve readability and maintainability. This commit also includes adjustments to the `systemGrant` methods to implement retry logic for transient failures during capability seeding, ensuring robustness in the face of database errors.
* refactor: Enhance logging and retry logic in seedSystemGrants method
Updated the logging format in the seedSystemGrants method to include error messages for better clarity. Improved the retry mechanism by explicitly mocking multiple failures in tests, ensuring robust error handling during transient database issues. Additionally, refined imports in the systemGrant schema for better type management.
* refactor: Consolidate imports in canDeleteAccount middleware
Merged logger and SystemCapabilities imports from the data-schemas module into a single line for improved readability and maintainability of the code. This change streamlines the import statements in the canDeleteAccount middleware.
* test: Enhance systemGrant tests for error handling and capability validation
Added tests to the systemGrant methods to handle various error scenarios, including E11000 race conditions, invalid ObjectId strings for USER and GROUP principals, and invalid capability strings. These enhancements improve the robustness of the capability granting and revoking logic, ensuring proper error propagation and validation of inputs.
* fix: Wrap hasCapability calls in deny-by-default try-catch at remaining sites
canAccessResource, files.js, and roles.js all had hasCapability inside
outer try-catch blocks that returned 500 on DB failure instead of
falling through to the regular ACL check. This contradicts the
deny-by-default pattern used everywhere else.
Also removes raw error.message from the roles.js 500 response to
prevent internal host/connection info leaking to clients.
* fix: Normalize user ID in canDeleteAccount before passing to hasCapability
requireCapability normalizes req.user.id via _id?.toString() fallback,
but canDeleteAccount passed raw req.user directly. If req.user.id is
absent (some auth layers only populate _id), getUserPrincipals received
undefined, silently returning empty principals and blocking the bypass.
* fix: Harden systemGrant schema and type safety
- Reject empty string tenantId in schema validator (was only blocking
null; empty string silently orphaned documents)
- Fix reverseImplications to use BaseSystemCapability[] instead of
string[], preserving the narrow discriminated type
- Document READ_ASSISTANTS as reserved/unenforced
* test: Use fake timers for seedSystemGrants retry tests and add tenantId validation
- Switch retry tests to jest.useFakeTimers() to eliminate 3+ seconds
of real setTimeout delays per test run
- Add regression test for empty-string tenantId rejection
* docs: Add TODO(#12091) comments for tenant-scoped capability gaps
In multi-tenant mode, platform-level grants (no tenantId) won't match
tenant-scoped queries, breaking admin access. getUserPrincipals also
returns cross-tenant group memberships. Both need fixes in #12091.
2026-03-07 13:56:32 -05:00
|
|
|
SystemGrant = dbModels.SystemGrant;
|
2025-07-26 12:28:31 -04:00
|
|
|
|
|
|
|
|
// Import permission service
|
|
|
|
|
const permissionService = require('~/server/services/PermissionService');
|
|
|
|
|
grantPermission = permissionService.grantPermission;
|
|
|
|
|
|
|
|
|
|
// Create test data
|
|
|
|
|
await setupTestData();
|
|
|
|
|
|
|
|
|
|
// Setup Express app
|
|
|
|
|
app = express();
|
|
|
|
|
app.use(express.json());
|
|
|
|
|
|
2025-08-30 15:58:49 -04:00
|
|
|
// Add user middleware before routes
|
|
|
|
|
app.use((req, res, next) => {
|
|
|
|
|
if (currentTestUser) {
|
|
|
|
|
req.user = {
|
|
|
|
|
...(currentTestUser.toObject ? currentTestUser.toObject() : currentTestUser),
|
|
|
|
|
id: currentTestUser._id.toString(),
|
|
|
|
|
_id: currentTestUser._id,
|
|
|
|
|
name: currentTestUser.name,
|
|
|
|
|
role: currentTestUser.role,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
next();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Set default user
|
|
|
|
|
currentTestUser = testUsers.owner;
|
2025-07-26 12:28:31 -04:00
|
|
|
|
2025-08-30 15:58:49 -04:00
|
|
|
// Import routes after middleware is set up
|
2025-07-26 12:28:31 -04:00
|
|
|
promptRoutes = require('./prompts');
|
|
|
|
|
app.use('/api/prompts', promptRoutes);
|
|
|
|
|
});
|
|
|
|
|
|
2025-08-30 15:58:49 -04:00
|
|
|
afterEach(() => {
|
|
|
|
|
// Always reset to owner user after each test for isolation
|
|
|
|
|
if (currentTestUser !== testUsers.owner) {
|
|
|
|
|
currentTestUser = testUsers.owner;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2025-07-26 12:28:31 -04:00
|
|
|
afterAll(async () => {
|
|
|
|
|
await mongoose.disconnect();
|
|
|
|
|
await mongoServer.stop();
|
|
|
|
|
jest.clearAllMocks();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
async function setupTestData() {
|
|
|
|
|
// Create access roles for promptGroups
|
|
|
|
|
testRoles = {
|
|
|
|
|
viewer: await AccessRole.create({
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_VIEWER,
|
2025-07-26 12:28:31 -04:00
|
|
|
name: 'Viewer',
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
2025-07-26 12:28:31 -04:00
|
|
|
permBits: PermissionBits.VIEW,
|
|
|
|
|
}),
|
|
|
|
|
editor: await AccessRole.create({
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_EDITOR,
|
2025-07-26 12:28:31 -04:00
|
|
|
name: 'Editor',
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
2025-07-26 12:28:31 -04:00
|
|
|
permBits: PermissionBits.VIEW | PermissionBits.EDIT,
|
|
|
|
|
}),
|
|
|
|
|
owner: await AccessRole.create({
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_OWNER,
|
2025-07-26 12:28:31 -04:00
|
|
|
name: 'Owner',
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
2025-07-26 12:28:31 -04:00
|
|
|
permBits:
|
|
|
|
|
PermissionBits.VIEW | PermissionBits.EDIT | PermissionBits.DELETE | PermissionBits.SHARE,
|
|
|
|
|
}),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Create test users
|
|
|
|
|
testUsers = {
|
|
|
|
|
owner: await User.create({
|
|
|
|
|
name: 'Prompt Owner',
|
|
|
|
|
email: 'owner@example.com',
|
|
|
|
|
role: SystemRoles.USER,
|
|
|
|
|
}),
|
|
|
|
|
viewer: await User.create({
|
|
|
|
|
name: 'Prompt Viewer',
|
|
|
|
|
email: 'viewer@example.com',
|
|
|
|
|
role: SystemRoles.USER,
|
|
|
|
|
}),
|
|
|
|
|
editor: await User.create({
|
|
|
|
|
name: 'Prompt Editor',
|
|
|
|
|
email: 'editor@example.com',
|
|
|
|
|
role: SystemRoles.USER,
|
|
|
|
|
}),
|
|
|
|
|
noAccess: await User.create({
|
|
|
|
|
name: 'No Access',
|
|
|
|
|
email: 'noaccess@example.com',
|
|
|
|
|
role: SystemRoles.USER,
|
|
|
|
|
}),
|
|
|
|
|
admin: await User.create({
|
|
|
|
|
name: 'Admin',
|
|
|
|
|
email: 'admin@example.com',
|
|
|
|
|
role: SystemRoles.ADMIN,
|
|
|
|
|
}),
|
|
|
|
|
};
|
|
|
|
|
|
📜 feat: Implement System Grants for Capability-Based Authorization (#11896)
* feat: Implement System Grants for Role-Based Capabilities
- Added a new `systemGrant` model and associated methods to manage role-based capabilities within the application.
- Introduced middleware functions `hasCapability` and `requireCapability` to check user permissions based on their roles.
- Updated the database seeding process to include system grants for the ADMIN role, ensuring all necessary capabilities are assigned on startup.
- Enhanced type definitions and schemas to support the new system grant functionality, improving overall type safety and clarity in the codebase.
* test: Add unit tests for capabilities middleware and system grant methods
- Introduced comprehensive unit tests for the capabilities middleware, including `hasCapability` and `requireCapability`, ensuring proper permission checks based on user roles.
- Added tests for the `SystemGrant` methods, verifying the seeding of system grants, capability granting, and revocation processes.
- Enhanced test coverage for edge cases, including idempotency of grant operations and handling of unexpected errors in middleware.
- Utilized mocks for database interactions to isolate tests and improve reliability.
* refactor: Transition to Capability-Based Access Control
- Replaced role-based access checks with capability-based checks across various middleware and routes, enhancing permission management.
- Introduced `hasCapability` and `requireCapability` functions to streamline capability verification for user actions.
- Updated relevant routes and middleware to utilize the new capability system, ensuring consistent permission enforcement.
- Enhanced type definitions and added tests for the new capability functions, improving overall code reliability and maintainability.
* test: Enhance capability-based access tests for ADMIN role
- Updated tests to reflect the new capability-based access control, specifically for the ADMIN role.
- Modified test descriptions to clarify that users with the MANAGE_AGENTS capability can bypass permission checks.
- Seeded capabilities for the ADMIN role in multiple test files to ensure consistent permission checks across different routes and middleware.
- Improved overall test coverage for capability verification, ensuring robust permission management.
* test: Update capability tests for MCP server access
- Renamed test to reflect the correct capability for bypassing permission checks, changing from MANAGE_AGENTS to MANAGE_MCP_SERVERS.
- Updated seeding of capabilities for the ADMIN role to align with the new capability structure.
- Ensured consistency in capability definitions across tests and middleware for improved permission management.
* feat: Add hasConfigCapability for enhanced config access control
- Introduced `hasConfigCapability` function to check user permissions for managing or reading specific config sections.
- Updated middleware to export the new capability function, ensuring consistent access control across the application.
- Enhanced unit tests to cover various scenarios for the new capability, improving overall test coverage and reliability.
* fix: Update tenantId filter in createSystemGrantMethods
- Added a condition to set tenantId filter to { $exists: false } when tenantId is null, ensuring proper handling of cases where tenantId is not provided.
- This change improves the robustness of the system grant methods by explicitly managing the absence of tenantId in the filter logic.
* fix: account deletion capability check
- Updated the `canDeleteAccount` middleware to ensure that the `hasManageUsers` capability check only occurs if a user is present, preventing potential errors when the user object is undefined.
- This change improves the robustness of the account deletion logic by ensuring proper handling of user permissions.
* refactor: Optimize seeding of system grants for ADMIN role
- Replaced sequential capability granting with parallel execution using Promise.all in the seedSystemGrants function.
- This change improves performance and efficiency during the initialization of system grants, ensuring all capabilities are granted concurrently.
* refactor: Simplify systemGrantSchema index definition
- Removed the sparse option from the unique index on principalType, principalId, capability, and tenantId in the systemGrantSchema.
- This change streamlines the index definition, potentially improving query performance and clarity in the schema design.
* refactor: Reorganize role capability check in roles route
- Moved the capability check for reading roles to occur after parsing the roleName, improving code clarity and structure.
- This change ensures that the authorization logic is consistently applied before fetching role details, enhancing overall permission management.
* refactor: Remove unused ISystemGrant interface from systemCapabilities.ts
- Deleted the ISystemGrant interface as it was no longer needed, streamlining the code and improving clarity.
- This change helps reduce clutter in the file and focuses on relevant capabilities for the system.
* refactor: Migrate SystemCapabilities to data-schemas
- Replaced imports of SystemCapabilities from 'librechat-data-provider' with imports from '@librechat/data-schemas' across multiple files.
- This change centralizes the management of system capabilities, improving code organization and maintainability.
* refactor: Update account deletion middleware and capability checks
- Modified the `canDeleteAccount` middleware to ensure that the account deletion permission is only granted to users with the `MANAGE_USERS` capability, improving security and clarity in permission management.
- Enhanced error logging for unauthorized account deletion attempts, providing better insights into permission issues.
- Updated the `capabilities.ts` file to ensure consistent handling of user authentication checks, improving robustness in capability verification.
- Refined type definitions in `systemGrant.ts` and `systemGrantMethods.ts` to utilize the `PrincipalType` enum, enhancing type safety and code clarity.
* refactor: Extract principal ID normalization into a separate function
- Introduced `normalizePrincipalId` function to streamline the normalization of principal IDs based on their type, enhancing code clarity and reusability.
- Updated references in `createSystemGrantMethods` to utilize the new normalization function, improving maintainability and reducing code duplication.
* test: Add unit tests for principalId normalization in systemGrant
- Introduced tests for the `grantCapability`, `revokeCapability`, and `getCapabilitiesForPrincipal` methods to verify correct handling of principalId normalization between string and ObjectId formats.
- Enhanced the `capabilities.ts` middleware to utilize the `PrincipalType` enum for improved type safety.
- Added a new utility function `normalizePrincipalId` to streamline principal ID normalization logic, ensuring consistent behavior across the application.
* feat: Introduce capability implications and enhance system grant methods
- Added `CapabilityImplications` to define relationships between broader and implied capabilities, allowing for more intuitive permission checks.
- Updated `createSystemGrantMethods` to expand capability queries to include implied capabilities, improving authorization logic.
- Enhanced `systemGrantSchema` to include an `expiresAt` field for future TTL enforcement of grants, and added validation to ensure `tenantId` is not set to null.
- Documented authorization requirements for prompt group and prompt deletion methods to clarify access control expectations.
* test: Add unit tests for canDeleteAccount middleware
- Introduced unit tests for the `canDeleteAccount` middleware to verify account deletion permissions based on user roles and capabilities.
- Covered scenarios for both allowed and blocked account deletions, including checks for ADMIN users with the `MANAGE_USERS` capability and handling of undefined user cases.
- Enhanced test structure to ensure clarity and maintainability of permission checks in the middleware.
* fix: Add principalType enum validation to SystemGrant schema
Without enum validation, any string value was accepted for principalType
and silently stored. Invalid documents would never match capability
queries, creating phantom grants impossible to diagnose without raw DB
inspection. All other ACL models in the codebase validate this field.
* fix: Replace seedSystemGrants Promise.all with bulkWrite for concurrency safety
When two server instances start simultaneously (K8s rolling deploy, PM2
cluster), both call seedSystemGrants. With Promise.all + findOneAndUpdate
upsert, both instances may attempt to insert the same documents, causing
E11000 duplicate key errors that crash server startup.
bulkWrite with ordered:false handles concurrent upserts gracefully and
reduces 17 individual round trips to a single network call. The returned
documents (previously discarded) are no longer fetched.
* perf: Add AsyncLocalStorage per-request cache for capability checks
Every hasCapability call previously required 2 DB round trips
(getUserPrincipals + SystemGrant.exists) — replacing what were O(1)
string comparisons. Routes like patchPromptGroup triggered this twice,
and hasConfigCapability's fallback path resolved principals twice.
This adds a per-request AsyncLocalStorage cache that:
- Caches resolved principals (same for all checks within one request)
- Caches capability check results (same user+cap = same answer)
- Automatically scoped to request lifetime (no stale grants)
- Falls through to DB when no store exists (background jobs, tests)
- Requires no signature changes to hasCapability
The capabilityContextMiddleware is registered at the app level before
all routes, initializing a fresh store per request.
* fix: Add error handling for inline hasCapability calls
canDeleteAccount, fetchAssistants, and validateAuthor all call
hasCapability without try-catch. These were previously O(1) string
comparisons that could never throw. Now they hit the database and can
fail on connection timeout or transient errors.
Wrap each call in try-catch, defaulting to deny (false) on error.
This ensures a DB hiccup returns a clean 403 instead of an unhandled
500 with a stack trace.
* test: Add canDeleteAccount DB-error resilience test
Tests that hasCapability rejection (e.g., DB timeout) results in a clean
403 rather than an unhandled exception. Validates the error handling
added in the previous commit.
* refactor: Use barrel import for hasCapability in validateAuthor
Import from ~/server/middleware barrel instead of directly from
~/server/middleware/roles/capabilities for consistency with other
non-middleware consumers. Files within the middleware barrel itself
must continue using direct imports to avoid circular requires.
* refactor: Remove misleading pre('save') hook from SystemGrant schema
The pre('save') hook normalized principalId for USER/GROUP principals,
but the primary write path (grantCapability) uses findOneAndUpdate —
which does not trigger save hooks. The normalization was already handled
explicitly in grantCapability itself. The hook created a false impression
of schema-level enforcement that only covered save()/create() paths.
Replace with a comment documenting that all writes must go through
grantCapability.
* feat: Add READ_ASSISTANTS capability to complete manage/read pair
Every other managed resource had a paired READ_X / MANAGE_X capability
except assistants. This adds READ_ASSISTANTS and registers the
MANAGE_ASSISTANTS → READ_ASSISTANTS implication in CapabilityImplications,
enabling future read-only assistant visibility grants.
* chore: Reorder systemGrant methods for clarity
Moved hasCapabilityForPrincipals to a more logical position in the returned object of createSystemGrantMethods, improving code readability. This change also maintains the inclusion of seedSystemGrants in the export, ensuring all necessary methods are available.
* fix: Wrap seedSystemGrants in try-catch to avoid blocking startup
Seeding capabilities is idempotent and will succeed on the next restart.
A transient DB error during seeding should not prevent the server from
starting — log the error and continue.
* refactor: Improve capability check efficiency and add audit logging
Move hasCapability calls after cheap early-exits in validateAuthor and
fetchAssistants so the DB check only runs when its result matters. Add
logger.debug on every capability bypass grant across all 7 call sites
for auditability, and log errors in catch blocks instead of silently
swallowing them.
* test: Add integration tests for AsyncLocalStorage capability caching
Exercises the full vertical — ALS context, generateCapabilityCheck,
real getUserPrincipals, real hasCapabilityForPrincipals, real MongoDB
via MongoMemoryServer. Covers per-request caching, cross-context
isolation, concurrent request isolation, negative caching, capability
implications, tenant scoping, group-based grants, and requireCapability
middleware.
* test: Add systemGrant data-layer and ALS edge-case integration tests
systemGrant.spec.ts (51 tests): Full integration tests for all
systemGrant methods against real MongoDB — grant/revoke lifecycle,
principalId normalization (string→ObjectId for USER/GROUP, string for
ROLE), capability implications (both directions), tenant scoping,
schema validation (null tenantId, invalid enum, required fields,
unique compound index).
capabilities.integration.spec.ts (27 tests): Adds ALS edge cases —
missing context degrades gracefully with no caching (background jobs,
child processes), nested middleware creates independent inner context,
optional-chaining safety when store is undefined, mid-request grant
changes are invisible due to result caching, requireCapability works
without ALS, and interleaved concurrent contexts maintain isolation.
* fix: Add worker thread guards to capability ALS usage
Detect when hasCapability or capabilityContextMiddleware is called from
a worker thread (where ALS context does not propagate from the parent).
hasCapability logs a warn-once per factory instance; the middleware logs
an error since mounting Express middleware in a worker is likely a
misconfiguration. Both continue to function correctly — the guard is
observability, not a hard block.
* fix: Include tenantId in ALS principal cache key for tenant isolation
The principal cache key was user.id:user.role, which would reuse
cached principals across tenants for the same user within a request.
When getUserPrincipals gains tenant-scoped group resolution, principals
from tenant-a would incorrectly serve tenant-b checks. Changed to
user.id:user.role:user.tenantId to prevent cross-tenant cache hits.
Adds integration test proving separate principal lookups per tenantId.
* test: Remove redundant mocked capabilities.spec.js
The JS wrapper test (7 tests, all mocked) is a strict subset of
capabilities.integration.spec.ts (28 tests, real MongoDB). Every
scenario it covered — hasCapability true/false, tenantId passthrough,
requireCapability 403/500, error handling — is tested with higher
fidelity in the integration suite.
* test: Replace mocked canDeleteAccount tests with real MongoDB integration
Remove hasCapability mock — tests now exercise the full capability
chain against real MongoDB (getUserPrincipals, hasCapabilityForPrincipals,
SystemGrant collection). Only mocks remaining are logger and cache.
Adds new coverage: admin role without grant is blocked, user-level
grant bypasses deletion restriction, null user handling.
* test: Add comprehensive tests for ACL entry management and user group methods
Introduces new tests for `deleteAclEntries`, `bulkWriteAclEntries`, and `findPublicResourceIds` in `aclEntry.spec.ts`, ensuring proper functionality for deleting and bulk managing ACL entries. Additionally, enhances `userGroup.spec.ts` with tests for finding groups by ID and name pattern, including external ID matching and source filtering. These changes improve coverage and validate the integrity of ACL and user group operations against real MongoDB interactions.
* refactor: Update capability checks and logging for better clarity and error handling
Replaced `MANAGE_USERS` with `ACCESS_ADMIN` in the `canDeleteAccount` middleware and related tests to align with updated permission structure. Enhanced logging in various middleware functions to use `logger.warn` for capability check failures, providing clearer error messages. Additionally, refactored capability checks in the `patchPromptGroup` and `validateAuthor` functions to improve readability and maintainability. This commit also includes adjustments to the `systemGrant` methods to implement retry logic for transient failures during capability seeding, ensuring robustness in the face of database errors.
* refactor: Enhance logging and retry logic in seedSystemGrants method
Updated the logging format in the seedSystemGrants method to include error messages for better clarity. Improved the retry mechanism by explicitly mocking multiple failures in tests, ensuring robust error handling during transient database issues. Additionally, refined imports in the systemGrant schema for better type management.
* refactor: Consolidate imports in canDeleteAccount middleware
Merged logger and SystemCapabilities imports from the data-schemas module into a single line for improved readability and maintainability of the code. This change streamlines the import statements in the canDeleteAccount middleware.
* test: Enhance systemGrant tests for error handling and capability validation
Added tests to the systemGrant methods to handle various error scenarios, including E11000 race conditions, invalid ObjectId strings for USER and GROUP principals, and invalid capability strings. These enhancements improve the robustness of the capability granting and revoking logic, ensuring proper error propagation and validation of inputs.
* fix: Wrap hasCapability calls in deny-by-default try-catch at remaining sites
canAccessResource, files.js, and roles.js all had hasCapability inside
outer try-catch blocks that returned 500 on DB failure instead of
falling through to the regular ACL check. This contradicts the
deny-by-default pattern used everywhere else.
Also removes raw error.message from the roles.js 500 response to
prevent internal host/connection info leaking to clients.
* fix: Normalize user ID in canDeleteAccount before passing to hasCapability
requireCapability normalizes req.user.id via _id?.toString() fallback,
but canDeleteAccount passed raw req.user directly. If req.user.id is
absent (some auth layers only populate _id), getUserPrincipals received
undefined, silently returning empty principals and blocking the bypass.
* fix: Harden systemGrant schema and type safety
- Reject empty string tenantId in schema validator (was only blocking
null; empty string silently orphaned documents)
- Fix reverseImplications to use BaseSystemCapability[] instead of
string[], preserving the narrow discriminated type
- Document READ_ASSISTANTS as reserved/unenforced
* test: Use fake timers for seedSystemGrants retry tests and add tenantId validation
- Switch retry tests to jest.useFakeTimers() to eliminate 3+ seconds
of real setTimeout delays per test run
- Add regression test for empty-string tenantId rejection
* docs: Add TODO(#12091) comments for tenant-scoped capability gaps
In multi-tenant mode, platform-level grants (no tenantId) won't match
tenant-scoped queries, breaking admin access. getUserPrincipals also
returns cross-tenant group memberships. Both need fixes in #12091.
2026-03-07 13:56:32 -05:00
|
|
|
// Seed capabilities for the ADMIN role
|
|
|
|
|
await SystemGrant.create([
|
|
|
|
|
{
|
|
|
|
|
principalType: PrincipalType.ROLE,
|
|
|
|
|
principalId: SystemRoles.ADMIN,
|
|
|
|
|
capability: SystemCapabilities.MANAGE_PROMPTS,
|
|
|
|
|
grantedAt: new Date(),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
principalType: PrincipalType.ROLE,
|
|
|
|
|
principalId: SystemRoles.ADMIN,
|
|
|
|
|
capability: SystemCapabilities.READ_PROMPTS,
|
|
|
|
|
grantedAt: new Date(),
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
|
2025-07-26 12:28:31 -04:00
|
|
|
// Mock getRoleByName
|
📦 refactor: Consolidate DB models, encapsulating Mongoose usage in `data-schemas` (#11830)
* chore: move database model methods to /packages/data-schemas
* chore: add TypeScript ESLint rule to warn on unused variables
* refactor: model imports to streamline access
- Consolidated model imports across various files to improve code organization and reduce redundancy.
- Updated imports for models such as Assistant, Message, Conversation, and others to a unified import path.
- Adjusted middleware and service files to reflect the new import structure, ensuring functionality remains intact.
- Enhanced test files to align with the new import paths, maintaining test coverage and integrity.
* chore: migrate database models to packages/data-schemas and refactor all direct Mongoose Model usage outside of data-schemas
* test: update agent model mocks in unit tests
- Added `getAgent` mock to `client.test.js` to enhance test coverage for agent-related functionality.
- Removed redundant `getAgent` and `getAgents` mocks from `openai.spec.js` and `responses.unit.spec.js` to streamline test setup and reduce duplication.
- Ensured consistency in agent mock implementations across test files.
* fix: update types in data-schemas
* refactor: enhance type definitions in transaction and spending methods
- Updated type definitions in `checkBalance.ts` to use specific request and response types.
- Refined `spendTokens.ts` to utilize a new `SpendTxData` interface for better clarity and type safety.
- Improved transaction handling in `transaction.ts` by introducing `TransactionResult` and `TxData` interfaces, ensuring consistent data structures across methods.
- Adjusted unit tests in `transaction.spec.ts` to accommodate new type definitions and enhance robustness.
* refactor: streamline model imports and enhance code organization
- Consolidated model imports across various controllers and services to a unified import path, improving code clarity and reducing redundancy.
- Updated multiple files to reflect the new import structure, ensuring all functionalities remain intact.
- Enhanced overall code organization by removing duplicate import statements and optimizing the usage of model methods.
* feat: implement loadAddedAgent and refactor agent loading logic
- Introduced `loadAddedAgent` function to handle loading agents from added conversations, supporting multi-convo parallel execution.
- Created a new `load.ts` file to encapsulate agent loading functionalities, including `loadEphemeralAgent` and `loadAgent`.
- Updated the `index.ts` file to export the new `load` module instead of the deprecated `loadAgent`.
- Enhanced type definitions and improved error handling in the agent loading process.
- Adjusted unit tests to reflect changes in the agent loading structure and ensure comprehensive coverage.
* refactor: enhance balance handling with new update interface
- Introduced `IBalanceUpdate` interface to streamline balance update operations across the codebase.
- Updated `upsertBalanceFields` method signatures in `balance.ts`, `transaction.ts`, and related tests to utilize the new interface for improved type safety.
- Adjusted type imports in `balance.spec.ts` to include `IBalanceUpdate`, ensuring consistency in balance management functionalities.
- Enhanced overall code clarity and maintainability by refining type definitions related to balance operations.
* feat: add unit tests for loadAgent functionality and enhance agent loading logic
- Introduced comprehensive unit tests for the `loadAgent` function, covering various scenarios including null and empty agent IDs, loading of ephemeral agents, and permission checks.
- Enhanced the `initializeClient` function by moving `getConvoFiles` to the correct position in the database method exports, ensuring proper functionality.
- Improved test coverage for agent loading, including handling of non-existent agents and user permissions.
* chore: reorder memory method exports for consistency
- Moved `deleteAllUserMemories` to the correct position in the exported memory methods, ensuring a consistent and logical order of method exports in `memory.ts`.
2026-02-17 18:23:44 -05:00
|
|
|
const { getRoleByName } = require('~/models');
|
2025-07-26 12:28:31 -04:00
|
|
|
getRoleByName.mockImplementation((roleName) => {
|
|
|
|
|
switch (roleName) {
|
|
|
|
|
case SystemRoles.USER:
|
|
|
|
|
return { permissions: { PROMPTS: { USE: true, CREATE: true } } };
|
|
|
|
|
case SystemRoles.ADMIN:
|
2026-01-10 14:02:56 -05:00
|
|
|
return { permissions: { PROMPTS: { USE: true, CREATE: true, SHARE: true } } };
|
2025-07-26 12:28:31 -04:00
|
|
|
default:
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe('Prompt Routes - ACL Permissions', () => {
|
|
|
|
|
let consoleErrorSpy;
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
|
consoleErrorSpy.mockRestore();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Simple test to verify route is loaded
|
|
|
|
|
it('should have routes loaded', async () => {
|
|
|
|
|
// This should at least not crash
|
|
|
|
|
const response = await request(app).get('/api/prompts/test-404');
|
2025-08-30 15:58:49 -04:00
|
|
|
|
2025-07-26 12:28:31 -04:00
|
|
|
// We expect a 401 or 404, not 500
|
|
|
|
|
expect(response.status).not.toBe(500);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('POST /api/prompts - Create Prompt', () => {
|
|
|
|
|
afterEach(async () => {
|
|
|
|
|
await Prompt.deleteMany({});
|
|
|
|
|
await PromptGroup.deleteMany({});
|
|
|
|
|
await AclEntry.deleteMany({});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should create a prompt and grant owner permissions', async () => {
|
|
|
|
|
const promptData = {
|
|
|
|
|
prompt: {
|
|
|
|
|
prompt: 'Test prompt content',
|
|
|
|
|
type: 'text',
|
|
|
|
|
},
|
|
|
|
|
group: {
|
|
|
|
|
name: 'Test Prompt Group',
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await request(app).post('/api/prompts').send(promptData);
|
|
|
|
|
|
|
|
|
|
expect(response.status).toBe(200);
|
|
|
|
|
expect(response.body.prompt).toBeDefined();
|
|
|
|
|
expect(response.body.prompt.prompt).toBe(promptData.prompt.prompt);
|
|
|
|
|
|
|
|
|
|
// Check ACL entry was created
|
|
|
|
|
const aclEntry = await AclEntry.findOne({
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
2025-07-26 12:28:31 -04:00
|
|
|
resourceId: response.body.prompt.groupId,
|
2025-08-02 16:02:56 -04:00
|
|
|
principalType: PrincipalType.USER,
|
2025-07-26 12:28:31 -04:00
|
|
|
principalId: testUsers.owner._id,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(aclEntry).toBeTruthy();
|
|
|
|
|
expect(aclEntry.roleId.toString()).toBe(testRoles.owner._id.toString());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should create a prompt group with prompt and grant owner permissions', async () => {
|
|
|
|
|
const promptData = {
|
|
|
|
|
prompt: {
|
|
|
|
|
prompt: 'Group prompt content',
|
|
|
|
|
// Remove 'name' from prompt - it's not in the schema
|
|
|
|
|
},
|
|
|
|
|
group: {
|
|
|
|
|
name: 'Test Group',
|
|
|
|
|
category: 'testing',
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await request(app).post('/api/prompts').send(promptData).expect(200);
|
|
|
|
|
|
|
|
|
|
expect(response.body.prompt).toBeDefined();
|
|
|
|
|
expect(response.body.group).toBeDefined();
|
|
|
|
|
expect(response.body.group.name).toBe(promptData.group.name);
|
|
|
|
|
|
|
|
|
|
// Check ACL entry was created for the promptGroup
|
|
|
|
|
const aclEntry = await AclEntry.findOne({
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
2025-07-26 12:28:31 -04:00
|
|
|
resourceId: response.body.group._id,
|
2025-08-02 16:02:56 -04:00
|
|
|
principalType: PrincipalType.USER,
|
2025-07-26 12:28:31 -04:00
|
|
|
principalId: testUsers.owner._id,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(aclEntry).toBeTruthy();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('GET /api/prompts/:promptId - Get Prompt', () => {
|
|
|
|
|
let testPrompt;
|
|
|
|
|
let testGroup;
|
|
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
|
// Create a prompt group first
|
|
|
|
|
testGroup = await PromptGroup.create({
|
|
|
|
|
name: 'Test Group',
|
|
|
|
|
category: 'testing',
|
|
|
|
|
author: testUsers.owner._id,
|
|
|
|
|
authorName: testUsers.owner.name,
|
|
|
|
|
productionId: new ObjectId(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Create a prompt
|
|
|
|
|
testPrompt = await Prompt.create({
|
|
|
|
|
prompt: 'Test prompt for retrieval',
|
|
|
|
|
name: 'Get Test',
|
|
|
|
|
author: testUsers.owner._id,
|
|
|
|
|
type: 'text',
|
|
|
|
|
groupId: testGroup._id,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterEach(async () => {
|
|
|
|
|
await Prompt.deleteMany({});
|
|
|
|
|
await PromptGroup.deleteMany({});
|
|
|
|
|
await AclEntry.deleteMany({});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should retrieve prompt when user has view permissions', async () => {
|
|
|
|
|
// Grant view permissions on the promptGroup
|
|
|
|
|
await grantPermission({
|
2025-08-02 16:02:56 -04:00
|
|
|
principalType: PrincipalType.USER,
|
2025-07-26 12:28:31 -04:00
|
|
|
principalId: testUsers.owner._id,
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
2025-07-26 12:28:31 -04:00
|
|
|
resourceId: testGroup._id,
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_VIEWER,
|
2025-07-26 12:28:31 -04:00
|
|
|
grantedBy: testUsers.owner._id,
|
|
|
|
|
});
|
|
|
|
|
|
2025-08-03 19:24:40 -04:00
|
|
|
const response = await request(app).get(`/api/prompts/${testPrompt._id}`);
|
|
|
|
|
expect(response.status).toBe(200);
|
2025-07-26 12:28:31 -04:00
|
|
|
expect(response.body._id).toBe(testPrompt._id.toString());
|
|
|
|
|
expect(response.body.prompt).toBe(testPrompt.prompt);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should deny access when user has no permissions', async () => {
|
|
|
|
|
// Change the user to one without access
|
|
|
|
|
setTestUser(app, testUsers.noAccess);
|
|
|
|
|
|
|
|
|
|
const response = await request(app).get(`/api/prompts/${testPrompt._id}`).expect(403);
|
|
|
|
|
|
|
|
|
|
// Verify error response
|
|
|
|
|
expect(response.body.error).toBe('Forbidden');
|
|
|
|
|
expect(response.body.message).toBe('Insufficient permissions to access this promptGroup');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should allow admin access without explicit permissions', async () => {
|
2025-08-30 15:58:49 -04:00
|
|
|
// Set admin user
|
|
|
|
|
setTestUser(app, testUsers.admin);
|
2025-07-26 12:28:31 -04:00
|
|
|
|
|
|
|
|
const response = await request(app).get(`/api/prompts/${testPrompt._id}`).expect(200);
|
|
|
|
|
|
|
|
|
|
expect(response.body._id).toBe(testPrompt._id.toString());
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('DELETE /api/prompts/:promptId - Delete Prompt', () => {
|
|
|
|
|
let testPrompt;
|
|
|
|
|
let testGroup;
|
|
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
|
// Create group with prompt
|
|
|
|
|
testGroup = await PromptGroup.create({
|
|
|
|
|
name: 'Delete Test Group',
|
|
|
|
|
category: 'testing',
|
|
|
|
|
author: testUsers.owner._id,
|
|
|
|
|
authorName: testUsers.owner.name,
|
|
|
|
|
productionId: new ObjectId(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
testPrompt = await Prompt.create({
|
|
|
|
|
prompt: 'Test prompt for deletion',
|
|
|
|
|
name: 'Delete Test',
|
|
|
|
|
author: testUsers.owner._id,
|
|
|
|
|
type: 'text',
|
|
|
|
|
groupId: testGroup._id,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Add prompt to group
|
|
|
|
|
testGroup.productionId = testPrompt._id;
|
|
|
|
|
testGroup.promptIds = [testPrompt._id];
|
|
|
|
|
await testGroup.save();
|
|
|
|
|
|
|
|
|
|
// Grant owner permissions on the promptGroup
|
|
|
|
|
await grantPermission({
|
2025-08-02 16:02:56 -04:00
|
|
|
principalType: PrincipalType.USER,
|
2025-07-26 12:28:31 -04:00
|
|
|
principalId: testUsers.owner._id,
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
2025-07-26 12:28:31 -04:00
|
|
|
resourceId: testGroup._id,
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_OWNER,
|
2025-07-26 12:28:31 -04:00
|
|
|
grantedBy: testUsers.owner._id,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterEach(async () => {
|
|
|
|
|
await Prompt.deleteMany({});
|
|
|
|
|
await PromptGroup.deleteMany({});
|
|
|
|
|
await AclEntry.deleteMany({});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should delete prompt when user has delete permissions', async () => {
|
|
|
|
|
const response = await request(app)
|
|
|
|
|
.delete(`/api/prompts/${testPrompt._id}`)
|
|
|
|
|
.query({ groupId: testGroup._id.toString() })
|
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
|
|
expect(response.body.prompt).toBe('Prompt deleted successfully');
|
|
|
|
|
|
|
|
|
|
// Verify prompt was deleted
|
|
|
|
|
const deletedPrompt = await Prompt.findById(testPrompt._id);
|
|
|
|
|
expect(deletedPrompt).toBeNull();
|
|
|
|
|
|
|
|
|
|
// Verify ACL entries were removed
|
|
|
|
|
const aclEntries = await AclEntry.find({
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
2025-07-26 12:28:31 -04:00
|
|
|
resourceId: testGroup._id,
|
|
|
|
|
});
|
|
|
|
|
expect(aclEntries).toHaveLength(0);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should deny deletion when user lacks delete permissions', async () => {
|
|
|
|
|
// Create a prompt as a different user (not the one trying to delete)
|
|
|
|
|
const authorPrompt = await Prompt.create({
|
|
|
|
|
prompt: 'Test prompt by another user',
|
|
|
|
|
name: 'Another User Prompt',
|
|
|
|
|
author: testUsers.editor._id, // Different author
|
|
|
|
|
type: 'text',
|
|
|
|
|
groupId: testGroup._id,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Grant only viewer permissions to viewer user on the promptGroup
|
|
|
|
|
await grantPermission({
|
2025-08-02 16:02:56 -04:00
|
|
|
principalType: PrincipalType.USER,
|
2025-07-26 12:28:31 -04:00
|
|
|
principalId: testUsers.viewer._id,
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
2025-07-26 12:28:31 -04:00
|
|
|
resourceId: testGroup._id,
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_VIEWER,
|
2025-07-26 12:28:31 -04:00
|
|
|
grantedBy: testUsers.editor._id,
|
|
|
|
|
});
|
|
|
|
|
|
2025-08-30 15:58:49 -04:00
|
|
|
// Set viewer user
|
|
|
|
|
setTestUser(app, testUsers.viewer);
|
2025-07-26 12:28:31 -04:00
|
|
|
|
|
|
|
|
await request(app)
|
|
|
|
|
.delete(`/api/prompts/${authorPrompt._id}`)
|
|
|
|
|
.query({ groupId: testGroup._id.toString() })
|
|
|
|
|
.expect(403);
|
|
|
|
|
|
|
|
|
|
// Verify prompt still exists
|
|
|
|
|
const prompt = await Prompt.findById(authorPrompt._id);
|
|
|
|
|
expect(prompt).toBeTruthy();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('PATCH /api/prompts/:promptId/tags/production - Make Production', () => {
|
|
|
|
|
let testPrompt;
|
|
|
|
|
let testGroup;
|
|
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
|
// Create group
|
|
|
|
|
testGroup = await PromptGroup.create({
|
|
|
|
|
name: 'Production Test Group',
|
|
|
|
|
category: 'testing',
|
|
|
|
|
author: testUsers.owner._id,
|
|
|
|
|
authorName: testUsers.owner.name,
|
|
|
|
|
productionId: new ObjectId(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
testPrompt = await Prompt.create({
|
|
|
|
|
prompt: 'Test prompt for production',
|
|
|
|
|
name: 'Production Test',
|
|
|
|
|
author: testUsers.owner._id,
|
|
|
|
|
type: 'text',
|
|
|
|
|
groupId: testGroup._id,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterEach(async () => {
|
|
|
|
|
await Prompt.deleteMany({});
|
|
|
|
|
await PromptGroup.deleteMany({});
|
|
|
|
|
await AclEntry.deleteMany({});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should make prompt production when user has edit permissions', async () => {
|
|
|
|
|
// Grant edit permissions on the promptGroup
|
|
|
|
|
await grantPermission({
|
2025-08-02 16:02:56 -04:00
|
|
|
principalType: PrincipalType.USER,
|
2025-07-26 12:28:31 -04:00
|
|
|
principalId: testUsers.owner._id,
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
2025-07-26 12:28:31 -04:00
|
|
|
resourceId: testGroup._id,
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_EDITOR,
|
2025-07-26 12:28:31 -04:00
|
|
|
grantedBy: testUsers.owner._id,
|
|
|
|
|
});
|
|
|
|
|
|
2025-08-30 15:58:49 -04:00
|
|
|
// Ensure owner user
|
|
|
|
|
setTestUser(app, testUsers.owner);
|
2025-07-26 12:28:31 -04:00
|
|
|
|
|
|
|
|
const response = await request(app)
|
|
|
|
|
.patch(`/api/prompts/${testPrompt._id}/tags/production`)
|
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
|
|
expect(response.body.message).toBe('Prompt production made successfully');
|
|
|
|
|
|
|
|
|
|
// Verify the group was updated
|
|
|
|
|
const updatedGroup = await PromptGroup.findById(testGroup._id);
|
|
|
|
|
expect(updatedGroup.productionId.toString()).toBe(testPrompt._id.toString());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should deny making production when user lacks edit permissions', async () => {
|
|
|
|
|
// Grant only view permissions to viewer on the promptGroup
|
|
|
|
|
await grantPermission({
|
2025-08-02 16:02:56 -04:00
|
|
|
principalType: PrincipalType.USER,
|
2025-07-26 12:28:31 -04:00
|
|
|
principalId: testUsers.viewer._id,
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
2025-07-26 12:28:31 -04:00
|
|
|
resourceId: testGroup._id,
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_VIEWER,
|
2025-07-26 12:28:31 -04:00
|
|
|
grantedBy: testUsers.owner._id,
|
|
|
|
|
});
|
|
|
|
|
|
2025-08-30 15:58:49 -04:00
|
|
|
// Set viewer user
|
|
|
|
|
setTestUser(app, testUsers.viewer);
|
2025-07-26 12:28:31 -04:00
|
|
|
|
|
|
|
|
await request(app).patch(`/api/prompts/${testPrompt._id}/tags/production`).expect(403);
|
|
|
|
|
|
|
|
|
|
// Verify prompt hasn't changed
|
|
|
|
|
const unchangedGroup = await PromptGroup.findById(testGroup._id);
|
|
|
|
|
expect(unchangedGroup.productionId.toString()).not.toBe(testPrompt._id.toString());
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('Public Access', () => {
|
|
|
|
|
let publicPrompt;
|
|
|
|
|
let publicGroup;
|
|
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
|
// Create a prompt group
|
|
|
|
|
publicGroup = await PromptGroup.create({
|
|
|
|
|
name: 'Public Test Group',
|
|
|
|
|
category: 'testing',
|
|
|
|
|
author: testUsers.owner._id,
|
|
|
|
|
authorName: testUsers.owner.name,
|
|
|
|
|
productionId: new ObjectId(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Create a public prompt
|
|
|
|
|
publicPrompt = await Prompt.create({
|
|
|
|
|
prompt: 'Public prompt content',
|
|
|
|
|
name: 'Public Test',
|
|
|
|
|
author: testUsers.owner._id,
|
|
|
|
|
type: 'text',
|
|
|
|
|
groupId: publicGroup._id,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Grant public viewer access on the promptGroup
|
|
|
|
|
await grantPermission({
|
2025-08-02 16:02:56 -04:00
|
|
|
principalType: PrincipalType.PUBLIC,
|
2025-07-26 12:28:31 -04:00
|
|
|
principalId: null,
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
2025-07-26 12:28:31 -04:00
|
|
|
resourceId: publicGroup._id,
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_VIEWER,
|
2025-07-26 12:28:31 -04:00
|
|
|
grantedBy: testUsers.owner._id,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterEach(async () => {
|
|
|
|
|
await Prompt.deleteMany({});
|
|
|
|
|
await PromptGroup.deleteMany({});
|
|
|
|
|
await AclEntry.deleteMany({});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should allow any user to view public prompts', async () => {
|
|
|
|
|
// Change user to someone without explicit permissions
|
|
|
|
|
setTestUser(app, testUsers.noAccess);
|
|
|
|
|
|
|
|
|
|
const response = await request(app).get(`/api/prompts/${publicPrompt._id}`).expect(200);
|
|
|
|
|
|
|
|
|
|
expect(response.body._id).toBe(publicPrompt._id.toString());
|
|
|
|
|
});
|
|
|
|
|
});
|
2025-08-30 15:58:49 -04:00
|
|
|
|
2025-12-02 00:10:30 -05:00
|
|
|
describe('PATCH /api/prompts/groups/:groupId - Update Prompt Group Security', () => {
|
|
|
|
|
let testGroup;
|
|
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
|
// Create a prompt group
|
|
|
|
|
testGroup = await PromptGroup.create({
|
|
|
|
|
name: 'Security Test Group',
|
|
|
|
|
category: 'security-test',
|
|
|
|
|
author: testUsers.owner._id,
|
|
|
|
|
authorName: testUsers.owner.name,
|
|
|
|
|
productionId: new ObjectId(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Grant owner permissions
|
|
|
|
|
await grantPermission({
|
|
|
|
|
principalType: PrincipalType.USER,
|
|
|
|
|
principalId: testUsers.owner._id,
|
|
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
|
|
|
resourceId: testGroup._id,
|
|
|
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_OWNER,
|
|
|
|
|
grantedBy: testUsers.owner._id,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterEach(async () => {
|
|
|
|
|
await PromptGroup.deleteMany({});
|
|
|
|
|
await AclEntry.deleteMany({});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should allow updating allowed fields (name, category, oneliner)', async () => {
|
|
|
|
|
const updateData = {
|
|
|
|
|
name: 'Updated Group Name',
|
|
|
|
|
category: 'updated-category',
|
|
|
|
|
oneliner: 'Updated description',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await request(app)
|
|
|
|
|
.patch(`/api/prompts/groups/${testGroup._id}`)
|
|
|
|
|
.send(updateData)
|
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
|
|
expect(response.body.name).toBe(updateData.name);
|
|
|
|
|
expect(response.body.category).toBe(updateData.category);
|
|
|
|
|
expect(response.body.oneliner).toBe(updateData.oneliner);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should reject request with author field (400 Bad Request)', async () => {
|
|
|
|
|
const maliciousUpdate = {
|
|
|
|
|
name: 'Legit Update',
|
|
|
|
|
author: testUsers.noAccess._id.toString(), // Try to change ownership
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await request(app)
|
|
|
|
|
.patch(`/api/prompts/groups/${testGroup._id}`)
|
|
|
|
|
.send(maliciousUpdate)
|
|
|
|
|
.expect(400);
|
|
|
|
|
|
|
|
|
|
// Verify the request was rejected
|
|
|
|
|
expect(response.body.error).toBe('Invalid request body');
|
|
|
|
|
expect(response.body.details).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should reject request with authorName field (400 Bad Request)', async () => {
|
|
|
|
|
const maliciousUpdate = {
|
|
|
|
|
name: 'Legit Update',
|
|
|
|
|
authorName: 'Malicious Author Name',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await request(app)
|
|
|
|
|
.patch(`/api/prompts/groups/${testGroup._id}`)
|
|
|
|
|
.send(maliciousUpdate)
|
|
|
|
|
.expect(400);
|
|
|
|
|
|
|
|
|
|
// Verify the request was rejected
|
|
|
|
|
expect(response.body.error).toBe('Invalid request body');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should reject request with _id field (400 Bad Request)', async () => {
|
|
|
|
|
const newId = new ObjectId();
|
|
|
|
|
const maliciousUpdate = {
|
|
|
|
|
name: 'Legit Update',
|
|
|
|
|
_id: newId.toString(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await request(app)
|
|
|
|
|
.patch(`/api/prompts/groups/${testGroup._id}`)
|
|
|
|
|
.send(maliciousUpdate)
|
|
|
|
|
.expect(400);
|
|
|
|
|
|
|
|
|
|
// Verify the request was rejected
|
|
|
|
|
expect(response.body.error).toBe('Invalid request body');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should reject request with productionId field (400 Bad Request)', async () => {
|
|
|
|
|
const newProductionId = new ObjectId();
|
|
|
|
|
const maliciousUpdate = {
|
|
|
|
|
name: 'Legit Update',
|
|
|
|
|
productionId: newProductionId.toString(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await request(app)
|
|
|
|
|
.patch(`/api/prompts/groups/${testGroup._id}`)
|
|
|
|
|
.send(maliciousUpdate)
|
|
|
|
|
.expect(400);
|
|
|
|
|
|
|
|
|
|
// Verify the request was rejected
|
|
|
|
|
expect(response.body.error).toBe('Invalid request body');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should reject request with createdAt field (400 Bad Request)', async () => {
|
|
|
|
|
const maliciousDate = new Date('2020-01-01');
|
|
|
|
|
const maliciousUpdate = {
|
|
|
|
|
name: 'Legit Update',
|
|
|
|
|
createdAt: maliciousDate.toISOString(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await request(app)
|
|
|
|
|
.patch(`/api/prompts/groups/${testGroup._id}`)
|
|
|
|
|
.send(maliciousUpdate)
|
|
|
|
|
.expect(400);
|
|
|
|
|
|
|
|
|
|
// Verify the request was rejected
|
|
|
|
|
expect(response.body.error).toBe('Invalid request body');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should reject request with __v field (400 Bad Request)', async () => {
|
|
|
|
|
const maliciousUpdate = {
|
|
|
|
|
name: 'Legit Update',
|
|
|
|
|
__v: 999,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await request(app)
|
|
|
|
|
.patch(`/api/prompts/groups/${testGroup._id}`)
|
|
|
|
|
.send(maliciousUpdate)
|
|
|
|
|
.expect(400);
|
|
|
|
|
|
|
|
|
|
// Verify the request was rejected
|
|
|
|
|
expect(response.body.error).toBe('Invalid request body');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should reject request with multiple sensitive fields (400 Bad Request)', async () => {
|
|
|
|
|
const maliciousUpdate = {
|
|
|
|
|
name: 'Legit Update',
|
|
|
|
|
author: testUsers.noAccess._id.toString(),
|
|
|
|
|
authorName: 'Hacker',
|
|
|
|
|
_id: new ObjectId().toString(),
|
|
|
|
|
productionId: new ObjectId().toString(),
|
|
|
|
|
createdAt: new Date('2020-01-01').toISOString(),
|
|
|
|
|
__v: 999,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await request(app)
|
|
|
|
|
.patch(`/api/prompts/groups/${testGroup._id}`)
|
|
|
|
|
.send(maliciousUpdate)
|
|
|
|
|
.expect(400);
|
|
|
|
|
|
|
|
|
|
// Verify the request was rejected with validation errors
|
|
|
|
|
expect(response.body.error).toBe('Invalid request body');
|
|
|
|
|
expect(response.body.details).toBeDefined();
|
|
|
|
|
expect(Array.isArray(response.body.details)).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2025-08-30 15:58:49 -04:00
|
|
|
describe('Pagination', () => {
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
|
// Create multiple prompt groups for pagination testing
|
|
|
|
|
const groups = [];
|
|
|
|
|
for (let i = 0; i < 15; i++) {
|
|
|
|
|
const group = await PromptGroup.create({
|
|
|
|
|
name: `Test Group ${i + 1}`,
|
|
|
|
|
category: 'pagination-test',
|
|
|
|
|
author: testUsers.owner._id,
|
|
|
|
|
authorName: testUsers.owner.name,
|
|
|
|
|
productionId: new ObjectId(),
|
|
|
|
|
updatedAt: new Date(Date.now() - i * 1000), // Stagger updatedAt for consistent ordering
|
|
|
|
|
});
|
|
|
|
|
groups.push(group);
|
|
|
|
|
|
|
|
|
|
// Grant owner permissions on each group
|
|
|
|
|
await grantPermission({
|
|
|
|
|
principalType: PrincipalType.USER,
|
|
|
|
|
principalId: testUsers.owner._id,
|
|
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
|
|
|
resourceId: group._id,
|
|
|
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_OWNER,
|
|
|
|
|
grantedBy: testUsers.owner._id,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterEach(async () => {
|
|
|
|
|
await PromptGroup.deleteMany({});
|
|
|
|
|
await AclEntry.deleteMany({});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should correctly indicate hasMore when there are more pages', async () => {
|
|
|
|
|
const response = await request(app)
|
|
|
|
|
.get('/api/prompts/groups')
|
|
|
|
|
.query({ limit: '10' })
|
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
|
|
expect(response.body.promptGroups).toHaveLength(10);
|
|
|
|
|
expect(response.body.has_more).toBe(true);
|
|
|
|
|
expect(response.body.after).toBeTruthy();
|
|
|
|
|
// Since has_more is true, pages should be a high number (9999 in our fix)
|
|
|
|
|
expect(parseInt(response.body.pages)).toBeGreaterThan(1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should correctly indicate no more pages on the last page', async () => {
|
|
|
|
|
// First get the cursor for page 2
|
|
|
|
|
const firstPage = await request(app)
|
|
|
|
|
.get('/api/prompts/groups')
|
|
|
|
|
.query({ limit: '10' })
|
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
|
|
expect(firstPage.body.has_more).toBe(true);
|
|
|
|
|
expect(firstPage.body.after).toBeTruthy();
|
|
|
|
|
|
|
|
|
|
// Now fetch the second page using the cursor
|
|
|
|
|
const response = await request(app)
|
|
|
|
|
.get('/api/prompts/groups')
|
|
|
|
|
.query({ limit: '10', cursor: firstPage.body.after })
|
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
|
|
expect(response.body.promptGroups).toHaveLength(5); // 15 total, 10 on page 1, 5 on page 2
|
|
|
|
|
expect(response.body.has_more).toBe(false);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should support cursor-based pagination', async () => {
|
|
|
|
|
// First page
|
|
|
|
|
const firstPage = await request(app)
|
|
|
|
|
.get('/api/prompts/groups')
|
|
|
|
|
.query({ limit: '5' })
|
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
|
|
expect(firstPage.body.promptGroups).toHaveLength(5);
|
|
|
|
|
expect(firstPage.body.has_more).toBe(true);
|
|
|
|
|
expect(firstPage.body.after).toBeTruthy();
|
|
|
|
|
|
|
|
|
|
// Second page using cursor
|
|
|
|
|
const secondPage = await request(app)
|
|
|
|
|
.get('/api/prompts/groups')
|
|
|
|
|
.query({ limit: '5', cursor: firstPage.body.after })
|
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
|
|
expect(secondPage.body.promptGroups).toHaveLength(5);
|
|
|
|
|
expect(secondPage.body.has_more).toBe(true);
|
|
|
|
|
expect(secondPage.body.after).toBeTruthy();
|
|
|
|
|
|
|
|
|
|
// Verify different groups
|
|
|
|
|
const firstPageIds = firstPage.body.promptGroups.map((g) => g._id);
|
|
|
|
|
const secondPageIds = secondPage.body.promptGroups.map((g) => g._id);
|
|
|
|
|
expect(firstPageIds).not.toEqual(secondPageIds);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should paginate correctly with category filtering', async () => {
|
|
|
|
|
// Create groups with different categories
|
|
|
|
|
await PromptGroup.deleteMany({}); // Clear existing groups
|
|
|
|
|
await AclEntry.deleteMany({});
|
|
|
|
|
|
|
|
|
|
// Create 8 groups with category 'test-cat-1'
|
|
|
|
|
for (let i = 0; i < 8; i++) {
|
|
|
|
|
const group = await PromptGroup.create({
|
|
|
|
|
name: `Category 1 Group ${i + 1}`,
|
|
|
|
|
category: 'test-cat-1',
|
|
|
|
|
author: testUsers.owner._id,
|
|
|
|
|
authorName: testUsers.owner.name,
|
|
|
|
|
productionId: new ObjectId(),
|
|
|
|
|
updatedAt: new Date(Date.now() - i * 1000),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await grantPermission({
|
|
|
|
|
principalType: PrincipalType.USER,
|
|
|
|
|
principalId: testUsers.owner._id,
|
|
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
|
|
|
resourceId: group._id,
|
|
|
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_OWNER,
|
|
|
|
|
grantedBy: testUsers.owner._id,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create 7 groups with category 'test-cat-2'
|
|
|
|
|
for (let i = 0; i < 7; i++) {
|
|
|
|
|
const group = await PromptGroup.create({
|
|
|
|
|
name: `Category 2 Group ${i + 1}`,
|
|
|
|
|
category: 'test-cat-2',
|
|
|
|
|
author: testUsers.owner._id,
|
|
|
|
|
authorName: testUsers.owner.name,
|
|
|
|
|
productionId: new ObjectId(),
|
|
|
|
|
updatedAt: new Date(Date.now() - (i + 8) * 1000),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await grantPermission({
|
|
|
|
|
principalType: PrincipalType.USER,
|
|
|
|
|
principalId: testUsers.owner._id,
|
|
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
|
|
|
resourceId: group._id,
|
|
|
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_OWNER,
|
|
|
|
|
grantedBy: testUsers.owner._id,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test pagination with category filter
|
|
|
|
|
const firstPage = await request(app)
|
|
|
|
|
.get('/api/prompts/groups')
|
|
|
|
|
.query({ limit: '5', category: 'test-cat-1' })
|
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
|
|
expect(firstPage.body.promptGroups).toHaveLength(5);
|
|
|
|
|
expect(firstPage.body.promptGroups.every((g) => g.category === 'test-cat-1')).toBe(true);
|
|
|
|
|
expect(firstPage.body.has_more).toBe(true);
|
|
|
|
|
expect(firstPage.body.after).toBeTruthy();
|
|
|
|
|
|
|
|
|
|
const secondPage = await request(app)
|
|
|
|
|
.get('/api/prompts/groups')
|
|
|
|
|
.query({ limit: '5', cursor: firstPage.body.after, category: 'test-cat-1' })
|
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
|
|
expect(secondPage.body.promptGroups).toHaveLength(3); // 8 total, 5 on page 1, 3 on page 2
|
|
|
|
|
expect(secondPage.body.promptGroups.every((g) => g.category === 'test-cat-1')).toBe(true);
|
|
|
|
|
expect(secondPage.body.has_more).toBe(false);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should paginate correctly with name/keyword filtering', async () => {
|
|
|
|
|
// Create groups with specific names
|
|
|
|
|
await PromptGroup.deleteMany({}); // Clear existing groups
|
|
|
|
|
await AclEntry.deleteMany({});
|
|
|
|
|
|
|
|
|
|
// Create 12 groups with 'Search' in the name
|
|
|
|
|
for (let i = 0; i < 12; i++) {
|
|
|
|
|
const group = await PromptGroup.create({
|
|
|
|
|
name: `Search Test Group ${i + 1}`,
|
|
|
|
|
category: 'search-test',
|
|
|
|
|
author: testUsers.owner._id,
|
|
|
|
|
authorName: testUsers.owner.name,
|
|
|
|
|
productionId: new ObjectId(),
|
|
|
|
|
updatedAt: new Date(Date.now() - i * 1000),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await grantPermission({
|
|
|
|
|
principalType: PrincipalType.USER,
|
|
|
|
|
principalId: testUsers.owner._id,
|
|
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
|
|
|
resourceId: group._id,
|
|
|
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_OWNER,
|
|
|
|
|
grantedBy: testUsers.owner._id,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create 5 groups without 'Search' in the name
|
|
|
|
|
for (let i = 0; i < 5; i++) {
|
|
|
|
|
const group = await PromptGroup.create({
|
|
|
|
|
name: `Other Group ${i + 1}`,
|
|
|
|
|
category: 'other-test',
|
|
|
|
|
author: testUsers.owner._id,
|
|
|
|
|
authorName: testUsers.owner.name,
|
|
|
|
|
productionId: new ObjectId(),
|
|
|
|
|
updatedAt: new Date(Date.now() - (i + 12) * 1000),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await grantPermission({
|
|
|
|
|
principalType: PrincipalType.USER,
|
|
|
|
|
principalId: testUsers.owner._id,
|
|
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
|
|
|
resourceId: group._id,
|
|
|
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_OWNER,
|
|
|
|
|
grantedBy: testUsers.owner._id,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test pagination with name filter
|
|
|
|
|
const firstPage = await request(app)
|
|
|
|
|
.get('/api/prompts/groups')
|
|
|
|
|
.query({ limit: '10', name: 'Search' })
|
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
|
|
expect(firstPage.body.promptGroups).toHaveLength(10);
|
|
|
|
|
expect(firstPage.body.promptGroups.every((g) => g.name.includes('Search'))).toBe(true);
|
|
|
|
|
expect(firstPage.body.has_more).toBe(true);
|
|
|
|
|
expect(firstPage.body.after).toBeTruthy();
|
|
|
|
|
|
|
|
|
|
const secondPage = await request(app)
|
|
|
|
|
.get('/api/prompts/groups')
|
|
|
|
|
.query({ limit: '10', cursor: firstPage.body.after, name: 'Search' })
|
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
|
|
expect(secondPage.body.promptGroups).toHaveLength(2); // 12 total, 10 on page 1, 2 on page 2
|
|
|
|
|
expect(secondPage.body.promptGroups.every((g) => g.name.includes('Search'))).toBe(true);
|
|
|
|
|
expect(secondPage.body.has_more).toBe(false);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should paginate correctly with combined filters', async () => {
|
|
|
|
|
// Create groups with various combinations
|
|
|
|
|
await PromptGroup.deleteMany({}); // Clear existing groups
|
|
|
|
|
await AclEntry.deleteMany({});
|
|
|
|
|
|
|
|
|
|
// Create 6 groups matching both category and name filters
|
|
|
|
|
for (let i = 0; i < 6; i++) {
|
|
|
|
|
const group = await PromptGroup.create({
|
|
|
|
|
name: `API Test Group ${i + 1}`,
|
|
|
|
|
category: 'api-category',
|
|
|
|
|
author: testUsers.owner._id,
|
|
|
|
|
authorName: testUsers.owner.name,
|
|
|
|
|
productionId: new ObjectId(),
|
|
|
|
|
updatedAt: new Date(Date.now() - i * 1000),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await grantPermission({
|
|
|
|
|
principalType: PrincipalType.USER,
|
|
|
|
|
principalId: testUsers.owner._id,
|
|
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
|
|
|
resourceId: group._id,
|
|
|
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_OWNER,
|
|
|
|
|
grantedBy: testUsers.owner._id,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create groups that only match one filter
|
|
|
|
|
for (let i = 0; i < 4; i++) {
|
|
|
|
|
const group = await PromptGroup.create({
|
|
|
|
|
name: `API Other Group ${i + 1}`,
|
|
|
|
|
category: 'other-category',
|
|
|
|
|
author: testUsers.owner._id,
|
|
|
|
|
authorName: testUsers.owner.name,
|
|
|
|
|
productionId: new ObjectId(),
|
|
|
|
|
updatedAt: new Date(Date.now() - (i + 6) * 1000),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await grantPermission({
|
|
|
|
|
principalType: PrincipalType.USER,
|
|
|
|
|
principalId: testUsers.owner._id,
|
|
|
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
|
|
|
resourceId: group._id,
|
|
|
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_OWNER,
|
|
|
|
|
grantedBy: testUsers.owner._id,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Test pagination with both filters
|
|
|
|
|
const response = await request(app)
|
|
|
|
|
.get('/api/prompts/groups')
|
|
|
|
|
.query({ limit: '5', name: 'API', category: 'api-category' })
|
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
|
|
expect(response.body.promptGroups).toHaveLength(5);
|
|
|
|
|
expect(
|
|
|
|
|
response.body.promptGroups.every(
|
|
|
|
|
(g) => g.name.includes('API') && g.category === 'api-category',
|
|
|
|
|
),
|
|
|
|
|
).toBe(true);
|
|
|
|
|
expect(response.body.has_more).toBe(true);
|
|
|
|
|
expect(response.body.after).toBeTruthy();
|
|
|
|
|
|
|
|
|
|
// Page 2
|
|
|
|
|
const page2 = await request(app)
|
|
|
|
|
.get('/api/prompts/groups')
|
|
|
|
|
.query({ limit: '5', cursor: response.body.after, name: 'API', category: 'api-category' })
|
|
|
|
|
.expect(200);
|
|
|
|
|
|
|
|
|
|
expect(page2.body.promptGroups).toHaveLength(1); // 6 total, 5 on page 1, 1 on page 2
|
|
|
|
|
expect(page2.body.has_more).toBe(false);
|
|
|
|
|
});
|
|
|
|
|
});
|
2025-07-26 12:28:31 -04:00
|
|
|
});
|