Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion api/server/controllers/UserController.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const {
persistMCPAuthorizationFenceRetry,
} = require('~/server/services/MCPAuthorizationFenceRetry');
const { processDeleteRequest } = require('~/server/services/Files/process');
const { getPromptService } = require('~/server/services/Prompts');
const subagentThreadTaskStore = require('~/server/services/Endpoints/agents/subagentThreadStore');
const {
drainAgentTriggerDeliveriesForUser,
Expand Down Expand Up @@ -517,7 +518,7 @@ const deleteUserController = async (req, res) => {
await db.deleteAssistants({ user: user.id });
await db.deleteConversationTags({ user: user.id });
await db.deleteAllUserMemories(user.id);
await db.deleteUserPrompts(user.id);
await getPromptService().deleteUserPrompts(user.id);
await db.deleteUserSkills(user.id);
await deleteUserMcpServers(user.id);
await db.deleteActions({ user: user.id });
Expand Down
6 changes: 6 additions & 0 deletions api/server/controllers/__tests__/deleteUser.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ jest.mock('~/server/services/Files/process', () => ({
processDeleteRequest: (...args) => mockProcessDeleteRequest(...args),
}));

jest.mock('~/server/services/Prompts', () => ({
getPromptService: () => ({
deleteUserPrompts: (...args) => mockDeleteUserPrompts(...args),
}),
}));

jest.mock('~/server/services/Agents/triggers', () => ({
drainAgentTriggerDeliveriesForUser: (...args) => mockDrainAgentTriggerDeliveriesForUser(...args),
prepareAgentTriggerUserPurge: (...args) => mockPrepareAgentTriggerUserPurge(...args),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,17 @@
const { ResourceType } = require('librechat-data-provider');
const { getPromptAccessResolvers } = require('~/server/services/Prompts');
const { canAccessResource } = require('./canAccessResource');
const { getPromptGroup } = require('~/models');

/**
* PromptGroup ID resolver function
* Resolves promptGroup ID to MongoDB ObjectId
*
* @param {string} groupId - PromptGroup ID from route parameter
* @returns {Promise<Object|null>} PromptGroup document with _id field, or null if not found
*/
const resolvePromptGroupId = async (groupId) => {
return await getPromptGroup({ _id: groupId });
};
const { resolvePromptGroup } = getPromptAccessResolvers();

/**
* PromptGroup-specific middleware factory that creates middleware to check promptGroup access permissions.
* This middleware extends the generic canAccessResource to handle promptGroup ID resolution.
* On success, the loaded group is available as `req.resourceAccess.resourceInfo`.
*
* @param {Object} options - Configuration options
* @param {number} options.requiredPermission - The permission bit required (1=view, 2=edit, 4=delete, 8=share)
* @param {string} [options.resourceIdParam='groupId'] - The name of the route parameter containing the promptGroup ID
* @returns {Function} Express middleware function
*
* @example
* // Basic usage for viewing promptGroups
* router.get('/prompts/groups/:groupId',
* canAccessPromptGroupResource({ requiredPermission: 1 }),
* getPromptGroup
* );
*
* @example
* // Custom resource ID parameter and edit permission
* router.patch('/prompts/groups/:id',
* canAccessPromptGroupResource({
* requiredPermission: 2,
* resourceIdParam: 'id'
* }),
* updatePromptGroup
* );
*/
const canAccessPromptGroupResource = (options) => {
const { requiredPermission, resourceIdParam = 'groupId' } = options;
Expand All @@ -52,7 +26,7 @@ const canAccessPromptGroupResource = (options) => {
resourceType: ResourceType.PROMPTGROUP,
requiredPermission,
resourceIdParam,
idResolver: resolvePromptGroupId,
idResolver: resolvePromptGroup,
});
};

Expand Down
30 changes: 5 additions & 25 deletions api/server/middleware/accessResources/canAccessPromptViaGroup.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,19 @@
const { ResourceType } = require('librechat-data-provider');
const { getPromptAccessResolvers } = require('~/server/services/Prompts');
const { canAccessResource } = require('./canAccessResource');
const { getPrompt } = require('~/models');

/**
* Prompt to PromptGroup ID resolver function
* Resolves prompt ID to its parent promptGroup ID
*
* @param {string} promptId - Prompt ID from route parameter
* @returns {Promise<Object|null>} Object with promptGroup's _id field, or null if not found
*/
const resolvePromptToGroupId = async (promptId) => {
const prompt = await getPrompt({ _id: promptId });
if (!prompt || !prompt.groupId) {
return null;
}
// Return an object with _id that matches the promptGroup ID
return { _id: prompt.groupId };
};
const { resolvePromptViaGroup } = getPromptAccessResolvers();

/**
* Middleware factory that checks promptGroup permissions when accessing individual prompts.
* This allows permission management at the promptGroup level while still supporting
* individual prompt access patterns.
* individual prompt access patterns. On success, the loaded revision is available as
* `req.resourceAccess.resourceInfo.prompt`.
*
* @param {Object} options - Configuration options
* @param {number} options.requiredPermission - The permission bit required (1=view, 2=edit, 4=delete, 8=share)
* @param {string} [options.resourceIdParam='promptId'] - The name of the route parameter containing the prompt ID
* @returns {Function} Express middleware function
*
* @example
* // Check promptGroup permissions when viewing a prompt
* router.get('/prompts/:promptId',
* canAccessPromptViaGroup({ requiredPermission: 1 }),
* getPrompt
* );
*/
const canAccessPromptViaGroup = (options) => {
const { requiredPermission, resourceIdParam = 'promptId' } = options;
Expand All @@ -46,7 +26,7 @@ const canAccessPromptViaGroup = (options) => {
resourceType: ResourceType.PROMPTGROUP,
requiredPermission,
resourceIdParam,
idResolver: resolvePromptToGroupId,
idResolver: resolvePromptViaGroup,
});
};

Expand Down
Loading
Loading