mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-23 18:56:12 +01:00
🔧 refactor: Permission handling for Resource Sharing (#11283)
* 🔧 refactor: permission handling for public sharing - Updated permission keys from SHARED_GLOBAL to SHARE across various files for consistency. - Added public access configuration in librechat.example.yaml. - Adjusted related tests and components to reflect the new permission structure. * chore: Update default SHARE permission to false * fix: Update SHARE permissions in tests and implementation - Added SHARE permission handling for user and admin roles in permissions.spec.ts and permissions.ts. - Updated expected permissions in tests to reflect new SHARE permission values for various permission types. * fix: Handle undefined values in PeoplePickerAdminSettings component - Updated the checked and value props of the Switch component to handle undefined values gracefully by defaulting to false. This ensures consistent behavior when the field value is not set. * feat: Add CREATE permission handling for prompts and agents - Introduced CREATE permission for user and admin roles in permissions.spec.ts and permissions.ts. - Updated expected permissions in tests to include CREATE permission for various permission types. * 🔧 refactor: Enhance permission handling for sharing dialog usability * refactor: public sharing permissions for resources - Added middleware to check SHARE_PUBLIC permissions for agents, prompts, and MCP servers. - Updated interface configuration in librechat.example.yaml to include public sharing options. - Enhanced components and hooks to support public sharing functionality. - Adjusted tests to validate new permission handling for public sharing across various resource types. * refactor: update Share2Icon styling in GenericGrantAccessDialog * refactor: update Share2Icon size in GenericGrantAccessDialog for consistency * refactor: improve layout and styling of Share2Icon in GenericGrantAccessDialog * refactor: update Share2Icon size in GenericGrantAccessDialog for improved consistency * chore: remove redundant public sharing option from People Picker * refactor: add SHARE_PUBLIC permission handling in updateInterfacePermissions tests
This commit is contained in:
parent
083251508e
commit
76e17ba701
32 changed files with 646 additions and 109 deletions
|
|
@ -46,7 +46,7 @@ describe('updateAccessPermissions', () => {
|
|||
[PermissionTypes.PROMPTS]: {
|
||||
CREATE: true,
|
||||
USE: true,
|
||||
SHARED_GLOBAL: false,
|
||||
SHARE: false,
|
||||
},
|
||||
},
|
||||
}).save();
|
||||
|
|
@ -55,7 +55,7 @@ describe('updateAccessPermissions', () => {
|
|||
[PermissionTypes.PROMPTS]: {
|
||||
CREATE: true,
|
||||
USE: true,
|
||||
SHARED_GLOBAL: true,
|
||||
SHARE: true,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ describe('updateAccessPermissions', () => {
|
|||
expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({
|
||||
CREATE: true,
|
||||
USE: true,
|
||||
SHARED_GLOBAL: true,
|
||||
SHARE: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ describe('updateAccessPermissions', () => {
|
|||
[PermissionTypes.PROMPTS]: {
|
||||
CREATE: true,
|
||||
USE: true,
|
||||
SHARED_GLOBAL: false,
|
||||
SHARE: false,
|
||||
},
|
||||
},
|
||||
}).save();
|
||||
|
|
@ -83,7 +83,7 @@ describe('updateAccessPermissions', () => {
|
|||
[PermissionTypes.PROMPTS]: {
|
||||
CREATE: true,
|
||||
USE: true,
|
||||
SHARED_GLOBAL: false,
|
||||
SHARE: false,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ describe('updateAccessPermissions', () => {
|
|||
expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({
|
||||
CREATE: true,
|
||||
USE: true,
|
||||
SHARED_GLOBAL: false,
|
||||
SHARE: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -110,20 +110,20 @@ describe('updateAccessPermissions', () => {
|
|||
[PermissionTypes.PROMPTS]: {
|
||||
CREATE: true,
|
||||
USE: true,
|
||||
SHARED_GLOBAL: false,
|
||||
SHARE: false,
|
||||
},
|
||||
},
|
||||
}).save();
|
||||
|
||||
await updateAccessPermissions(SystemRoles.USER, {
|
||||
[PermissionTypes.PROMPTS]: { SHARED_GLOBAL: true },
|
||||
[PermissionTypes.PROMPTS]: { SHARE: true },
|
||||
});
|
||||
|
||||
const updatedRole = await getRoleByName(SystemRoles.USER);
|
||||
expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({
|
||||
CREATE: true,
|
||||
USE: true,
|
||||
SHARED_GLOBAL: true,
|
||||
SHARE: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -134,7 +134,7 @@ describe('updateAccessPermissions', () => {
|
|||
[PermissionTypes.PROMPTS]: {
|
||||
CREATE: true,
|
||||
USE: true,
|
||||
SHARED_GLOBAL: false,
|
||||
SHARE: false,
|
||||
},
|
||||
},
|
||||
}).save();
|
||||
|
|
@ -147,7 +147,7 @@ describe('updateAccessPermissions', () => {
|
|||
expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({
|
||||
CREATE: true,
|
||||
USE: false,
|
||||
SHARED_GLOBAL: false,
|
||||
SHARE: false,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -155,13 +155,13 @@ describe('updateAccessPermissions', () => {
|
|||
await new Role({
|
||||
name: SystemRoles.USER,
|
||||
permissions: {
|
||||
[PermissionTypes.PROMPTS]: { CREATE: true, USE: true, SHARED_GLOBAL: false },
|
||||
[PermissionTypes.PROMPTS]: { CREATE: true, USE: true, SHARE: false },
|
||||
[PermissionTypes.BOOKMARKS]: { USE: true },
|
||||
},
|
||||
}).save();
|
||||
|
||||
await updateAccessPermissions(SystemRoles.USER, {
|
||||
[PermissionTypes.PROMPTS]: { USE: false, SHARED_GLOBAL: true },
|
||||
[PermissionTypes.PROMPTS]: { USE: false, SHARE: true },
|
||||
[PermissionTypes.BOOKMARKS]: { USE: false },
|
||||
});
|
||||
|
||||
|
|
@ -169,7 +169,7 @@ describe('updateAccessPermissions', () => {
|
|||
expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({
|
||||
CREATE: true,
|
||||
USE: false,
|
||||
SHARED_GLOBAL: true,
|
||||
SHARE: true,
|
||||
});
|
||||
expect(updatedRole.permissions[PermissionTypes.BOOKMARKS]).toEqual({ USE: false });
|
||||
});
|
||||
|
|
@ -178,19 +178,19 @@ describe('updateAccessPermissions', () => {
|
|||
await new Role({
|
||||
name: SystemRoles.USER,
|
||||
permissions: {
|
||||
[PermissionTypes.PROMPTS]: { CREATE: true, USE: true, SHARED_GLOBAL: false },
|
||||
[PermissionTypes.PROMPTS]: { CREATE: true, USE: true, SHARE: false },
|
||||
},
|
||||
}).save();
|
||||
|
||||
await updateAccessPermissions(SystemRoles.USER, {
|
||||
[PermissionTypes.PROMPTS]: { USE: false, SHARED_GLOBAL: true },
|
||||
[PermissionTypes.PROMPTS]: { USE: false, SHARE: true },
|
||||
});
|
||||
|
||||
const updatedRole = await getRoleByName(SystemRoles.USER);
|
||||
expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({
|
||||
CREATE: true,
|
||||
USE: false,
|
||||
SHARED_GLOBAL: true,
|
||||
SHARE: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -214,13 +214,13 @@ describe('updateAccessPermissions', () => {
|
|||
await new Role({
|
||||
name: SystemRoles.USER,
|
||||
permissions: {
|
||||
[PermissionTypes.PROMPTS]: { CREATE: true, USE: true, SHARED_GLOBAL: false },
|
||||
[PermissionTypes.PROMPTS]: { CREATE: true, USE: true, SHARE: false },
|
||||
[PermissionTypes.MULTI_CONVO]: { USE: false },
|
||||
},
|
||||
}).save();
|
||||
|
||||
await updateAccessPermissions(SystemRoles.USER, {
|
||||
[PermissionTypes.PROMPTS]: { SHARED_GLOBAL: true },
|
||||
[PermissionTypes.PROMPTS]: { SHARE: true },
|
||||
[PermissionTypes.MULTI_CONVO]: { USE: true },
|
||||
});
|
||||
|
||||
|
|
@ -228,7 +228,7 @@ describe('updateAccessPermissions', () => {
|
|||
expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({
|
||||
CREATE: true,
|
||||
USE: true,
|
||||
SHARED_GLOBAL: true,
|
||||
SHARE: true,
|
||||
});
|
||||
expect(updatedRole.permissions[PermissionTypes.MULTI_CONVO]).toEqual({ USE: true });
|
||||
});
|
||||
|
|
@ -271,7 +271,7 @@ describe('initializeRoles', () => {
|
|||
});
|
||||
|
||||
// Example: Check default values for ADMIN role
|
||||
expect(adminRole.permissions[PermissionTypes.PROMPTS].SHARED_GLOBAL).toBe(true);
|
||||
expect(adminRole.permissions[PermissionTypes.PROMPTS].SHARE).toBe(true);
|
||||
expect(adminRole.permissions[PermissionTypes.BOOKMARKS].USE).toBe(true);
|
||||
expect(adminRole.permissions[PermissionTypes.AGENTS].CREATE).toBe(true);
|
||||
});
|
||||
|
|
@ -283,7 +283,7 @@ describe('initializeRoles', () => {
|
|||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.USE]: false,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARED_GLOBAL]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
},
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: false },
|
||||
},
|
||||
|
|
@ -320,7 +320,7 @@ describe('initializeRoles', () => {
|
|||
expect(userRole.permissions[PermissionTypes.AGENTS]).toBeDefined();
|
||||
expect(userRole.permissions[PermissionTypes.AGENTS].CREATE).toBeDefined();
|
||||
expect(userRole.permissions[PermissionTypes.AGENTS].USE).toBeDefined();
|
||||
expect(userRole.permissions[PermissionTypes.AGENTS].SHARED_GLOBAL).toBeDefined();
|
||||
expect(userRole.permissions[PermissionTypes.AGENTS].SHARE).toBeDefined();
|
||||
});
|
||||
|
||||
it('should handle multiple runs without duplicating or modifying data', async () => {
|
||||
|
|
@ -348,7 +348,7 @@ describe('initializeRoles', () => {
|
|||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.USE]: false,
|
||||
[Permissions.CREATE]: false,
|
||||
[Permissions.SHARED_GLOBAL]: false,
|
||||
[Permissions.SHARE]: false,
|
||||
},
|
||||
[PermissionTypes.BOOKMARKS]:
|
||||
roleDefaults[SystemRoles.ADMIN].permissions[PermissionTypes.BOOKMARKS],
|
||||
|
|
@ -365,7 +365,7 @@ describe('initializeRoles', () => {
|
|||
expect(adminRole.permissions[PermissionTypes.AGENTS]).toBeDefined();
|
||||
expect(adminRole.permissions[PermissionTypes.AGENTS].CREATE).toBeDefined();
|
||||
expect(adminRole.permissions[PermissionTypes.AGENTS].USE).toBeDefined();
|
||||
expect(adminRole.permissions[PermissionTypes.AGENTS].SHARED_GLOBAL).toBeDefined();
|
||||
expect(adminRole.permissions[PermissionTypes.AGENTS].SHARE).toBeDefined();
|
||||
});
|
||||
|
||||
it('should include MULTI_CONVO permissions when creating default roles', async () => {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ describe('canAccessAgentResource middleware', () => {
|
|||
AGENTS: {
|
||||
USE: true,
|
||||
CREATE: true,
|
||||
SHARED_GLOBAL: false,
|
||||
SHARE: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ describe('canAccessMCPServerResource middleware', () => {
|
|||
await Role.create({
|
||||
name: 'test-role',
|
||||
permissions: {
|
||||
MCPSERVERS: {
|
||||
MCP_SERVERS: {
|
||||
USE: true,
|
||||
CREATE: true,
|
||||
SHARED_GLOBAL: false,
|
||||
SHARE: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ describe('fileAccess middleware', () => {
|
|||
AGENTS: {
|
||||
USE: true,
|
||||
CREATE: true,
|
||||
SHARED_GLOBAL: false,
|
||||
SHARE: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
84
api/server/middleware/checkSharePublicAccess.js
Normal file
84
api/server/middleware/checkSharePublicAccess.js
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
const { logger } = require('@librechat/data-schemas');
|
||||
const { ResourceType, PermissionTypes, Permissions } = require('librechat-data-provider');
|
||||
const { getRoleByName } = require('~/models/Role');
|
||||
|
||||
/**
|
||||
* Maps resource types to their corresponding permission types
|
||||
*/
|
||||
const resourceToPermissionType = {
|
||||
[ResourceType.AGENT]: PermissionTypes.AGENTS,
|
||||
[ResourceType.PROMPTGROUP]: PermissionTypes.PROMPTS,
|
||||
[ResourceType.MCPSERVER]: PermissionTypes.MCP_SERVERS,
|
||||
};
|
||||
|
||||
/**
|
||||
* Middleware to check if user has SHARE_PUBLIC permission for a resource type
|
||||
* Only enforced when request body contains `public: true`
|
||||
* @param {import('express').Request} req - Express request
|
||||
* @param {import('express').Response} res - Express response
|
||||
* @param {import('express').NextFunction} next - Express next function
|
||||
*/
|
||||
const checkSharePublicAccess = async (req, res, next) => {
|
||||
try {
|
||||
const { public: isPublic } = req.body;
|
||||
|
||||
// Only check if trying to enable public sharing
|
||||
if (!isPublic) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const user = req.user;
|
||||
if (!user || !user.role) {
|
||||
return res.status(401).json({
|
||||
error: 'Unauthorized',
|
||||
message: 'Authentication required',
|
||||
});
|
||||
}
|
||||
|
||||
const { resourceType } = req.params;
|
||||
const permissionType = resourceToPermissionType[resourceType];
|
||||
|
||||
if (!permissionType) {
|
||||
return res.status(400).json({
|
||||
error: 'Bad Request',
|
||||
message: `Unsupported resource type for public sharing: ${resourceType}`,
|
||||
});
|
||||
}
|
||||
|
||||
const role = await getRoleByName(user.role);
|
||||
if (!role || !role.permissions) {
|
||||
return res.status(403).json({
|
||||
error: 'Forbidden',
|
||||
message: 'No permissions configured for user role',
|
||||
});
|
||||
}
|
||||
|
||||
const resourcePerms = role.permissions[permissionType] || {};
|
||||
const canSharePublic = resourcePerms[Permissions.SHARE_PUBLIC] === true;
|
||||
|
||||
if (!canSharePublic) {
|
||||
logger.warn(
|
||||
`[checkSharePublicAccess][${user.id}] User denied SHARE_PUBLIC for ${resourceType}`,
|
||||
);
|
||||
return res.status(403).json({
|
||||
error: 'Forbidden',
|
||||
message: `You do not have permission to share ${resourceType} resources publicly`,
|
||||
});
|
||||
}
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`[checkSharePublicAccess][${req.user?.id}] Error checking SHARE_PUBLIC permission`,
|
||||
error,
|
||||
);
|
||||
return res.status(500).json({
|
||||
error: 'Internal Server Error',
|
||||
message: 'Failed to check public sharing permissions',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
checkSharePublicAccess,
|
||||
};
|
||||
164
api/server/middleware/checkSharePublicAccess.spec.js
Normal file
164
api/server/middleware/checkSharePublicAccess.spec.js
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
const { ResourceType, PermissionTypes, Permissions } = require('librechat-data-provider');
|
||||
const { checkSharePublicAccess } = require('./checkSharePublicAccess');
|
||||
const { getRoleByName } = require('~/models/Role');
|
||||
|
||||
jest.mock('~/models/Role');
|
||||
|
||||
describe('checkSharePublicAccess middleware', () => {
|
||||
let mockReq;
|
||||
let mockRes;
|
||||
let mockNext;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockReq = {
|
||||
user: { id: 'user123', role: 'USER' },
|
||||
params: { resourceType: ResourceType.AGENT },
|
||||
body: {},
|
||||
};
|
||||
mockRes = {
|
||||
status: jest.fn().mockReturnThis(),
|
||||
json: jest.fn(),
|
||||
};
|
||||
mockNext = jest.fn();
|
||||
});
|
||||
|
||||
it('should call next() when public is not true', async () => {
|
||||
mockReq.body = { public: false };
|
||||
|
||||
await checkSharePublicAccess(mockReq, mockRes, mockNext);
|
||||
|
||||
expect(mockNext).toHaveBeenCalled();
|
||||
expect(mockRes.status).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call next() when public is undefined', async () => {
|
||||
mockReq.body = { updated: [] };
|
||||
|
||||
await checkSharePublicAccess(mockReq, mockRes, mockNext);
|
||||
|
||||
expect(mockNext).toHaveBeenCalled();
|
||||
expect(mockRes.status).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return 401 when user is not authenticated', async () => {
|
||||
mockReq.body = { public: true };
|
||||
mockReq.user = null;
|
||||
|
||||
await checkSharePublicAccess(mockReq, mockRes, mockNext);
|
||||
|
||||
expect(mockRes.status).toHaveBeenCalledWith(401);
|
||||
expect(mockRes.json).toHaveBeenCalledWith({
|
||||
error: 'Unauthorized',
|
||||
message: 'Authentication required',
|
||||
});
|
||||
expect(mockNext).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return 403 when user role has no SHARE_PUBLIC permission for agents', async () => {
|
||||
mockReq.body = { public: true };
|
||||
mockReq.params = { resourceType: ResourceType.AGENT };
|
||||
getRoleByName.mockResolvedValue({
|
||||
permissions: {
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await checkSharePublicAccess(mockReq, mockRes, mockNext);
|
||||
|
||||
expect(mockRes.status).toHaveBeenCalledWith(403);
|
||||
expect(mockRes.json).toHaveBeenCalledWith({
|
||||
error: 'Forbidden',
|
||||
message: `You do not have permission to share ${ResourceType.AGENT} resources publicly`,
|
||||
});
|
||||
expect(mockNext).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call next() when user has SHARE_PUBLIC permission for agents', async () => {
|
||||
mockReq.body = { public: true };
|
||||
mockReq.params = { resourceType: ResourceType.AGENT };
|
||||
getRoleByName.mockResolvedValue({
|
||||
permissions: {
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await checkSharePublicAccess(mockReq, mockRes, mockNext);
|
||||
|
||||
expect(mockNext).toHaveBeenCalled();
|
||||
expect(mockRes.status).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should check prompts permission for promptgroup resource type', async () => {
|
||||
mockReq.body = { public: true };
|
||||
mockReq.params = { resourceType: ResourceType.PROMPTGROUP };
|
||||
getRoleByName.mockResolvedValue({
|
||||
permissions: {
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await checkSharePublicAccess(mockReq, mockRes, mockNext);
|
||||
|
||||
expect(mockNext).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should check mcp_servers permission for mcpserver resource type', async () => {
|
||||
mockReq.body = { public: true };
|
||||
mockReq.params = { resourceType: ResourceType.MCPSERVER };
|
||||
getRoleByName.mockResolvedValue({
|
||||
permissions: {
|
||||
[PermissionTypes.MCP_SERVERS]: {
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await checkSharePublicAccess(mockReq, mockRes, mockNext);
|
||||
|
||||
expect(mockNext).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return 400 for unsupported resource type', async () => {
|
||||
mockReq.body = { public: true };
|
||||
mockReq.params = { resourceType: 'unsupported' };
|
||||
|
||||
await checkSharePublicAccess(mockReq, mockRes, mockNext);
|
||||
|
||||
expect(mockRes.status).toHaveBeenCalledWith(400);
|
||||
expect(mockRes.json).toHaveBeenCalledWith({
|
||||
error: 'Bad Request',
|
||||
message: 'Unsupported resource type for public sharing: unsupported',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return 403 when role has no permissions object', async () => {
|
||||
mockReq.body = { public: true };
|
||||
getRoleByName.mockResolvedValue({ permissions: null });
|
||||
|
||||
await checkSharePublicAccess(mockReq, mockRes, mockNext);
|
||||
|
||||
expect(mockRes.status).toHaveBeenCalledWith(403);
|
||||
});
|
||||
|
||||
it('should return 500 on error', async () => {
|
||||
mockReq.body = { public: true };
|
||||
getRoleByName.mockRejectedValue(new Error('Database error'));
|
||||
|
||||
await checkSharePublicAccess(mockReq, mockRes, mockNext);
|
||||
|
||||
expect(mockRes.status).toHaveBeenCalledWith(500);
|
||||
expect(mockRes.json).toHaveBeenCalledWith({
|
||||
error: 'Internal Server Error',
|
||||
message: 'Failed to check public sharing permissions',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -51,9 +51,9 @@ describe('Access Middleware', () => {
|
|||
permissions: {
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.SHARED_GLOBAL]: false,
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
},
|
||||
[PermissionTypes.MEMORIES]: {
|
||||
[Permissions.USE]: true,
|
||||
|
|
@ -65,7 +65,7 @@ describe('Access Middleware', () => {
|
|||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: false,
|
||||
[Permissions.SHARED_GLOBAL]: false,
|
||||
[Permissions.SHARE]: false,
|
||||
},
|
||||
[PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true },
|
||||
|
|
@ -79,9 +79,9 @@ describe('Access Middleware', () => {
|
|||
permissions: {
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.SHARED_GLOBAL]: true,
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
},
|
||||
[PermissionTypes.MEMORIES]: {
|
||||
[Permissions.USE]: true,
|
||||
|
|
@ -93,7 +93,7 @@ describe('Access Middleware', () => {
|
|||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARED_GLOBAL]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
},
|
||||
[PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true },
|
||||
|
|
@ -110,7 +110,7 @@ describe('Access Middleware', () => {
|
|||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.USE]: false,
|
||||
[Permissions.CREATE]: false,
|
||||
[Permissions.SHARED_GLOBAL]: false,
|
||||
[Permissions.SHARE]: false,
|
||||
},
|
||||
// Has permissions for other types
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
|
|
@ -241,7 +241,7 @@ describe('Access Middleware', () => {
|
|||
req: {},
|
||||
user: { id: 'admin123', role: 'admin' },
|
||||
permissionType: PermissionTypes.AGENTS,
|
||||
permissions: [Permissions.SHARED_GLOBAL],
|
||||
permissions: [Permissions.SHARE],
|
||||
getRoleByName,
|
||||
});
|
||||
expect(shareResult).toBe(true);
|
||||
|
|
@ -318,7 +318,7 @@ describe('Access Middleware', () => {
|
|||
|
||||
const middleware = generateCheckAccess({
|
||||
permissionType: PermissionTypes.AGENTS,
|
||||
permissions: [Permissions.USE, Permissions.CREATE, Permissions.SHARED_GLOBAL],
|
||||
permissions: [Permissions.USE, Permissions.CREATE, Permissions.SHARE],
|
||||
getRoleByName,
|
||||
});
|
||||
await middleware(req, res, next);
|
||||
|
|
@ -349,7 +349,7 @@ describe('Access Middleware', () => {
|
|||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.USE]: false,
|
||||
[Permissions.CREATE]: false,
|
||||
[Permissions.SHARED_GLOBAL]: false,
|
||||
[Permissions.SHARE]: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ const {
|
|||
} = require('~/server/controllers/PermissionsController');
|
||||
const { requireJwtAuth, checkBan, uaParser, canAccessResource } = require('~/server/middleware');
|
||||
const { checkPeoplePickerAccess } = require('~/server/middleware/checkPeoplePickerAccess');
|
||||
const { checkSharePublicAccess } = require('~/server/middleware/checkSharePublicAccess');
|
||||
const { findMCPServerById } = require('~/models');
|
||||
|
||||
const router = express.Router();
|
||||
|
|
@ -91,10 +92,12 @@ router.get(
|
|||
* PUT /api/permissions/{resourceType}/{resourceId}
|
||||
* Bulk update permissions for a specific resource
|
||||
* SECURITY: Requires SHARE permission to modify resource permissions
|
||||
* SECURITY: Requires SHARE_PUBLIC permission to enable public sharing
|
||||
*/
|
||||
router.put(
|
||||
'/:resourceType/:resourceId',
|
||||
checkResourcePermissionAccess(PermissionBits.SHARE),
|
||||
checkSharePublicAccess,
|
||||
updateResourcePermissions,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const checkGlobalAgentShare = generateCheckAccess({
|
|||
permissionType: PermissionTypes.AGENTS,
|
||||
permissions: [Permissions.USE, Permissions.CREATE],
|
||||
bodyProps: {
|
||||
[Permissions.SHARED_GLOBAL]: ['projectIds', 'removeProjectIds'],
|
||||
[Permissions.SHARE]: ['projectIds', 'removeProjectIds'],
|
||||
},
|
||||
getRoleByName,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ const checkGlobalPromptShare = generateCheckAccess({
|
|||
permissionType: PermissionTypes.PROMPTS,
|
||||
permissions: [Permissions.USE, Permissions.CREATE],
|
||||
bodyProps: {
|
||||
[Permissions.SHARED_GLOBAL]: ['projectIds', 'removeProjectIds'],
|
||||
[Permissions.SHARE]: ['projectIds', 'removeProjectIds'],
|
||||
},
|
||||
getRoleByName,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ async function setupTestData() {
|
|||
case SystemRoles.USER:
|
||||
return { permissions: { PROMPTS: { USE: true, CREATE: true } } };
|
||||
case SystemRoles.ADMIN:
|
||||
return { permissions: { PROMPTS: { USE: true, CREATE: true, SHARED_GLOBAL: true } } };
|
||||
return { permissions: { PROMPTS: { USE: true, CREATE: true, SHARE: true } } };
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@ import { useLocalize } from '~/hooks';
|
|||
import type { PermissionConfig } from '~/components/ui';
|
||||
|
||||
const permissions: PermissionConfig[] = [
|
||||
{ permission: Permissions.SHARED_GLOBAL, labelKey: 'com_ui_prompts_allow_share' },
|
||||
{ permission: Permissions.CREATE, labelKey: 'com_ui_prompts_allow_create' },
|
||||
{ permission: Permissions.USE, labelKey: 'com_ui_prompts_allow_use' },
|
||||
{ permission: Permissions.CREATE, labelKey: 'com_ui_prompts_allow_create' },
|
||||
{ permission: Permissions.SHARE, labelKey: 'com_ui_prompts_allow_share' },
|
||||
{ permission: Permissions.SHARE_PUBLIC, labelKey: 'com_ui_prompts_allow_share_public' },
|
||||
];
|
||||
|
||||
const AdminSettings = () => {
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ const RightPanel = React.memo(
|
|||
const editorMode = useRecoilValue(store.promptsEditorMode);
|
||||
const hasShareAccess = useHasAccess({
|
||||
permissionType: PermissionTypes.PROMPTS,
|
||||
permission: Permissions.SHARED_GLOBAL,
|
||||
permission: Permissions.SHARE,
|
||||
});
|
||||
|
||||
const updateGroupMutation = useUpdatePromptGroup({
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ const SharePrompt = React.memo(
|
|||
({ group, disabled }: { group?: TPromptGroup; disabled: boolean }) => {
|
||||
const { user } = useAuthContext();
|
||||
|
||||
// Check if user has permission to share prompts globally
|
||||
// Check if user has permission to share prompts
|
||||
const hasAccessToSharePrompts = useHasAccess({
|
||||
permissionType: PermissionTypes.PROMPTS,
|
||||
permission: Permissions.SHARED_GLOBAL,
|
||||
permission: Permissions.SHARE,
|
||||
});
|
||||
|
||||
// Check user's permissions on this specific promptGroup
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import {
|
|||
usePeoplePickerPermissions,
|
||||
useResourcePermissionState,
|
||||
useCopyToClipboard,
|
||||
useCanSharePublic,
|
||||
useLocalize,
|
||||
} from '~/hooks';
|
||||
import UnifiedPeopleSearch from './PeoplePicker/UnifiedPeopleSearch';
|
||||
|
|
@ -33,6 +34,7 @@ export default function GenericGrantAccessDialog({
|
|||
resourceType,
|
||||
onGrantAccess,
|
||||
disabled = false,
|
||||
buttonClassName,
|
||||
children,
|
||||
}: {
|
||||
resourceDbId?: string | null;
|
||||
|
|
@ -41,15 +43,19 @@ export default function GenericGrantAccessDialog({
|
|||
resourceType: ResourceType;
|
||||
onGrantAccess?: (shares: TPrincipal[], isPublic: boolean, publicRole?: AccessRoleIds) => void;
|
||||
disabled?: boolean;
|
||||
buttonClassName?: string;
|
||||
children?: React.ReactNode;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
const { showToast } = useToastContext();
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [isCopying, setIsCopying] = useState(false);
|
||||
|
||||
// Use shared hooks
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const canSharePublic = useCanSharePublic(resourceType);
|
||||
const { hasPeoplePickerAccess, peoplePickerTypeFilter } = usePeoplePickerPermissions();
|
||||
|
||||
/** User can use the share dialog if they have people picker access OR can share publicly */
|
||||
const canUseShareDialog = hasPeoplePickerAccess || canSharePublic;
|
||||
|
||||
const {
|
||||
config,
|
||||
permissionsData,
|
||||
|
|
@ -65,7 +71,7 @@ export default function GenericGrantAccessDialog({
|
|||
setPublicRole,
|
||||
} = useResourcePermissionState(resourceType, resourceDbId, isModalOpen);
|
||||
|
||||
// State for unified list of all shares (existing + newly added)
|
||||
/** State for unified list of all shares (existing + newly added) */
|
||||
const [allShares, setAllShares] = useState<TPrincipal[]>([]);
|
||||
const [hasChanges, setHasChanges] = useState(false);
|
||||
const [defaultPermissionId, setDefaultPermissionId] = useState<AccessRoleIds | undefined>(
|
||||
|
|
@ -88,6 +94,11 @@ export default function GenericGrantAccessDialog({
|
|||
return null;
|
||||
}
|
||||
|
||||
// Don't render if user has no useful sharing permissions
|
||||
if (!canUseShareDialog) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!config) {
|
||||
console.error(`Unsupported resource type: ${resourceType}`);
|
||||
return null;
|
||||
|
|
@ -238,11 +249,11 @@ export default function GenericGrantAccessDialog({
|
|||
})}
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
className="h-full"
|
||||
className={cn('h-9', buttonClassName)}
|
||||
>
|
||||
<div className="flex min-w-[32px] items-center justify-center gap-2 text-blue-500">
|
||||
<span className="flex h-6 w-6 items-center justify-center">
|
||||
<Share2Icon className="icon-md h-4 w-4" aria-hidden="true" />
|
||||
<Share2Icon className="icon-md h-4 w-4" />
|
||||
</span>
|
||||
{totalCurrentShares > 0 && (
|
||||
<Label className="cursor-pointer text-sm font-medium text-text-secondary">
|
||||
|
|
@ -332,16 +343,20 @@ export default function GenericGrantAccessDialog({
|
|||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex border-t border-border-light" />
|
||||
{canSharePublic && (
|
||||
<>
|
||||
<div className="flex border-t border-border-light" />
|
||||
|
||||
{/* Public Access Section */}
|
||||
<PublicSharingToggle
|
||||
isPublic={isPublic}
|
||||
publicRole={publicRole}
|
||||
onPublicToggle={handlePublicToggle}
|
||||
onPublicRoleChange={handlePublicRoleChange}
|
||||
resourceType={resourceType}
|
||||
/>
|
||||
{/* Public Access Section */}
|
||||
<PublicSharingToggle
|
||||
isPublic={isPublic}
|
||||
publicRole={publicRole}
|
||||
onPublicToggle={handlePublicToggle}
|
||||
onPublicRoleChange={handlePublicRoleChange}
|
||||
resourceType={resourceType}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Footer Actions */}
|
||||
<div className="flex justify-between pt-4">
|
||||
|
|
|
|||
|
|
@ -57,9 +57,9 @@ const LabelController: React.FC<LabelControllerProps> = ({
|
|||
render={({ field }) => (
|
||||
<Switch
|
||||
{...field}
|
||||
checked={field.value}
|
||||
checked={field.value ?? false}
|
||||
onCheckedChange={field.onChange}
|
||||
value={field.value.toString()}
|
||||
value={(field.value ?? false).toString()}
|
||||
aria-label={label}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ import { useLocalize } from '~/hooks';
|
|||
import type { PermissionConfig } from '~/components/ui';
|
||||
|
||||
const permissions: PermissionConfig[] = [
|
||||
{ permission: Permissions.SHARED_GLOBAL, labelKey: 'com_ui_agents_allow_share' },
|
||||
{ permission: Permissions.CREATE, labelKey: 'com_ui_agents_allow_create' },
|
||||
{ permission: Permissions.USE, labelKey: 'com_ui_agents_allow_use' },
|
||||
{ permission: Permissions.CREATE, labelKey: 'com_ui_agents_allow_create' },
|
||||
{ permission: Permissions.SHARE, labelKey: 'com_ui_agents_allow_share' },
|
||||
{ permission: Permissions.SHARE_PUBLIC, labelKey: 'com_ui_agents_allow_share_public' },
|
||||
];
|
||||
|
||||
const AdminSettings = () => {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export default function AgentFooter({
|
|||
const agent_id = useWatch({ control, name: 'id' });
|
||||
const hasAccessToShareAgents = useHasAccess({
|
||||
permissionType: PermissionTypes.AGENTS,
|
||||
permission: Permissions.SHARED_GLOBAL,
|
||||
permission: Permissions.SHARE,
|
||||
});
|
||||
const { hasPermission, isLoading: permissionsLoading } = useResourcePermissions(
|
||||
ResourceType.AGENT,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ const permissions: PermissionConfig[] = [
|
|||
{ permission: Permissions.USE, labelKey: 'com_ui_mcp_servers_allow_use' },
|
||||
{ permission: Permissions.CREATE, labelKey: 'com_ui_mcp_servers_allow_create' },
|
||||
{ permission: Permissions.SHARE, labelKey: 'com_ui_mcp_servers_allow_share' },
|
||||
{ permission: Permissions.SHARE_PUBLIC, labelKey: 'com_ui_mcp_servers_allow_share_public' },
|
||||
];
|
||||
|
||||
const MCPAdminSettings = () => {
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
export { usePeoplePickerPermissions } from './usePeoplePickerPermissions';
|
||||
export { useResourcePermissionState } from './useResourcePermissionState';
|
||||
export { useCanSharePublic } from './useCanSharePublic';
|
||||
|
|
|
|||
22
client/src/hooks/Sharing/useCanSharePublic.ts
Normal file
22
client/src/hooks/Sharing/useCanSharePublic.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import { ResourceType, PermissionTypes, Permissions } from 'librechat-data-provider';
|
||||
import { useHasAccess } from '~/hooks';
|
||||
|
||||
const resourceToPermissionMap: Record<ResourceType, PermissionTypes> = {
|
||||
[ResourceType.AGENT]: PermissionTypes.AGENTS,
|
||||
[ResourceType.PROMPTGROUP]: PermissionTypes.PROMPTS,
|
||||
[ResourceType.MCPSERVER]: PermissionTypes.MCP_SERVERS,
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to check if a user can share a specific resource type publicly (with everyone)
|
||||
* @param resourceType The type of resource to check public sharing permission for
|
||||
* @returns boolean indicating if the user can share the resource publicly
|
||||
*/
|
||||
export const useCanSharePublic = (resourceType: ResourceType): boolean => {
|
||||
const permissionType = resourceToPermissionMap[resourceType];
|
||||
const hasAccess = useHasAccess({
|
||||
permissionType,
|
||||
permission: Permissions.SHARE_PUBLIC,
|
||||
});
|
||||
return hasAccess;
|
||||
};
|
||||
|
|
@ -4,6 +4,7 @@ import { useHasAccess } from '~/hooks';
|
|||
|
||||
/**
|
||||
* Hook to check people picker permissions and return the appropriate type filter
|
||||
* Note: SHARE_PUBLIC is now per-resource type (AGENTS, PROMPTS, MCP_SERVERS), not on PEOPLE_PICKER
|
||||
* @returns Object with permission states and type filter
|
||||
*/
|
||||
export const usePeoplePickerPermissions = () => {
|
||||
|
|
|
|||
|
|
@ -699,6 +699,7 @@
|
|||
"com_ui_agents": "Agents",
|
||||
"com_ui_agents_allow_create": "Allow creating Agents",
|
||||
"com_ui_agents_allow_share": "Allow sharing Agents",
|
||||
"com_ui_agents_allow_share_public": "Allow sharing Agents publicly",
|
||||
"com_ui_agents_allow_use": "Allow using Agents",
|
||||
"com_ui_all": "all",
|
||||
"com_ui_all_proper": "All",
|
||||
|
|
@ -1088,6 +1089,7 @@
|
|||
"com_ui_mcp_servers": "MCP Servers",
|
||||
"com_ui_mcp_servers_allow_create": "Allow users to create MCP servers",
|
||||
"com_ui_mcp_servers_allow_share": "Allow users to share MCP servers",
|
||||
"com_ui_mcp_servers_allow_share_public": "Allow users to share MCP servers publicly",
|
||||
"com_ui_mcp_servers_allow_use": "Allow users to use MCP servers",
|
||||
"com_ui_mcp_title_invalid": "Title can only contain letters, numbers, and spaces",
|
||||
"com_ui_mcp_transport": "Transport",
|
||||
|
|
@ -1207,6 +1209,7 @@
|
|||
"com_ui_prompts": "Prompts",
|
||||
"com_ui_prompts_allow_create": "Allow creating Prompts",
|
||||
"com_ui_prompts_allow_share": "Allow sharing Prompts",
|
||||
"com_ui_prompts_allow_share_public": "Allow sharing Prompts publicly",
|
||||
"com_ui_prompts_allow_use": "Allow using Prompts",
|
||||
"com_ui_provider": "Provider",
|
||||
"com_ui_quality": "Quality",
|
||||
|
|
|
|||
|
|
@ -85,10 +85,16 @@ interface:
|
|||
parameters: true
|
||||
sidePanel: true
|
||||
presets: true
|
||||
prompts: true
|
||||
prompts:
|
||||
use: true
|
||||
share: false
|
||||
public: false
|
||||
bookmarks: true
|
||||
multiConvo: true
|
||||
agents: true
|
||||
agents:
|
||||
use: true
|
||||
share: false
|
||||
public: false
|
||||
peoplePicker:
|
||||
users: true
|
||||
groups: true
|
||||
|
|
@ -102,9 +108,11 @@ interface:
|
|||
# - use: Allow users to use configured MCP servers
|
||||
# - create: Allow users to create and manage new MCP servers
|
||||
# - share: Allow users to share MCP servers with other users
|
||||
# - public: Allow users to share MCP servers publicly (with everyone)
|
||||
use: false
|
||||
create: false
|
||||
share: false
|
||||
create: false
|
||||
public: false
|
||||
# Creation / edit MCP server config Dialog config example
|
||||
# trustCheckbox:
|
||||
# label:
|
||||
|
|
|
|||
|
|
@ -17,11 +17,19 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
it('should call updateAccessPermissions with the correct parameters when permission types are true', async () => {
|
||||
const config = {
|
||||
interface: {
|
||||
prompts: true,
|
||||
prompts: {
|
||||
use: true,
|
||||
share: false,
|
||||
public: false,
|
||||
},
|
||||
bookmarks: true,
|
||||
memories: true,
|
||||
multiConvo: true,
|
||||
agents: true,
|
||||
agents: {
|
||||
use: true,
|
||||
share: false,
|
||||
public: false,
|
||||
},
|
||||
temporaryChat: true,
|
||||
runCode: true,
|
||||
webSearch: true,
|
||||
|
|
@ -35,6 +43,12 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
marketplace: {
|
||||
use: true,
|
||||
},
|
||||
mcpServers: {
|
||||
use: true,
|
||||
create: true,
|
||||
share: false,
|
||||
public: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
const configDefaults = { interface: {} } as TConfigDefaults;
|
||||
|
|
@ -50,6 +64,9 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
const expectedPermissionsForUser = {
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.MEMORIES]: {
|
||||
|
|
@ -62,6 +79,9 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
[PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.RUN_CODE]: { [Permissions.USE]: true },
|
||||
|
|
@ -78,12 +98,16 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
};
|
||||
|
||||
const expectedPermissionsForAdmin = {
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.MEMORIES]: {
|
||||
|
|
@ -96,6 +120,9 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
[PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.RUN_CODE]: { [Permissions.USE]: true },
|
||||
|
|
@ -111,7 +138,8 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[PermissionTypes.MCP_SERVERS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -135,11 +163,19 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
it('should call updateAccessPermissions with false when permission types are false', async () => {
|
||||
const config = {
|
||||
interface: {
|
||||
prompts: false,
|
||||
prompts: {
|
||||
use: false,
|
||||
share: false,
|
||||
public: false,
|
||||
},
|
||||
bookmarks: false,
|
||||
memories: false,
|
||||
multiConvo: false,
|
||||
agents: false,
|
||||
agents: {
|
||||
use: false,
|
||||
share: false,
|
||||
public: false,
|
||||
},
|
||||
temporaryChat: false,
|
||||
runCode: false,
|
||||
webSearch: false,
|
||||
|
|
@ -153,6 +189,12 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
marketplace: {
|
||||
use: false,
|
||||
},
|
||||
mcpServers: {
|
||||
use: true,
|
||||
create: true,
|
||||
share: false,
|
||||
public: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
const configDefaults = { interface: {} } as TConfigDefaults;
|
||||
|
|
@ -168,6 +210,9 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
const expectedPermissionsForUser = {
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.USE]: false,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: false },
|
||||
[PermissionTypes.MEMORIES]: {
|
||||
|
|
@ -180,6 +225,9 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: false },
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.USE]: false,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
[PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: false },
|
||||
[PermissionTypes.RUN_CODE]: { [Permissions.USE]: false },
|
||||
|
|
@ -196,12 +244,16 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
};
|
||||
|
||||
const expectedPermissionsForAdmin = {
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.USE]: false,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: false },
|
||||
[PermissionTypes.MEMORIES]: {
|
||||
|
|
@ -214,6 +266,9 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: false },
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.USE]: false,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
[PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: false },
|
||||
[PermissionTypes.RUN_CODE]: { [Permissions.USE]: false },
|
||||
|
|
@ -229,7 +284,8 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[PermissionTypes.MCP_SERVERS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -286,6 +342,9 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
const expectedPermissionsForUser = {
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.MEMORIES]: {
|
||||
|
|
@ -298,6 +357,9 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
[PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.RUN_CODE]: { [Permissions.USE]: true },
|
||||
|
|
@ -314,12 +376,16 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
};
|
||||
|
||||
const expectedPermissionsForAdmin = {
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
},
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.MEMORIES]: {
|
||||
|
|
@ -332,6 +398,9 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
},
|
||||
[PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.RUN_CODE]: { [Permissions.USE]: true },
|
||||
|
|
@ -348,6 +417,7 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -417,6 +487,9 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
const expectedPermissionsForUser = {
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: false },
|
||||
[PermissionTypes.MEMORIES]: {
|
||||
|
|
@ -429,6 +502,9 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
[PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.RUN_CODE]: { [Permissions.USE]: false },
|
||||
|
|
@ -445,12 +521,16 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
};
|
||||
|
||||
const expectedPermissionsForAdmin = {
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
},
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: false },
|
||||
[PermissionTypes.MEMORIES]: {
|
||||
|
|
@ -463,6 +543,9 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
},
|
||||
[PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.RUN_CODE]: { [Permissions.USE]: false },
|
||||
|
|
@ -479,6 +562,7 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -535,6 +619,9 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
const expectedPermissionsForUser = {
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.MEMORIES]: {
|
||||
|
|
@ -547,6 +634,9 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
[PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.RUN_CODE]: { [Permissions.USE]: true },
|
||||
|
|
@ -563,12 +653,16 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
};
|
||||
|
||||
const expectedPermissionsForAdmin = {
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
},
|
||||
[PermissionTypes.BOOKMARKS]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.MEMORIES]: {
|
||||
|
|
@ -581,6 +675,9 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
},
|
||||
[PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true },
|
||||
[PermissionTypes.RUN_CODE]: { [Permissions.USE]: true },
|
||||
|
|
@ -597,6 +694,7 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -684,6 +782,7 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -712,6 +811,7 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -790,6 +890,9 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
const expectedPermissionsForUser = {
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
}, // Explicitly configured
|
||||
// All other permissions that don't exist in the database
|
||||
[PermissionTypes.MEMORIES]: {
|
||||
|
|
@ -815,12 +918,16 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
},
|
||||
};
|
||||
|
||||
const expectedPermissionsForAdmin = {
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
}, // Explicitly configured
|
||||
// All other permissions that don't exist in the database
|
||||
[PermissionTypes.MEMORIES]: {
|
||||
|
|
@ -846,6 +953,7 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -1016,19 +1124,31 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
// Check PROMPTS permissions use role defaults
|
||||
expect(userCall[1][PermissionTypes.PROMPTS]).toEqual({
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
});
|
||||
|
||||
expect(adminCall[1][PermissionTypes.PROMPTS]).toEqual({
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
});
|
||||
|
||||
// Check AGENTS permissions use role defaults
|
||||
expect(userCall[1][PermissionTypes.AGENTS]).toEqual({
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
});
|
||||
|
||||
expect(adminCall[1][PermissionTypes.AGENTS]).toEqual({
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
});
|
||||
|
||||
// Check MEMORIES permissions use role defaults
|
||||
|
|
@ -1258,6 +1378,9 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
// Explicitly configured permissions should be updated
|
||||
expect(userCall[1][PermissionTypes.PROMPTS]).toEqual({
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
});
|
||||
expect(userCall[1][PermissionTypes.BOOKMARKS]).toEqual({ [Permissions.USE]: true });
|
||||
expect(userCall[1][PermissionTypes.MARKETPLACE]).toEqual({ [Permissions.USE]: true });
|
||||
|
|
@ -1579,7 +1702,12 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
// Memory permissions should be updated even though they already exist
|
||||
expect(userCall[1][PermissionTypes.MEMORIES]).toEqual(expectedMemoryPermissions);
|
||||
// Prompts should be updated (explicitly configured)
|
||||
expect(userCall[1][PermissionTypes.PROMPTS]).toEqual({ [Permissions.USE]: true });
|
||||
expect(userCall[1][PermissionTypes.PROMPTS]).toEqual({
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: false,
|
||||
[Permissions.SHARE_PUBLIC]: false,
|
||||
});
|
||||
// Bookmarks should be updated (explicitly configured)
|
||||
expect(userCall[1][PermissionTypes.BOOKMARKS]).toEqual({ [Permissions.USE]: true });
|
||||
|
||||
|
|
@ -1589,7 +1717,12 @@ describe('updateInterfacePermissions - permissions', () => {
|
|||
);
|
||||
// Memory permissions should be updated even though they already exist
|
||||
expect(adminCall[1][PermissionTypes.MEMORIES]).toEqual(expectedMemoryPermissions);
|
||||
expect(adminCall[1][PermissionTypes.PROMPTS]).toEqual({ [Permissions.USE]: true });
|
||||
expect(adminCall[1][PermissionTypes.PROMPTS]).toEqual({
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
});
|
||||
expect(adminCall[1][PermissionTypes.BOOKMARKS]).toEqual({ [Permissions.USE]: true });
|
||||
|
||||
// Verify the existing role data was passed to updateAccessPermissions
|
||||
|
|
|
|||
|
|
@ -141,12 +141,52 @@ export async function updateInterfacePermissions({
|
|||
}
|
||||
};
|
||||
|
||||
// Helper to extract value from boolean or object config
|
||||
const getConfigUse = (
|
||||
config: boolean | { use?: boolean; share?: boolean; public?: boolean } | undefined,
|
||||
) => (typeof config === 'boolean' ? config : config?.use);
|
||||
const getConfigShare = (
|
||||
config: boolean | { use?: boolean; share?: boolean; public?: boolean } | undefined,
|
||||
) => (typeof config === 'boolean' ? undefined : config?.share);
|
||||
const getConfigPublic = (
|
||||
config: boolean | { use?: boolean; share?: boolean; public?: boolean } | undefined,
|
||||
) => (typeof config === 'boolean' ? undefined : config?.public);
|
||||
|
||||
// Get default use values (for backward compat when config is boolean)
|
||||
const promptsDefaultUse =
|
||||
typeof defaults.prompts === 'boolean' ? defaults.prompts : defaults.prompts?.use;
|
||||
const agentsDefaultUse =
|
||||
typeof defaults.agents === 'boolean' ? defaults.agents : defaults.agents?.use;
|
||||
const promptsDefaultShare =
|
||||
typeof defaults.prompts === 'object' ? defaults.prompts?.share : undefined;
|
||||
const agentsDefaultShare =
|
||||
typeof defaults.agents === 'object' ? defaults.agents?.share : undefined;
|
||||
const promptsDefaultPublic =
|
||||
typeof defaults.prompts === 'object' ? defaults.prompts?.public : undefined;
|
||||
const agentsDefaultPublic =
|
||||
typeof defaults.agents === 'object' ? defaults.agents?.public : undefined;
|
||||
|
||||
const allPermissions: Partial<Record<PermissionTypes, Record<string, boolean | undefined>>> = {
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.USE]: getPermissionValue(
|
||||
loadedInterface.prompts,
|
||||
getConfigUse(loadedInterface.prompts),
|
||||
defaultPerms[PermissionTypes.PROMPTS]?.[Permissions.USE],
|
||||
defaults.prompts,
|
||||
promptsDefaultUse,
|
||||
),
|
||||
[Permissions.CREATE]: getPermissionValue(
|
||||
undefined,
|
||||
defaultPerms[PermissionTypes.PROMPTS]?.[Permissions.CREATE],
|
||||
true,
|
||||
),
|
||||
[Permissions.SHARE]: getPermissionValue(
|
||||
getConfigShare(loadedInterface.prompts),
|
||||
defaultPerms[PermissionTypes.PROMPTS]?.[Permissions.SHARE],
|
||||
promptsDefaultShare,
|
||||
),
|
||||
[Permissions.SHARE_PUBLIC]: getPermissionValue(
|
||||
getConfigPublic(loadedInterface.prompts),
|
||||
defaultPerms[PermissionTypes.PROMPTS]?.[Permissions.SHARE_PUBLIC],
|
||||
promptsDefaultPublic,
|
||||
),
|
||||
},
|
||||
[PermissionTypes.BOOKMARKS]: {
|
||||
|
|
@ -194,9 +234,24 @@ export async function updateInterfacePermissions({
|
|||
},
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.USE]: getPermissionValue(
|
||||
loadedInterface.agents,
|
||||
getConfigUse(loadedInterface.agents),
|
||||
defaultPerms[PermissionTypes.AGENTS]?.[Permissions.USE],
|
||||
defaults.agents,
|
||||
agentsDefaultUse,
|
||||
),
|
||||
[Permissions.CREATE]: getPermissionValue(
|
||||
undefined,
|
||||
defaultPerms[PermissionTypes.AGENTS]?.[Permissions.CREATE],
|
||||
true,
|
||||
),
|
||||
[Permissions.SHARE]: getPermissionValue(
|
||||
getConfigShare(loadedInterface.agents),
|
||||
defaultPerms[PermissionTypes.AGENTS]?.[Permissions.SHARE],
|
||||
agentsDefaultShare,
|
||||
),
|
||||
[Permissions.SHARE_PUBLIC]: getPermissionValue(
|
||||
getConfigPublic(loadedInterface.agents),
|
||||
defaultPerms[PermissionTypes.AGENTS]?.[Permissions.SHARE_PUBLIC],
|
||||
agentsDefaultPublic,
|
||||
),
|
||||
},
|
||||
[PermissionTypes.TEMPORARY_CHAT]: {
|
||||
|
|
@ -274,6 +329,11 @@ export async function updateInterfacePermissions({
|
|||
defaultPerms[PermissionTypes.MCP_SERVERS]?.[Permissions.SHARE],
|
||||
defaults.mcpServers?.share,
|
||||
),
|
||||
[Permissions.SHARE_PUBLIC]: getPermissionValue(
|
||||
loadedInterface.mcpServers?.public,
|
||||
defaultPerms[PermissionTypes.MCP_SERVERS]?.[Permissions.SHARE_PUBLIC],
|
||||
defaults.mcpServers?.public,
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ describe('access middleware', () => {
|
|||
permissions: {
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.SHARED_GLOBAL]: false,
|
||||
[Permissions.SHARE]: false,
|
||||
},
|
||||
},
|
||||
} as unknown as IRole;
|
||||
|
|
@ -223,9 +223,9 @@ describe('access middleware', () => {
|
|||
|
||||
const result = await checkAccess({
|
||||
...defaultParams,
|
||||
permissions: [Permissions.USE, Permissions.SHARED_GLOBAL],
|
||||
permissions: [Permissions.USE, Permissions.SHARE],
|
||||
bodyProps: {
|
||||
[Permissions.SHARED_GLOBAL]: ['projectIds', 'removeProjectIds'],
|
||||
[Permissions.SHARE]: ['projectIds', 'removeProjectIds'],
|
||||
} as Record<Permissions, string[]>,
|
||||
checkObject,
|
||||
});
|
||||
|
|
@ -237,7 +237,7 @@ describe('access middleware', () => {
|
|||
name: 'user',
|
||||
permissions: {
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.SHARED_GLOBAL]: false,
|
||||
[Permissions.SHARE]: false,
|
||||
},
|
||||
},
|
||||
} as unknown as IRole;
|
||||
|
|
@ -251,9 +251,9 @@ describe('access middleware', () => {
|
|||
|
||||
const result = await checkAccess({
|
||||
...defaultParams,
|
||||
permissions: [Permissions.SHARED_GLOBAL],
|
||||
permissions: [Permissions.SHARE],
|
||||
bodyProps: {
|
||||
[Permissions.SHARED_GLOBAL]: ['projectIds', 'removeProjectIds'],
|
||||
[Permissions.SHARE]: ['projectIds', 'removeProjectIds'],
|
||||
} as Record<Permissions, string[]>,
|
||||
checkObject,
|
||||
});
|
||||
|
|
@ -337,7 +337,7 @@ describe('access middleware', () => {
|
|||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARED_GLOBAL]: false,
|
||||
[Permissions.SHARE]: false,
|
||||
},
|
||||
},
|
||||
} as unknown as IRole;
|
||||
|
|
@ -350,9 +350,9 @@ describe('access middleware', () => {
|
|||
|
||||
const middleware = generateCheckAccess({
|
||||
permissionType: PermissionTypes.AGENTS,
|
||||
permissions: [Permissions.USE, Permissions.CREATE, Permissions.SHARED_GLOBAL],
|
||||
permissions: [Permissions.USE, Permissions.CREATE, Permissions.SHARE],
|
||||
bodyProps: {
|
||||
[Permissions.SHARED_GLOBAL]: ['projectIds', 'removeProjectIds'],
|
||||
[Permissions.SHARE]: ['projectIds', 'removeProjectIds'],
|
||||
} as Record<Permissions, string[]>,
|
||||
getRoleByName: mockGetRoleByName,
|
||||
});
|
||||
|
|
@ -490,7 +490,7 @@ describe('access middleware', () => {
|
|||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARED_GLOBAL]: false,
|
||||
[Permissions.SHARE]: false,
|
||||
},
|
||||
},
|
||||
} as unknown as IRole;
|
||||
|
|
|
|||
|
|
@ -587,6 +587,7 @@ const mcpServersSchema = z
|
|||
use: z.boolean().optional(),
|
||||
create: z.boolean().optional(),
|
||||
share: z.boolean().optional(),
|
||||
public: z.boolean().optional(),
|
||||
trustCheckbox: z
|
||||
.object({
|
||||
label: localizedStringSchema.optional(),
|
||||
|
|
@ -617,8 +618,26 @@ export const interfaceSchema = z
|
|||
bookmarks: z.boolean().optional(),
|
||||
memories: z.boolean().optional(),
|
||||
presets: z.boolean().optional(),
|
||||
prompts: z.boolean().optional(),
|
||||
agents: z.boolean().optional(),
|
||||
prompts: z
|
||||
.union([
|
||||
z.boolean(),
|
||||
z.object({
|
||||
use: z.boolean().optional(),
|
||||
share: z.boolean().optional(),
|
||||
public: z.boolean().optional(),
|
||||
}),
|
||||
])
|
||||
.optional(),
|
||||
agents: z
|
||||
.union([
|
||||
z.boolean(),
|
||||
z.object({
|
||||
use: z.boolean().optional(),
|
||||
share: z.boolean().optional(),
|
||||
public: z.boolean().optional(),
|
||||
}),
|
||||
])
|
||||
.optional(),
|
||||
temporaryChat: z.boolean().optional(),
|
||||
temporaryChatRetention: z.number().min(1).max(8760).optional(),
|
||||
runCode: z.boolean().optional(),
|
||||
|
|
@ -647,8 +666,16 @@ export const interfaceSchema = z
|
|||
multiConvo: true,
|
||||
bookmarks: true,
|
||||
memories: true,
|
||||
prompts: true,
|
||||
agents: true,
|
||||
prompts: {
|
||||
use: true,
|
||||
share: false,
|
||||
public: false,
|
||||
},
|
||||
agents: {
|
||||
use: true,
|
||||
share: false,
|
||||
public: false,
|
||||
},
|
||||
temporaryChat: true,
|
||||
runCode: true,
|
||||
webSearch: true,
|
||||
|
|
@ -664,6 +691,7 @@ export const interfaceSchema = z
|
|||
use: true,
|
||||
create: true,
|
||||
share: false,
|
||||
public: false,
|
||||
},
|
||||
fileSearch: true,
|
||||
fileCitations: true,
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ export enum PermissionTypes {
|
|||
* Enum for Role-Based Access Control Constants
|
||||
*/
|
||||
export enum Permissions {
|
||||
SHARED_GLOBAL = 'SHARED_GLOBAL',
|
||||
USE = 'USE',
|
||||
CREATE = 'CREATE',
|
||||
UPDATE = 'UPDATE',
|
||||
|
|
@ -74,13 +73,15 @@ export enum Permissions {
|
|||
VIEW_USERS = 'VIEW_USERS',
|
||||
VIEW_GROUPS = 'VIEW_GROUPS',
|
||||
VIEW_ROLES = 'VIEW_ROLES',
|
||||
/** Can share resources publicly (with everyone) */
|
||||
SHARE_PUBLIC = 'SHARE_PUBLIC',
|
||||
}
|
||||
|
||||
export const promptPermissionsSchema = z.object({
|
||||
[Permissions.SHARED_GLOBAL]: z.boolean().default(false),
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
[Permissions.CREATE]: z.boolean().default(true),
|
||||
// [Permissions.SHARE]: z.boolean().default(false),
|
||||
[Permissions.SHARE]: z.boolean().default(false),
|
||||
[Permissions.SHARE_PUBLIC]: z.boolean().default(false),
|
||||
});
|
||||
export type TPromptPermissions = z.infer<typeof promptPermissionsSchema>;
|
||||
|
||||
|
|
@ -99,10 +100,10 @@ export const memoryPermissionsSchema = z.object({
|
|||
export type TMemoryPermissions = z.infer<typeof memoryPermissionsSchema>;
|
||||
|
||||
export const agentPermissionsSchema = z.object({
|
||||
[Permissions.SHARED_GLOBAL]: z.boolean().default(false),
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
[Permissions.CREATE]: z.boolean().default(true),
|
||||
// [Permissions.SHARE]: z.boolean().default(false),
|
||||
[Permissions.SHARE]: z.boolean().default(false),
|
||||
[Permissions.SHARE_PUBLIC]: z.boolean().default(false),
|
||||
});
|
||||
export type TAgentPermissions = z.infer<typeof agentPermissionsSchema>;
|
||||
|
||||
|
|
@ -152,6 +153,7 @@ export const mcpServersPermissionsSchema = z.object({
|
|||
[Permissions.USE]: z.boolean().default(true),
|
||||
[Permissions.CREATE]: z.boolean().default(true),
|
||||
[Permissions.SHARE]: z.boolean().default(false),
|
||||
[Permissions.SHARE_PUBLIC]: z.boolean().default(false),
|
||||
});
|
||||
export type TMcpServersPermissions = z.infer<typeof mcpServersPermissionsSchema>;
|
||||
|
||||
|
|
|
|||
|
|
@ -43,10 +43,10 @@ const defaultRolesSchema = z.object({
|
|||
name: z.literal(SystemRoles.ADMIN),
|
||||
permissions: permissionsSchema.extend({
|
||||
[PermissionTypes.PROMPTS]: promptPermissionsSchema.extend({
|
||||
[Permissions.SHARED_GLOBAL]: z.boolean().default(true),
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
[Permissions.CREATE]: z.boolean().default(true),
|
||||
// [Permissions.SHARE]: z.boolean().default(true),
|
||||
[Permissions.SHARE]: z.boolean().default(true),
|
||||
[Permissions.SHARE_PUBLIC]: z.boolean().default(true),
|
||||
}),
|
||||
[PermissionTypes.BOOKMARKS]: bookmarkPermissionsSchema.extend({
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
|
|
@ -59,10 +59,10 @@ const defaultRolesSchema = z.object({
|
|||
[Permissions.OPT_OUT]: z.boolean().default(true),
|
||||
}),
|
||||
[PermissionTypes.AGENTS]: agentPermissionsSchema.extend({
|
||||
[Permissions.SHARED_GLOBAL]: z.boolean().default(true),
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
[Permissions.CREATE]: z.boolean().default(true),
|
||||
// [Permissions.SHARE]: z.boolean().default(true),
|
||||
[Permissions.SHARE]: z.boolean().default(true),
|
||||
[Permissions.SHARE_PUBLIC]: z.boolean().default(true),
|
||||
}),
|
||||
[PermissionTypes.MULTI_CONVO]: multiConvoPermissionsSchema.extend({
|
||||
[Permissions.USE]: z.boolean().default(true),
|
||||
|
|
@ -94,6 +94,7 @@ const defaultRolesSchema = z.object({
|
|||
[Permissions.USE]: z.boolean().default(true),
|
||||
[Permissions.CREATE]: z.boolean().default(true),
|
||||
[Permissions.SHARE]: z.boolean().default(true),
|
||||
[Permissions.SHARE_PUBLIC]: z.boolean().default(true),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
|
@ -108,9 +109,10 @@ export const roleDefaults = defaultRolesSchema.parse({
|
|||
name: SystemRoles.ADMIN,
|
||||
permissions: {
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.SHARED_GLOBAL]: true,
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
},
|
||||
[PermissionTypes.BOOKMARKS]: {
|
||||
[Permissions.USE]: true,
|
||||
|
|
@ -123,9 +125,10 @@ export const roleDefaults = defaultRolesSchema.parse({
|
|||
[Permissions.OPT_OUT]: true,
|
||||
},
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.SHARED_GLOBAL]: true,
|
||||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
},
|
||||
[PermissionTypes.MULTI_CONVO]: {
|
||||
[Permissions.USE]: true,
|
||||
|
|
@ -157,6 +160,7 @@ export const roleDefaults = defaultRolesSchema.parse({
|
|||
[Permissions.USE]: true,
|
||||
[Permissions.CREATE]: true,
|
||||
[Permissions.SHARE]: true,
|
||||
[Permissions.SHARE_PUBLIC]: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -11,9 +11,10 @@ const rolePermissionsSchema = new Schema(
|
|||
[Permissions.USE]: { type: Boolean },
|
||||
},
|
||||
[PermissionTypes.PROMPTS]: {
|
||||
[Permissions.SHARED_GLOBAL]: { type: Boolean },
|
||||
[Permissions.USE]: { type: Boolean },
|
||||
[Permissions.CREATE]: { type: Boolean },
|
||||
[Permissions.SHARE]: { type: Boolean },
|
||||
[Permissions.SHARE_PUBLIC]: { type: Boolean },
|
||||
},
|
||||
[PermissionTypes.MEMORIES]: {
|
||||
[Permissions.USE]: { type: Boolean },
|
||||
|
|
@ -23,9 +24,10 @@ const rolePermissionsSchema = new Schema(
|
|||
[Permissions.OPT_OUT]: { type: Boolean },
|
||||
},
|
||||
[PermissionTypes.AGENTS]: {
|
||||
[Permissions.SHARED_GLOBAL]: { type: Boolean },
|
||||
[Permissions.USE]: { type: Boolean },
|
||||
[Permissions.CREATE]: { type: Boolean },
|
||||
[Permissions.SHARE]: { type: Boolean },
|
||||
[Permissions.SHARE_PUBLIC]: { type: Boolean },
|
||||
},
|
||||
[PermissionTypes.MULTI_CONVO]: {
|
||||
[Permissions.USE]: { type: Boolean },
|
||||
|
|
@ -57,6 +59,7 @@ const rolePermissionsSchema = new Schema(
|
|||
[Permissions.USE]: { type: Boolean },
|
||||
[Permissions.CREATE]: { type: Boolean },
|
||||
[Permissions.SHARE]: { type: Boolean },
|
||||
[Permissions.SHARE_PUBLIC]: { type: Boolean },
|
||||
},
|
||||
},
|
||||
{ _id: false },
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ export interface IRole extends Document {
|
|||
[Permissions.USE]?: boolean;
|
||||
};
|
||||
[PermissionTypes.PROMPTS]?: {
|
||||
[Permissions.SHARED_GLOBAL]?: boolean;
|
||||
[Permissions.USE]?: boolean;
|
||||
[Permissions.CREATE]?: boolean;
|
||||
[Permissions.SHARE]?: boolean;
|
||||
[Permissions.SHARE_PUBLIC]?: boolean;
|
||||
};
|
||||
[PermissionTypes.MEMORIES]?: {
|
||||
[Permissions.USE]?: boolean;
|
||||
|
|
@ -21,9 +22,10 @@ export interface IRole extends Document {
|
|||
[Permissions.READ]?: boolean;
|
||||
};
|
||||
[PermissionTypes.AGENTS]?: {
|
||||
[Permissions.SHARED_GLOBAL]?: boolean;
|
||||
[Permissions.USE]?: boolean;
|
||||
[Permissions.CREATE]?: boolean;
|
||||
[Permissions.SHARE]?: boolean;
|
||||
[Permissions.SHARE_PUBLIC]?: boolean;
|
||||
};
|
||||
[PermissionTypes.MULTI_CONVO]?: {
|
||||
[Permissions.USE]?: boolean;
|
||||
|
|
@ -55,6 +57,7 @@ export interface IRole extends Document {
|
|||
[Permissions.USE]?: boolean;
|
||||
[Permissions.CREATE]?: boolean;
|
||||
[Permissions.SHARE]?: boolean;
|
||||
[Permissions.SHARE_PUBLIC]?: boolean;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue