diff --git a/apps/files/src/actions/convertAction.ts b/apps/files/src/actions/convertAction.ts index 9443809ea0a..449c07bf292 100644 --- a/apps/files/src/actions/convertAction.ts +++ b/apps/files/src/actions/convertAction.ts @@ -36,6 +36,10 @@ export function registerConvertActions() { // cannot create the converted file in a public share if we don't have create permissions return false } + // Conversion reads the source file, so it requires read permission + if (nodes.some((node) => (node.permissions & Permission.READ) === 0)) { + return false + } // Check that all nodes have the same mime type return nodes.every((node) => from === node.mime) }, diff --git a/apps/files_sharing/src/services/SharingService.spec.ts b/apps/files_sharing/src/services/SharingService.spec.ts index cbaa06a03cb..6840fc73367 100644 --- a/apps/files_sharing/src/services/SharingService.spec.ts +++ b/apps/files_sharing/src/services/SharingService.spec.ts @@ -535,6 +535,34 @@ describe('SharingService share to Node mapping', () => { expect(file.attributes.favorite).toBe(0) }) + test('Pending share has no permissions', async () => { + axios.get + .mockReturnValueOnce(Promise.resolve({ + data: { ocs: { data: [shareFile] } }, + })) + .mockReturnValueOnce(Promise.resolve({ + data: { ocs: { data: [] } }, + })) + + const shares = await getContents(false, false, true, false) + + expect(axios.get).toHaveBeenCalledTimes(2) + expect(shares.contents).toHaveLength(1) + expect(shares.contents[0].permissions).toBe(0) + }) + + test('Deleted share has no permissions', async () => { + axios.get.mockReturnValueOnce(Promise.resolve({ + data: { ocs: { data: [shareFolder] } }, + })) + + const shares = await getContents(false, false, false, true) + + expect(axios.get).toHaveBeenCalledTimes(1) + expect(shares.contents).toHaveLength(1) + expect(shares.contents[0].permissions).toBe(0) + }) + test('Empty', async () => { vi.spyOn(logger, 'error').mockImplementationOnce(() => {}) axios.get.mockReturnValueOnce(Promise.resolve({ diff --git a/apps/files_sharing/src/services/SharingService.ts b/apps/files_sharing/src/services/SharingService.ts index c905f08d853..aaa2eab0e6c 100644 --- a/apps/files_sharing/src/services/SharingService.ts +++ b/apps/files_sharing/src/services/SharingService.ts @@ -24,8 +24,9 @@ const headers = { /** * * @param ocsEntry + * @param unmounted whether the share is not mounted into the filesystem (pending or deleted) */ -async function ocsEntryToNode(ocsEntry: any): Promise { +async function ocsEntryToNode(ocsEntry: any, unmounted = false): Promise { try { // Federated share handling if (ocsEntry?.remote_id !== undefined) { @@ -57,6 +58,13 @@ async function ocsEntryToNode(ocsEntry: any): Promise { ocsEntry.displayname_owner = ocsEntry.owner } + // Pending and deleted shares are not mounted into the user's filesystem, + // so no file operation can act on them until they are accepted or restored. + if (unmounted) { + ocsEntry.item_permissions = Permission.NONE + ocsEntry.permissions = Permission.NONE + } + const isFolder = ocsEntry?.item_type === 'folder' const hasPreview = ocsEntry?.has_preview === true const Node = isFolder ? Folder : File @@ -238,24 +246,25 @@ function groupBy(nodes: (Folder | File)[], key: string) { * @param filterTypes */ export async function getContents(sharedWithYou = true, sharedWithOthers = true, pendingShares = false, deletedshares = false, filterTypes: number[] = []): Promise { - const promises = [] as AxiosPromise>[] + const requests = [] as { promise: AxiosPromise>, unmounted: boolean }[] if (sharedWithYou) { - promises.push(getSharedWithYou(), getRemoteShares()) + requests.push({ promise: getSharedWithYou(), unmounted: false }, { promise: getRemoteShares(), unmounted: false }) } if (sharedWithOthers) { - promises.push(getSharedWithOthers()) + requests.push({ promise: getSharedWithOthers(), unmounted: false }) } if (pendingShares) { - promises.push(getPendingShares(), getRemotePendingShares()) + requests.push({ promise: getPendingShares(), unmounted: true }, { promise: getRemotePendingShares(), unmounted: true }) } if (deletedshares) { - promises.push(getDeletedShares()) + requests.push({ promise: getDeletedShares(), unmounted: true }) } - const responses = await Promise.all(promises) - const data = responses.map((response) => response.data.ocs.data).flat() - let contents = (await Promise.all(data.map(ocsEntryToNode))) + const responses = await Promise.all(requests.map(({ promise }) => promise)) + const data = responses.flatMap((response, index) => response.data.ocs.data + .map((entry) => ({ entry, unmounted: requests[index].unmounted }))) + let contents = (await Promise.all(data.map(({ entry, unmounted }) => ocsEntryToNode(entry, unmounted)))) .filter((node) => node !== null) as (Folder | File)[] if (filterTypes.length > 0) {