Skip to content

Commit

Permalink
Merge pull request #9299 from owncloud/resolve-public-to-internal
Browse files Browse the repository at this point in the history
[full-ci] feat: resolve public link to actual location if possible
  • Loading branch information
JammingBen authored Jul 7, 2023
2 parents a7e6126 + 7224a1b commit 05f5207
Show file tree
Hide file tree
Showing 14 changed files with 242 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Bugfix: Authenticated public links breaking uploads

Opening public links in an authenticated context no longer breaks uploading resources.

https://github.com/owncloud/web/pull/9299
https://github.com/owncloud/web/issues/9298
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Enhancement: Resolve pulic links to their actual location

Public links are now being resolved to their actual location if the user has proper access to the resource (either via space or share).

https://github.com/owncloud/web/pull/9299
https://github.com/owncloud/web/issues/9296
5 changes: 4 additions & 1 deletion packages/web-app-files/src/services/folder/loaderSpace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ export class FolderLoaderSpace implements FolderLoader {
path,
fileId
})
replaceInvalidFileRoute({ space, resource: currentFolder, path, fileId })
// if current folder has no id (= singe file public link) we must not correct the route
if (currentFolder.id) {
replaceInvalidFileRoute({ space, resource: currentFolder, path, fileId })
}

if (path === '/') {
if (space.driveType === 'share') {
Expand Down
58 changes: 46 additions & 12 deletions packages/web-app-files/src/views/FilesDrop.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
<template>
<div id="files-drop-container" class="oc-height-1-1 oc-flex oc-flex-column oc-flex-between">
<app-loading-spinner v-if="loading" />
<div
v-else
id="files-drop-container"
class="oc-height-1-1 oc-flex oc-flex-column oc-flex-between"
>
<div v-if="dragareaEnabled" class="dragarea" />
<h1 class="oc-invisible-sr">{{ pageTitle }}</h1>
<div class="oc-p oc-height-1-1">
<div v-if="loading" key="loading-drop" class="oc-flex oc-flex-column oc-flex-middle">
<h2 class="oc-login-card-title">
<span v-text="$gettext('Loading public link…')" />
</h2>
<oc-spinner :aria-hidden="true" />
</div>
<div v-else key="loaded-drop" class="oc-flex oc-flex-column oc-flex-middle">
<div key="loaded-drop" class="oc-flex oc-flex-column oc-flex-middle">
<div class="oc-text-center oc-width-1-1 oc-width-xxlarge@m">
<h2 v-text="title" />
<resource-upload
Expand Down Expand Up @@ -38,10 +37,10 @@
<script lang="ts">
import { mapGetters } from 'vuex'
import { DavProperties, DavProperty } from 'web-client/src/webdav/constants'
import { createLocationPublic } from '../router'
import { createLocationPublic, createLocationSpaces } from '../router'
import ResourceUpload from '../components/AppBar/Upload/ResourceUpload.vue'
import { defineComponent, onMounted, onBeforeUnmount, ref, unref } from 'vue'
import { computed, defineComponent, onMounted, onBeforeUnmount, ref, unref } from 'vue'
import { useUpload } from 'web-runtime/src/composables/upload'
import { useGettext } from 'vue3-gettext'
import {
Expand All @@ -51,14 +50,19 @@ import {
useStore,
useRouter,
useRoute,
useCapabilitySpacesEnabled
useCapabilitySpacesEnabled,
useGetMatchingSpace,
useUserContext,
useRouteQuery,
queryItemAsString
} from 'web-pkg/src/composables'
import { eventBus } from 'web-pkg/src/services/eventBus'
import { linkRoleUploaderFolder } from 'web-client/src/helpers/share'
import { useService } from 'web-pkg/src/composables/service'
import { UppyService } from 'web-runtime/src/services/uppyService'
import { useAuthService } from 'web-pkg/src/composables/authContext/useAuthService'
import { HandleUpload } from 'web-app-files/src/HandleUpload'
import { createFileRouteOptions } from 'web-pkg/src/helpers/router'
export default defineComponent({
components: {
Expand All @@ -75,8 +79,15 @@ export default defineComponent({
const clientService = useClientService()
const publicToken = usePublicLinkToken({ store })
const publicLinkPassword = usePublicLinkPassword({ store })
const isUserContext = useUserContext({ store })
const { getInternalSpace } = useGetMatchingSpace()
useUpload({ uppyService })
const fileIdQueryItem = useRouteQuery('fileId')
const fileId = computed(() => {
return queryItemAsString(unref(fileIdQueryItem))
})
if (!uppyService.getPlugin('HandleUpload')) {
uppyService.addPlugin(HandleUpload, {
clientService,
Expand Down Expand Up @@ -106,8 +117,31 @@ export default defineComponent({
dragareaEnabled.value = (event.dataTransfer.types || []).some((e) => e === 'Files')
}
const resolvePublicLink = () => {
const resolveToInternalLocation = (path: string) => {
const internalSpace = getInternalSpace(unref(fileId).split('!')[0])
if (internalSpace) {
const routeOpts = createFileRouteOptions(internalSpace, { fileId: unref(fileId), path })
return router.push(createLocationSpaces('files-spaces-generic', routeOpts))
}
// no internal space found -> share -> resolve via private link as it holds all the necessary logic
return router.push({ name: 'resolvePrivateLink', params: { fileId: unref(fileId) } })
}
const resolvePublicLink = async () => {
loading.value = true
if (unref(isUserContext) && unref(fileId)) {
try {
const path = await clientService.owncloudSdk.files.getPathForFileId(unref(fileId))
await resolveToInternalLocation(path)
loading.value = false
return
} catch (e) {
// getPathForFileId failed means the user doesn't have internal access to the resource
}
}
clientService.owncloudSdk.publicFiles
.list(unref(publicToken), unref(publicLinkPassword), DavProperties.PublicLink, '0')
.then((files) => {
Expand Down
85 changes: 73 additions & 12 deletions packages/web-app-files/src/views/spaces/DriveResolver.vue
Original file line number Diff line number Diff line change
@@ -1,46 +1,66 @@
<template>
<drive-redirect
v-if="!space"
:drive-alias-and-item="driveAliasAndItem"
:append-home-folder="isSpaceRoute"
/>
<generic-trash v-else-if="isTrashRoute" :space="space" :item-id="itemId" />
<generic-space v-else :space="space" :item="item" :item-id="itemId" />
<app-loading-spinner v-if="loading" />
<template v-else>
<drive-redirect
v-if="!space"
:drive-alias-and-item="driveAliasAndItem"
:append-home-folder="isSpaceRoute"
/>
<generic-trash v-else-if="isTrashRoute" :space="space" :item-id="itemId" />
<generic-space v-else :space="space" :item="item" :item-id="itemId" />
</template>
</template>

<script lang="ts">
import DriveRedirect from './DriveRedirect.vue'
import GenericSpace from './GenericSpace.vue'
import GenericTrash from './GenericTrash.vue'
import { defineComponent, onMounted, unref } from 'vue'
import { computed, defineComponent, onMounted, ref, unref } from 'vue'
import {
queryItemAsString,
useClientService,
useDriveResolver,
useGetMatchingSpace,
useRouteParam,
useRouteQuery,
useRouter,
useStore
useStore,
useUserContext
} from 'web-pkg/src/composables'
import { useActiveLocation } from '../../composables'
import { isLocationSpacesActive, isLocationTrashActive } from '../../router'
import { createLocationSpaces, isLocationSpacesActive, isLocationTrashActive } from '../../router'
import { isPublicSpaceResource, PublicSpaceResource, SpaceResource } from 'web-client/src/helpers'
import { locationPublicUpload } from 'web-app-files/src/router/public'
import { linkRoleUploaderFolder } from 'web-client/src/helpers/share'
import { createFileRouteOptions } from 'web-pkg/src/helpers/router'
import AppLoadingSpinner from 'web-pkg/src/components/AppLoadingSpinner.vue'
import { dirname } from 'path'
export default defineComponent({
components: {
DriveRedirect,
GenericSpace,
GenericTrash
GenericTrash,
AppLoadingSpinner
},
setup() {
const store = useStore()
const isUserContext = useUserContext({ store })
const clientService = useClientService()
const router = useRouter()
const driveAliasAndItem = useRouteParam('driveAliasAndItem')
const isSpaceRoute = useActiveLocation(isLocationSpacesActive, 'files-spaces-generic')
const isTrashRoute = useActiveLocation(isLocationTrashActive, 'files-trash-generic')
const resolvedDrive = useDriveResolver({ store, driveAliasAndItem })
const { getInternalSpace } = useGetMatchingSpace()
const loading = ref(true)
const fileIdQueryItem = useRouteQuery('fileId')
const fileId = computed(() => {
return queryItemAsString(unref(fileIdQueryItem))
})
const getSpaceResource = async (): Promise<SpaceResource> => {
const space = unref(resolvedDrive.space)
Expand All @@ -52,9 +72,47 @@ export default defineComponent({
}
}
const resolveToInternalLocation = async (path: string) => {
const internalSpace = getInternalSpace(unref(fileId).split('!')[0])
if (internalSpace) {
const resource = await clientService.webdav.getFileInfo(internalSpace, { path })
const resourceId = resource.type !== 'folder' ? resource.parentFolderId : resource.fileId
const resourcePath = resource.type !== 'folder' ? dirname(path) : path
resolvedDrive.space.value = internalSpace
resolvedDrive.item.value = resourcePath
const { params, query } = createFileRouteOptions(internalSpace, {
fileId: resourceId,
path: resourcePath
})
return router.push(
createLocationSpaces('files-spaces-generic', {
params,
query: { ...query, scrollTo: unref(resource).fileId }
})
)
}
// no internal space found -> share -> resolve via private link as it holds all the necessary logic
return router.push({ name: 'resolvePrivateLink', params: { fileId: unref(fileId) } })
}
onMounted(async () => {
const space = unref(resolvedDrive.space)
if (space && isPublicSpaceResource(space)) {
if (unref(isUserContext) && unref(fileId)) {
try {
const path = await clientService.owncloudSdk.files.getPathForFileId(unref(fileId))
await resolveToInternalLocation(path)
loading.value = false
return
} catch (e) {
// getPathForFileId failed means the user doesn't have internal access to the resource
}
}
let publicSpace = (await getSpaceResource()) as PublicSpaceResource
if (linkRoleUploaderFolder.bitmask(false) === publicSpace.publicLinkPermission) {
Expand All @@ -64,13 +122,16 @@ export default defineComponent({
})
}
}
loading.value = false
})
return {
...resolvedDrive,
driveAliasAndItem,
isSpaceRoute,
isTrashRoute
isTrashRoute,
loading
}
}
})
Expand Down
8 changes: 2 additions & 6 deletions packages/web-app-files/tests/unit/views/FilesDrop.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,16 @@ import { mock, mockDeep } from 'jest-mock-extended'
import { ClientService } from 'web-pkg'

describe('FilesDrop view', () => {
it('drop container always present', () => {
const { wrapper } = getMountedWrapper()
expect(wrapper.find('#files-drop-container').exists()).toBeTruthy()
})
describe('different files view states', () => {
it('shows the loading spinner during loading', () => {
const { wrapper } = getMountedWrapper()
expect(wrapper.find('oc-spinner-stub').exists()).toBeTruthy()
expect(wrapper.find('app-loading-spinner').exists()).toBeTruthy()
})
it('shows the "resource-upload"-component after loading', async () => {
const { wrapper } = getMountedWrapper()
wrapper.vm.loading = false
await wrapper.vm.$nextTick()
expect(wrapper.find('oc-spinner-stub').exists()).toBeFalsy()
expect(wrapper.find('app-loading-spinner').exists()).toBeFalsy()
expect(wrapper.find('resource-upload-stub').exists()).toBeTruthy()
})
})
Expand Down
Loading

0 comments on commit 05f5207

Please sign in to comment.