diff --git a/packages/web-client/src/webdav/getFileUrl.ts b/packages/web-client/src/webdav/getFileUrl.ts index 0532e458a7..2082826ff3 100644 --- a/packages/web-client/src/webdav/getFileUrl.ts +++ b/packages/web-client/src/webdav/getFileUrl.ts @@ -16,7 +16,7 @@ export const GetFileUrlFactory = ( resource: Resource, { disposition = 'attachment', - isUrlSigningEnabled = false, + isUrlSigningEnabled = true, signUrlTimeout = 86400, version = null, doHeadRequest = false, @@ -24,7 +24,9 @@ export const GetFileUrlFactory = ( ...opts }: { disposition?: 'inline' | 'attachment' + /** @deprecated no need to specify, the server always supports this */ isUrlSigningEnabled?: boolean + /** @deprecated this has no effect */ signUrlTimeout?: number version?: string doHeadRequest?: boolean @@ -34,23 +36,19 @@ export const GetFileUrlFactory = ( const inlineDisposition = disposition === 'inline' let { downloadURL } = resource - let signed = true if (!downloadURL && !inlineDisposition) { // compute unsigned url downloadURL = version ? dav.getFileUrl(urlJoin('meta', resource.fileId, 'v', version)) : dav.getFileUrl(resource.webDavPath) - if (username && doHeadRequest) { - await axiosClient.head(downloadURL) - } + if (username) { + if (doHeadRequest) { + await axiosClient.head(downloadURL) + } - // sign url - if (isUrlSigningEnabled && username) { const ocsClient = ocs(baseUrl, axiosClient) downloadURL = await ocsClient.signUrl(downloadURL, username) - } else { - signed = false } } @@ -64,7 +62,7 @@ export const GetFileUrlFactory = ( // const combinedQuery = [queryStr, signedQuery].filter(Boolean).join('&') // downloadURL = [url, combinedQuery].filter(Boolean).join('?') - if (!signed || inlineDisposition) { + if (inlineDisposition) { const response = await getFileContentsFactory.getFileContents(space, resource, { responseType: 'blob', ...opts diff --git a/packages/web-pkg/src/composables/actions/files/useFileActionsDownloadArchive.ts b/packages/web-pkg/src/composables/actions/files/useFileActionsDownloadArchive.ts index 0ed32369d2..8436f1a928 100644 --- a/packages/web-pkg/src/composables/actions/files/useFileActionsDownloadArchive.ts +++ b/packages/web-pkg/src/composables/actions/files/useFileActionsDownloadArchive.ts @@ -7,22 +7,19 @@ import { import { useIsFilesAppActive } from '../helpers' import { isProjectSpaceResource, isPublicSpaceResource, Resource } from '@opencloud-eu/web-client' import { computed, unref } from 'vue' -import { useLoadingService } from '../../loadingService' import { useRouter } from '../../router' import { FileAction, FileActionOptions } from '../types' import { useGettext } from 'vue3-gettext' import { useArchiverService } from '../../archiverService' import { formatFileSize } from '../../../helpers/filesize' -import { useAuthStore, useMessages } from '../../piniaStores' +import { useMessages } from '../../piniaStores' export const useFileActionsDownloadArchive = () => { const { showErrorMessage } = useMessages() const router = useRouter() - const loadingService = useLoadingService() const archiverService = useArchiverService() const { $ngettext, $gettext, current } = useGettext() - const authStore = useAuthStore() const isFilesAppActive = useIsFilesAppActive() const handler = ({ space, resources }: FileActionOptions) => { @@ -37,8 +34,7 @@ export const useFileActionsDownloadArchive = () => { fileIds: resources.map((resource) => resource.fileId), ...(space && isPublicSpaceResource(space) && { - publicToken: space.id, - publicLinkPassword: authStore.publicLinkPassword + publicToken: space.id }) }) .catch((e) => { @@ -73,8 +69,8 @@ export const useFileActionsDownloadArchive = () => { { name: 'download-archive', icon: 'inbox-archive', - handler: async (args) => { - await loadingService.addTask(() => handler(args)) + handler: (args) => { + handler(args) }, label: () => $gettext('Download'), disabledTooltip: ({ resources }) => { diff --git a/packages/web-pkg/src/composables/appDefaults/useAppFileHandling.ts b/packages/web-pkg/src/composables/appDefaults/useAppFileHandling.ts index 6b8550fcc4..15721e24fb 100644 --- a/packages/web-pkg/src/composables/appDefaults/useAppFileHandling.ts +++ b/packages/web-pkg/src/composables/appDefaults/useAppFileHandling.ts @@ -8,7 +8,7 @@ import { FileResource, SpaceResource } from '@opencloud-eu/web-client' import { useClientService } from '../clientService' import { ListFilesOptions } from '@opencloud-eu/web-client/webdav' import { WebDAV } from '@opencloud-eu/web-client/webdav' -import { useCapabilityStore, useUserStore } from '../piniaStores' +import { useUserStore } from '../piniaStores' interface AppFileHandlingOptions { clientService: ClientService @@ -39,7 +39,6 @@ export function useAppFileHandling({ clientService }: AppFileHandlingOptions): AppFileHandlingResult { clientService = clientService || useClientService() - const capabilityStore = useCapabilityStore() const userStore = useUserStore() const getUrlForResource = ( @@ -48,7 +47,6 @@ export function useAppFileHandling({ options?: UrlForResourceOptions ) => { return clientService.webdav.getFileUrl(space, resource, { - isUrlSigningEnabled: capabilityStore.supportUrlSigning, username: userStore.user?.onPremisesSamAccountName, ...options }) diff --git a/packages/web-pkg/src/composables/download/useDownloadFile.ts b/packages/web-pkg/src/composables/download/useDownloadFile.ts index 571329235e..c155d309e9 100644 --- a/packages/web-pkg/src/composables/download/useDownloadFile.ts +++ b/packages/web-pkg/src/composables/download/useDownloadFile.ts @@ -2,7 +2,7 @@ import { useClientService } from '../clientService' import { triggerDownloadWithFilename } from '../../../src/helpers' import { useGettext } from 'vue3-gettext' import { ClientService } from '../../services' -import { useCapabilityStore, useMessages, useUserStore } from '../piniaStores' +import { useMessages, useUserStore } from '../piniaStores' import { Resource, SpaceResource } from '@opencloud-eu/web-client' export interface DownloadFileOptions { @@ -13,15 +13,12 @@ export const useDownloadFile = (options?: DownloadFileOptions) => { const { showErrorMessage } = useMessages() const clientService = options?.clientService || useClientService() const { $gettext } = useGettext() - const capabilityStore = useCapabilityStore() const userStore = useUserStore() const downloadFile = async (space: SpaceResource, file: Resource, version: string = null) => { try { const url = await clientService.webdav.getFileUrl(space, file, { version, - doHeadRequest: true, - isUrlSigningEnabled: capabilityStore.supportUrlSigning, username: userStore.user?.onPremisesSamAccountName }) triggerDownloadWithFilename(url, file.name) diff --git a/packages/web-pkg/src/composables/piniaStores/capabilities.ts b/packages/web-pkg/src/composables/piniaStores/capabilities.ts index 27fb1ce3d2..3f46579600 100644 --- a/packages/web-pkg/src/composables/piniaStores/capabilities.ts +++ b/packages/web-pkg/src/composables/piniaStores/capabilities.ts @@ -74,6 +74,7 @@ export const useCapabilityStore = defineStore('capabilities', () => { isInitialized.value = true } + /** @deprecated the server always supports this */ const supportUrlSigning = computed(() => unref(capabilities).core['support-url-signing']) const supportSSE = computed(() => unref(capabilities).core['support-sse']) const personalDataExport = computed(() => unref(capabilities).graph['personal-data-export']) diff --git a/packages/web-pkg/src/helpers/download.ts b/packages/web-pkg/src/helpers/download.ts index 9848ca993b..0f2d089f6b 100644 --- a/packages/web-pkg/src/helpers/download.ts +++ b/packages/web-pkg/src/helpers/download.ts @@ -1,4 +1,4 @@ -export const triggerDownloadWithFilename = (url: string, name: string) => { +export const triggerDownloadWithFilename = (url: string, name = '') => { const a = document.createElement('a') a.style.display = 'none' document.body.appendChild(a) diff --git a/packages/web-pkg/src/services/archiver.ts b/packages/web-pkg/src/services/archiver.ts index 862419a093..c2dc5a4c66 100644 --- a/packages/web-pkg/src/services/archiver.ts +++ b/packages/web-pkg/src/services/archiver.ts @@ -1,5 +1,5 @@ import { RuntimeError } from '../errors' -import { HttpError, urlJoin } from '@opencloud-eu/web-client' +import { urlJoin } from '@opencloud-eu/web-client' import { ClientService } from '../services' import { triggerDownloadWithFilename } from '../helpers/download' import { Ref, ref, computed, unref } from 'vue' @@ -8,12 +8,9 @@ import { UserStore } from '../composables' import { compareVersions } from '../utils' interface TriggerDownloadOptions { - dir?: string files?: string[] fileIds?: string[] - downloadSecret?: string publicToken?: string - publicLinkPassword?: string } export class ArchiverService { @@ -60,36 +57,13 @@ export class ArchiverService { const url = options.publicToken ? downloadUrl - : await this.clientService.ocsUserContext.signUrl( + : await this.clientService.ocs.signUrl( downloadUrl, this.userStore.user?.onPremisesSamAccountName ) - try { - // use fetch because we can't reliably retrieve large data streams with axios - const response = await fetch(url, { - headers: { - ...this.clientService.getRequestHeaders({ useAuth: !options.publicLinkPassword }), - ...(!!options.publicLinkPassword && { - Authorization: - 'Basic ' + - Buffer.from(['public', options.publicLinkPassword].join(':')).toString('base64') - }) - } - }) - - if (!response.ok) { - throw new HttpError('', response) - } - - const blob = await response.blob() - const objectUrl = URL.createObjectURL(blob) - const fileName = this.getFileNameFromResponseHeaders(response.headers) - triggerDownloadWithFilename(objectUrl, fileName) - return url - } catch (e) { - throw new HttpError('archive could not be fetched', e.response) - } + triggerDownloadWithFilename(url) + return url } private buildDownloadUrl(options: TriggerDownloadOptions): string { @@ -112,9 +86,4 @@ export class ArchiverService { } return urlJoin(this.serverUrl, capability.archiver_url) } - - private getFileNameFromResponseHeaders(headers: Headers) { - const fileName = headers.get('content-disposition')?.split('"')[1] - return decodeURI(fileName) - } } diff --git a/packages/web-pkg/tests/unit/services/archiver.spec.ts b/packages/web-pkg/tests/unit/services/archiver.spec.ts index 69bf153de5..da90fb9e1d 100644 --- a/packages/web-pkg/tests/unit/services/archiver.spec.ts +++ b/packages/web-pkg/tests/unit/services/archiver.spec.ts @@ -6,7 +6,6 @@ import { unref, ref, Ref } from 'vue' import { ArchiverCapability } from '@opencloud-eu/web-client/ocs' import { createTestingPinia } from '@opencloud-eu/web-test-helpers' import { useUserStore } from '../../../src/composables/piniaStores' -import { Mock } from 'vitest' const serverUrl = 'https://demo.opencloud.eu' const getArchiverServiceInstance = (capabilities: Ref) => { @@ -14,22 +13,12 @@ const getArchiverServiceInstance = (capabilities: Ref) => const userStore = useUserStore() const clientServiceMock = mockDeep() - clientServiceMock.ocsUserContext.signUrl.mockImplementation((url) => Promise.resolve(url)) + clientServiceMock.ocs.signUrl.mockImplementation((url) => Promise.resolve(url)) return new ArchiverService(clientServiceMock, userStore, serverUrl, capabilities) } describe('archiver', () => { - beforeEach(() => { - global.window.fetch = vi.fn(() => - Promise.resolve({ - blob: () => Promise.resolve({ data: new ArrayBuffer(8) }), - headers: { get: () => 'filename="download.tar"' }, - ok: true - }) - ) as Mock - }) - describe('availability', () => { it('is unavailable if no version given via capabilities', () => { const capabilities = ref([mock({ version: undefined })]) @@ -68,10 +57,8 @@ describe('archiver', () => { }) it('returns a download url for a valid archive download trigger', async () => { const archiverService = getArchiverServiceInstance(capabilities) - window.URL.createObjectURL = vi.fn(() => '') const fileId = 'asdf' const url = await archiverService.triggerDownload({ fileIds: [fileId] }) - expect(window.URL.createObjectURL).toHaveBeenCalled() expect(url.startsWith(archiverUrl)).toBeTruthy() expect(url.indexOf(`id=${fileId}`)).toBeGreaterThan(-1) }) diff --git a/tests/e2e/support/objects/account/actions.ts b/tests/e2e/support/objects/account/actions.ts index 680fdb7ae8..70462d40a3 100644 --- a/tests/e2e/support/objects/account/actions.ts +++ b/tests/e2e/support/objects/account/actions.ts @@ -97,12 +97,6 @@ export const downloadGdprExport = async (args: { page: Page }): Promise const [download] = await Promise.all([ page.waitForEvent('download'), - page.waitForResponse( - (resp) => - resp.url().endsWith('.personal_data_export.json') && - resp.status() === 200 && - resp.request().method() === 'HEAD' - ), page.locator(downloadExportButton).click() ]) await page.locator(requestExportButton).waitFor() diff --git a/tests/e2e/support/objects/app-files/resource/actions.ts b/tests/e2e/support/objects/app-files/resource/actions.ts index 5de23a1b42..b2eb96cb5b 100644 --- a/tests/e2e/support/objects/app-files/resource/actions.ts +++ b/tests/e2e/support/objects/app-files/resource/actions.ts @@ -1,4 +1,4 @@ -import { Download, Locator, Page, Response, expect } from '@playwright/test' +import { Download, Locator, Page, expect } from '@playwright/test' import util from 'util' import path from 'path' import { waitForResources } from './utils' @@ -1263,21 +1263,14 @@ export interface downloadResourceVersionArgs { export const downloadResourceVersion = async (args: downloadResourceVersionArgs) => { const { page, files, folder } = args const fileName = files.map((file) => path.basename(file.name)) - const downloads: Response[] = [] await clickResource({ page, path: folder }) await sidebar.open({ page, resource: fileName[0] }) await sidebar.openPanel({ page, name: 'versions' }) - const [download] = await Promise.all([ - page.waitForResponse( - (resp) => - resp.url().includes('/v/') && resp.status() === 200 && resp.request().method() === 'HEAD' - ), + await Promise.all([ page.waitForEvent('download'), page.locator('//*[@data-testid="file-versions-download-button"]').first().click() ]) await sidebar.close({ page: page }) - downloads.push(download) - return downloads } export interface deleteResourceTrashbinArgs { diff --git a/tests/e2e/support/objects/app-files/resource/index.ts b/tests/e2e/support/objects/app-files/resource/index.ts index 33a91ecdb1..97933b9f60 100644 --- a/tests/e2e/support/objects/app-files/resource/index.ts +++ b/tests/e2e/support/objects/app-files/resource/index.ts @@ -1,4 +1,4 @@ -import { Download, Locator, Page, Response } from '@playwright/test' +import { Download, Locator, Page } from '@playwright/test' import * as po from './actions' import { Space } from '../../../types' import { showShareIndicator } from './utils' @@ -114,11 +114,10 @@ export class Resource { await this.#page.goto(startUrl) } - async downloadVersion(args: Omit): Promise { + async downloadVersion(args: Omit) { const startUrl = this.#page.url() - const downloads = await po.downloadResourceVersion({ ...args, page: this.#page }) + await po.downloadResourceVersion({ ...args, page: this.#page }) await this.#page.goto(startUrl) - return downloads } async deleteTrashBin(args: Omit): Promise {