mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-21 21:50:49 +02:00

* WIP: app.locals refactoring
WIP: appConfig
fix: update memory configuration retrieval to use getAppConfig based on user role
fix: update comment for AppConfig interface to clarify purpose
🏷️ refactor: Update tests to use getAppConfig for endpoint configurations
ci: Update AppService tests to initialize app config instead of app.locals
ci: Integrate getAppConfig into remaining tests
refactor: Update multer storage destination to use promise-based getAppConfig and improve error handling in tests
refactor: Rename initializeAppConfig to setAppConfig and update related tests
ci: Mock getAppConfig in various tests to provide default configurations
refactor: Update convertMCPToolsToPlugins to use mcpManager for server configuration and adjust related tests
chore: rename `Config/getAppConfig` -> `Config/app`
fix: streamline OpenAI image tools configuration by removing direct appConfig dependency and using function parameters
chore: correct parameter documentation for imageOutputType in ToolService.js
refactor: remove `getCustomConfig` dependency in config route
refactor: update domain validation to use appConfig for allowed domains
refactor: use appConfig registration property
chore: remove app parameter from AppService invocation
refactor: update AppConfig interface to correct registration and turnstile configurations
refactor: remove getCustomConfig dependency and use getAppConfig in PluginController, multer, and MCP services
refactor: replace getCustomConfig with getAppConfig in STTService, TTSService, and related files
refactor: replace getCustomConfig with getAppConfig in Conversation and Message models, update tempChatRetention functions to use AppConfig type
refactor: update getAppConfig calls in Conversation and Message models to include user role for temporary chat expiration
ci: update related tests
refactor: update getAppConfig call in getCustomConfigSpeech to include user role
fix: update appConfig usage to access allowedDomains from actions instead of registration
refactor: enhance AppConfig to include fileStrategies and update related file strategy logic
refactor: update imports to use normalizeEndpointName from @librechat/api and remove redundant definitions
chore: remove deprecated unused RunManager
refactor: get balance config primarily from appConfig
refactor: remove customConfig dependency for appConfig and streamline loadConfigModels logic
refactor: remove getCustomConfig usage and use app config in file citations
refactor: consolidate endpoint loading logic into loadEndpoints function
refactor: update appConfig access to use endpoints structure across various services
refactor: implement custom endpoints configuration and streamline endpoint loading logic
refactor: update getAppConfig call to include user role parameter
refactor: streamline endpoint configuration and enhance appConfig usage across services
refactor: replace getMCPAuthMap with getUserMCPAuthMap and remove unused getCustomConfig file
refactor: add type annotation for loadedEndpoints in loadEndpoints function
refactor: move /services/Files/images/parse to TS API
chore: add missing FILE_CITATIONS permission to IRole interface
refactor: restructure toolkits to TS API
refactor: separate manifest logic into its own module
refactor: consolidate tool loading logic into a new tools module for startup logic
refactor: move interface config logic to TS API
refactor: migrate checkEmailConfig to TypeScript and update imports
refactor: add FunctionTool interface and availableTools to AppConfig
refactor: decouple caching and DB operations from AppService, make part of consolidated `getAppConfig`
WIP: fix tests
* fix: rebase conflicts
* refactor: remove app.locals references
* refactor: replace getBalanceConfig with getAppConfig in various strategies and middleware
* refactor: replace appConfig?.balance with getBalanceConfig in various controllers and clients
* test: add balance configuration to titleConvo method in AgentClient tests
* chore: remove unused `openai-chat-tokens` package
* chore: remove unused imports in initializeMCPs.js
* refactor: update balance configuration to use getAppConfig instead of getBalanceConfig
* refactor: integrate configMiddleware for centralized configuration handling
* refactor: optimize email domain validation by removing unnecessary async calls
* refactor: simplify multer storage configuration by removing async calls
* refactor: reorder imports for better readability in user.js
* refactor: replace getAppConfig calls with req.config for improved performance
* chore: replace getAppConfig calls with req.config in tests for centralized configuration handling
* chore: remove unused override config
* refactor: add configMiddleware to endpoint route and replace getAppConfig with req.config
* chore: remove customConfig parameter from TTSService constructor
* refactor: pass appConfig from request to processFileCitations for improved configuration handling
* refactor: remove configMiddleware from endpoint route and retrieve appConfig directly in getEndpointsConfig if not in `req.config`
* test: add mockAppConfig to processFileCitations tests for improved configuration handling
* fix: pass req.config to hasCustomUserVars and call without await after synchronous refactor
* fix: type safety in useExportConversation
* refactor: retrieve appConfig using getAppConfig in PluginController and remove configMiddleware from plugins route, to avoid always retrieving when plugins are cached
* chore: change `MongoUser` typedef to `IUser`
* fix: Add `user` and `config` fields to ServerRequest and update JSDoc type annotations from Express.Request to ServerRequest
* fix: remove unused setAppConfig mock from Server configuration tests
613 lines
19 KiB
JavaScript
613 lines
19 KiB
JavaScript
const express = require('express');
|
|
const request = require('supertest');
|
|
const mongoose = require('mongoose');
|
|
const { ObjectId } = require('mongodb');
|
|
const { MongoMemoryServer } = require('mongodb-memory-server');
|
|
const {
|
|
SystemRoles,
|
|
ResourceType,
|
|
AccessRoleIds,
|
|
PrincipalType,
|
|
PermissionBits,
|
|
} = require('librechat-data-provider');
|
|
|
|
// Mock modules before importing
|
|
jest.mock('~/server/services/Config', () => ({
|
|
getCachedTools: jest.fn().mockResolvedValue({}),
|
|
}));
|
|
|
|
jest.mock('~/models/Role', () => ({
|
|
getRoleByName: jest.fn(),
|
|
}));
|
|
|
|
jest.mock('~/server/middleware', () => ({
|
|
requireJwtAuth: (req, res, next) => next(),
|
|
canAccessPromptViaGroup: jest.requireActual('~/server/middleware').canAccessPromptViaGroup,
|
|
canAccessPromptGroupResource:
|
|
jest.requireActual('~/server/middleware').canAccessPromptGroupResource,
|
|
}));
|
|
|
|
let app;
|
|
let mongoServer;
|
|
let promptRoutes;
|
|
let Prompt, PromptGroup, AclEntry, AccessRole, User;
|
|
let testUsers, testRoles;
|
|
let grantPermission;
|
|
|
|
// Helper function to set user in middleware
|
|
function setTestUser(app, user) {
|
|
app.use((req, res, next) => {
|
|
req.user = {
|
|
...(user.toObject ? user.toObject() : user),
|
|
id: user.id || user._id.toString(),
|
|
_id: user._id,
|
|
name: user.name,
|
|
role: user.role,
|
|
};
|
|
if (user.role === SystemRoles.ADMIN) {
|
|
console.log('Setting admin user with role:', req.user.role);
|
|
}
|
|
next();
|
|
});
|
|
}
|
|
|
|
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;
|
|
|
|
// 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());
|
|
|
|
// Mock authentication middleware - default to owner
|
|
setTestUser(app, testUsers.owner);
|
|
|
|
// Import routes after mocks are set up
|
|
promptRoutes = require('./prompts');
|
|
app.use('/api/prompts', promptRoutes);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await mongoose.disconnect();
|
|
await mongoServer.stop();
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
async function setupTestData() {
|
|
// Create access roles for promptGroups
|
|
testRoles = {
|
|
viewer: await AccessRole.create({
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_VIEWER,
|
|
name: 'Viewer',
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
permBits: PermissionBits.VIEW,
|
|
}),
|
|
editor: await AccessRole.create({
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_EDITOR,
|
|
name: 'Editor',
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
permBits: PermissionBits.VIEW | PermissionBits.EDIT,
|
|
}),
|
|
owner: await AccessRole.create({
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_OWNER,
|
|
name: 'Owner',
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
permBits:
|
|
PermissionBits.VIEW | PermissionBits.EDIT | PermissionBits.DELETE | PermissionBits.SHARE,
|
|
}),
|
|
};
|
|
|
|
// Create test users
|
|
testUsers = {
|
|
owner: await User.create({
|
|
id: new ObjectId().toString(),
|
|
_id: new ObjectId(),
|
|
name: 'Prompt Owner',
|
|
email: 'owner@example.com',
|
|
role: SystemRoles.USER,
|
|
}),
|
|
viewer: await User.create({
|
|
id: new ObjectId().toString(),
|
|
_id: new ObjectId(),
|
|
name: 'Prompt Viewer',
|
|
email: 'viewer@example.com',
|
|
role: SystemRoles.USER,
|
|
}),
|
|
editor: await User.create({
|
|
id: new ObjectId().toString(),
|
|
_id: new ObjectId(),
|
|
name: 'Prompt Editor',
|
|
email: 'editor@example.com',
|
|
role: SystemRoles.USER,
|
|
}),
|
|
noAccess: await User.create({
|
|
id: new ObjectId().toString(),
|
|
_id: new ObjectId(),
|
|
name: 'No Access',
|
|
email: 'noaccess@example.com',
|
|
role: SystemRoles.USER,
|
|
}),
|
|
admin: await User.create({
|
|
id: new ObjectId().toString(),
|
|
_id: new ObjectId(),
|
|
name: 'Admin',
|
|
email: 'admin@example.com',
|
|
role: SystemRoles.ADMIN,
|
|
}),
|
|
};
|
|
|
|
// Mock getRoleByName
|
|
const { getRoleByName } = require('~/models/Role');
|
|
getRoleByName.mockImplementation((roleName) => {
|
|
switch (roleName) {
|
|
case SystemRoles.USER:
|
|
return { permissions: { PROMPTS: { USE: true, CREATE: true } } };
|
|
case SystemRoles.ADMIN:
|
|
return { permissions: { PROMPTS: { USE: true, CREATE: true, SHARED_GLOBAL: true } } };
|
|
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');
|
|
console.log('Test 404 response status:', response.status);
|
|
console.log('Test 404 response body:', response.body);
|
|
// 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);
|
|
|
|
if (response.status !== 200) {
|
|
console.log('POST /api/prompts error status:', response.status);
|
|
console.log('POST /api/prompts error body:', response.body);
|
|
console.log('Console errors:', consoleErrorSpy.mock.calls);
|
|
}
|
|
|
|
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({
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
resourceId: response.body.prompt.groupId,
|
|
principalType: PrincipalType.USER,
|
|
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({
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
resourceId: response.body.group._id,
|
|
principalType: PrincipalType.USER,
|
|
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({
|
|
principalType: PrincipalType.USER,
|
|
principalId: testUsers.owner._id,
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
resourceId: testGroup._id,
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_VIEWER,
|
|
grantedBy: testUsers.owner._id,
|
|
});
|
|
|
|
const response = await request(app).get(`/api/prompts/${testPrompt._id}`);
|
|
expect(response.status).toBe(200);
|
|
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 () => {
|
|
// First, reset the app to remove previous middleware
|
|
app = express();
|
|
app.use(express.json());
|
|
|
|
// Set admin user BEFORE adding routes
|
|
app.use((req, res, next) => {
|
|
req.user = {
|
|
...testUsers.admin.toObject(),
|
|
id: testUsers.admin._id.toString(),
|
|
_id: testUsers.admin._id,
|
|
name: testUsers.admin.name,
|
|
role: testUsers.admin.role,
|
|
};
|
|
next();
|
|
});
|
|
|
|
// Now add the routes
|
|
const promptRoutes = require('./prompts');
|
|
app.use('/api/prompts', promptRoutes);
|
|
|
|
console.log('Admin user:', testUsers.admin);
|
|
console.log('Admin role:', testUsers.admin.role);
|
|
console.log('SystemRoles.ADMIN:', SystemRoles.ADMIN);
|
|
|
|
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({
|
|
principalType: PrincipalType.USER,
|
|
principalId: testUsers.owner._id,
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
resourceId: testGroup._id,
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_OWNER,
|
|
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({
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
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({
|
|
principalType: PrincipalType.USER,
|
|
principalId: testUsers.viewer._id,
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
resourceId: testGroup._id,
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_VIEWER,
|
|
grantedBy: testUsers.editor._id,
|
|
});
|
|
|
|
// Recreate app with viewer user
|
|
app = express();
|
|
app.use(express.json());
|
|
app.use((req, res, next) => {
|
|
req.user = {
|
|
...testUsers.viewer.toObject(),
|
|
id: testUsers.viewer._id.toString(),
|
|
_id: testUsers.viewer._id,
|
|
name: testUsers.viewer.name,
|
|
role: testUsers.viewer.role,
|
|
};
|
|
next();
|
|
});
|
|
const promptRoutes = require('./prompts');
|
|
app.use('/api/prompts', promptRoutes);
|
|
|
|
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({
|
|
principalType: PrincipalType.USER,
|
|
principalId: testUsers.owner._id,
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
resourceId: testGroup._id,
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_EDITOR,
|
|
grantedBy: testUsers.owner._id,
|
|
});
|
|
|
|
// Recreate app to ensure fresh middleware
|
|
app = express();
|
|
app.use(express.json());
|
|
app.use((req, res, next) => {
|
|
req.user = {
|
|
...testUsers.owner.toObject(),
|
|
id: testUsers.owner._id.toString(),
|
|
_id: testUsers.owner._id,
|
|
name: testUsers.owner.name,
|
|
role: testUsers.owner.role,
|
|
};
|
|
next();
|
|
});
|
|
const promptRoutes = require('./prompts');
|
|
app.use('/api/prompts', promptRoutes);
|
|
|
|
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({
|
|
principalType: PrincipalType.USER,
|
|
principalId: testUsers.viewer._id,
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
resourceId: testGroup._id,
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_VIEWER,
|
|
grantedBy: testUsers.owner._id,
|
|
});
|
|
|
|
// Recreate app with viewer user
|
|
app = express();
|
|
app.use(express.json());
|
|
app.use((req, res, next) => {
|
|
req.user = {
|
|
...testUsers.viewer.toObject(),
|
|
id: testUsers.viewer._id.toString(),
|
|
_id: testUsers.viewer._id,
|
|
name: testUsers.viewer.name,
|
|
role: testUsers.viewer.role,
|
|
};
|
|
next();
|
|
});
|
|
const promptRoutes = require('./prompts');
|
|
app.use('/api/prompts', promptRoutes);
|
|
|
|
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({
|
|
principalType: PrincipalType.PUBLIC,
|
|
principalId: null,
|
|
resourceType: ResourceType.PROMPTGROUP,
|
|
resourceId: publicGroup._id,
|
|
accessRoleId: AccessRoleIds.PROMPTGROUP_VIEWER,
|
|
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());
|
|
});
|
|
});
|
|
});
|