From dda9e0acbbd17fecfd9a9a37dcae8379e8e66e7f Mon Sep 17 00:00:00 2001 From: Jannik Stehle Date: Wed, 5 Nov 2025 10:35:04 +0100 Subject: [PATCH] fix: archive download on password protected links (cherry picked from commit 3cf16ce1697e3773546bb029444f1499137606f5) --- .../files/useFileActionsDownloadArchive.ts | 6 ++-- packages/web-pkg/src/services/archiver.ts | 31 +++++++++++++++++-- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/packages/web-pkg/src/composables/actions/files/useFileActionsDownloadArchive.ts b/packages/web-pkg/src/composables/actions/files/useFileActionsDownloadArchive.ts index 8436f1a928..2006dd87d9 100644 --- a/packages/web-pkg/src/composables/actions/files/useFileActionsDownloadArchive.ts +++ b/packages/web-pkg/src/composables/actions/files/useFileActionsDownloadArchive.ts @@ -13,13 +13,14 @@ import { FileAction, FileActionOptions } from '../types' import { useGettext } from 'vue3-gettext' import { useArchiverService } from '../../archiverService' import { formatFileSize } from '../../../helpers/filesize' -import { useMessages } from '../../piniaStores' +import { useAuthStore, useMessages } from '../../piniaStores' export const useFileActionsDownloadArchive = () => { const { showErrorMessage } = useMessages() const router = useRouter() const archiverService = useArchiverService() const { $ngettext, $gettext, current } = useGettext() + const authStore = useAuthStore() const isFilesAppActive = useIsFilesAppActive() const handler = ({ space, resources }: FileActionOptions) => { @@ -34,7 +35,8 @@ export const useFileActionsDownloadArchive = () => { fileIds: resources.map((resource) => resource.fileId), ...(space && isPublicSpaceResource(space) && { - publicToken: space.id + publicToken: space.id, + publicLinkPassword: authStore.publicLinkPassword }) }) .catch((e) => { diff --git a/packages/web-pkg/src/services/archiver.ts b/packages/web-pkg/src/services/archiver.ts index c2dc5a4c66..7037a58a15 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 { urlJoin } from '@opencloud-eu/web-client' +import { HttpError, urlJoin } from '@opencloud-eu/web-client' import { ClientService } from '../services' import { triggerDownloadWithFilename } from '../helpers/download' import { Ref, ref, computed, unref } from 'vue' @@ -11,6 +11,7 @@ interface TriggerDownloadOptions { files?: string[] fileIds?: string[] publicToken?: string + publicLinkPassword?: string } export class ArchiverService { @@ -55,14 +56,38 @@ export class ArchiverService { throw new RuntimeError('download url could not be built') } - const url = options.publicToken + let url = options.publicToken ? downloadUrl : await this.clientService.ocs.signUrl( downloadUrl, this.userStore.user?.onPremisesSamAccountName ) - triggerDownloadWithFilename(url) + let fileName: string + if (options.publicLinkPassword) { + // FIXME: remove as soon as we remove client-side URL signing https://github.com/opencloud-eu/opencloud/issues/1197 + try { + const response = await fetch(url, { + headers: { + ...this.clientService.getRequestHeaders({ useAuth: false }), + Authorization: + 'Basic ' + + Buffer.from(['public', options.publicLinkPassword].join(':')).toString('base64') + } + }) + + if (!response.ok) { + throw new HttpError('', response) + } + + const blob = await response.blob() + url = URL.createObjectURL(blob) + fileName = decodeURI(response.headers.get('content-disposition')?.split('"')[1]) + } catch (e) { + throw new Error('archive could not be fetched') + } + } + triggerDownloadWithFilename(url, fileName) return url }