mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 06:00:56 +02:00
🔧 refactor: Integrate PrincipalModel Enum for Principal Handling
- Replaced string literals for principal models ('User', 'Group') with the new PrincipalModel enum across various models, services, and tests to enhance type safety and consistency. - Updated permission handling in multiple files to utilize the PrincipalModel enum, improving maintainability and reducing potential errors. - Ensured all relevant tests reflect these changes to maintain coverage and functionality.
This commit is contained in:
parent
49d1cefe71
commit
28d63dab71
10 changed files with 61 additions and 30 deletions
|
@ -7,6 +7,7 @@ const {
|
||||||
ResourceType,
|
ResourceType,
|
||||||
AccessRoleIds,
|
AccessRoleIds,
|
||||||
PrincipalType,
|
PrincipalType,
|
||||||
|
PrincipalModel,
|
||||||
PermissionBits,
|
PermissionBits,
|
||||||
} = require('librechat-data-provider');
|
} = require('librechat-data-provider');
|
||||||
|
|
||||||
|
@ -211,7 +212,7 @@ describe('PromptGroup Migration Script', () => {
|
||||||
await AclEntry.create({
|
await AclEntry.create({
|
||||||
principalType: PrincipalType.USER,
|
principalType: PrincipalType.USER,
|
||||||
principalId: testOwner._id,
|
principalId: testOwner._id,
|
||||||
principalModel: 'User',
|
principalModel: PrincipalModel.USER,
|
||||||
resourceType: ResourceType.PROMPTGROUP,
|
resourceType: ResourceType.PROMPTGROUP,
|
||||||
resourceId: promptGroup1._id,
|
resourceId: promptGroup1._id,
|
||||||
permBits: ownerRole.permBits,
|
permBits: ownerRole.permBits,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const mongoose = require('mongoose');
|
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 { MongoMemoryServer } = require('mongodb-memory-server');
|
||||||
const { canAccessAgentResource } = require('./canAccessAgentResource');
|
const { canAccessAgentResource } = require('./canAccessAgentResource');
|
||||||
const { User, Role, AclEntry } = require('~/db/models');
|
const { User, Role, AclEntry } = require('~/db/models');
|
||||||
|
@ -99,7 +99,7 @@ describe('canAccessAgentResource middleware', () => {
|
||||||
await AclEntry.create({
|
await AclEntry.create({
|
||||||
principalType: PrincipalType.USER,
|
principalType: PrincipalType.USER,
|
||||||
principalId: testUser._id,
|
principalId: testUser._id,
|
||||||
principalModel: 'User',
|
principalModel: PrincipalModel.USER,
|
||||||
resourceType: ResourceType.AGENT,
|
resourceType: ResourceType.AGENT,
|
||||||
resourceId: agent._id,
|
resourceId: agent._id,
|
||||||
permBits: 15, // All permissions (1+2+4+8)
|
permBits: 15, // All permissions (1+2+4+8)
|
||||||
|
@ -136,7 +136,7 @@ describe('canAccessAgentResource middleware', () => {
|
||||||
await AclEntry.create({
|
await AclEntry.create({
|
||||||
principalType: PrincipalType.USER,
|
principalType: PrincipalType.USER,
|
||||||
principalId: otherUser._id,
|
principalId: otherUser._id,
|
||||||
principalModel: 'User',
|
principalModel: PrincipalModel.USER,
|
||||||
resourceType: ResourceType.AGENT,
|
resourceType: ResourceType.AGENT,
|
||||||
resourceId: agent._id,
|
resourceId: agent._id,
|
||||||
permBits: 15, // All permissions
|
permBits: 15, // All permissions
|
||||||
|
@ -177,7 +177,7 @@ describe('canAccessAgentResource middleware', () => {
|
||||||
await AclEntry.create({
|
await AclEntry.create({
|
||||||
principalType: PrincipalType.USER,
|
principalType: PrincipalType.USER,
|
||||||
principalId: testUser._id,
|
principalId: testUser._id,
|
||||||
principalModel: 'User',
|
principalModel: PrincipalModel.USER,
|
||||||
resourceType: ResourceType.AGENT,
|
resourceType: ResourceType.AGENT,
|
||||||
resourceId: agent._id,
|
resourceId: agent._id,
|
||||||
permBits: 1, // VIEW permission
|
permBits: 1, // VIEW permission
|
||||||
|
@ -214,7 +214,7 @@ describe('canAccessAgentResource middleware', () => {
|
||||||
await AclEntry.create({
|
await AclEntry.create({
|
||||||
principalType: PrincipalType.USER,
|
principalType: PrincipalType.USER,
|
||||||
principalId: testUser._id,
|
principalId: testUser._id,
|
||||||
principalModel: 'User',
|
principalModel: PrincipalModel.USER,
|
||||||
resourceType: ResourceType.AGENT,
|
resourceType: ResourceType.AGENT,
|
||||||
resourceId: agent._id,
|
resourceId: agent._id,
|
||||||
permBits: 1, // VIEW permission only
|
permBits: 1, // VIEW permission only
|
||||||
|
@ -261,7 +261,7 @@ describe('canAccessAgentResource middleware', () => {
|
||||||
await AclEntry.create({
|
await AclEntry.create({
|
||||||
principalType: PrincipalType.USER,
|
principalType: PrincipalType.USER,
|
||||||
principalId: testUser._id,
|
principalId: testUser._id,
|
||||||
principalModel: 'User',
|
principalModel: PrincipalModel.USER,
|
||||||
resourceType: ResourceType.AGENT,
|
resourceType: ResourceType.AGENT,
|
||||||
resourceId: agent._id,
|
resourceId: agent._id,
|
||||||
permBits: 15, // All permissions
|
permBits: 15, // All permissions
|
||||||
|
@ -297,7 +297,7 @@ describe('canAccessAgentResource middleware', () => {
|
||||||
await AclEntry.create({
|
await AclEntry.create({
|
||||||
principalType: PrincipalType.USER,
|
principalType: PrincipalType.USER,
|
||||||
principalId: testUser._id,
|
principalId: testUser._id,
|
||||||
principalModel: 'User',
|
principalModel: PrincipalModel.USER,
|
||||||
resourceType: ResourceType.AGENT,
|
resourceType: ResourceType.AGENT,
|
||||||
resourceId: agent._id,
|
resourceId: agent._id,
|
||||||
permBits: 15, // All permissions (1+2+4+8)
|
permBits: 15, // All permissions (1+2+4+8)
|
||||||
|
@ -357,7 +357,7 @@ describe('canAccessAgentResource middleware', () => {
|
||||||
await AclEntry.create({
|
await AclEntry.create({
|
||||||
principalType: PrincipalType.USER,
|
principalType: PrincipalType.USER,
|
||||||
principalId: testUser._id,
|
principalId: testUser._id,
|
||||||
principalModel: 'User',
|
principalModel: PrincipalModel.USER,
|
||||||
resourceType: ResourceType.AGENT,
|
resourceType: ResourceType.AGENT,
|
||||||
resourceId: agent._id,
|
resourceId: agent._id,
|
||||||
permBits: 15, // All permissions
|
permBits: 15, // All permissions
|
||||||
|
|
|
@ -22,6 +22,16 @@ jest.mock('librechat-data-provider', () => ({
|
||||||
GROUP: 'group',
|
GROUP: 'group',
|
||||||
PUBLIC: 'public',
|
PUBLIC: 'public',
|
||||||
},
|
},
|
||||||
|
PrincipalModel: {
|
||||||
|
USER: 'User',
|
||||||
|
GROUP: 'Group',
|
||||||
|
},
|
||||||
|
ResourceType: {
|
||||||
|
AGENT: 'agent',
|
||||||
|
PROJECT: 'project',
|
||||||
|
FILE: 'file',
|
||||||
|
PROMPTGROUP: 'promptGroup',
|
||||||
|
},
|
||||||
FileContext: { message_attachment: 'message_attachment' },
|
FileContext: { message_attachment: 'message_attachment' },
|
||||||
FileSources: { local: 'local' },
|
FileSources: { local: 'local' },
|
||||||
EModelEndpoint: { assistants: 'assistants' },
|
EModelEndpoint: { assistants: 'assistants' },
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const mongoose = require('mongoose');
|
const mongoose = require('mongoose');
|
||||||
const { isEnabled } = require('@librechat/api');
|
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 { getTransactionSupport, logger } = require('@librechat/data-schemas');
|
||||||
const {
|
const {
|
||||||
entraIdPrincipalFeatureEnabled,
|
entraIdPrincipalFeatureEnabled,
|
||||||
|
@ -627,9 +627,10 @@ const bulkUpdateResourcePermissions = async ({
|
||||||
principalType: principal.type,
|
principalType: principal.type,
|
||||||
resourceType,
|
resourceType,
|
||||||
resourceId,
|
resourceId,
|
||||||
...(principal.type !== 'public' && {
|
...(principal.type !== PrincipalType.PUBLIC && {
|
||||||
principalId: principal.id,
|
principalId: principal.id,
|
||||||
principalModel: principal.type === 'user' ? 'User' : 'Group',
|
principalModel:
|
||||||
|
principal.type === PrincipalType.USER ? PrincipalModel.USER : PrincipalModel.GROUP,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
const mongoose = require('mongoose');
|
const mongoose = require('mongoose');
|
||||||
const { RoleBits, createModels } = require('@librechat/data-schemas');
|
const { RoleBits, createModels } = require('@librechat/data-schemas');
|
||||||
const { MongoMemoryServer } = require('mongodb-memory-server');
|
const { MongoMemoryServer } = require('mongodb-memory-server');
|
||||||
const { AccessRoleIds, ResourceType, PrincipalType } = require('librechat-data-provider');
|
const {
|
||||||
|
ResourceType,
|
||||||
|
AccessRoleIds,
|
||||||
|
PrincipalType,
|
||||||
|
PrincipalModel,
|
||||||
|
} = require('librechat-data-provider');
|
||||||
const {
|
const {
|
||||||
bulkUpdateResourcePermissions,
|
bulkUpdateResourcePermissions,
|
||||||
getEffectivePermissions,
|
getEffectivePermissions,
|
||||||
|
@ -87,10 +92,10 @@ describe('PermissionService', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(entry).toBeDefined();
|
expect(entry).toBeDefined();
|
||||||
expect(entry.principalType).toBe('user');
|
expect(entry.principalType).toBe(PrincipalType.USER);
|
||||||
expect(entry.principalId.toString()).toBe(userId.toString());
|
expect(entry.principalId.toString()).toBe(userId.toString());
|
||||||
expect(entry.principalModel).toBe('User');
|
expect(entry.principalModel).toBe(PrincipalModel.USER);
|
||||||
expect(entry.resourceType).toBe('agent');
|
expect(entry.resourceType).toBe(ResourceType.AGENT);
|
||||||
expect(entry.resourceId.toString()).toBe(resourceId.toString());
|
expect(entry.resourceId.toString()).toBe(resourceId.toString());
|
||||||
|
|
||||||
// Get the role to verify the permission bits are correctly set
|
// Get the role to verify the permission bits are correctly set
|
||||||
|
@ -114,7 +119,7 @@ describe('PermissionService', () => {
|
||||||
expect(entry).toBeDefined();
|
expect(entry).toBeDefined();
|
||||||
expect(entry.principalType).toBe(PrincipalType.GROUP);
|
expect(entry.principalType).toBe(PrincipalType.GROUP);
|
||||||
expect(entry.principalId.toString()).toBe(groupId.toString());
|
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
|
// Get the role to verify the permission bits are correctly set
|
||||||
const role = await findRoleByIdentifier(AccessRoleIds.AGENT_EDITOR);
|
const role = await findRoleByIdentifier(AccessRoleIds.AGENT_EDITOR);
|
||||||
|
@ -433,7 +438,7 @@ describe('PermissionService', () => {
|
||||||
await AclEntry.create({
|
await AclEntry.create({
|
||||||
principalType: PrincipalType.USER,
|
principalType: PrincipalType.USER,
|
||||||
principalId: userId,
|
principalId: userId,
|
||||||
principalModel: 'User',
|
principalModel: PrincipalModel.USER,
|
||||||
resourceType: ResourceType.AGENT,
|
resourceType: ResourceType.AGENT,
|
||||||
resourceId: childResourceId,
|
resourceId: childResourceId,
|
||||||
permBits: RoleBits.VIEWER,
|
permBits: RoleBits.VIEWER,
|
||||||
|
|
|
@ -19,6 +19,14 @@ export enum PrincipalType {
|
||||||
PUBLIC = 'public',
|
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)
|
* Source of the principal (local LibreChat or external Entra ID)
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
import mongoose from 'mongoose';
|
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 { MongoMemoryServer } from 'mongodb-memory-server';
|
||||||
import type * as t from '~/types';
|
import type * as t from '~/types';
|
||||||
import { createAclEntryMethods } from './aclEntry';
|
import { createAclEntryMethods } from './aclEntry';
|
||||||
|
@ -47,7 +52,7 @@ describe('AclEntry Model Tests', () => {
|
||||||
expect(entry).toBeDefined();
|
expect(entry).toBeDefined();
|
||||||
expect(entry?.principalType).toBe(PrincipalType.USER);
|
expect(entry?.principalType).toBe(PrincipalType.USER);
|
||||||
expect(entry?.principalId?.toString()).toBe(userId.toString());
|
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?.resourceType).toBe(ResourceType.AGENT);
|
||||||
expect(entry?.resourceId.toString()).toBe(resourceId.toString());
|
expect(entry?.resourceId.toString()).toBe(resourceId.toString());
|
||||||
expect(entry?.permBits).toBe(PermissionBits.VIEW);
|
expect(entry?.permBits).toBe(PermissionBits.VIEW);
|
||||||
|
@ -68,7 +73,7 @@ describe('AclEntry Model Tests', () => {
|
||||||
expect(entry).toBeDefined();
|
expect(entry).toBeDefined();
|
||||||
expect(entry?.principalType).toBe(PrincipalType.GROUP);
|
expect(entry?.principalType).toBe(PrincipalType.GROUP);
|
||||||
expect(entry?.principalId?.toString()).toBe(groupId.toString());
|
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);
|
expect(entry?.permBits).toBe(PermissionBits.VIEW | PermissionBits.EDIT);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -469,7 +474,7 @@ describe('AclEntry Model Tests', () => {
|
||||||
await AclEntry.create({
|
await AclEntry.create({
|
||||||
principalType: PrincipalType.USER,
|
principalType: PrincipalType.USER,
|
||||||
principalId: userId,
|
principalId: userId,
|
||||||
principalModel: 'User',
|
principalModel: PrincipalModel.USER,
|
||||||
resourceType: ResourceType.AGENT,
|
resourceType: ResourceType.AGENT,
|
||||||
resourceId: childResourceId,
|
resourceId: childResourceId,
|
||||||
permBits: PermissionBits.VIEW,
|
permBits: PermissionBits.VIEW,
|
||||||
|
|
|
@ -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 { Model, Types, DeleteResult, ClientSession } from 'mongoose';
|
||||||
import type { IAclEntry } from '~/types';
|
import type { IAclEntry } from '~/types';
|
||||||
|
|
||||||
|
@ -148,7 +148,8 @@ export function createAclEntryMethods(mongoose: typeof import('mongoose')) {
|
||||||
|
|
||||||
if (principalType !== PrincipalType.PUBLIC) {
|
if (principalType !== PrincipalType.PUBLIC) {
|
||||||
query.principalId = principalId;
|
query.principalId = principalId;
|
||||||
query.principalModel = principalType === PrincipalType.USER ? 'User' : 'Group';
|
query.principalModel =
|
||||||
|
principalType === PrincipalType.USER ? PrincipalModel.USER : PrincipalModel.GROUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
const update = {
|
const update = {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Schema } from 'mongoose';
|
import { Schema } from 'mongoose';
|
||||||
import { PrincipalType } from 'librechat-data-provider';
|
import { PrincipalType, PrincipalModel, ResourceType } from 'librechat-data-provider';
|
||||||
import type { IAclEntry } from '~/types';
|
import type { IAclEntry } from '~/types';
|
||||||
|
|
||||||
const aclEntrySchema = new Schema<IAclEntry>(
|
const aclEntrySchema = new Schema<IAclEntry>(
|
||||||
|
@ -19,14 +19,14 @@ const aclEntrySchema = new Schema<IAclEntry>(
|
||||||
},
|
},
|
||||||
principalModel: {
|
principalModel: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: ['User', 'Group'],
|
enum: Object.values(PrincipalModel),
|
||||||
required: function (this: IAclEntry) {
|
required: function (this: IAclEntry) {
|
||||||
return this.principalType !== PrincipalType.PUBLIC;
|
return this.principalType !== PrincipalType.PUBLIC;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
resourceType: {
|
resourceType: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: ['agent', 'project', 'file', 'prompt', 'promptGroup'],
|
enum: Object.values(ResourceType),
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
resourceId: {
|
resourceId: {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { Document, Types } from 'mongoose';
|
import type { Document, Types } from 'mongoose';
|
||||||
import { PrincipalType } from 'librechat-data-provider';
|
import { PrincipalType, PrincipalModel, ResourceType } from 'librechat-data-provider';
|
||||||
|
|
||||||
export type AclEntry = {
|
export type AclEntry = {
|
||||||
/** The type of principal ('user', 'group', 'public') */
|
/** The type of principal ('user', 'group', 'public') */
|
||||||
|
@ -7,9 +7,9 @@ export type AclEntry = {
|
||||||
/** The ID of the principal (null for 'public') */
|
/** The ID of the principal (null for 'public') */
|
||||||
principalId?: Types.ObjectId;
|
principalId?: Types.ObjectId;
|
||||||
/** The model name for the principal ('User' or 'Group') */
|
/** The model name for the principal ('User' or 'Group') */
|
||||||
principalModel?: 'User' | 'Group';
|
principalModel?: PrincipalModel;
|
||||||
/** The type of resource ('agent', 'project', 'file', 'promptGroup') */
|
/** The type of resource ('agent', 'project', 'file', 'promptGroup') */
|
||||||
resourceType: 'agent' | 'project' | 'file' | 'promptGroup';
|
resourceType: ResourceType;
|
||||||
/** The ID of the resource */
|
/** The ID of the resource */
|
||||||
resourceId: Types.ObjectId;
|
resourceId: Types.ObjectId;
|
||||||
/** Permission bits for this entry */
|
/** Permission bits for this entry */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue