fix(permissions): honor boolean sharedLinks config

SHARED_LINKS has no USE permission, so boolean config produced
an empty update payload — gate conditions only matched object
form, making `sharedLinks: false` a no-op on existing perms.
This commit is contained in:
Atef Bellaaj 2026-05-14 15:55:32 +02:00
parent ac4aa80f10
commit 4ee3416326
2 changed files with 87 additions and 5 deletions

View file

@ -2641,4 +2641,78 @@ describe('updateInterfacePermissions - permissions', () => {
expect(userCall[1][PermissionTypes.MCP_SERVERS][Permissions.CREATE]).toBe(true);
});
it('should disable all SHARED_LINKS permissions when sharedLinks: false (boolean)', async () => {
mockGetRoleByName.mockResolvedValue({
permissions: {
[PermissionTypes.SHARED_LINKS]: {
[Permissions.CREATE]: true,
[Permissions.SHARE]: true,
[Permissions.SHARE_PUBLIC]: true,
},
},
});
const config = {
interface: {
sharedLinks: false,
},
};
const configDefaults = { interface: {} } as TConfigDefaults;
const interfaceConfig = await loadDefaultInterface({ config, configDefaults });
const appConfig = { config, interfaceConfig } as unknown as AppConfig;
await updateInterfacePermissions({
appConfig,
getRoleByName: mockGetRoleByName,
updateAccessPermissions: mockUpdateAccessPermissions,
});
const userCall = mockUpdateAccessPermissions.mock.calls.find(
(call) => call[0] === SystemRoles.USER,
);
expect(userCall[1][PermissionTypes.SHARED_LINKS]).toEqual({
[Permissions.CREATE]: false,
[Permissions.SHARE]: false,
[Permissions.SHARE_PUBLIC]: false,
});
});
it('should enable all SHARED_LINKS permissions when sharedLinks: true (boolean)', async () => {
mockGetRoleByName.mockResolvedValue({
permissions: {
[PermissionTypes.SHARED_LINKS]: {
[Permissions.CREATE]: false,
[Permissions.SHARE]: false,
[Permissions.SHARE_PUBLIC]: false,
},
},
});
const config = {
interface: {
sharedLinks: true,
},
};
const configDefaults = { interface: {} } as TConfigDefaults;
const interfaceConfig = await loadDefaultInterface({ config, configDefaults });
const appConfig = { config, interfaceConfig } as unknown as AppConfig;
await updateInterfacePermissions({
appConfig,
getRoleByName: mockGetRoleByName,
updateAccessPermissions: mockUpdateAccessPermissions,
});
const userCall = mockUpdateAccessPermissions.mock.calls.find(
(call) => call[0] === SystemRoles.USER,
);
expect(userCall[1][PermissionTypes.SHARED_LINKS]).toEqual({
[Permissions.CREATE]: true,
[Permissions.SHARE]: true,
[Permissions.SHARE_PUBLIC]: true,
});
});
});

View file

@ -481,28 +481,36 @@ export async function updateInterfacePermissions({
: {}),
},
[PermissionTypes.SHARED_LINKS]: {
...((typeof interfaceConfig?.sharedLinks === 'object' &&
...(typeof interfaceConfig?.sharedLinks === 'boolean' ||
(typeof interfaceConfig?.sharedLinks === 'object' &&
'create' in interfaceConfig.sharedLinks) ||
!existingPermissions?.[PermissionTypes.SHARED_LINKS]
? {
[Permissions.CREATE]: getPermissionValue(
getConfigCreate(loadedInterface.sharedLinks),
typeof loadedInterface.sharedLinks === 'boolean'
? loadedInterface.sharedLinks
: getConfigCreate(loadedInterface.sharedLinks),
defaultPerms[PermissionTypes.SHARED_LINKS]?.[Permissions.CREATE],
sharedLinksDefaultCreate ?? true,
),
}
: {}),
...((typeof interfaceConfig?.sharedLinks === 'object' &&
...(typeof interfaceConfig?.sharedLinks === 'boolean' ||
(typeof interfaceConfig?.sharedLinks === 'object' &&
('share' in interfaceConfig.sharedLinks || 'public' in interfaceConfig.sharedLinks)) ||
!existingPermissions?.[PermissionTypes.SHARED_LINKS]
? {
[Permissions.SHARE]: getPermissionValue(
getConfigShare(loadedInterface.sharedLinks),
typeof loadedInterface.sharedLinks === 'boolean'
? loadedInterface.sharedLinks
: getConfigShare(loadedInterface.sharedLinks),
defaultPerms[PermissionTypes.SHARED_LINKS]?.[Permissions.SHARE],
sharedLinksDefaultShare,
),
[Permissions.SHARE_PUBLIC]: getPermissionValue(
getConfigPublic(loadedInterface.sharedLinks),
typeof loadedInterface.sharedLinks === 'boolean'
? loadedInterface.sharedLinks
: getConfigPublic(loadedInterface.sharedLinks),
defaultPerms[PermissionTypes.SHARED_LINKS]?.[Permissions.SHARE_PUBLIC],
sharedLinksDefaultPublic,
),