mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-22 19:30:15 +01:00
WIP: add user role check optimization to user principal check, update type comparisons
This commit is contained in:
parent
89f0a4e02f
commit
ecd7bf0d51
19 changed files with 481 additions and 71 deletions
|
|
@ -53,7 +53,9 @@ describe('Role-based Permissions Integration', () => {
|
|||
role: 'admin',
|
||||
});
|
||||
|
||||
const principals = await methods.getUserPrincipals(adminUser._id as mongoose.Types.ObjectId);
|
||||
const principals = await methods.getUserPrincipals({
|
||||
userId: adminUser._id as mongoose.Types.ObjectId,
|
||||
});
|
||||
|
||||
// Should have user, role, and public principals
|
||||
expect(principals).toHaveLength(3);
|
||||
|
|
@ -84,9 +86,9 @@ describe('Role-based Permissions Integration', () => {
|
|||
role: null, // Explicitly set to null to override default
|
||||
});
|
||||
|
||||
const principals = await methods.getUserPrincipals(
|
||||
regularUser._id as mongoose.Types.ObjectId,
|
||||
);
|
||||
const principals = await methods.getUserPrincipals({
|
||||
userId: regularUser._id as mongoose.Types.ObjectId,
|
||||
});
|
||||
|
||||
// Should only have user and public principals
|
||||
expect(principals).toHaveLength(2);
|
||||
|
|
@ -116,7 +118,9 @@ describe('Role-based Permissions Integration', () => {
|
|||
memberIds: [(user._id as mongoose.Types.ObjectId).toString()],
|
||||
});
|
||||
|
||||
const principals = await methods.getUserPrincipals(user._id as mongoose.Types.ObjectId);
|
||||
const principals = await methods.getUserPrincipals({
|
||||
userId: user._id as mongoose.Types.ObjectId,
|
||||
});
|
||||
|
||||
// Should have user, role, 2 groups, and public
|
||||
expect(principals).toHaveLength(5);
|
||||
|
|
@ -156,7 +160,9 @@ describe('Role-based Permissions Integration', () => {
|
|||
role: testCase.role,
|
||||
});
|
||||
|
||||
const principals = await methods.getUserPrincipals(user._id as mongoose.Types.ObjectId);
|
||||
const principals = await methods.getUserPrincipals({
|
||||
userId: user._id as mongoose.Types.ObjectId,
|
||||
});
|
||||
const rolePrincipal = principals.find((p) => p.principalType === PrincipalType.ROLE);
|
||||
|
||||
expect(rolePrincipal).toBeDefined();
|
||||
|
|
@ -292,7 +298,9 @@ describe('Role-based Permissions Integration', () => {
|
|||
});
|
||||
|
||||
// Initial principals
|
||||
let principals = await methods.getUserPrincipals(user._id as mongoose.Types.ObjectId);
|
||||
let principals = await methods.getUserPrincipals({
|
||||
userId: user._id as mongoose.Types.ObjectId,
|
||||
});
|
||||
let rolePrincipal = principals.find((p) => p.principalType === PrincipalType.ROLE);
|
||||
expect(rolePrincipal?.principalId).toBe('viewer');
|
||||
|
||||
|
|
@ -301,7 +309,9 @@ describe('Role-based Permissions Integration', () => {
|
|||
await user.save();
|
||||
|
||||
// Get principals again
|
||||
principals = await methods.getUserPrincipals(user._id as mongoose.Types.ObjectId);
|
||||
principals = await methods.getUserPrincipals({
|
||||
userId: user._id as mongoose.Types.ObjectId,
|
||||
});
|
||||
rolePrincipal = principals.find((p) => p.principalType === PrincipalType.ROLE);
|
||||
expect(rolePrincipal?.principalId).toBe('editor');
|
||||
});
|
||||
|
|
@ -315,7 +325,9 @@ describe('Role-based Permissions Integration', () => {
|
|||
});
|
||||
|
||||
// Initial check
|
||||
let principals = await methods.getUserPrincipals(user._id as mongoose.Types.ObjectId);
|
||||
let principals = await methods.getUserPrincipals({
|
||||
userId: user._id as mongoose.Types.ObjectId,
|
||||
});
|
||||
expect(principals).toHaveLength(3); // user, role, public
|
||||
|
||||
// Remove role
|
||||
|
|
@ -323,7 +335,9 @@ describe('Role-based Permissions Integration', () => {
|
|||
await user.save();
|
||||
|
||||
// Check again
|
||||
principals = await methods.getUserPrincipals(user._id as mongoose.Types.ObjectId);
|
||||
principals = await methods.getUserPrincipals({
|
||||
userId: user._id as mongoose.Types.ObjectId,
|
||||
});
|
||||
expect(principals).toHaveLength(2); // user, public
|
||||
const rolePrincipal = principals.find((p) => p.principalType === PrincipalType.ROLE);
|
||||
expect(rolePrincipal).toBeUndefined();
|
||||
|
|
@ -352,7 +366,9 @@ describe('Role-based Permissions Integration', () => {
|
|||
|
||||
const user = await User.create(userData);
|
||||
|
||||
const principals = await methods.getUserPrincipals(user._id as mongoose.Types.ObjectId);
|
||||
const principals = await methods.getUserPrincipals({
|
||||
userId: user._id as mongoose.Types.ObjectId,
|
||||
});
|
||||
const rolePrincipal = principals.find((p) => p.principalType === PrincipalType.ROLE);
|
||||
|
||||
if (testCase.expected) {
|
||||
|
|
|
|||
|
|
@ -325,7 +325,9 @@ describe('User Group Methods Tests', () => {
|
|||
);
|
||||
|
||||
/** Get user principals */
|
||||
const principals = await methods.getUserPrincipals(testUser1._id as mongoose.Types.ObjectId);
|
||||
const principals = await methods.getUserPrincipals({
|
||||
userId: testUser1._id as mongoose.Types.ObjectId,
|
||||
});
|
||||
|
||||
/** Should include user, role (default USER), group, and public principals */
|
||||
expect(principals).toHaveLength(4);
|
||||
|
|
@ -349,7 +351,9 @@ describe('User Group Methods Tests', () => {
|
|||
|
||||
test('should return user and public principals for non-existent user in getUserPrincipals', async () => {
|
||||
const nonExistentId = new mongoose.Types.ObjectId();
|
||||
const principals = await methods.getUserPrincipals(nonExistentId);
|
||||
const principals = await methods.getUserPrincipals({
|
||||
userId: nonExistentId,
|
||||
});
|
||||
|
||||
/** Should still return user and public principals even for non-existent user */
|
||||
expect(principals).toHaveLength(2);
|
||||
|
|
|
|||
|
|
@ -237,35 +237,45 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
|
|||
/**
|
||||
* Get a list of all principal identifiers for a user (user ID + group IDs + public)
|
||||
* For use in permission checks
|
||||
* @param userId - The user ID
|
||||
* @param params - Parameters object
|
||||
* @param params.userId - The user ID
|
||||
* @param params.role - Optional user role (if not provided, will query from DB)
|
||||
* @param session - Optional MongoDB session for transactions
|
||||
* @returns Array of principal objects with type and id
|
||||
*/
|
||||
async function getUserPrincipals(
|
||||
userId: string | Types.ObjectId,
|
||||
params: {
|
||||
userId: string | Types.ObjectId;
|
||||
role?: string | null;
|
||||
},
|
||||
session?: ClientSession,
|
||||
): Promise<Array<{ principalType: string; principalId?: string | Types.ObjectId }>> {
|
||||
const { userId, role } = params;
|
||||
const principals: Array<{ principalType: string; principalId?: string | Types.ObjectId }> = [
|
||||
{ principalType: PrincipalType.USER, principalId: userId },
|
||||
];
|
||||
|
||||
// Get user to check their role
|
||||
const User = mongoose.models.User as Model<IUser>;
|
||||
const query = User.findById(userId).select('role');
|
||||
if (session) {
|
||||
query.session(session);
|
||||
// If role is not provided, query user to get it
|
||||
let userRole = role;
|
||||
if (userRole === undefined) {
|
||||
const User = mongoose.models.User as Model<IUser>;
|
||||
const query = User.findById(userId).select('role');
|
||||
if (session) {
|
||||
query.session(session);
|
||||
}
|
||||
const user = await query.lean();
|
||||
userRole = user?.role;
|
||||
}
|
||||
const user = await query.lean();
|
||||
|
||||
// Add role as a principal if user has one
|
||||
if (user?.role && user.role.trim()) {
|
||||
principals.push({ principalType: PrincipalType.ROLE, principalId: user.role });
|
||||
if (userRole && userRole.trim()) {
|
||||
principals.push({ principalType: PrincipalType.ROLE, principalId: userRole });
|
||||
}
|
||||
|
||||
const userGroups = await getUserGroups(userId, session);
|
||||
if (userGroups && userGroups.length > 0) {
|
||||
userGroups.forEach((group) => {
|
||||
principals.push({ principalType: PrincipalType.GROUP, principalId: group._id.toString() });
|
||||
principals.push({ principalType: PrincipalType.GROUP, principalId: group._id });
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue