2025-06-23 10:22:27 -04:00
|
|
|
const mongoose = require('mongoose');
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
const { isEnabled } = require('@librechat/api');
|
2025-06-23 10:22:27 -04:00
|
|
|
const { getTransactionSupport, logger } = require('@librechat/data-schemas');
|
2025-08-03 19:24:40 -04:00
|
|
|
const { ResourceType, PrincipalType, PrincipalModel } = require('librechat-data-provider');
|
2025-06-23 10:22:27 -04:00
|
|
|
const {
|
|
|
|
|
entraIdPrincipalFeatureEnabled,
|
|
|
|
|
getUserOwnedEntraGroups,
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
getUserEntraGroups,
|
2025-06-23 10:22:27 -04:00
|
|
|
getGroupMembers,
|
|
|
|
|
getGroupOwners,
|
|
|
|
|
} = require('~/server/services/GraphApiService');
|
|
|
|
|
const {
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
findAccessibleResources: findAccessibleResourcesACL,
|
|
|
|
|
getEffectivePermissions: getEffectivePermissionsACL,
|
2025-12-04 21:37:23 +01:00
|
|
|
getEffectivePermissionsForResources: getEffectivePermissionsForResourcesACL,
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
grantPermission: grantPermissionACL,
|
|
|
|
|
findEntriesByPrincipalsAndResource,
|
2025-06-23 10:22:27 -04:00
|
|
|
findGroupByExternalId,
|
|
|
|
|
findRoleByIdentifier,
|
|
|
|
|
getUserPrincipals,
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
hasPermission,
|
2025-06-23 10:22:27 -04:00
|
|
|
createGroup,
|
|
|
|
|
createUser,
|
|
|
|
|
updateUser,
|
|
|
|
|
findUser,
|
|
|
|
|
} = require('~/models');
|
|
|
|
|
const { AclEntry, AccessRole, Group } = require('~/db/models');
|
|
|
|
|
|
|
|
|
|
/** @type {boolean|null} */
|
|
|
|
|
let transactionSupportCache = null;
|
|
|
|
|
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
/**
|
|
|
|
|
* Validates that the resourceType is one of the supported enum values
|
|
|
|
|
* @param {string} resourceType - The resource type to validate
|
|
|
|
|
* @throws {Error} If resourceType is not valid
|
|
|
|
|
*/
|
|
|
|
|
const validateResourceType = (resourceType) => {
|
|
|
|
|
const validTypes = Object.values(ResourceType);
|
|
|
|
|
if (!validTypes.includes(resourceType)) {
|
|
|
|
|
throw new Error(`Invalid resourceType: ${resourceType}. Valid types: ${validTypes.join(', ')}`);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-06-23 10:22:27 -04:00
|
|
|
/**
|
|
|
|
|
* @import { TPrincipal } from 'librechat-data-provider'
|
|
|
|
|
*/
|
|
|
|
|
/**
|
|
|
|
|
* Grant a permission to a principal for a resource using a role
|
|
|
|
|
* @param {Object} params - Parameters for granting role-based permission
|
2025-08-03 19:24:40 -04:00
|
|
|
* @param {string} params.principalType - PrincipalType.USER, PrincipalType.GROUP, or PrincipalType.PUBLIC
|
|
|
|
|
* @param {string|mongoose.Types.ObjectId|null} params.principalId - The ID of the principal (null for PrincipalType.PUBLIC)
|
2025-06-23 10:22:27 -04:00
|
|
|
* @param {string} params.resourceType - Type of resource (e.g., 'agent')
|
|
|
|
|
* @param {string|mongoose.Types.ObjectId} params.resourceId - The ID of the resource
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
* @param {string} params.accessRoleId - The ID of the role (e.g., AccessRoleIds.AGENT_VIEWER, AccessRoleIds.AGENT_EDITOR)
|
2025-06-23 10:22:27 -04:00
|
|
|
* @param {string|mongoose.Types.ObjectId} params.grantedBy - User ID granting the permission
|
|
|
|
|
* @param {mongoose.ClientSession} [params.session] - Optional MongoDB session for transactions
|
|
|
|
|
* @returns {Promise<Object>} The created or updated ACL entry
|
|
|
|
|
*/
|
|
|
|
|
const grantPermission = async ({
|
|
|
|
|
principalType,
|
|
|
|
|
principalId,
|
|
|
|
|
resourceType,
|
|
|
|
|
resourceId,
|
|
|
|
|
accessRoleId,
|
|
|
|
|
grantedBy,
|
|
|
|
|
session,
|
|
|
|
|
}) => {
|
|
|
|
|
try {
|
2025-08-02 16:02:56 -04:00
|
|
|
if (!Object.values(PrincipalType).includes(principalType)) {
|
2025-06-23 10:22:27 -04:00
|
|
|
throw new Error(`Invalid principal type: ${principalType}`);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-02 16:02:56 -04:00
|
|
|
if (principalType !== PrincipalType.PUBLIC && !principalId) {
|
2025-08-03 19:24:40 -04:00
|
|
|
throw new Error('Principal ID is required for user, group, and role principals');
|
2025-06-23 10:22:27 -04:00
|
|
|
}
|
|
|
|
|
|
2025-08-03 19:24:40 -04:00
|
|
|
// Validate principalId based on type
|
|
|
|
|
if (principalId && principalType === PrincipalType.ROLE) {
|
|
|
|
|
// Role IDs are strings (role names)
|
|
|
|
|
if (typeof principalId !== 'string' || principalId.trim().length === 0) {
|
|
|
|
|
throw new Error(`Invalid role ID: ${principalId}`);
|
|
|
|
|
}
|
|
|
|
|
} else if (
|
|
|
|
|
principalType &&
|
|
|
|
|
principalType !== PrincipalType.PUBLIC &&
|
|
|
|
|
!mongoose.Types.ObjectId.isValid(principalId)
|
|
|
|
|
) {
|
|
|
|
|
// User and Group IDs must be valid ObjectIds
|
2025-06-23 10:22:27 -04:00
|
|
|
throw new Error(`Invalid principal ID: ${principalId}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!resourceId || !mongoose.Types.ObjectId.isValid(resourceId)) {
|
|
|
|
|
throw new Error(`Invalid resource ID: ${resourceId}`);
|
|
|
|
|
}
|
|
|
|
|
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
validateResourceType(resourceType);
|
|
|
|
|
|
2025-06-23 10:22:27 -04:00
|
|
|
// Get the role to determine permission bits
|
|
|
|
|
const role = await findRoleByIdentifier(accessRoleId);
|
|
|
|
|
if (!role) {
|
|
|
|
|
throw new Error(`Role ${accessRoleId} not found`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure the role is for the correct resource type
|
|
|
|
|
if (role.resourceType !== resourceType) {
|
|
|
|
|
throw new Error(
|
|
|
|
|
`Role ${accessRoleId} is for ${role.resourceType} resources, not ${resourceType}`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return await grantPermissionACL(
|
|
|
|
|
principalType,
|
|
|
|
|
principalId,
|
|
|
|
|
resourceType,
|
|
|
|
|
resourceId,
|
|
|
|
|
role.permBits,
|
|
|
|
|
grantedBy,
|
|
|
|
|
session,
|
|
|
|
|
role._id,
|
|
|
|
|
);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`[PermissionService.grantPermission] Error: ${error.message}`);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check if a user has specific permission bits on a resource
|
|
|
|
|
* @param {Object} params - Parameters for checking permissions
|
|
|
|
|
* @param {string|mongoose.Types.ObjectId} params.userId - The ID of the user
|
2025-08-03 19:24:40 -04:00
|
|
|
* @param {string} [params.role] - Optional user role (if not provided, will query from DB)
|
2025-06-23 10:22:27 -04:00
|
|
|
* @param {string} params.resourceType - Type of resource (e.g., 'agent')
|
|
|
|
|
* @param {string|mongoose.Types.ObjectId} params.resourceId - The ID of the resource
|
|
|
|
|
* @param {number} params.requiredPermissions - The permission bits required (e.g., 1 for VIEW, 3 for VIEW+EDIT)
|
|
|
|
|
* @returns {Promise<boolean>} Whether the user has the required permission bits
|
|
|
|
|
*/
|
2025-08-03 19:24:40 -04:00
|
|
|
const checkPermission = async ({ userId, role, resourceType, resourceId, requiredPermission }) => {
|
2025-06-23 10:22:27 -04:00
|
|
|
try {
|
|
|
|
|
if (typeof requiredPermission !== 'number' || requiredPermission < 1) {
|
|
|
|
|
throw new Error('requiredPermission must be a positive number');
|
|
|
|
|
}
|
|
|
|
|
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
validateResourceType(resourceType);
|
|
|
|
|
|
2025-08-03 19:24:40 -04:00
|
|
|
const principals = await getUserPrincipals({ userId, role });
|
2025-06-23 10:22:27 -04:00
|
|
|
|
|
|
|
|
if (principals.length === 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return await hasPermission(principals, resourceType, resourceId, requiredPermission);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`[PermissionService.checkPermission] Error: ${error.message}`);
|
|
|
|
|
if (error.message.includes('requiredPermission must be')) {
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get effective permission bitmask for a user on a resource
|
|
|
|
|
* @param {Object} params - Parameters for getting effective permissions
|
|
|
|
|
* @param {string|mongoose.Types.ObjectId} params.userId - The ID of the user
|
2025-08-03 19:24:40 -04:00
|
|
|
* @param {string} [params.role] - Optional user role (if not provided, will query from DB)
|
2025-06-23 10:22:27 -04:00
|
|
|
* @param {string} params.resourceType - Type of resource (e.g., 'agent')
|
|
|
|
|
* @param {string|mongoose.Types.ObjectId} params.resourceId - The ID of the resource
|
|
|
|
|
* @returns {Promise<number>} Effective permission bitmask
|
|
|
|
|
*/
|
2025-08-03 19:24:40 -04:00
|
|
|
const getEffectivePermissions = async ({ userId, role, resourceType, resourceId }) => {
|
2025-06-23 10:22:27 -04:00
|
|
|
try {
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
validateResourceType(resourceType);
|
|
|
|
|
|
2025-08-03 19:24:40 -04:00
|
|
|
const principals = await getUserPrincipals({ userId, role });
|
2025-06-23 10:22:27 -04:00
|
|
|
|
|
|
|
|
if (principals.length === 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
🛸 feat: Remote Agent Access with External API Support (#11503)
* 🪪 feat: Microsoft Graph Access Token Placeholder for MCP Servers (#10867)
* feat: MCP Graph Token env var
* Addressing copilot remarks
* Addressed Copilot review remarks
* Fixed graphtokenservice mock in MCP test suite
* fix: remove unnecessary type check and cast in resolveGraphTokensInRecord
* ci: add Graph Token integration tests in MCPManager
* refactor: update user type definitions to use Partial<IUser> in multiple functions
* test: enhance MCP tests for graph token processing and user placeholder resolution
- Added comprehensive tests to validate the interaction between preProcessGraphTokens and processMCPEnv.
- Ensured correct resolution of graph tokens and user placeholders in various configurations.
- Mocked OIDC utilities to facilitate testing of token extraction and validation.
- Verified that original options remain unchanged after processing.
* chore: import order
* chore: imports
---------
Co-authored-by: Danny Avila <danny@librechat.ai>
* WIP: OpenAI-compatible API for LibreChat agents
- Added OpenAIChatCompletionController for handling chat completions.
- Introduced ListModelsController and GetModelController for listing and retrieving agent details.
- Created routes for OpenAI API endpoints, including /v1/chat/completions and /v1/models.
- Developed event handlers for streaming responses in OpenAI format.
- Implemented request validation and error handling for API interactions.
- Integrated content aggregation and response formatting to align with OpenAI specifications.
This commit establishes a foundational API for interacting with LibreChat agents in a manner compatible with OpenAI's chat completion interface.
* refactor: OpenAI-spec content aggregation for improved performance and clarity
* fix: OpenAI chat completion controller with safe user handling for correct tool loading
* refactor: Remove conversation ID from OpenAI response context and related handlers
* refactor: OpenAI chat completion handling with streaming support
- Introduced a lightweight tracker for streaming responses, allowing for efficient tracking of emitted content and usage metadata.
- Updated the OpenAIChatCompletionController to utilize the new tracker, improving the handling of streaming and non-streaming responses.
- Refactored event handlers to accommodate the new streaming logic, ensuring proper management of tool calls and content aggregation.
- Adjusted response handling to streamline error reporting during streaming sessions.
* WIP: Open Responses API with core service, types, and handlers
- Added Open Responses API module with comprehensive types and enums.
- Implemented core service for processing requests, including validation and input conversion.
- Developed event handlers for streaming responses and non-streaming aggregation.
- Established response building logic and error handling mechanisms.
- Created detailed types for input and output content, ensuring compliance with Open Responses specification.
* feat: Implement response storage and retrieval in Open Responses API
- Added functionality to save user input messages and assistant responses to the database when the `store` flag is set to true.
- Introduced a new endpoint to retrieve stored responses by ID, allowing users to access previous interactions.
- Enhanced the response creation process to include database operations for conversation and message storage.
- Implemented tests to validate the storage and retrieval of responses, ensuring correct behavior for both existing and non-existent response IDs.
* refactor: Open Responses API with additional token tracking and validation
- Added support for tracking cached tokens in response usage, improving token management.
- Updated response structure to include new properties for top log probabilities and detailed usage metrics.
- Enhanced tests to validate the presence and types of new properties in API responses, ensuring compliance with updated specifications.
- Refactored response handling to accommodate new fields and improve overall clarity and performance.
* refactor: Update reasoning event handlers and types for consistency
- Renamed reasoning text events to simplify naming conventions, changing `emitReasoningTextDelta` to `emitReasoningDelta` and `emitReasoningTextDone` to `emitReasoningDone`.
- Updated event types in the API to reflect the new naming, ensuring consistency across the codebase.
- Added `logprobs` property to output events for enhanced tracking of log probabilities.
* feat: Add validation for streaming events in Open Responses API tests
* feat: Implement response.created event in Open Responses API
- Added emitResponseCreated function to emit the response.created event as the first event in the streaming sequence, adhering to the Open Responses specification.
- Updated createResponse function to emit response.created followed by response.in_progress.
- Enhanced tests to validate the order of emitted events, ensuring response.created is triggered before response.in_progress.
* feat: Responses API with attachment event handling
- Introduced `createResponsesToolEndCallback` to handle attachment events in the Responses API, emitting `librechat:attachment` events as per the Open Responses extension specification.
- Updated the `createResponse` function to utilize the new callback for processing tool outputs and emitting attachments during streaming.
- Added helper functions for writing attachment events and defined types for attachment data, ensuring compatibility with the Open Responses protocol.
- Enhanced tests to validate the integration of attachment events within the Responses API workflow.
* WIP: remote agent auth
* fix: Improve loading state handling in AgentApiKeys component
- Updated the rendering logic to conditionally display loading spinner and API keys based on the loading state.
- Removed unnecessary imports and streamlined the component for better readability.
* refactor: Update API key access handling in routes
- Replaced `checkAccess` with `generateCheckAccess` for improved access control.
- Consolidated access checks into a single `checkApiKeyAccess` function, enhancing code readability and maintainability.
- Streamlined route definitions for creating, listing, retrieving, and deleting API keys.
* fix: Add permission handling for REMOTE_AGENT resource type
* feat: Enhance permission handling for REMOTE_AGENT resources
- Updated the deleteAgent and deleteUserAgents functions to handle permissions for both AGENT and REMOTE_AGENT resource types.
- Introduced new functions to enrich REMOTE_AGENT principals and backfill permissions for AGENT owners.
- Modified createAgentHandler and duplicateAgentHandler to grant permissions for REMOTE_AGENT alongside AGENT.
- Added utility functions for retrieving effective permissions for REMOTE_AGENT resources, ensuring consistent access control across the application.
* refactor: Rename and update roles for remote agent access
- Changed role name from API User to Editor in translation files for clarity.
- Updated default editor role ID from REMOTE_AGENT_USER to REMOTE_AGENT_EDITOR in resource configurations.
- Adjusted role localization to reflect the new Editor role.
- Modified access permissions to align with the updated role definitions across the application.
* feat: Introduce remote agent permissions and update access handling
- Added support for REMOTE_AGENTS in permission schemas, including use, create, share, and share_public permissions.
- Updated the interface configuration to include remote agent settings.
- Modified middleware and API key access checks to align with the new remote agent permission structure.
- Enhanced role defaults to incorporate remote agent permissions, ensuring consistent access control across the application.
* refactor: Update AgentApiKeys component and permissions handling
- Refactored the AgentApiKeys component to improve structure and readability, including the introduction of ApiKeysContent for better separation of concerns.
- Updated CreateKeyDialog to accept an onKeyCreated callback, enhancing its functionality.
- Adjusted permission checks in Data component to use REMOTE_AGENTS and USE permissions, aligning with recent permission schema changes.
- Enhanced loading state handling and dialog management for a smoother user experience.
* refactor: Update remote agent access checks in API routes
- Replaced existing access checks with `generateCheckAccess` for remote agents in the API keys and agents routes.
- Introduced specific permission checks for creating, listing, retrieving, and deleting API keys, enhancing access control.
- Improved code structure by consolidating permission handling for remote agents across multiple routes.
* fix: Correct query parameters in ApiKeysContent component
- Updated the useGetAgentApiKeysQuery call to include an object for the enabled parameter, ensuring proper functionality when the component is open.
- This change improves the handling of API key retrieval based on the component's open state.
* feat: Implement remote agents permissions and update API routes
- Added new API route for updating remote agents permissions, enhancing role management capabilities.
- Introduced remote agents permissions handling in the AgentApiKeys component, including a dedicated settings dialog.
- Updated localization files to include new remote agents permission labels for better user experience.
- Refactored data provider to support remote agents permissions updates, ensuring consistent access control across the application.
* feat: Add remote agents permissions to role schema and interface
- Introduced new permissions for REMOTE_AGENTS in the role schema, including USE, CREATE, SHARE, and SHARE_PUBLIC.
- Updated the IRole interface to reflect the new remote agents permissions structure, enhancing role management capabilities.
* feat: Add remote agents settings button to API keys dialog
* feat: Update AgentFooter to include remote agent sharing permissions
- Refactored access checks to incorporate permissions for sharing remote agents.
- Enhanced conditional rendering logic to allow sharing by users with remote agent permissions.
- Improved loading state handling for remote agent permissions, ensuring a smoother user experience.
* refactor: Update API key creation access check and localization strings
- Replaced the access check for creating API keys to use the existing remote agents access check.
- Updated localization strings to correct the descriptions for remote agent permissions, ensuring clarity in user interface.
* fix: resource permission mapping to include remote agents
- Changed the resourceToPermissionMap to use a Partial<Record> for better flexibility.
- Added mapping for REMOTE_AGENT permissions, enhancing the sharing capabilities for remote agents.
* feat: Implement remote access checks for agent models
- Enhanced ListModelsController and GetModelController to include checks for user permissions on remote agents.
- Integrated findAccessibleResources to filter agents based on VIEW permission for REMOTE_AGENT.
- Updated response handling to ensure users can only access agents they have permissions for, improving security and access control.
* fix: Update user parameter type in processUserPlaceholders function
- Changed the user parameter type in the processUserPlaceholders function from Partial<Partial<IUser>> to Partial<IUser> for improved type clarity and consistency.
* refactor: Simplify integration test structure by removing conditional describe
- Replaced conditional describeWithApiKey with a standard describe for all integration tests in responses.spec.js.
- This change enhances test clarity and ensures all tests are executed consistently, regardless of the SKIP_INTEGRATION_TESTS flag.
* test: Update AgentFooter tests to reflect new grant access dialog ID
- Changed test IDs for the grant access dialog in AgentFooter tests to include the resource type, ensuring accurate identification in the test cases.
- This update improves test clarity and aligns with recent changes in the component's implementation.
* test: Enhance integration tests for Open Responses API
- Updated integration tests in responses.spec.js to utilize an authRequest helper for consistent authorization handling across all test cases.
- Introduced a test user and API key creation to improve test setup and ensure proper permission checks for remote agents.
- Added checks for existing access roles and created necessary roles if they do not exist, enhancing test reliability and coverage.
* feat: Extend accessRole schema to include remoteAgent resource type
- Updated the accessRole schema to add 'remoteAgent' to the resourceType enum, enhancing the flexibility of role assignments and permissions management.
* test: refactored test setup to create a minimal Express app for responses routes, enhancing test structure and maintainability.
* test: Enhance abort.spec.js by mocking additional modules for improved test isolation
- Updated the test setup in abort.spec.js to include actual implementations of '@librechat/data-schemas' and '@librechat/api' while maintaining mock functionality.
- This change improves test reliability and ensures that the tests are more representative of the actual module behavior.
* refactor: Update conversation ID generation to use UUID
- Replaced the nanoid with uuidv4 for generating conversation IDs in the createResponse function, enhancing uniqueness and consistency in ID generation.
* test: Add remote agent access roles to AccessRole model tests
- Included additional access roles for remote agents (REMOTE_AGENT_EDITOR, REMOTE_AGENT_OWNER, REMOTE_AGENT_VIEWER) in the AccessRole model tests to ensure comprehensive coverage of role assignments and permissions management.
* chore: Add deletion of user agent API keys in user deletion process
- Updated the user deletion process in UserController and delete-user.js to include the removal of user agent API keys, ensuring comprehensive cleanup of user data upon account deletion.
* test: Add remote agents permissions to permissions.spec.ts
- Enhanced the permissions tests by including comprehensive permission settings for remote agents across various scenarios, ensuring accurate validation of access controls for remote agent roles.
* chore: Update remote agents translations for clarity and consistency
- Removed outdated remote agents translation entries and added revised entries to improve clarity on API key creation and sharing permissions for remote agents. This enhances user understanding of the available functionalities.
* feat: Add indexing and TTL for agent API keys
- Introduced an index on the `key` field for improved query performance.
- Added a TTL index on the `expiresAt` field to enable automatic cleanup of expired API keys, ensuring efficient management of stored keys.
* chore: Update API route documentation for clarity
- Revised comments in the agents route file to clarify the handling of API key authentication.
- Removed outdated endpoint listings to streamline the documentation and focus on current functionality.
---------
Co-authored-by: Max Sanna <max@maxsanna.com>
2026-01-26 10:50:30 -05:00
|
|
|
|
2025-06-23 10:22:27 -04:00
|
|
|
return await getEffectivePermissionsACL(principals, resourceType, resourceId);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`[PermissionService.getEffectivePermissions] Error: ${error.message}`);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-12-04 21:37:23 +01:00
|
|
|
/**
|
|
|
|
|
* Get effective permissions for multiple resources in a batch operation
|
|
|
|
|
* Returns map of resourceId → effectivePermissionBits
|
|
|
|
|
*
|
|
|
|
|
* @param {Object} params - Parameters
|
|
|
|
|
* @param {string|mongoose.Types.ObjectId} params.userId - User ID
|
|
|
|
|
* @param {string} [params.role] - User role (for group membership)
|
|
|
|
|
* @param {string} params.resourceType - Resource type (must be valid ResourceType)
|
|
|
|
|
* @param {Array<mongoose.Types.ObjectId>} params.resourceIds - Array of resource IDs
|
|
|
|
|
* @returns {Promise<Map<string, number>>} Map of resourceId string → permission bits
|
|
|
|
|
* @throws {Error} If resourceType is invalid
|
|
|
|
|
*/
|
|
|
|
|
const getResourcePermissionsMap = async ({ userId, role, resourceType, resourceIds }) => {
|
|
|
|
|
// Validate resource type - throw on invalid type
|
|
|
|
|
validateResourceType(resourceType);
|
|
|
|
|
|
|
|
|
|
// Handle empty input
|
|
|
|
|
if (!Array.isArray(resourceIds) || resourceIds.length === 0) {
|
|
|
|
|
return new Map();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Get user principals (user + groups + public)
|
|
|
|
|
const principals = await getUserPrincipals({ userId, role });
|
|
|
|
|
|
|
|
|
|
// Use batch method from aclEntry
|
|
|
|
|
const permissionsMap = await getEffectivePermissionsForResourcesACL(
|
|
|
|
|
principals,
|
|
|
|
|
resourceType,
|
|
|
|
|
resourceIds,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
logger.debug(
|
|
|
|
|
`[PermissionService.getResourcePermissionsMap] Computed permissions for ${resourceIds.length} resources, ${permissionsMap.size} have permissions`,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return permissionsMap;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`[PermissionService.getResourcePermissionsMap] Error: ${error.message}`, error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-06-23 10:22:27 -04:00
|
|
|
/**
|
|
|
|
|
* Find all resources of a specific type that a user has access to with specific permission bits
|
|
|
|
|
* @param {Object} params - Parameters for finding accessible resources
|
|
|
|
|
* @param {string|mongoose.Types.ObjectId} params.userId - The ID of the user
|
2025-08-03 19:24:40 -04:00
|
|
|
* @param {string} [params.role] - Optional user role (if not provided, will query from DB)
|
2025-06-23 10:22:27 -04:00
|
|
|
* @param {string} params.resourceType - Type of resource (e.g., 'agent')
|
|
|
|
|
* @param {number} params.requiredPermissions - The minimum permission bits required (e.g., 1 for VIEW, 3 for VIEW+EDIT)
|
|
|
|
|
* @returns {Promise<Array>} Array of resource IDs
|
|
|
|
|
*/
|
2025-08-03 19:24:40 -04:00
|
|
|
const findAccessibleResources = async ({ userId, role, resourceType, requiredPermissions }) => {
|
2025-06-23 10:22:27 -04:00
|
|
|
try {
|
|
|
|
|
if (typeof requiredPermissions !== 'number' || requiredPermissions < 1) {
|
|
|
|
|
throw new Error('requiredPermissions must be a positive number');
|
|
|
|
|
}
|
|
|
|
|
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
validateResourceType(resourceType);
|
|
|
|
|
|
2025-06-23 10:22:27 -04:00
|
|
|
// Get all principals for the user (user + groups + public)
|
2025-08-03 19:24:40 -04:00
|
|
|
const principalsList = await getUserPrincipals({ userId, role });
|
2025-06-23 10:22:27 -04:00
|
|
|
|
|
|
|
|
if (principalsList.length === 0) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
return await findAccessibleResourcesACL(principalsList, resourceType, requiredPermissions);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`[PermissionService.findAccessibleResources] Error: ${error.message}`);
|
|
|
|
|
// Re-throw validation errors
|
|
|
|
|
if (error.message.includes('requiredPermissions must be')) {
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Find all publicly accessible resources of a specific type
|
|
|
|
|
* @param {Object} params - Parameters for finding publicly accessible resources
|
|
|
|
|
* @param {string} params.resourceType - Type of resource (e.g., 'agent')
|
|
|
|
|
* @param {number} params.requiredPermissions - The minimum permission bits required (e.g., 1 for VIEW, 3 for VIEW+EDIT)
|
|
|
|
|
* @returns {Promise<Array>} Array of resource IDs
|
|
|
|
|
*/
|
|
|
|
|
const findPubliclyAccessibleResources = async ({ resourceType, requiredPermissions }) => {
|
|
|
|
|
try {
|
|
|
|
|
if (typeof requiredPermissions !== 'number' || requiredPermissions < 1) {
|
|
|
|
|
throw new Error('requiredPermissions must be a positive number');
|
|
|
|
|
}
|
|
|
|
|
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
validateResourceType(resourceType);
|
|
|
|
|
|
2025-06-23 10:22:27 -04:00
|
|
|
// Find all public ACL entries where the public principal has at least the required permission bits
|
|
|
|
|
const entries = await AclEntry.find({
|
2025-08-02 16:02:56 -04:00
|
|
|
principalType: PrincipalType.PUBLIC,
|
2025-06-23 10:22:27 -04:00
|
|
|
resourceType,
|
|
|
|
|
permBits: { $bitsAllSet: requiredPermissions },
|
|
|
|
|
}).distinct('resourceId');
|
|
|
|
|
|
|
|
|
|
return entries;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`[PermissionService.findPubliclyAccessibleResources] Error: ${error.message}`);
|
|
|
|
|
// Re-throw validation errors
|
|
|
|
|
if (error.message.includes('requiredPermissions must be')) {
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get available roles for a resource type
|
|
|
|
|
* @param {Object} params - Parameters for getting available roles
|
|
|
|
|
* @param {string} params.resourceType - Type of resource (e.g., 'agent')
|
|
|
|
|
* @returns {Promise<Array>} Array of role definitions
|
|
|
|
|
*/
|
|
|
|
|
const getAvailableRoles = async ({ resourceType }) => {
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
validateResourceType(resourceType);
|
|
|
|
|
|
|
|
|
|
return await AccessRole.find({ resourceType }).lean();
|
2025-06-23 10:22:27 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Ensures a principal exists in the database based on TPrincipal data
|
|
|
|
|
* Creates user if it doesn't exist locally (for Entra ID users)
|
|
|
|
|
* @param {Object} principal - TPrincipal object from frontend
|
2025-08-03 19:24:40 -04:00
|
|
|
* @param {string} principal.type - PrincipalType.USER, PrincipalType.GROUP, or PrincipalType.PUBLIC
|
2025-06-23 10:22:27 -04:00
|
|
|
* @param {string} [principal.id] - Local database ID (null for Entra ID principals not yet synced)
|
|
|
|
|
* @param {string} principal.name - Display name
|
|
|
|
|
* @param {string} [principal.email] - Email address
|
|
|
|
|
* @param {string} [principal.source] - 'local' or 'entra'
|
|
|
|
|
* @param {string} [principal.idOnTheSource] - Entra ID object ID for external principals
|
|
|
|
|
* @returns {Promise<string|null>} Returns the principalId for database operations, null for public
|
|
|
|
|
*/
|
|
|
|
|
const ensurePrincipalExists = async function (principal) {
|
2025-08-03 19:24:40 -04:00
|
|
|
if (principal.type === PrincipalType.PUBLIC) {
|
2025-06-23 10:22:27 -04:00
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (principal.id) {
|
|
|
|
|
return principal.id;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 19:24:40 -04:00
|
|
|
if (principal.type === PrincipalType.USER && principal.source === 'entra') {
|
2025-06-23 10:22:27 -04:00
|
|
|
if (!principal.email || !principal.idOnTheSource) {
|
|
|
|
|
throw new Error('Entra ID user principals must have email and idOnTheSource');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let existingUser = await findUser({ idOnTheSource: principal.idOnTheSource });
|
|
|
|
|
|
|
|
|
|
if (!existingUser) {
|
2025-12-01 09:41:25 -05:00
|
|
|
existingUser = await findUser({ email: principal.email });
|
2025-06-23 10:22:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (existingUser) {
|
|
|
|
|
if (!existingUser.idOnTheSource && principal.idOnTheSource) {
|
|
|
|
|
await updateUser(existingUser._id, {
|
|
|
|
|
idOnTheSource: principal.idOnTheSource,
|
|
|
|
|
provider: 'openid',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return existingUser._id.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const userData = {
|
|
|
|
|
name: principal.name,
|
|
|
|
|
email: principal.email.toLowerCase(),
|
|
|
|
|
emailVerified: false,
|
|
|
|
|
provider: 'openid',
|
|
|
|
|
idOnTheSource: principal.idOnTheSource,
|
|
|
|
|
};
|
|
|
|
|
|
2025-09-17 20:21:36 +02:00
|
|
|
const userId = await createUser(userData, true, true);
|
2025-06-23 10:22:27 -04:00
|
|
|
return userId.toString();
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-03 19:24:40 -04:00
|
|
|
if (principal.type === PrincipalType.GROUP) {
|
2025-06-23 10:22:27 -04:00
|
|
|
throw new Error('Group principals should be handled by group-specific methods');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new Error(`Unsupported principal type: ${principal.type}`);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Ensures a group principal exists in the database based on TPrincipal data
|
|
|
|
|
* Creates group if it doesn't exist locally (for Entra ID groups)
|
|
|
|
|
* For Entra ID groups, always synchronizes member IDs when authentication context is provided
|
|
|
|
|
* @param {Object} principal - TPrincipal object from frontend
|
2025-08-03 19:24:40 -04:00
|
|
|
* @param {string} principal.type - Must be PrincipalType.GROUP
|
2025-06-23 10:22:27 -04:00
|
|
|
* @param {string} [principal.id] - Local database ID (null for Entra ID principals not yet synced)
|
|
|
|
|
* @param {string} principal.name - Display name
|
|
|
|
|
* @param {string} [principal.email] - Email address
|
|
|
|
|
* @param {string} [principal.description] - Group description
|
|
|
|
|
* @param {string} [principal.source] - 'local' or 'entra'
|
|
|
|
|
* @param {string} [principal.idOnTheSource] - Entra ID object ID for external principals
|
|
|
|
|
* @param {Object} [authContext] - Optional authentication context for fetching member data
|
|
|
|
|
* @param {string} [authContext.accessToken] - Access token for Graph API calls
|
|
|
|
|
* @param {string} [authContext.sub] - Subject identifier
|
|
|
|
|
* @returns {Promise<string>} Returns the groupId for database operations
|
|
|
|
|
*/
|
|
|
|
|
const ensureGroupPrincipalExists = async function (principal, authContext = null) {
|
2025-08-03 19:24:40 -04:00
|
|
|
if (principal.type !== PrincipalType.GROUP) {
|
|
|
|
|
throw new Error(`Invalid principal type: ${principal.type}. Expected '${PrincipalType.GROUP}'`);
|
2025-06-23 10:22:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (principal.source === 'entra') {
|
|
|
|
|
if (!principal.name || !principal.idOnTheSource) {
|
|
|
|
|
throw new Error('Entra ID group principals must have name and idOnTheSource');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let memberIds = [];
|
|
|
|
|
if (authContext && authContext.accessToken && authContext.sub) {
|
|
|
|
|
try {
|
|
|
|
|
memberIds = await getGroupMembers(
|
|
|
|
|
authContext.accessToken,
|
|
|
|
|
authContext.sub,
|
|
|
|
|
principal.idOnTheSource,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Include group owners as members if feature is enabled
|
|
|
|
|
if (isEnabled(process.env.ENTRA_ID_INCLUDE_OWNERS_AS_MEMBERS)) {
|
|
|
|
|
const ownerIds = await getGroupOwners(
|
|
|
|
|
authContext.accessToken,
|
|
|
|
|
authContext.sub,
|
|
|
|
|
principal.idOnTheSource,
|
|
|
|
|
);
|
|
|
|
|
if (ownerIds && ownerIds.length > 0) {
|
|
|
|
|
memberIds.push(...ownerIds);
|
|
|
|
|
// Remove duplicates
|
|
|
|
|
memberIds = [...new Set(memberIds)];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error('Failed to fetch group members from Graph API:', error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let existingGroup = await findGroupByExternalId(principal.idOnTheSource, 'entra');
|
|
|
|
|
|
|
|
|
|
if (!existingGroup && principal.email) {
|
|
|
|
|
existingGroup = await Group.findOne({ email: principal.email.toLowerCase() }).lean();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (existingGroup) {
|
|
|
|
|
const updateData = {};
|
|
|
|
|
let needsUpdate = false;
|
|
|
|
|
|
|
|
|
|
if (!existingGroup.idOnTheSource && principal.idOnTheSource) {
|
|
|
|
|
updateData.idOnTheSource = principal.idOnTheSource;
|
|
|
|
|
updateData.source = 'entra';
|
|
|
|
|
needsUpdate = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (principal.description && existingGroup.description !== principal.description) {
|
|
|
|
|
updateData.description = principal.description;
|
|
|
|
|
needsUpdate = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (principal.email && existingGroup.email !== principal.email.toLowerCase()) {
|
|
|
|
|
updateData.email = principal.email.toLowerCase();
|
|
|
|
|
needsUpdate = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (authContext && authContext.accessToken && authContext.sub) {
|
|
|
|
|
updateData.memberIds = memberIds;
|
|
|
|
|
needsUpdate = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (needsUpdate) {
|
|
|
|
|
await Group.findByIdAndUpdate(existingGroup._id, { $set: updateData }, { new: true });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return existingGroup._id.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const groupData = {
|
|
|
|
|
name: principal.name,
|
|
|
|
|
source: 'entra',
|
|
|
|
|
idOnTheSource: principal.idOnTheSource,
|
|
|
|
|
memberIds: memberIds, // Store idOnTheSource values of group members (empty if no auth context)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (principal.email) {
|
|
|
|
|
groupData.email = principal.email.toLowerCase();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (principal.description) {
|
|
|
|
|
groupData.description = principal.description;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const newGroup = await createGroup(groupData);
|
|
|
|
|
return newGroup._id.toString();
|
|
|
|
|
}
|
|
|
|
|
if (principal.id && authContext == null) {
|
|
|
|
|
return principal.id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new Error(`Unsupported group principal source: ${principal.source}`);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Synchronize user's Entra ID group memberships on sign-in
|
|
|
|
|
* Gets user's group IDs from GraphAPI and updates memberships only for existing groups in database
|
|
|
|
|
* Optionally includes groups the user owns if ENTRA_ID_INCLUDE_OWNERS_AS_MEMBERS is enabled
|
|
|
|
|
* @param {Object} user - User object with authentication context
|
|
|
|
|
* @param {string} user.openidId - User's OpenID subject identifier
|
|
|
|
|
* @param {string} user.idOnTheSource - User's Entra ID (oid from token claims)
|
|
|
|
|
* @param {string} user.provider - Authentication provider ('openid')
|
|
|
|
|
* @param {string} accessToken - Access token for Graph API calls
|
|
|
|
|
* @param {mongoose.ClientSession} [session] - Optional MongoDB session for transactions
|
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
|
*/
|
|
|
|
|
const syncUserEntraGroupMemberships = async (user, accessToken, session = null) => {
|
|
|
|
|
try {
|
|
|
|
|
if (!entraIdPrincipalFeatureEnabled(user) || !accessToken || !user.idOnTheSource) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const memberGroupIds = await getUserEntraGroups(accessToken, user.openidId);
|
|
|
|
|
let allGroupIds = [...(memberGroupIds || [])];
|
|
|
|
|
|
|
|
|
|
// Include owned groups if feature is enabled
|
|
|
|
|
if (isEnabled(process.env.ENTRA_ID_INCLUDE_OWNERS_AS_MEMBERS)) {
|
|
|
|
|
const ownedGroupIds = await getUserOwnedEntraGroups(accessToken, user.openidId);
|
|
|
|
|
if (ownedGroupIds && ownedGroupIds.length > 0) {
|
|
|
|
|
allGroupIds.push(...ownedGroupIds);
|
|
|
|
|
// Remove duplicates
|
|
|
|
|
allGroupIds = [...new Set(allGroupIds)];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!allGroupIds || allGroupIds.length === 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sessionOptions = session ? { session } : {};
|
|
|
|
|
|
|
|
|
|
await Group.updateMany(
|
|
|
|
|
{
|
|
|
|
|
idOnTheSource: { $in: allGroupIds },
|
|
|
|
|
source: 'entra',
|
|
|
|
|
memberIds: { $ne: user.idOnTheSource },
|
|
|
|
|
},
|
|
|
|
|
{ $addToSet: { memberIds: user.idOnTheSource } },
|
|
|
|
|
sessionOptions,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
await Group.updateMany(
|
|
|
|
|
{
|
|
|
|
|
source: 'entra',
|
|
|
|
|
memberIds: user.idOnTheSource,
|
|
|
|
|
idOnTheSource: { $nin: allGroupIds },
|
|
|
|
|
},
|
|
|
|
|
{ $pull: { memberIds: user.idOnTheSource } },
|
|
|
|
|
sessionOptions,
|
|
|
|
|
);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`[PermissionService.syncUserEntraGroupMemberships] Error syncing groups:`, error);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check if public has a specific permission on a resource
|
|
|
|
|
* @param {Object} params - Parameters for checking public permission
|
|
|
|
|
* @param {string} params.resourceType - Type of resource (e.g., 'agent')
|
|
|
|
|
* @param {string|mongoose.Types.ObjectId} params.resourceId - The ID of the resource
|
|
|
|
|
* @param {number} params.requiredPermissions - The permission bits required (e.g., 1 for VIEW, 3 for VIEW+EDIT)
|
|
|
|
|
* @returns {Promise<boolean>} Whether public has the required permission bits
|
|
|
|
|
*/
|
|
|
|
|
const hasPublicPermission = async ({ resourceType, resourceId, requiredPermissions }) => {
|
|
|
|
|
try {
|
|
|
|
|
if (typeof requiredPermissions !== 'number' || requiredPermissions < 1) {
|
|
|
|
|
throw new Error('requiredPermissions must be a positive number');
|
|
|
|
|
}
|
|
|
|
|
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
validateResourceType(resourceType);
|
|
|
|
|
|
2025-06-23 10:22:27 -04:00
|
|
|
// Use public principal to check permissions
|
2025-08-02 16:02:56 -04:00
|
|
|
const publicPrincipal = [{ principalType: PrincipalType.PUBLIC }];
|
2025-06-23 10:22:27 -04:00
|
|
|
|
|
|
|
|
const entries = await findEntriesByPrincipalsAndResource(
|
|
|
|
|
publicPrincipal,
|
|
|
|
|
resourceType,
|
|
|
|
|
resourceId,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Check if any entry has the required permission bits
|
|
|
|
|
return entries.some((entry) => (entry.permBits & requiredPermissions) === requiredPermissions);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`[PermissionService.hasPublicPermission] Error: ${error.message}`);
|
|
|
|
|
// Re-throw validation errors
|
|
|
|
|
if (error.message.includes('requiredPermissions must be')) {
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Bulk update permissions for a resource (grant, update, revoke)
|
|
|
|
|
* Efficiently handles multiple permission changes in a single transaction
|
|
|
|
|
*
|
|
|
|
|
* @param {Object} params - Parameters for bulk permission update
|
|
|
|
|
* @param {string} params.resourceType - Type of resource (e.g., 'agent')
|
|
|
|
|
* @param {string|mongoose.Types.ObjectId} params.resourceId - The ID of the resource
|
|
|
|
|
* @param {Array<TPrincipal>} params.updatedPrincipals - Array of principals to grant/update permissions for
|
|
|
|
|
* @param {Array<TPrincipal>} params.revokedPrincipals - Array of principals to revoke permissions from
|
|
|
|
|
* @param {string|mongoose.Types.ObjectId} params.grantedBy - User ID making the changes
|
|
|
|
|
* @param {mongoose.ClientSession} [params.session] - Optional MongoDB session for transactions
|
|
|
|
|
* @returns {Promise<Object>} Results object with granted, updated, revoked arrays and error details
|
|
|
|
|
*/
|
|
|
|
|
const bulkUpdateResourcePermissions = async ({
|
|
|
|
|
resourceType,
|
|
|
|
|
resourceId,
|
|
|
|
|
updatedPrincipals = [],
|
|
|
|
|
revokedPrincipals = [],
|
|
|
|
|
grantedBy,
|
|
|
|
|
session,
|
|
|
|
|
}) => {
|
|
|
|
|
const supportsTransactions = await getTransactionSupport(mongoose, transactionSupportCache);
|
|
|
|
|
transactionSupportCache = supportsTransactions;
|
|
|
|
|
let localSession = session;
|
|
|
|
|
let shouldEndSession = false;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
if (!Array.isArray(updatedPrincipals)) {
|
|
|
|
|
throw new Error('updatedPrincipals must be an array');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!Array.isArray(revokedPrincipals)) {
|
|
|
|
|
throw new Error('revokedPrincipals must be an array');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!resourceId || !mongoose.Types.ObjectId.isValid(resourceId)) {
|
|
|
|
|
throw new Error(`Invalid resource ID: ${resourceId}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!localSession && supportsTransactions) {
|
|
|
|
|
localSession = await mongoose.startSession();
|
|
|
|
|
localSession.startTransaction();
|
|
|
|
|
shouldEndSession = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sessionOptions = localSession ? { session: localSession } : {};
|
|
|
|
|
|
|
|
|
|
const roles = await AccessRole.find({ resourceType }).lean();
|
|
|
|
|
const rolesMap = new Map();
|
|
|
|
|
roles.forEach((role) => {
|
|
|
|
|
rolesMap.set(role.accessRoleId, role);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const results = {
|
|
|
|
|
granted: [],
|
|
|
|
|
updated: [],
|
|
|
|
|
revoked: [],
|
|
|
|
|
errors: [],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const bulkWrites = [];
|
|
|
|
|
|
|
|
|
|
for (const principal of updatedPrincipals) {
|
|
|
|
|
try {
|
|
|
|
|
if (!principal.accessRoleId) {
|
|
|
|
|
results.errors.push({
|
|
|
|
|
principal,
|
|
|
|
|
error: 'accessRoleId is required for updated principals',
|
|
|
|
|
});
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const role = rolesMap.get(principal.accessRoleId);
|
|
|
|
|
if (!role) {
|
|
|
|
|
results.errors.push({
|
|
|
|
|
principal,
|
|
|
|
|
error: `Role ${principal.accessRoleId} not found`,
|
|
|
|
|
});
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const query = {
|
|
|
|
|
principalType: principal.type,
|
|
|
|
|
resourceType,
|
|
|
|
|
resourceId,
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-03 19:24:40 -04:00
|
|
|
if (principal.type !== PrincipalType.PUBLIC) {
|
|
|
|
|
query.principalId =
|
|
|
|
|
principal.type === PrincipalType.ROLE
|
|
|
|
|
? principal.id
|
|
|
|
|
: new mongoose.Types.ObjectId(principal.id);
|
2025-06-23 10:22:27 -04:00
|
|
|
}
|
|
|
|
|
|
2025-08-03 19:24:40 -04:00
|
|
|
const principalModelMap = {
|
|
|
|
|
[PrincipalType.USER]: PrincipalModel.USER,
|
|
|
|
|
[PrincipalType.GROUP]: PrincipalModel.GROUP,
|
|
|
|
|
[PrincipalType.ROLE]: PrincipalModel.ROLE,
|
|
|
|
|
};
|
|
|
|
|
|
2025-06-23 10:22:27 -04:00
|
|
|
const update = {
|
|
|
|
|
$set: {
|
|
|
|
|
permBits: role.permBits,
|
|
|
|
|
roleId: role._id,
|
|
|
|
|
grantedBy,
|
|
|
|
|
grantedAt: new Date(),
|
|
|
|
|
},
|
|
|
|
|
$setOnInsert: {
|
|
|
|
|
principalType: principal.type,
|
|
|
|
|
resourceType,
|
|
|
|
|
resourceId,
|
2025-08-02 16:14:11 -04:00
|
|
|
...(principal.type !== PrincipalType.PUBLIC && {
|
2025-08-03 19:24:40 -04:00
|
|
|
principalId:
|
|
|
|
|
principal.type === PrincipalType.ROLE
|
|
|
|
|
? principal.id
|
|
|
|
|
: new mongoose.Types.ObjectId(principal.id),
|
|
|
|
|
principalModel: principalModelMap[principal.type],
|
2025-06-23 10:22:27 -04:00
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bulkWrites.push({
|
|
|
|
|
updateOne: {
|
|
|
|
|
filter: query,
|
|
|
|
|
update: update,
|
|
|
|
|
upsert: true,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
results.granted.push({
|
|
|
|
|
type: principal.type,
|
|
|
|
|
id: principal.id,
|
|
|
|
|
name: principal.name,
|
|
|
|
|
email: principal.email,
|
|
|
|
|
source: principal.source,
|
|
|
|
|
avatar: principal.avatar,
|
|
|
|
|
description: principal.description,
|
|
|
|
|
idOnTheSource: principal.idOnTheSource,
|
|
|
|
|
accessRoleId: principal.accessRoleId,
|
|
|
|
|
memberCount: principal.memberCount,
|
|
|
|
|
memberIds: principal.memberIds,
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
results.errors.push({
|
|
|
|
|
principal,
|
|
|
|
|
error: error.message,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bulkWrites.length > 0) {
|
|
|
|
|
await AclEntry.bulkWrite(bulkWrites, sessionOptions);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const deleteQueries = [];
|
|
|
|
|
for (const principal of revokedPrincipals) {
|
|
|
|
|
try {
|
|
|
|
|
const query = {
|
|
|
|
|
principalType: principal.type,
|
|
|
|
|
resourceType,
|
|
|
|
|
resourceId,
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-03 19:24:40 -04:00
|
|
|
if (principal.type !== PrincipalType.PUBLIC) {
|
|
|
|
|
query.principalId =
|
|
|
|
|
principal.type === PrincipalType.ROLE
|
|
|
|
|
? principal.id
|
|
|
|
|
: new mongoose.Types.ObjectId(principal.id);
|
2025-06-23 10:22:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
deleteQueries.push(query);
|
|
|
|
|
|
|
|
|
|
results.revoked.push({
|
|
|
|
|
type: principal.type,
|
|
|
|
|
id: principal.id,
|
|
|
|
|
name: principal.name,
|
|
|
|
|
email: principal.email,
|
|
|
|
|
source: principal.source,
|
|
|
|
|
avatar: principal.avatar,
|
|
|
|
|
description: principal.description,
|
|
|
|
|
idOnTheSource: principal.idOnTheSource,
|
|
|
|
|
memberCount: principal.memberCount,
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
results.errors.push({
|
|
|
|
|
principal,
|
|
|
|
|
error: error.message,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (deleteQueries.length > 0) {
|
|
|
|
|
await AclEntry.deleteMany(
|
|
|
|
|
{
|
|
|
|
|
$or: deleteQueries,
|
|
|
|
|
},
|
|
|
|
|
sessionOptions,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (shouldEndSession && supportsTransactions) {
|
|
|
|
|
await localSession.commitTransaction();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return results;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
if (shouldEndSession && supportsTransactions) {
|
|
|
|
|
await localSession.abortTransaction();
|
|
|
|
|
}
|
|
|
|
|
logger.error(`[PermissionService.bulkUpdateResourcePermissions] Error: ${error.message}`);
|
|
|
|
|
throw error;
|
|
|
|
|
} finally {
|
|
|
|
|
if (shouldEndSession && localSession) {
|
|
|
|
|
localSession.endSession();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-07-26 12:28:31 -04:00
|
|
|
/**
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
* Remove all permissions for a resource (cleanup when resource is deleted)
|
|
|
|
|
* @param {Object} params - Parameters for removing all permissions
|
2025-07-26 12:28:31 -04:00
|
|
|
* @param {string} params.resourceType - Type of resource (e.g., 'agent', 'prompt')
|
|
|
|
|
* @param {string|mongoose.Types.ObjectId} params.resourceId - The ID of the resource
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
* @returns {Promise<Object>} Result of the deletion operation
|
2025-07-26 12:28:31 -04:00
|
|
|
*/
|
|
|
|
|
const removeAllPermissions = async ({ resourceType, resourceId }) => {
|
|
|
|
|
try {
|
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids
chore: move sharing related components to dedicated "Sharing" directory
chore: remove PublicSharingToggle component and update index exports
chore: move non-sidepanel agent components to `~/components/Agents`
chore: move AgentCategoryDisplay component with tests
chore: remove commented out code
refactor: change PERMISSION_BITS from const to enum for better type safety
refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks
refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety
refactor: remove unused canAccessPromptResource middleware and related code
refactor: remove unused prompt access roles from createAccessRoleMethods
refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value
refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety
refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety
refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety
refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components
refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency
chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions
chore: move SearchPicker to PeoplePicker dir
refactor: implement debouncing for query changes in SearchPicker for improved performance
chore: fix typing, import order for agent admin settings
fix: agent admin settings, prevent agent form submission
refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`
refactor: replace PermissionBits with PERMISSION_BITS
refactor: replace PERMISSION_BITS with PermissionBits
2025-07-28 17:52:36 -04:00
|
|
|
validateResourceType(resourceType);
|
|
|
|
|
|
2025-07-26 12:28:31 -04:00
|
|
|
if (!resourceId || !mongoose.Types.ObjectId.isValid(resourceId)) {
|
|
|
|
|
throw new Error(`Invalid resource ID: ${resourceId}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = await AclEntry.deleteMany({
|
|
|
|
|
resourceType,
|
|
|
|
|
resourceId,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error(`[PermissionService.removeAllPermissions] Error: ${error.message}`);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-06-23 10:22:27 -04:00
|
|
|
module.exports = {
|
|
|
|
|
grantPermission,
|
|
|
|
|
checkPermission,
|
|
|
|
|
getEffectivePermissions,
|
2025-12-04 21:37:23 +01:00
|
|
|
getResourcePermissionsMap,
|
2025-06-23 10:22:27 -04:00
|
|
|
findAccessibleResources,
|
|
|
|
|
findPubliclyAccessibleResources,
|
|
|
|
|
hasPublicPermission,
|
|
|
|
|
getAvailableRoles,
|
|
|
|
|
bulkUpdateResourcePermissions,
|
|
|
|
|
ensurePrincipalExists,
|
|
|
|
|
ensureGroupPrincipalExists,
|
|
|
|
|
syncUserEntraGroupMemberships,
|
2025-07-26 12:28:31 -04:00
|
|
|
removeAllPermissions,
|
2025-06-23 10:22:27 -04:00
|
|
|
};
|