diff --git a/api/models/PromptGroupMigration.spec.js b/api/models/PromptGroupMigration.spec.js index 4214b684d7..f568012cb3 100644 --- a/api/models/PromptGroupMigration.spec.js +++ b/api/models/PromptGroupMigration.spec.js @@ -7,6 +7,7 @@ const { ResourceType, AccessRoleIds, PrincipalType, + PrincipalModel, PermissionBits, } = require('librechat-data-provider'); @@ -211,7 +212,7 @@ describe('PromptGroup Migration Script', () => { await AclEntry.create({ principalType: PrincipalType.USER, principalId: testOwner._id, - principalModel: 'User', + principalModel: PrincipalModel.USER, resourceType: ResourceType.PROMPTGROUP, resourceId: promptGroup1._id, permBits: ownerRole.permBits, diff --git a/api/server/middleware/accessResources/canAccessAgentResource.spec.js b/api/server/middleware/accessResources/canAccessAgentResource.spec.js index 2f6d5b0ede..c7f3efc6bb 100644 --- a/api/server/middleware/accessResources/canAccessAgentResource.spec.js +++ b/api/server/middleware/accessResources/canAccessAgentResource.spec.js @@ -1,5 +1,5 @@ const mongoose = require('mongoose'); -const { ResourceType, PrincipalType } = require('librechat-data-provider'); +const { ResourceType, PrincipalType, PrincipalModel } = require('librechat-data-provider'); const { MongoMemoryServer } = require('mongodb-memory-server'); const { canAccessAgentResource } = require('./canAccessAgentResource'); const { User, Role, AclEntry } = require('~/db/models'); @@ -99,7 +99,7 @@ describe('canAccessAgentResource middleware', () => { await AclEntry.create({ principalType: PrincipalType.USER, principalId: testUser._id, - principalModel: 'User', + principalModel: PrincipalModel.USER, resourceType: ResourceType.AGENT, resourceId: agent._id, permBits: 15, // All permissions (1+2+4+8) @@ -136,7 +136,7 @@ describe('canAccessAgentResource middleware', () => { await AclEntry.create({ principalType: PrincipalType.USER, principalId: otherUser._id, - principalModel: 'User', + principalModel: PrincipalModel.USER, resourceType: ResourceType.AGENT, resourceId: agent._id, permBits: 15, // All permissions @@ -177,7 +177,7 @@ describe('canAccessAgentResource middleware', () => { await AclEntry.create({ principalType: PrincipalType.USER, principalId: testUser._id, - principalModel: 'User', + principalModel: PrincipalModel.USER, resourceType: ResourceType.AGENT, resourceId: agent._id, permBits: 1, // VIEW permission @@ -214,7 +214,7 @@ describe('canAccessAgentResource middleware', () => { await AclEntry.create({ principalType: PrincipalType.USER, principalId: testUser._id, - principalModel: 'User', + principalModel: PrincipalModel.USER, resourceType: ResourceType.AGENT, resourceId: agent._id, permBits: 1, // VIEW permission only @@ -261,7 +261,7 @@ describe('canAccessAgentResource middleware', () => { await AclEntry.create({ principalType: PrincipalType.USER, principalId: testUser._id, - principalModel: 'User', + principalModel: PrincipalModel.USER, resourceType: ResourceType.AGENT, resourceId: agent._id, permBits: 15, // All permissions @@ -297,7 +297,7 @@ describe('canAccessAgentResource middleware', () => { await AclEntry.create({ principalType: PrincipalType.USER, principalId: testUser._id, - principalModel: 'User', + principalModel: PrincipalModel.USER, resourceType: ResourceType.AGENT, resourceId: agent._id, permBits: 15, // All permissions (1+2+4+8) @@ -357,7 +357,7 @@ describe('canAccessAgentResource middleware', () => { await AclEntry.create({ principalType: PrincipalType.USER, principalId: testUser._id, - principalModel: 'User', + principalModel: PrincipalModel.USER, resourceType: ResourceType.AGENT, resourceId: agent._id, permBits: 15, // All permissions diff --git a/api/server/services/Files/processFiles.test.js b/api/server/services/Files/processFiles.test.js index 5da8b862d0..d389ca17ad 100644 --- a/api/server/services/Files/processFiles.test.js +++ b/api/server/services/Files/processFiles.test.js @@ -22,6 +22,16 @@ jest.mock('librechat-data-provider', () => ({ GROUP: 'group', PUBLIC: 'public', }, + PrincipalModel: { + USER: 'User', + GROUP: 'Group', + }, + ResourceType: { + AGENT: 'agent', + PROJECT: 'project', + FILE: 'file', + PROMPTGROUP: 'promptGroup', + }, FileContext: { message_attachment: 'message_attachment' }, FileSources: { local: 'local' }, EModelEndpoint: { assistants: 'assistants' }, diff --git a/api/server/services/PermissionService.js b/api/server/services/PermissionService.js index 5fd068d133..b525f82890 100644 --- a/api/server/services/PermissionService.js +++ b/api/server/services/PermissionService.js @@ -1,6 +1,6 @@ const mongoose = require('mongoose'); const { isEnabled } = require('@librechat/api'); -const { ResourceType, PrincipalType } = require('librechat-data-provider'); +const { ResourceType, PrincipalType, PrincipalModel } = require('librechat-data-provider'); const { getTransactionSupport, logger } = require('@librechat/data-schemas'); const { entraIdPrincipalFeatureEnabled, @@ -627,9 +627,10 @@ const bulkUpdateResourcePermissions = async ({ principalType: principal.type, resourceType, resourceId, - ...(principal.type !== 'public' && { + ...(principal.type !== PrincipalType.PUBLIC && { principalId: principal.id, - principalModel: principal.type === 'user' ? 'User' : 'Group', + principalModel: + principal.type === PrincipalType.USER ? PrincipalModel.USER : PrincipalModel.GROUP, }), }, }; diff --git a/api/server/services/PermissionService.spec.js b/api/server/services/PermissionService.spec.js index 93e53d4e68..30b4460975 100644 --- a/api/server/services/PermissionService.spec.js +++ b/api/server/services/PermissionService.spec.js @@ -1,7 +1,12 @@ const mongoose = require('mongoose'); const { RoleBits, createModels } = require('@librechat/data-schemas'); const { MongoMemoryServer } = require('mongodb-memory-server'); -const { AccessRoleIds, ResourceType, PrincipalType } = require('librechat-data-provider'); +const { + ResourceType, + AccessRoleIds, + PrincipalType, + PrincipalModel, +} = require('librechat-data-provider'); const { bulkUpdateResourcePermissions, getEffectivePermissions, @@ -87,10 +92,10 @@ describe('PermissionService', () => { }); expect(entry).toBeDefined(); - expect(entry.principalType).toBe('user'); + expect(entry.principalType).toBe(PrincipalType.USER); expect(entry.principalId.toString()).toBe(userId.toString()); - expect(entry.principalModel).toBe('User'); - expect(entry.resourceType).toBe('agent'); + expect(entry.principalModel).toBe(PrincipalModel.USER); + expect(entry.resourceType).toBe(ResourceType.AGENT); expect(entry.resourceId.toString()).toBe(resourceId.toString()); // Get the role to verify the permission bits are correctly set @@ -114,7 +119,7 @@ describe('PermissionService', () => { expect(entry).toBeDefined(); expect(entry.principalType).toBe(PrincipalType.GROUP); expect(entry.principalId.toString()).toBe(groupId.toString()); - expect(entry.principalModel).toBe('Group'); + expect(entry.principalModel).toBe(PrincipalModel.GROUP); // Get the role to verify the permission bits are correctly set const role = await findRoleByIdentifier(AccessRoleIds.AGENT_EDITOR); @@ -433,7 +438,7 @@ describe('PermissionService', () => { await AclEntry.create({ principalType: PrincipalType.USER, principalId: userId, - principalModel: 'User', + principalModel: PrincipalModel.USER, resourceType: ResourceType.AGENT, resourceId: childResourceId, permBits: RoleBits.VIEWER, diff --git a/packages/data-provider/src/accessPermissions.ts b/packages/data-provider/src/accessPermissions.ts index 4b6961a8ed..4ea1d5e4e3 100644 --- a/packages/data-provider/src/accessPermissions.ts +++ b/packages/data-provider/src/accessPermissions.ts @@ -19,6 +19,14 @@ export enum PrincipalType { PUBLIC = 'public', } +/** + * Principal model types for MongoDB references + */ +export enum PrincipalModel { + USER = 'User', + GROUP = 'Group', +} + /** * Source of the principal (local LibreChat or external Entra ID) */ diff --git a/packages/data-schemas/src/methods/aclEntry.spec.ts b/packages/data-schemas/src/methods/aclEntry.spec.ts index 25bb215b32..69983f369f 100644 --- a/packages/data-schemas/src/methods/aclEntry.spec.ts +++ b/packages/data-schemas/src/methods/aclEntry.spec.ts @@ -1,5 +1,10 @@ import mongoose from 'mongoose'; -import { ResourceType, PermissionBits, PrincipalType } from 'librechat-data-provider'; +import { + ResourceType, + PrincipalType, + PrincipalModel, + PermissionBits, +} from 'librechat-data-provider'; import { MongoMemoryServer } from 'mongodb-memory-server'; import type * as t from '~/types'; import { createAclEntryMethods } from './aclEntry'; @@ -47,7 +52,7 @@ describe('AclEntry Model Tests', () => { expect(entry).toBeDefined(); expect(entry?.principalType).toBe(PrincipalType.USER); expect(entry?.principalId?.toString()).toBe(userId.toString()); - expect(entry?.principalModel).toBe('User'); + expect(entry?.principalModel).toBe(PrincipalModel.USER); expect(entry?.resourceType).toBe(ResourceType.AGENT); expect(entry?.resourceId.toString()).toBe(resourceId.toString()); expect(entry?.permBits).toBe(PermissionBits.VIEW); @@ -68,7 +73,7 @@ describe('AclEntry Model Tests', () => { expect(entry).toBeDefined(); expect(entry?.principalType).toBe(PrincipalType.GROUP); expect(entry?.principalId?.toString()).toBe(groupId.toString()); - expect(entry?.principalModel).toBe('Group'); + expect(entry?.principalModel).toBe(PrincipalModel.GROUP); expect(entry?.permBits).toBe(PermissionBits.VIEW | PermissionBits.EDIT); }); @@ -469,7 +474,7 @@ describe('AclEntry Model Tests', () => { await AclEntry.create({ principalType: PrincipalType.USER, principalId: userId, - principalModel: 'User', + principalModel: PrincipalModel.USER, resourceType: ResourceType.AGENT, resourceId: childResourceId, permBits: PermissionBits.VIEW, diff --git a/packages/data-schemas/src/methods/aclEntry.ts b/packages/data-schemas/src/methods/aclEntry.ts index 237e238642..2284214259 100644 --- a/packages/data-schemas/src/methods/aclEntry.ts +++ b/packages/data-schemas/src/methods/aclEntry.ts @@ -1,4 +1,4 @@ -import { PrincipalType } from 'librechat-data-provider'; +import { PrincipalType, PrincipalModel } from 'librechat-data-provider'; import type { Model, Types, DeleteResult, ClientSession } from 'mongoose'; import type { IAclEntry } from '~/types'; @@ -148,7 +148,8 @@ export function createAclEntryMethods(mongoose: typeof import('mongoose')) { if (principalType !== PrincipalType.PUBLIC) { query.principalId = principalId; - query.principalModel = principalType === PrincipalType.USER ? 'User' : 'Group'; + query.principalModel = + principalType === PrincipalType.USER ? PrincipalModel.USER : PrincipalModel.GROUP; } const update = { diff --git a/packages/data-schemas/src/schema/aclEntry.ts b/packages/data-schemas/src/schema/aclEntry.ts index b3c3d4b05c..597e6d9d5c 100644 --- a/packages/data-schemas/src/schema/aclEntry.ts +++ b/packages/data-schemas/src/schema/aclEntry.ts @@ -1,5 +1,5 @@ import { Schema } from 'mongoose'; -import { PrincipalType } from 'librechat-data-provider'; +import { PrincipalType, PrincipalModel, ResourceType } from 'librechat-data-provider'; import type { IAclEntry } from '~/types'; const aclEntrySchema = new Schema( @@ -19,14 +19,14 @@ const aclEntrySchema = new Schema( }, principalModel: { type: String, - enum: ['User', 'Group'], + enum: Object.values(PrincipalModel), required: function (this: IAclEntry) { return this.principalType !== PrincipalType.PUBLIC; }, }, resourceType: { type: String, - enum: ['agent', 'project', 'file', 'prompt', 'promptGroup'], + enum: Object.values(ResourceType), required: true, }, resourceId: { diff --git a/packages/data-schemas/src/types/aclEntry.ts b/packages/data-schemas/src/types/aclEntry.ts index ff03b23e8c..13fa69ad5f 100644 --- a/packages/data-schemas/src/types/aclEntry.ts +++ b/packages/data-schemas/src/types/aclEntry.ts @@ -1,5 +1,5 @@ import type { Document, Types } from 'mongoose'; -import { PrincipalType } from 'librechat-data-provider'; +import { PrincipalType, PrincipalModel, ResourceType } from 'librechat-data-provider'; export type AclEntry = { /** The type of principal ('user', 'group', 'public') */ @@ -7,9 +7,9 @@ export type AclEntry = { /** The ID of the principal (null for 'public') */ principalId?: Types.ObjectId; /** The model name for the principal ('User' or 'Group') */ - principalModel?: 'User' | 'Group'; + principalModel?: PrincipalModel; /** The type of resource ('agent', 'project', 'file', 'promptGroup') */ - resourceType: 'agent' | 'project' | 'file' | 'promptGroup'; + resourceType: ResourceType; /** The ID of the resource */ resourceId: Types.ObjectId; /** Permission bits for this entry */