diff --git a/api/server/routes/roles.js b/api/server/routes/roles.js index aefbfcec0..66f77a747 100644 --- a/api/server/routes/roles.js +++ b/api/server/routes/roles.js @@ -3,6 +3,7 @@ const { promptPermissionsSchema, memoryPermissionsSchema, agentPermissionsSchema, + peoplePickerPermissionsSchema, PermissionTypes, roleDefaults, SystemRoles, @@ -13,6 +14,76 @@ const { updateRoleByName, getRoleByName } = require('~/models/Role'); const router = express.Router(); router.use(requireJwtAuth); +/** + * Permission configuration mapping + * Maps route paths to their corresponding schemas and permission types + */ +const permissionConfigs = { + prompts: { + schema: promptPermissionsSchema, + permissionType: PermissionTypes.PROMPTS, + errorMessage: 'Invalid prompt permissions.', + }, + agents: { + schema: agentPermissionsSchema, + permissionType: PermissionTypes.AGENTS, + errorMessage: 'Invalid agent permissions.', + }, + memories: { + schema: memoryPermissionsSchema, + permissionType: PermissionTypes.MEMORIES, + errorMessage: 'Invalid memory permissions.', + }, + 'people-picker': { + schema: peoplePickerPermissionsSchema, + permissionType: PermissionTypes.PEOPLE_PICKER, + errorMessage: 'Invalid people picker permissions.', + }, +}; + +/** + * Generic handler for updating permissions + * @param {string} permissionKey - The key from permissionConfigs + * @returns {Function} Express route handler + */ +const createPermissionUpdateHandler = (permissionKey) => { + const config = permissionConfigs[permissionKey]; + + return async (req, res) => { + const { roleName: _r } = req.params; + // TODO: TEMP, use a better parsing for roleName + const roleName = _r.toUpperCase(); + const updates = req.body; + + try { + const parsedUpdates = config.schema.partial().parse(updates); + + const role = await getRoleByName(roleName); + if (!role) { + return res.status(404).send({ message: 'Role not found' }); + } + + const currentPermissions = + role.permissions?.[config.permissionType] || role[config.permissionType] || {}; + + const mergedUpdates = { + permissions: { + ...role.permissions, + [config.permissionType]: { + ...currentPermissions, + ...parsedUpdates, + }, + }, + }; + + const updatedRole = await updateRoleByName(roleName, mergedUpdates); + res.status(200).send(updatedRole); + } catch (error) { + return res.status(400).send({ message: config.errorMessage, error: error.errors }); + } + }; +}; + /** * GET /api/roles/:roleName * Get a specific role by name @@ -45,117 +116,24 @@ router.get('/:roleName', async (req, res) => { * PUT /api/roles/:roleName/prompts * Update prompt permissions for a specific role */ -router.put('/:roleName/prompts', checkAdmin, async (req, res) => { - const { roleName: _r } = req.params; - // TODO: TEMP, use a better parsing for roleName - const roleName = _r.toUpperCase(); - /** @type {TRole['permissions']['PROMPTS']} */ - const updates = req.body; - - try { - const parsedUpdates = promptPermissionsSchema.partial().parse(updates); - - const role = await getRoleByName(roleName); - if (!role) { - return res.status(404).send({ message: 'Role not found' }); - } - - const currentPermissions = - role.permissions?.[PermissionTypes.PROMPTS] || role[PermissionTypes.PROMPTS] || {}; - - const mergedUpdates = { - permissions: { - ...role.permissions, - [PermissionTypes.PROMPTS]: { - ...currentPermissions, - ...parsedUpdates, - }, - }, - }; - - const updatedRole = await updateRoleByName(roleName, mergedUpdates); - res.status(200).send(updatedRole); - } catch (error) { - return res.status(400).send({ message: 'Invalid prompt permissions.', error: error.errors }); - } -}); +router.put('/:roleName/prompts', checkAdmin, createPermissionUpdateHandler('prompts')); /** * PUT /api/roles/:roleName/agents * Update agent permissions for a specific role */ -router.put('/:roleName/agents', checkAdmin, async (req, res) => { - const { roleName: _r } = req.params; - // TODO: TEMP, use a better parsing for roleName - const roleName = _r.toUpperCase(); - /** @type {TRole['permissions']['AGENTS']} */ - const updates = req.body; - - try { - const parsedUpdates = agentPermissionsSchema.partial().parse(updates); - - const role = await getRoleByName(roleName); - if (!role) { - return res.status(404).send({ message: 'Role not found' }); - } - - const currentPermissions = - role.permissions?.[PermissionTypes.AGENTS] || role[PermissionTypes.AGENTS] || {}; - - const mergedUpdates = { - permissions: { - ...role.permissions, - [PermissionTypes.AGENTS]: { - ...currentPermissions, - ...parsedUpdates, - }, - }, - }; - - const updatedRole = await updateRoleByName(roleName, mergedUpdates); - res.status(200).send(updatedRole); - } catch (error) { - return res.status(400).send({ message: 'Invalid agent permissions.', error: error.errors }); - } -}); +router.put('/:roleName/agents', checkAdmin, createPermissionUpdateHandler('agents')); /** * PUT /api/roles/:roleName/memories * Update memory permissions for a specific role */ -router.put('/:roleName/memories', checkAdmin, async (req, res) => { - const { roleName: _r } = req.params; - // TODO: TEMP, use a better parsing for roleName - const roleName = _r.toUpperCase(); - /** @type {TRole['permissions']['MEMORIES']} */ - const updates = req.body; +router.put('/:roleName/memories', checkAdmin, createPermissionUpdateHandler('memories')); - try { - const parsedUpdates = memoryPermissionsSchema.partial().parse(updates); - - const role = await getRoleByName(roleName); - if (!role) { - return res.status(404).send({ message: 'Role not found' }); - } - - const currentPermissions = - role.permissions?.[PermissionTypes.MEMORIES] || role[PermissionTypes.MEMORIES] || {}; - - const mergedUpdates = { - permissions: { - ...role.permissions, - [PermissionTypes.MEMORIES]: { - ...currentPermissions, - ...parsedUpdates, - }, - }, - }; - - const updatedRole = await updateRoleByName(roleName, mergedUpdates); - res.status(200).send(updatedRole); - } catch (error) { - return res.status(400).send({ message: 'Invalid memory permissions.', error: error.errors }); - } -}); +/** + * PUT /api/roles/:roleName/people-picker + * Update people picker permissions for a specific role + */ +router.put('/:roleName/people-picker', checkAdmin, createPermissionUpdateHandler('people-picker')); module.exports = router; diff --git a/api/server/services/AppService.interface.spec.js b/api/server/services/AppService.interface.spec.js index 0b2266f3e..81ebec60d 100644 --- a/api/server/services/AppService.interface.spec.js +++ b/api/server/services/AppService.interface.spec.js @@ -94,109 +94,71 @@ describe('AppService interface configuration', () => { mockLoadCustomConfig.mockResolvedValue({ interface: { peoplePicker: { - admin: { - users: true, - groups: true, - roles: true, - }, - user: { - users: false, - groups: false, - roles: false, - }, + users: true, + groups: true, + roles: true, }, }, }); loadDefaultInterface.mockResolvedValue({ peoplePicker: { - admin: { - users: true, - groups: true, - roles: true, - }, - user: { - users: false, - groups: false, - roles: false, - }, + users: true, + groups: true, + roles: true, }, }); await AppService(app); expect(app.locals.interfaceConfig.peoplePicker).toBeDefined(); - expect(app.locals.interfaceConfig.peoplePicker.admin).toMatchObject({ + expect(app.locals.interfaceConfig.peoplePicker).toMatchObject({ users: true, groups: true, roles: true, }); - expect(app.locals.interfaceConfig.peoplePicker.user).toMatchObject({ - users: false, - groups: false, - roles: false, - }); expect(loadDefaultInterface).toHaveBeenCalled(); }); - it('should handle mixed peoplePicker permissions for roles', async () => { + it('should handle mixed peoplePicker permissions', async () => { mockLoadCustomConfig.mockResolvedValue({ interface: { peoplePicker: { - admin: { - users: true, - groups: true, - roles: false, - }, - user: { - users: true, - groups: false, - roles: true, - }, + users: true, + groups: false, + roles: true, }, }, }); loadDefaultInterface.mockResolvedValue({ peoplePicker: { - admin: { - users: true, - groups: true, - roles: false, - }, - user: { - users: true, - groups: false, - roles: true, - }, + users: true, + groups: false, + roles: true, }, }); await AppService(app); - expect(app.locals.interfaceConfig.peoplePicker.admin.roles).toBe(false); - expect(app.locals.interfaceConfig.peoplePicker.user.roles).toBe(true); + expect(app.locals.interfaceConfig.peoplePicker.users).toBe(true); + expect(app.locals.interfaceConfig.peoplePicker.groups).toBe(false); + expect(app.locals.interfaceConfig.peoplePicker.roles).toBe(true); }); - it('should set default peoplePicker roles permissions when not provided', async () => { + it('should set default peoplePicker permissions when not provided', async () => { mockLoadCustomConfig.mockResolvedValue({}); loadDefaultInterface.mockResolvedValue({ peoplePicker: { - admin: { - users: true, - groups: true, - roles: true, - }, - user: { - users: false, - groups: false, - roles: false, - }, + users: true, + groups: true, + roles: true, }, }); await AppService(app); expect(app.locals.interfaceConfig.peoplePicker).toBeDefined(); - expect(app.locals.interfaceConfig.peoplePicker.admin.roles).toBe(true); - expect(app.locals.interfaceConfig.peoplePicker.user.roles).toBe(false); + expect(app.locals.interfaceConfig.peoplePicker.users).toBe(true); + expect(app.locals.interfaceConfig.peoplePicker.groups).toBe(true); + expect(app.locals.interfaceConfig.peoplePicker.roles).toBe(true); }); }); diff --git a/api/server/services/AppService.spec.js b/api/server/services/AppService.spec.js index f1c5c4076..db53af1b2 100644 --- a/api/server/services/AppService.spec.js +++ b/api/server/services/AppService.spec.js @@ -970,20 +970,13 @@ describe('AppService updating app.locals and issuing warnings', () => { expect(app.locals.ocr.mistralModel).toEqual('mistral-medium'); }); - it('should correctly configure peoplePicker with roles permission when specified', async () => { + it('should correctly configure peoplePicker permissions when specified', async () => { const mockConfig = { interface: { peoplePicker: { - admin: { - users: true, - groups: true, - roles: true, - }, - user: { - users: false, - groups: false, - roles: true, - }, + users: true, + groups: true, + roles: true, }, }, }; @@ -993,21 +986,16 @@ describe('AppService updating app.locals and issuing warnings', () => { const app = { locals: {} }; await AppService(app); - // Check that interface config includes the roles permission + // Check that interface config includes the permissions expect(app.locals.interfaceConfig.peoplePicker).toBeDefined(); - expect(app.locals.interfaceConfig.peoplePicker.admin).toMatchObject({ + expect(app.locals.interfaceConfig.peoplePicker).toMatchObject({ users: true, groups: true, roles: true, }); - expect(app.locals.interfaceConfig.peoplePicker.user).toMatchObject({ - users: false, - groups: false, - roles: true, - }); }); - it('should use default peoplePicker roles permissions when not specified', async () => { + it('should use default peoplePicker permissions when not specified', async () => { const mockConfig = { interface: { // No peoplePicker configuration @@ -1019,9 +1007,10 @@ describe('AppService updating app.locals and issuing warnings', () => { const app = { locals: {} }; await AppService(app); - // Check that default roles permissions are applied + // Check that default permissions are applied expect(app.locals.interfaceConfig.peoplePicker).toBeDefined(); - expect(app.locals.interfaceConfig.peoplePicker.admin.roles).toBe(true); - expect(app.locals.interfaceConfig.peoplePicker.user.roles).toBe(false); + expect(app.locals.interfaceConfig.peoplePicker.users).toBe(true); + expect(app.locals.interfaceConfig.peoplePicker.groups).toBe(true); + expect(app.locals.interfaceConfig.peoplePicker.roles).toBe(true); }); }); diff --git a/api/server/services/start/interface.js b/api/server/services/start/interface.js index 039c6376b..7eca6beea 100644 --- a/api/server/services/start/interface.js +++ b/api/server/services/start/interface.js @@ -54,16 +54,9 @@ async function loadDefaultInterface(config, configDefaults, roleName = SystemRol fileCitations: interfaceConfig?.fileCitations ?? defaults.fileCitations, customWelcome: interfaceConfig?.customWelcome ?? defaults.customWelcome, peoplePicker: { - admin: { - users: interfaceConfig?.peoplePicker?.admin?.users ?? defaults.peoplePicker?.admin.users, - groups: interfaceConfig?.peoplePicker?.admin?.groups ?? defaults.peoplePicker?.admin.groups, - roles: interfaceConfig?.peoplePicker?.admin?.roles ?? defaults.peoplePicker?.admin.roles, - }, - user: { - users: interfaceConfig?.peoplePicker?.user?.users ?? defaults.peoplePicker?.user.users, - groups: interfaceConfig?.peoplePicker?.user?.groups ?? defaults.peoplePicker?.user.groups, - roles: interfaceConfig?.peoplePicker?.user?.roles ?? defaults.peoplePicker?.user.roles, - }, + users: interfaceConfig?.peoplePicker?.users ?? defaults.peoplePicker?.users, + groups: interfaceConfig?.peoplePicker?.groups ?? defaults.peoplePicker?.groups, + roles: interfaceConfig?.peoplePicker?.roles ?? defaults.peoplePicker?.roles, }, marketplace: { admin: { @@ -88,9 +81,12 @@ async function loadDefaultInterface(config, configDefaults, roleName = SystemRol [PermissionTypes.RUN_CODE]: { [Permissions.USE]: loadedInterface.runCode }, [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: loadedInterface.webSearch }, [PermissionTypes.PEOPLE_PICKER]: { - [Permissions.VIEW_USERS]: loadedInterface.peoplePicker.user?.users, - [Permissions.VIEW_GROUPS]: loadedInterface.peoplePicker.user?.groups, - [Permissions.VIEW_ROLES]: loadedInterface.peoplePicker.user?.roles, + [Permissions.VIEW_USERS]: + roleName === SystemRoles.USER ? false : loadedInterface.peoplePicker?.users, + [Permissions.VIEW_GROUPS]: + roleName === SystemRoles.USER ? false : loadedInterface.peoplePicker?.groups, + [Permissions.VIEW_ROLES]: + roleName === SystemRoles.USER ? false : loadedInterface.peoplePicker?.roles, }, [PermissionTypes.MARKETPLACE]: { [Permissions.USE]: loadedInterface.marketplace.user?.use, @@ -111,9 +107,9 @@ async function loadDefaultInterface(config, configDefaults, roleName = SystemRol [PermissionTypes.RUN_CODE]: { [Permissions.USE]: loadedInterface.runCode }, [PermissionTypes.WEB_SEARCH]: { [Permissions.USE]: loadedInterface.webSearch }, [PermissionTypes.PEOPLE_PICKER]: { - [Permissions.VIEW_USERS]: loadedInterface.peoplePicker.admin?.users, - [Permissions.VIEW_GROUPS]: loadedInterface.peoplePicker.admin?.groups, - [Permissions.VIEW_ROLES]: loadedInterface.peoplePicker.admin?.roles, + [Permissions.VIEW_USERS]: loadedInterface.peoplePicker?.users, + [Permissions.VIEW_GROUPS]: loadedInterface.peoplePicker?.groups, + [Permissions.VIEW_ROLES]: loadedInterface.peoplePicker?.roles, }, [PermissionTypes.MARKETPLACE]: { [Permissions.USE]: loadedInterface.marketplace.admin?.use, diff --git a/client/src/components/Prompts/AdminSettings.tsx b/client/src/components/Prompts/AdminSettings.tsx index 98d2ca846..86c851bb4 100644 --- a/client/src/components/Prompts/AdminSettings.tsx +++ b/client/src/components/Prompts/AdminSettings.tsx @@ -153,7 +153,7 @@ const AdminSettings = () => { {localize('com_ui_admin')} - + {`${localize('com_ui_admin_settings')} - ${localize('com_ui_prompts')}`} diff --git a/client/src/components/Sharing/GenericGrantAccessDialog.tsx b/client/src/components/Sharing/GenericGrantAccessDialog.tsx index 037f1f4d5..12b16b796 100644 --- a/client/src/components/Sharing/GenericGrantAccessDialog.tsx +++ b/client/src/components/Sharing/GenericGrantAccessDialog.tsx @@ -21,6 +21,7 @@ import { useLocalize, } from '~/hooks'; import UnifiedPeopleSearch from './PeoplePicker/UnifiedPeopleSearch'; +import PeoplePickerAdminSettings from './PeoplePickerAdminSettings'; import PublicSharingToggle from './PublicSharingToggle'; import { SelectedPrincipalsList } from './PeoplePicker'; import { cn } from '~/utils'; @@ -366,7 +367,8 @@ export default function GenericGrantAccessDialog({ )} - + + {localize('com_ui_cancel')} diff --git a/client/src/components/Sharing/PeoplePickerAdminSettings.tsx b/client/src/components/Sharing/PeoplePickerAdminSettings.tsx new file mode 100644 index 000000000..8f1500877 --- /dev/null +++ b/client/src/components/Sharing/PeoplePickerAdminSettings.tsx @@ -0,0 +1,221 @@ +import { useMemo, useEffect, useState } from 'react'; +import * as Ariakit from '@ariakit/react'; +import { ShieldEllipsis } from 'lucide-react'; +import { useForm, Controller } from 'react-hook-form'; +import { Permissions, SystemRoles, roleDefaults, PermissionTypes } from 'librechat-data-provider'; +import { + Button, + Switch, + OGDialog, + DropdownPopup, + OGDialogTitle, + OGDialogContent, + OGDialogTrigger, + useToastContext, +} from '@librechat/client'; +import type { Control, UseFormSetValue, UseFormGetValues } from 'react-hook-form'; +import { useUpdatePeoplePickerPermissionsMutation } from '~/data-provider'; +import { useLocalize, useAuthContext } from '~/hooks'; + +type FormValues = { + [Permissions.VIEW_USERS]: boolean; + [Permissions.VIEW_GROUPS]: boolean; + [Permissions.VIEW_ROLES]: boolean; +}; + +type LabelControllerProps = { + label: string; + peoplePickerPerm: Permissions.VIEW_USERS | Permissions.VIEW_GROUPS | Permissions.VIEW_ROLES; + control: Control; + setValue: UseFormSetValue; + getValues: UseFormGetValues; +}; + +const LabelController: React.FC = ({ + control, + peoplePickerPerm, + label, + getValues, + setValue, +}) => ( + + + setValue(peoplePickerPerm, !getValues(peoplePickerPerm), { + shouldDirty: true, + }) + } + tabIndex={0} + > + {label} + + ( + + )} + /> + +); + +const PeoplePickerAdminSettings = () => { + const localize = useLocalize(); + const { showToast } = useToastContext(); + const { user, roles } = useAuthContext(); + const { mutate, isLoading } = useUpdatePeoplePickerPermissionsMutation({ + onSuccess: () => { + showToast({ status: 'success', message: localize('com_ui_saved') }); + }, + onError: () => { + showToast({ status: 'error', message: localize('com_ui_error_save_admin_settings') }); + }, + }); + + const [isRoleMenuOpen, setIsRoleMenuOpen] = useState(false); + const [selectedRole, setSelectedRole] = useState(SystemRoles.USER); + + const defaultValues = useMemo(() => { + const rolePerms = roles?.[selectedRole]?.permissions; + if (rolePerms) { + return rolePerms[PermissionTypes.PEOPLE_PICKER]; + } + return roleDefaults[selectedRole].permissions[PermissionTypes.PEOPLE_PICKER]; + }, [roles, selectedRole]); + + const { + reset, + control, + setValue, + getValues, + handleSubmit, + formState: { isSubmitting }, + } = useForm({ + mode: 'onChange', + defaultValues, + }); + + useEffect(() => { + const value = roles?.[selectedRole]?.permissions?.[PermissionTypes.PEOPLE_PICKER]; + if (value) { + reset(value); + } else { + reset(roleDefaults[selectedRole].permissions[PermissionTypes.PEOPLE_PICKER]); + } + }, [roles, selectedRole, reset]); + + if (user?.role !== SystemRoles.ADMIN) { + return null; + } + + const labelControllerData: { + peoplePickerPerm: Permissions.VIEW_USERS | Permissions.VIEW_GROUPS | Permissions.VIEW_ROLES; + label: string; + }[] = [ + { + peoplePickerPerm: Permissions.VIEW_USERS, + label: localize('com_ui_people_picker_allow_view_users'), + }, + { + peoplePickerPerm: Permissions.VIEW_GROUPS, + label: localize('com_ui_people_picker_allow_view_groups'), + }, + { + peoplePickerPerm: Permissions.VIEW_ROLES, + label: localize('com_ui_people_picker_allow_view_roles'), + }, + ]; + + const onSubmit = (data: FormValues) => { + mutate({ roleName: selectedRole, updates: data }); + }; + + const roleDropdownItems = [ + { + label: SystemRoles.USER, + onClick: () => { + setSelectedRole(SystemRoles.USER); + }, + }, + { + label: SystemRoles.ADMIN, + onClick: () => { + setSelectedRole(SystemRoles.ADMIN); + }, + }, + ]; + + return ( + + + + + {localize('com_ui_admin_settings')} + + + + {`${localize('com_ui_admin_settings')} - ${localize( + 'com_ui_people_picker', + )}`} + + {/* Role selection dropdown */} + + {localize('com_ui_role_select')}: + + {selectedRole} + + } + items={roleDropdownItems} + itemClassName="items-center justify-center" + sameWidth={true} + /> + + {/* Permissions form */} + + + {labelControllerData.map(({ peoplePickerPerm, label }) => ( + + + + ))} + + + + {localize('com_ui_save')} + + + + + + + ); +}; + +export default PeoplePickerAdminSettings; diff --git a/client/src/components/SidePanel/Agents/AdminSettings.tsx b/client/src/components/SidePanel/Agents/AdminSettings.tsx index 9858e58b7..39990f773 100644 --- a/client/src/components/SidePanel/Agents/AdminSettings.tsx +++ b/client/src/components/SidePanel/Agents/AdminSettings.tsx @@ -157,7 +157,7 @@ const AdminSettings = () => { {localize('com_ui_admin_settings')} - + {`${localize('com_ui_admin_settings')} - ${localize( 'com_ui_agents', )}`} diff --git a/client/src/components/SidePanel/Memories/AdminSettings.tsx b/client/src/components/SidePanel/Memories/AdminSettings.tsx index f9c3e12f4..57e13621c 100644 --- a/client/src/components/SidePanel/Memories/AdminSettings.tsx +++ b/client/src/components/SidePanel/Memories/AdminSettings.tsx @@ -146,7 +146,7 @@ const AdminSettings = () => { {localize('com_ui_admin_settings')} - + {`${localize('com_ui_admin_settings')} - ${localize( 'com_ui_memories', )}`} diff --git a/client/src/data-provider/roles.ts b/client/src/data-provider/roles.ts index cdabc4c20..9bc4d6ad2 100644 --- a/client/src/data-provider/roles.ts +++ b/client/src/data-provider/roles.ts @@ -4,6 +4,7 @@ import { dataService, promptPermissionsSchema, memoryPermissionsSchema, + peoplePickerPermissionsSchema, } from 'librechat-data-provider'; import type { UseQueryOptions, @@ -132,3 +133,39 @@ export const useUpdateMemoryPermissionsMutation = ( }, ); }; + +export const useUpdatePeoplePickerPermissionsMutation = ( + options?: t.UpdatePeoplePickerPermOptions, +): UseMutationResult< + t.UpdatePermResponse, + t.TError | undefined, + t.UpdatePeoplePickerPermVars, + unknown +> => { + const queryClient = useQueryClient(); + const { onMutate, onSuccess, onError } = options ?? {}; + return useMutation( + (variables) => { + peoplePickerPermissionsSchema.partial().parse(variables.updates); + return dataService.updatePeoplePickerPermissions(variables); + }, + { + onSuccess: (data, variables, context) => { + queryClient.invalidateQueries([QueryKeys.roles, variables.roleName]); + if (onSuccess) { + onSuccess(data, variables, context); + } + }, + onError: (...args) => { + const error = args[0]; + if (error != null) { + console.error('Failed to update people picker permissions:', error); + } + if (onError) { + onError(...args); + } + }, + onMutate, + }, + ); +}; diff --git a/client/src/locales/en/translation.json b/client/src/locales/en/translation.json index c835c842f..4ef730066 100644 --- a/client/src/locales/en/translation.json +++ b/client/src/locales/en/translation.json @@ -588,6 +588,10 @@ "com_ui_agents_allow_create": "Allow creating Agents", "com_ui_agents_allow_share_global": "Allow sharing Agents to all users", "com_ui_agents_allow_use": "Allow using Agents", + "com_ui_people_picker": "People Picker", + "com_ui_people_picker_allow_view_users": "Allow viewing users", + "com_ui_people_picker_allow_view_groups": "Allow viewing groups", + "com_ui_people_picker_allow_view_roles": "Allow viewing roles", "com_ui_all": "all", "com_ui_all_proper": "All", "com_ui_analyzing": "Analyzing", diff --git a/packages/data-provider/src/api-endpoints.ts b/packages/data-provider/src/api-endpoints.ts index e9e2afb49..60894fd1b 100644 --- a/packages/data-provider/src/api-endpoints.ts +++ b/packages/data-provider/src/api-endpoints.ts @@ -275,6 +275,8 @@ export const getRole = (roleName: string) => `${roles()}/${roleName.toLowerCase( export const updatePromptPermissions = (roleName: string) => `${getRole(roleName)}/prompts`; export const updateMemoryPermissions = (roleName: string) => `${getRole(roleName)}/memories`; export const updateAgentPermissions = (roleName: string) => `${getRole(roleName)}/agents`; +export const updatePeoplePickerPermissions = (roleName: string) => + `${getRole(roleName)}/people-picker`; /* Conversation Tags */ export const conversationTags = (tag?: string) => diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts index e86c159ba..95ec80e77 100644 --- a/packages/data-provider/src/config.ts +++ b/packages/data-provider/src/config.ts @@ -534,20 +534,9 @@ export const interfaceSchema = z webSearch: z.boolean().optional(), peoplePicker: z .object({ - admin: z - .object({ - users: z.boolean().optional(), - groups: z.boolean().optional(), - roles: z.boolean().optional(), - }) - .optional(), - user: z - .object({ - users: z.boolean().optional(), - groups: z.boolean().optional(), - roles: z.boolean().optional(), - }) - .optional(), + users: z.boolean().optional(), + groups: z.boolean().optional(), + roles: z.boolean().optional(), }) .optional(), marketplace: z @@ -582,16 +571,9 @@ export const interfaceSchema = z runCode: true, webSearch: true, peoplePicker: { - admin: { - users: true, - groups: true, - roles: true, - }, - user: { - users: false, - groups: false, - roles: false, - }, + users: true, + groups: true, + roles: true, }, marketplace: { admin: { diff --git a/packages/data-provider/src/data-service.ts b/packages/data-provider/src/data-service.ts index 60d575e19..23ff0180b 100644 --- a/packages/data-provider/src/data-service.ts +++ b/packages/data-provider/src/data-service.ts @@ -791,6 +791,15 @@ export function updateMemoryPermissions( return request.put(endpoints.updateMemoryPermissions(variables.roleName), variables.updates); } +export function updatePeoplePickerPermissions( + variables: m.UpdatePeoplePickerPermVars, +): Promise { + return request.put( + endpoints.updatePeoplePickerPermissions(variables.roleName), + variables.updates, + ); +} + /* Tags */ export function getConversationTags(): Promise { return request.get(endpoints.conversationTags()); diff --git a/packages/data-provider/src/types/mutations.ts b/packages/data-provider/src/types/mutations.ts index 759220c56..7a8ff00ff 100644 --- a/packages/data-provider/src/types/mutations.ts +++ b/packages/data-provider/src/types/mutations.ts @@ -273,6 +273,7 @@ export type UpdatePermVars = { export type UpdatePromptPermVars = UpdatePermVars; export type UpdateMemoryPermVars = UpdatePermVars; export type UpdateAgentPermVars = UpdatePermVars; +export type UpdatePeoplePickerPermVars = UpdatePermVars; export type UpdatePermResponse = r.TRole; @@ -297,6 +298,13 @@ export type UpdateAgentPermOptions = MutationOptions< types.TError | null | undefined >; +export type UpdatePeoplePickerPermOptions = MutationOptions< + UpdatePermResponse, + UpdatePeoplePickerPermVars, + unknown, + types.TError | null | undefined +>; + export type UpdateConversationTagOptions = MutationOptions< types.TConversationTag, types.TConversationTagRequest