🛂 feat: Role as Permission Principal Type

WIP: Role as Permission Principal Type

WIP: add user role check optimization to user principal check, update type comparisons

WIP: cover edge cases for string vs ObjectId handling in permission granting and checking

chore: Update people picker access middleware to use PrincipalType constants

feat: Enhance people picker access control to include roles permissions

chore: add missing default role schema values for people picker perms, cleanup typing

feat: Enhance PeoplePicker component with role-specific UI and localization updates

chore: Add missing `VIEW_ROLES` permission to role schema
This commit is contained in:
Danny Avila 2025-08-03 19:24:40 -04:00
parent 28d63dab71
commit 39346d6b8e
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
49 changed files with 2879 additions and 258 deletions

View file

@ -1,4 +1,4 @@
const { PermissionTypes, Permissions } = require('librechat-data-provider');
const { PrincipalType, PermissionTypes, Permissions } = require('librechat-data-provider');
const { getRoleByName } = require('~/models/Role');
const { logger } = require('~/config');
@ -7,7 +7,8 @@ const { logger } = require('~/config');
* Checks specific permission based on the 'type' query parameter:
* - type=user: requires VIEW_USERS permission
* - type=group: requires VIEW_GROUPS permission
* - no type (mixed search): requires either VIEW_USERS OR VIEW_GROUPS
* - type=role: requires VIEW_ROLES permission
* - no type (mixed search): requires either VIEW_USERS OR VIEW_GROUPS OR VIEW_ROLES
*/
const checkPeoplePickerAccess = async (req, res, next) => {
try {
@ -31,29 +32,38 @@ const checkPeoplePickerAccess = async (req, res, next) => {
const peoplePickerPerms = role.permissions[PermissionTypes.PEOPLE_PICKER] || {};
const canViewUsers = peoplePickerPerms[Permissions.VIEW_USERS] === true;
const canViewGroups = peoplePickerPerms[Permissions.VIEW_GROUPS] === true;
const canViewRoles = peoplePickerPerms[Permissions.VIEW_ROLES] === true;
if (type === 'user') {
if (!canViewUsers) {
return res.status(403).json({
error: 'Forbidden',
message: 'Insufficient permissions to search for users',
});
}
} else if (type === 'group') {
if (!canViewGroups) {
return res.status(403).json({
error: 'Forbidden',
message: 'Insufficient permissions to search for groups',
});
}
} else {
if (!canViewUsers || !canViewGroups) {
return res.status(403).json({
error: 'Forbidden',
message: 'Insufficient permissions to search for both users and groups',
});
}
const permissionChecks = {
[PrincipalType.USER]: {
hasPermission: canViewUsers,
message: 'Insufficient permissions to search for users',
},
[PrincipalType.GROUP]: {
hasPermission: canViewGroups,
message: 'Insufficient permissions to search for groups',
},
[PrincipalType.ROLE]: {
hasPermission: canViewRoles,
message: 'Insufficient permissions to search for roles',
},
};
const check = permissionChecks[type];
if (check && !check.hasPermission) {
return res.status(403).json({
error: 'Forbidden',
message: check.message,
});
}
if (!type && !canViewUsers && !canViewGroups && !canViewRoles) {
return res.status(403).json({
error: 'Forbidden',
message: 'Insufficient permissions to search for users, groups, or roles',
});
}
next();
} catch (error) {
logger.error(