From 28dabe3a39aa8d356da14ad2916bdc8c114c449c Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Mon, 23 May 2022 10:10:56 +0200 Subject: [PATCH] use ocis storageId whenever possible use roles whenever possible use fileId for public link sharing fix indirect sharing-indicators behavior fix retrieving of shares from shares tree fix reshare permissions on share roots fix sharesTree errors for links, take parent permissions into account add ocis resharing env var to docker compose config load indicators in share jail when resharing is enabled make use of resharing composable refactor share loading out of link and share panel component into parent component to load the data only once cleanup expected failures & acceptance tests add gherkin debug helpers bump sdk bump ocis --- .drone.env | 2 +- .drone.star | 1 + .../unreleased/enhancement-ocis-resharing | 7 + docker-compose.yml | 6 +- .../SideBar/Details/FileDetails.vue | 5 +- .../InviteCollaboratorForm.vue | 25 +-- .../SideBar/Shares/Collaborators/ListItem.vue | 3 +- .../Shares/Collaborators/RoleDropdown.vue | 8 +- .../components/SideBar/Shares/FileLinks.vue | 73 ++++++--- .../components/SideBar/Shares/FileShares.vue | 62 +++---- .../components/SideBar/Shares/SharesPanel.vue | 55 +++++++ .../SideBar/Shares/SpaceMembers.vue | 2 +- .../web-app-files/src/helpers/share/role.ts | 12 ++ .../src/helpers/statusIndicators.js | 12 +- .../services/folder/legacy/loaderPersonal.ts | 10 +- .../services/folder/spaces/loaderPersonal.ts | 11 +- .../services/folder/spaces/loaderProject.ts | 10 +- .../src/services/folder/spaces/loaderShare.ts | 23 ++- packages/web-app-files/src/store/actions.js | 152 +++++++----------- packages/web-app-files/src/store/mutations.js | 9 +- .../__snapshots__/RoleDropdown.spec.js.snap | 89 +++++++++- .../SideBar/Shares/FileLinks.spec.js | 19 ++- .../SideBar/Shares/FileShares.spec.js | 15 +- .../SideBar/Shares/SharesPanel.spec.js | 49 ++++++ .../__snapshots__/FileShares.spec.js.snap | 8 +- .../tests/unit/store/actions.spec.js | 6 +- ...-failures-with-ocis-server-ocis-storage.md | 2 - .../sharePermissionsUsers.feature | 34 ++-- .../sharePermissionsUsers.feature | 50 +++--- .../stepDefinitions/debugContext.js | 5 + .../e2e/cucumber/steps/app-files/resource.ts | 8 +- tests/e2e/support/api/graph/user.ts | 1 + tests/e2e/support/environment/actor/shared.ts | 3 +- .../objects/app-files/resource/actions.ts | 4 + 34 files changed, 505 insertions(+), 276 deletions(-) create mode 100644 changelog/unreleased/enhancement-ocis-resharing create mode 100644 packages/web-app-files/tests/unit/components/SideBar/Shares/SharesPanel.spec.js create mode 100644 tests/acceptance/stepDefinitions/debugContext.js diff --git a/.drone.env b/.drone.env index d8592750807..d09a2e87afc 100644 --- a/.drone.env +++ b/.drone.env @@ -1,3 +1,3 @@ # The version of OCIS to use in pipelines that test against OCIS -OCIS_COMMITID=49541e6cc244f933e1d42b0462664eb6f1ab4ad4 +OCIS_COMMITID=ee97594742b23550d1f35a1002a3d83700950622 OCIS_BRANCH=master diff --git a/.drone.star b/.drone.star index da9f69e2a2f..095bb231d46 100644 --- a/.drone.star +++ b/.drone.star @@ -2154,6 +2154,7 @@ def ocisService(): "STORAGE_USERS_DRIVER_OWNCLOUD_DATADIR": "/srv/app/tmp/ocis/owncloud/data", "WEB_ASSET_PATH": "%s/dist" % dir["web"], "WEB_UI_CONFIG": "/srv/config/drone/config-ocis.json", + "FRONTEND_ENABLE_RESHARING": "true", }, "commands": [ "cd %s/ocis-build" % dir["base"], diff --git a/changelog/unreleased/enhancement-ocis-resharing b/changelog/unreleased/enhancement-ocis-resharing new file mode 100644 index 00000000000..dacd2a5f1eb --- /dev/null +++ b/changelog/unreleased/enhancement-ocis-resharing @@ -0,0 +1,7 @@ +Enhancement: Re-sharing for ocis + +We've enhanced web to be able to re-share resources when using an ownCloud infinite scale backend. It now works for project and personal spaces as well as the sharing jail. +Besides that we also send roles, space-ref and path as separate values to the sharing api which simplifies the usage of it. + +https://github.com/owncloud/web/pull/7086 +https://github.com/owncloud/web/issues/6894 diff --git a/docker-compose.yml b/docker-compose.yml index 084670fa021..d0dc73eb9ef 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,6 +17,9 @@ services: OCIS_JWT_SECRET: "${OCIS_JWT_SECRET:-some-ocis-jwt-secret}" OCIS_MACHINE_AUTH_API_KEY: "${OCIS_MACHINE_AUTH_API_KEY:-some-ocis-machine-auth-api-key}" + # FRONTEND + FRONTEND_ENABLE_RESHARING: "true" + # WEB WEB_UI_CONFIG: ${WEB_UI_CONFIG:-/web/config.json} WEB_ASSET_PATH: ${WEB_ASSET_PATH:-/web/dist} @@ -32,7 +35,7 @@ services: IDP_LDAP_BIND_PASSWORD: "${IDP_LDAP_BIND_PASSWORD:-some-ldap-idp-password}" IDP_IDENTIFIER_REGISTRATION_CONF: ${IDP_IDENTIFIER_REGISTRATION_CONF:-/web/identifier-registration.yml} - # Storage + # STORAGE STORAGE_HOME_DRIVER: ${STORAGE_HOME_DRIVER:-ocis} STORAGE_USERS_DRIVER: ${STORAGE_USERS_DRIVER:-ocis} STORAGE_TRANSFER_SECRET: "${STORAGE_TRANSFER_SECRET:-some-ocis-transfer-secret}" @@ -43,6 +46,7 @@ services: AUTH_BASIC_LDAP_BIND_PASSWORD: "${AUTH_BASIC_LDAP_BIND_PASSWORD:-some-ldap-reva-password}" GRAPH_LDAP_BIND_PASSWORD: "${GRAPH_LDAP_BIND_PASSWORD:-some-ldap-idm-password}" + # PROXY PROXY_ENABLE_BASIC_AUTH: "${PROXY_ENABLE_BASIC_AUTH:-true}" volumes: - ./dev/docker/ocis.entrypoint.sh:/usr/bin/entrypoint diff --git a/packages/web-app-files/src/components/SideBar/Details/FileDetails.vue b/packages/web-app-files/src/components/SideBar/Details/FileDetails.vue index a82e561adc2..34aaae4bed1 100644 --- a/packages/web-app-files/src/components/SideBar/Details/FileDetails.vue +++ b/packages/web-app-files/src/components/SideBar/Details/FileDetails.vue @@ -179,8 +179,7 @@ export default defineComponent({ return { sharedParentDir, - sharedParentRoute, - currentStorageId + sharedParentRoute } }, @@ -393,7 +392,7 @@ export default defineComponent({ client: this.$client, path: this.file.path, $gettext: this.$gettext, - ...(this.currentStorageId && { storageId: this.currentStorageId }) + storageId: this.file.fileId }) this.shareIndicators = getIndicators(this.file, this.sharesTree) }, diff --git a/packages/web-app-files/src/components/SideBar/Shares/Collaborators/InviteCollaborator/InviteCollaboratorForm.vue b/packages/web-app-files/src/components/SideBar/Shares/Collaborators/InviteCollaborator/InviteCollaboratorForm.vue index 71ae9934cd7..79d76d93b63 100644 --- a/packages/web-app-files/src/components/SideBar/Shares/Collaborators/InviteCollaborator/InviteCollaboratorForm.vue +++ b/packages/web-app-files/src/components/SideBar/Shares/Collaborators/InviteCollaborator/InviteCollaboratorForm.vue @@ -92,7 +92,10 @@ import { SpacePeopleShareRoles } from '../../../../../helpers/share' import { clientService } from 'web-pkg/src/services' -import { useCapabilityFilesSharingResharing } from 'web-pkg/src/composables' +import { + useCapabilityFilesSharingResharing, + useCapabilityShareJailEnabled +} from 'web-pkg/src/composables' import { shareInviteCollaboratorHelp, shareInviteCollaboratorHelpCern, @@ -122,7 +125,8 @@ export default { setup() { return { - hasResharing: useCapabilityFilesSharingResharing() + hasResharing: useCapabilityFilesSharingResharing(), + hasShareJail: useCapabilityShareJailEnabled() } }, @@ -186,9 +190,6 @@ export default { return this.$gettext('Invite') }, - currentStorageId() { - return this.$route.params.storageId - }, resourceIsSpace() { return this.highlightedFile.type === 'space' }, @@ -326,24 +327,24 @@ export default { this.selectedRole.permissions(this.hasResharing || this.resourceIsSpace) ) - let storageId - if (this.resourceIsSpace) { - storageId = this.highlightedFile.id - } else if (this.currentStorageId) { - storageId = this.currentStorageId + let path = this.highlightedFile.path + // sharing a share root from the share jail -> use resource name as path + if (this.hasShareJail && path === '/') { + path = `/${this.highlightedFile.name}` } this.addShare({ client: this.$client, graphClient: this.graphClient, - path: this.highlightedFile.path, + path, $gettext: this.$gettext, shareWith: collaborator.value.shareWith, displayName: collaborator.label, shareType: collaborator.value.shareType, permissions: bitmask, + role: this.selectedRole, expirationDate: this.expirationDate, - storageId + storageId: this.highlightedFile.fileId || this.highlightedFile.id }) }) ) diff --git a/packages/web-app-files/src/components/SideBar/Shares/Collaborators/ListItem.vue b/packages/web-app-files/src/components/SideBar/Shares/Collaborators/ListItem.vue index 1e6c4636827..710d100d453 100644 --- a/packages/web-app-files/src/components/SideBar/Shares/Collaborators/ListItem.vue +++ b/packages/web-app-files/src/components/SideBar/Shares/Collaborators/ListItem.vue @@ -309,7 +309,8 @@ export default { graphClient: this.graphClient, share: this.share, permissions: bitmask, - expirationDate: expirationDate || '' + expirationDate: expirationDate || '', + role }) } } diff --git a/packages/web-app-files/src/components/SideBar/Shares/Collaborators/RoleDropdown.vue b/packages/web-app-files/src/components/SideBar/Shares/Collaborators/RoleDropdown.vue index ba5be30c0f0..cc6c09316e5 100644 --- a/packages/web-app-files/src/components/SideBar/Shares/Collaborators/RoleDropdown.vue +++ b/packages/web-app-files/src/components/SideBar/Shares/Collaborators/RoleDropdown.vue @@ -89,7 +89,6 @@ import { PeopleShareRoles, SharePermissions, ShareRole, - ShareTypes, SpacePeopleShareRoles } from '../../../../helpers/share' import * as uuid from 'uuid' @@ -157,11 +156,8 @@ export default { return this.allowSharePermission && this.resource.canShare() }, share() { - const userShares = this.sharesTree[this.resource.path]?.filter((s) => - ShareTypes.containsAnyValue(ShareTypes.individuals, [s.shareType]) - ) - - return userShares?.length ? userShares[0] : undefined + // the root share has an empty key in the shares tree. That's the reason why we retrieve the share by an empty key here + return this.sharesTree['']?.find((s) => s.incoming) }, allowCustomSharing() { return this.capabilities?.files_sharing?.allow_custom diff --git a/packages/web-app-files/src/components/SideBar/Shares/FileLinks.vue b/packages/web-app-files/src/components/SideBar/Shares/FileLinks.vue index a32c84dcaea..0a039603ab4 100644 --- a/packages/web-app-files/src/components/SideBar/Shares/FileLinks.vue +++ b/packages/web-app-files/src/components/SideBar/Shares/FileLinks.vue @@ -110,7 +110,12 @@ import { dirname } from 'path' import { defineComponent } from '@vue/composition-api' import { DateTime } from 'luxon' import { mapGetters, mapActions, mapState } from 'vuex' -import { useStore, useCapabilitySpacesEnabled } from 'web-pkg/src/composables' +import { + useStore, + useCapabilitySpacesEnabled, + useCapabilityShareJailEnabled, + useCapabilityFilesSharingResharing +} from 'web-pkg/src/composables' import { clientService } from 'web-pkg/src/services' import mixins from '../../../mixins' import { shareViaLinkHelp, shareViaIndirectLinkHelp } from '../../../helpers/contextualHelpers' @@ -118,9 +123,10 @@ import { getParentPaths } from '../../../helpers/path' import { ShareTypes, LinkShareRoles, SharePermissions } from '../../../helpers/share' import { cloneStateObject } from '../../../helpers/store' import { showQuickLinkPasswordModal } from '../../../quickActions' -import CreateQuickLink from './Links/CreateQuickLink.vue' import DetailsAndEdit from './Links/DetailsAndEdit.vue' import NameAndCopy from './Links/NameAndCopy.vue' +import CreateQuickLink from './Links/CreateQuickLink.vue' +import { isLocationSpacesActive } from '../../../router' export default defineComponent({ name: 'FileLinks', @@ -144,6 +150,8 @@ export default defineComponent({ return { graphClient, hasSpaces: useCapabilitySpacesEnabled(), + hasShareJail: useCapabilityShareJailEnabled(), + hasResharing: useCapabilityFilesSharingResharing(), indirectLinkListCollapsed, linkListCollapsed } @@ -180,6 +188,11 @@ export default defineComponent({ return this.currentFileOutgoingLinks.find((link) => link.quicklink === true) }, + share() { + // the root share has an empty key in the shares tree. That's the reason why we retrieve the share by an empty key here + return this.sharesTree['']?.find((s) => s.incoming) + }, + expirationDate() { const expireDate = this.capabilities.files_sharing.public.expire_date @@ -211,6 +224,14 @@ export default defineComponent({ }, availableRoleOptions() { + if (this.highlightedFile.isReceivedShare() && this.canCreatePublicLinks && this.share) { + return LinkShareRoles.filterByBitmask( + parseInt(this.share.permissions), + this.highlightedFile.isFolder, + this.capabilities.files_sharing?.public?.can_edit + ) + } + const roles = LinkShareRoles.list( this.highlightedFile.isFolder, this.capabilities.files_sharing?.public?.can_edit @@ -244,6 +265,15 @@ export default defineComponent({ }, canCreatePublicLinks() { + if (this.highlightedFile.isReceivedShare() && !this.hasResharing) { + return false + } + + const isShareJail = isLocationSpacesActive(this.$router, 'files-spaces-share') + if (isShareJail && !this.hasResharing) { + return false + } + return this.highlightedFile.canShare({ user: this.user }) }, @@ -333,28 +363,12 @@ export default defineComponent({ if (this.resourceIsSpace) { return this.highlightedFile.id } + return this.$route.params.storageId || null } }, - watch: { - highlightedFile(newItem, oldItem) { - if (oldItem !== newItem) { - this.reloadLinks() - } - } - }, - mounted() { - this.reloadLinks() - }, - methods: { - ...mapActions('Files', [ - 'addLink', - 'updateLink', - 'removeLink', - 'loadSharesTree', - 'loadCurrentFileOutgoingShares' - ]), + ...mapActions('Files', ['addLink', 'updateLink', 'removeLink']), ...mapActions(['showMessage', 'createModal', 'hideModal']), toggleLinkListCollapsed() { @@ -485,18 +499,24 @@ export default defineComponent({ permissions: link.permissions, quicklink: link.quicklink, name: link.name, + spaceRef: this.highlightedFile.fileId, ...(this.currentStorageId && { - storageId: this.currentStorageId, - spaceRef: `${this.currentStorageId}${this.highlightedFile.path}` + storageId: this.currentStorageId }) } }, async createLink({ params, onError = (e) => {} }) { + let path = this.highlightedFile.path + // sharing a share root from the share jail -> use resource name as path + if (this.hasShareJail && path === '/') { + path = `/${this.highlightedFile.name}` + } await this.addLink({ - path: this.highlightedFile.path, + path, client: this.$client, $gettext: this.$gettext, + storageId: this.highlightedFile.fileId || this.highlightedFile.id, params }).catch((e) => { onError(e) @@ -555,12 +575,17 @@ export default defineComponent({ async deleteLink({ client, share, resource }) { this.hideModal() + let path = resource.path + // sharing a share root from the share jail -> use resource name as path + if (this.hasShareJail && path === '/') { + path = `/${resource.name}` + } // removeLink currently fetches all shares from the backend in order to reload the shares indicators // TODO: Check if to-removed link is last link share and only reload if it's the last link await this.removeLink({ client, share, - resource, + path, ...(this.currentStorageId && { storageId: this.currentStorageId }) }) .then( diff --git a/packages/web-app-files/src/components/SideBar/Shares/FileShares.vue b/packages/web-app-files/src/components/SideBar/Shares/FileShares.vue index 97c8fcd2d75..19b4ff5f027 100644 --- a/packages/web-app-files/src/components/SideBar/Shares/FileShares.vue +++ b/packages/web-app-files/src/components/SideBar/Shares/FileShares.vue @@ -61,7 +61,6 @@ diff --git a/packages/web-app-files/src/components/SideBar/Shares/SpaceMembers.vue b/packages/web-app-files/src/components/SideBar/Shares/SpaceMembers.vue index ec997355022..149778dd337 100644 --- a/packages/web-app-files/src/components/SideBar/Shares/SpaceMembers.vue +++ b/packages/web-app-files/src/components/SideBar/Shares/SpaceMembers.vue @@ -148,7 +148,7 @@ export default defineComponent({ client: this.$client, graphClient: this.graphClient, share: share, - resource: this.highlightedFile + path: this.highlightedFile.path }) .then(() => { this.hideModal() diff --git a/packages/web-app-files/src/helpers/share/role.ts b/packages/web-app-files/src/helpers/share/role.ts index 1f000d55144..ac4180617dc 100644 --- a/packages/web-app-files/src/helpers/share/role.ts +++ b/packages/web-app-files/src/helpers/share/role.ts @@ -324,6 +324,18 @@ export abstract class LinkShareRoles { return [...this.all, linkRoleEditorFile] // Always return all roles .find((r) => r.folder === isFolder && r.bitmask(false) === bitmask) } + + /** + * Filter all roles that have either exactly the permissions from the bitmask or a subset of them. + * @param bitmask + * @param isFolder + * @param canEditFile + */ + static filterByBitmask(bitmask: number, isFolder: boolean, canEditFile = false): ShareRole[] { + return [...this.all, ...(canEditFile ? [linkRoleEditorFile] : [])].filter((r) => { + return r.folder === isFolder && bitmask === (bitmask | r.bitmask(false)) + }) + } } /** diff --git a/packages/web-app-files/src/helpers/statusIndicators.js b/packages/web-app-files/src/helpers/statusIndicators.js index 52ba511bc56..8096ff3dd91 100644 --- a/packages/web-app-files/src/helpers/statusIndicators.js +++ b/packages/web-app-files/src/helpers/statusIndicators.js @@ -18,9 +18,9 @@ const isDirectUserShare = (resource) => { return ShareTypes.containsAnyValue(ShareTypes.authenticated, $shareTypes(resource)) } -const isIndirectUserShare = (resource, sharesTree) => { +const isIndirectUserShare = (resource, sharesTree, hasShareJail) => { return ( - resource.isReceivedShare() || + (resource.isReceivedShare() && !hasShareJail) || ShareTypes.containsAnyValue( ShareTypes.authenticated, shareTypesIndirect(resource.path, sharesTree) @@ -39,8 +39,8 @@ const isIndirectLinkShare = (resource, sharesTree) => { ) } -const isUserShare = (resource, sharesTree) => { - return isDirectUserShare(resource) || isIndirectUserShare(resource, sharesTree) +const isUserShare = (resource, sharesTree, hasShareJail) => { + return isDirectUserShare(resource) || isIndirectUserShare(resource, sharesTree, hasShareJail) } const isLinkShare = (resource, sharesTree) => { @@ -88,13 +88,13 @@ const shareTypesIndirect = (path, sharesTree) => { } // TODO: Think of a different way how to access sharesTree -export const getIndicators = (resource, sharesTree) => { +export const getIndicators = (resource, sharesTree, hasShareJail = false) => { const indicators = [ { id: `files-sharing-${resource.getDomSelector()}`, accessibleDescription: shareUserIconDescribedBy(resource, sharesTree), label: $gettext('Show invited people'), - visible: isUserShare(resource, sharesTree), + visible: isUserShare(resource, sharesTree, hasShareJail), icon: 'group', target: 'sharing-item', type: isDirectUserShare(resource) ? 'user-direct' : 'user-indirect', diff --git a/packages/web-app-files/src/services/folder/legacy/loaderPersonal.ts b/packages/web-app-files/src/services/folder/legacy/loaderPersonal.ts index a2aa5fb8f32..178fa64cf30 100644 --- a/packages/web-app-files/src/services/folder/legacy/loaderPersonal.ts +++ b/packages/web-app-files/src/services/folder/legacy/loaderPersonal.ts @@ -7,6 +7,8 @@ import { isLocationSpacesActive } from '../../../router' import { Store } from 'vuex' import { fetchResources } from '../util' import get from 'lodash-es/get' +import { useCapabilityShareJailEnabled } from 'web-pkg/src/composables' +import { getIndicators } from '../../../helpers/statusIndicators' export class FolderLoaderLegacyPersonal implements FolderLoader { public isEnabled(store: Store): boolean { @@ -39,15 +41,19 @@ export class FolderLoaderLegacyPersonal implements FolderLoader { resources = resources.map(buildResource) const currentFolder = resources.shift() + const hasShareJail = useCapabilityShareJailEnabled(store) yield store.dispatch('Files/loadSharesTree', { client, path: currentFolder.path }) + for (const file of resources) { + file.indicators = getIndicators(file, store.state.Files.sharesTree, hasShareJail.value) + } + store.commit('Files/LOAD_FILES', { currentFolder, - files: resources, - loadIndicators: true + files: resources }) // fetch user quota diff --git a/packages/web-app-files/src/services/folder/spaces/loaderPersonal.ts b/packages/web-app-files/src/services/folder/spaces/loaderPersonal.ts index b17b7e5485e..d8479d58b26 100644 --- a/packages/web-app-files/src/services/folder/spaces/loaderPersonal.ts +++ b/packages/web-app-files/src/services/folder/spaces/loaderPersonal.ts @@ -7,6 +7,8 @@ import { isLocationSpacesActive } from '../../../router' import { Store } from 'vuex' import { fetchResources } from '../util' import get from 'lodash-es/get' +import { useCapabilityShareJailEnabled } from 'web-pkg/src/composables' +import { getIndicators } from '../../../helpers/statusIndicators' export class FolderLoaderSpacesPersonal implements FolderLoader { public isEnabled(store: Store): boolean { @@ -35,16 +37,19 @@ export class FolderLoaderSpacesPersonal implements FolderLoader { resources = resources.map(buildResource) const currentFolder = resources.shift() - + const hasShareJail = useCapabilityShareJailEnabled(store) yield store.dispatch('Files/loadSharesTree', { client: clientService.owncloudSdk, path: currentFolder.path }) + for (const file of resources) { + file.indicators = getIndicators(file, store.state.Files.sharesTree, hasShareJail.value) + } + store.commit('Files/LOAD_FILES', { currentFolder, - files: resources, - loadIndicators: true + files: resources }) // fetch user quota diff --git a/packages/web-app-files/src/services/folder/spaces/loaderProject.ts b/packages/web-app-files/src/services/folder/spaces/loaderProject.ts index 366ce28d615..c4f0399d6d0 100644 --- a/packages/web-app-files/src/services/folder/spaces/loaderProject.ts +++ b/packages/web-app-files/src/services/folder/spaces/loaderProject.ts @@ -7,6 +7,8 @@ import { buildResource, buildSpace, buildWebDavSpacesPath } from '../../../helpe import { DavProperties } from 'web-pkg/src/constants' import { Store } from 'vuex' import get from 'lodash-es/get' +import { useCapabilityShareJailEnabled } from 'web-pkg/src/composables' +import { getIndicators } from '../../../helpers/statusIndicators' export class FolderLoaderSpacesProject implements FolderLoader { public isEnabled(store: Store): boolean { @@ -65,15 +67,19 @@ export class FolderLoaderSpacesProject implements FolderLoader { } const currentFolder = resources.shift() + const hasShareJail = useCapabilityShareJailEnabled(store) yield store.dispatch('Files/loadSharesTree', { client: clientService.owncloudSdk, path: currentFolder.path }) + for (const file of resources) { + file.indicators = getIndicators(file, store.state.Files.sharesTree, hasShareJail.value) + } + ref.LOAD_FILES({ currentFolder, - files: resources, - loadIndicators: true + files: resources }) ref.UPSERT_SPACE(space) diff --git a/packages/web-app-files/src/services/folder/spaces/loaderShare.ts b/packages/web-app-files/src/services/folder/spaces/loaderShare.ts index 5acf92a6a6b..2067730b01e 100644 --- a/packages/web-app-files/src/services/folder/spaces/loaderShare.ts +++ b/packages/web-app-files/src/services/folder/spaces/loaderShare.ts @@ -5,6 +5,12 @@ import { isLocationSpacesActive } from '../../../router' import { buildResource, buildWebDavSpacesPath } from '../../../helpers/resources' import { Store } from 'vuex' import get from 'lodash-es/get' +import { + useCapabilityFilesSharingResharing, + useCapabilityShareJailEnabled +} from 'web-pkg/src/composables' +import { DavProperties } from 'web-pkg/src/constants' +import { getIndicators } from '../../../helpers/statusIndicators' export const SHARE_JAIL_ID = 'a0ca6a90-a365-4782-871e-d44447bbc668' @@ -27,11 +33,26 @@ export class FolderLoaderSpacesShare implements FolderLoader { buildWebDavSpacesPath( [SHARE_JAIL_ID, shareId].join('!'), path || router.currentRoute.params.item || '' - ) + ), + 1, + DavProperties.Default ) const resources = webDavResponse.map(buildResource) const currentFolder = resources.shift() + const hasResharing = useCapabilityFilesSharingResharing(store) + const hasShareJail = useCapabilityShareJailEnabled(store) + + if (hasResharing.value) { + yield store.dispatch('Files/loadSharesTree', { + client: clientService.owncloudSdk, + path: currentFolder.path + }) + + for (const file of resources) { + file.indicators = getIndicators(file, store.state.Files.sharesTree, hasShareJail.value) + } + } store.commit('Files/LOAD_FILES', { currentFolder, diff --git a/packages/web-app-files/src/store/actions.js b/packages/web-app-files/src/store/actions.js index cac63c5ec1c..4731e9a4026 100644 --- a/packages/web-app-files/src/store/actions.js +++ b/packages/web-app-files/src/store/actions.js @@ -14,7 +14,7 @@ import { $gettext, $gettextInterpolate } from '../gettext' import { loadPreview, move, copy } from '../helpers/resource' import { avatarUrl } from '../helpers/user' import { has } from 'lodash-es' -import { ShareTypes, SpacePeopleShareRoles } from '../helpers/share' +import { ShareTypes } from '../helpers/share' import { sortSpaceMembers } from '../helpers/space' import get from 'lodash-es/get' import { ClipboardActions } from '../helpers/clipboardActions' @@ -243,11 +243,6 @@ export default { context.commit('CURRENT_FILE_OUTGOING_SHARES_ERROR', null) context.commit('CURRENT_FILE_OUTGOING_SHARES_LOADING', true) - let spaceRef - if (storageId) { - spaceRef = `${storageId}${path}` - } - if (resource?.type === 'space') { const promises = [] const spaceMembers = [] @@ -264,7 +259,7 @@ export default { } promises.push( - client.shares.getShares(path, { reshares: true, spaceRef }).then((data) => { + client.shares.getShares(path, { reshares: true, spaceRef: storageId }).then((data) => { for (const element of data) { spaceLinks.push( buildShare( @@ -294,7 +289,7 @@ export default { // see https://owncloud.dev/owncloud-sdk/Shares.html client.shares - .getShares(path, { reshares: true, spaceRef }) + .getShares(path, { reshares: true, spaceRef: storageId }) .then((data) => { context.commit( 'CURRENT_FILE_OUTGOING_SHARES_SET', @@ -342,70 +337,56 @@ export default { context.commit('INCOMING_SHARES_LOADING', false) }) }, - changeShare( + async changeShare( { commit, getters, rootGetters }, - { client, graphClient, share, permissions, expirationDate } + { client, graphClient, share, permissions, expirationDate, role } ) { - const params = { - permissions: permissions, - expireDate: expirationDate + if (!permissions && !role) { + throw new Error('Nothing changed') } - if (!params.permissions) { - return new Promise((resolve, reject) => { - reject(new Error('Nothing changed')) + if (share.shareType === ShareTypes.space.value) { + await client.shares.shareSpaceWithUser('', share.collaborator.name, share.id, { + permissions, + role: role.name }) - } - if (share.shareType === ShareTypes.space.value) { - return new Promise((resolve, reject) => { - client.shares - .shareSpaceWithUser('', share.collaborator.name, share.id, { - permissions - }) - .then(() => { - const role = SpacePeopleShareRoles.getByBitmask(permissions) - const shareObj = { - role: role.name, - onPremisesSamAccountName: share.collaborator.name, - displayName: share.collaborator.displayName - } - const updatedShare = buildSpaceShare(shareObj, share.id) - commit('CURRENT_FILE_OUTGOING_SHARES_UPSERT', updatedShare) - - graphClient.drives.getDrive(share.id).then((response) => { - const space = buildSpace(response.data) - commit('UPDATE_RESOURCE_FIELD', { - id: share.id, - field: 'spaceRoles', - value: space.spaceRoles - }) - }) + const spaceShare = buildSpaceShare( + { + role: role.name, + onPremisesSamAccountName: share.collaborator.name, + displayName: share.collaborator.displayName + }, + share.id + ) - resolve(updatedShare) - }) - .catch((e) => { - reject(e) - }) + commit('CURRENT_FILE_OUTGOING_SHARES_UPSERT', spaceShare) + + const { data: drive } = await graphClient.drives.getDrive(share.id) + const space = buildSpace(drive) + commit('UPDATE_RESOURCE_FIELD', { + id: share.id, + field: 'spaceRoles', + value: space.spaceRoles }) + + return } - return new Promise((resolve, reject) => { - client.shares - .updateShare(share.id, params) - .then((updatedShare) => { - const share = buildCollaboratorShare( - updatedShare.shareInfo, - getters.highlightedFile, - allowSharePermissions(rootGetters) - ) - commit('CURRENT_FILE_OUTGOING_SHARES_UPSERT', share) - resolve(share) - }) - .catch((e) => { - reject(e) - }) + const updatedShare = await client.shares.updateShare(share.id, { + role: role.name, + permissions, + expireDate: expirationDate }) + + commit( + 'CURRENT_FILE_OUTGOING_SHARES_UPSERT', + buildCollaboratorShare( + updatedShare.shareInfo, + getters.highlightedFile, + allowSharePermissions(rootGetters) + ) + ) }, addShare( context, @@ -416,22 +397,19 @@ export default { shareWith, shareType, permissions, + role, expirationDate, storageId, displayName } ) { - let spaceRef - if (storageId) { - spaceRef = `${storageId}${path}` - } - if (shareType === ShareTypes.group.value) { client.shares .shareFileWithGroup(path, shareWith, { - permissions: permissions, - expirationDate: expirationDate, - spaceRef + permissions, + role: role.name, + expirationDate, + spaceRef: storageId }) .then((share) => { context.commit( @@ -462,10 +440,10 @@ export default { if (shareType === ShareTypes.space.value) { client.shares .shareSpaceWithUser(path, shareWith, storageId, { - permissions + permissions, + role: role.name }) .then(() => { - const role = SpacePeopleShareRoles.getByBitmask(permissions) const shareObj = { role: role.name, onPremisesSamAccountName: shareWith, @@ -505,10 +483,11 @@ export default { const remoteShare = shareType === ShareTypes.remote.value client.shares .shareFileWithUser(path, shareWith, { - permissions: permissions, remoteUser: remoteShare, - expirationDate: expirationDate, - spaceRef + permissions, + role: role.name, + expirationDate, + spaceRef: storageId }) .then((share) => { context.commit( @@ -534,7 +513,7 @@ export default { ) }) }, - deleteShare(context, { client, graphClient, share, resource, storageId }) { + deleteShare(context, { client, graphClient, share, path, storageId }) { const additionalParams = {} if (share.shareType === ShareTypes.space.value) { additionalParams.shareWith = share.collaborator.name @@ -547,7 +526,7 @@ export default { if (share.shareType !== ShareTypes.space.value) { context.dispatch('updateCurrentFileShareTypes') - context.dispatch('loadIndicators', { client, currentFolder: resource.path, storageId }) + context.dispatch('loadIndicators', { client, currentFolder: path, storageId }) } else { context.commit('CURRENT_FILE_OUTGOING_SHARES_LOADING', true) @@ -587,20 +566,7 @@ export default { const parentPaths = getParentPaths(path, true) const sharesTree = {} - if (!parentPaths.length) { - return Promise.resolve() - } - - let spaceRef - if (storageId) { - spaceRef = `${storageId}${path}` - } - - // remove last entry which is the root folder - parentPaths.pop() - context.commit('SHARESTREE_LOADING', true) - const shareQueriesQueue = new PQueue({ concurrency: 2 }) const shareQueriesPromises = [] parentPaths.forEach((queryPath) => { @@ -613,7 +579,7 @@ export default { shareQueriesPromises.push( shareQueriesQueue.add(() => client.shares - .getShares(queryPath, { reshares: true, spaceRef }) + .getShares(queryPath, { reshares: true, spaceRef: storageId }) .then((data) => { data.forEach((element) => { sharesTree[queryPath].push({ @@ -638,7 +604,7 @@ export default { shareQueriesPromises.push( shareQueriesQueue.add(() => client.shares - .getShares(queryPath, { shared_with_me: true, spaceRef }) + .getShares(queryPath, { shared_with_me: true, spaceRef: storageId }) .then((data) => { data.forEach((element) => { sharesTree[queryPath].push({ @@ -710,13 +676,13 @@ export default { }) }) }, - removeLink(context, { share, client, resource, storageId }) { + removeLink(context, { share, client, path, storageId }) { client.shares .deleteShare(share.id) .then(() => { context.commit('CURRENT_FILE_OUTGOING_SHARES_REMOVE', share) context.dispatch('updateCurrentFileShareTypes') - context.dispatch('loadIndicators', { client, currentFolder: resource.path, storageId }) + context.dispatch('loadIndicators', { client, currentFolder: path, storageId }) }) .catch((e) => context.commit('CURRENT_FILE_OUTGOING_SHARES_ERROR', e.message)) }, diff --git a/packages/web-app-files/src/store/mutations.js b/packages/web-app-files/src/store/mutations.js index 80799d86937..ad6b31af398 100644 --- a/packages/web-app-files/src/store/mutations.js +++ b/packages/web-app-files/src/store/mutations.js @@ -53,15 +53,8 @@ export default { CLEAR_SPACES(state) { state.spaces = [] }, - LOAD_FILES(state, { currentFolder, files, loadIndicators = false }) { + LOAD_FILES(state, { currentFolder, files }) { state.currentFolder = currentFolder - - if (loadIndicators) { - for (const file of files) { - file.indicators = getIndicators(file, state.sharesTree) - } - } - state.files = files }, SET_CURRENT_FOLDER(state, currentFolder) { diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/Collaborators/__snapshots__/RoleDropdown.spec.js.snap b/packages/web-app-files/tests/unit/components/SideBar/Shares/Collaborators/__snapshots__/RoleDropdown.spec.js.snap index dc646f453ff..ff7c9de7ed3 100644 --- a/packages/web-app-files/tests/unit/components/SideBar/Shares/Collaborators/__snapshots__/RoleDropdown.spec.js.snap +++ b/packages/web-app-files/tests/unit/components/SideBar/Shares/Collaborators/__snapshots__/RoleDropdown.spec.js.snap @@ -12,6 +12,12 @@ exports[`RoleDropdown for file shares custom permissions inherits the parents sh +
  • + + + + +
  • @@ -26,6 +32,15 @@ exports[`RoleDropdown for file shares custom permissions inherits the parents sh
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • @@ -50,6 +65,12 @@ exports[`RoleDropdown for file shares custom permissions inherits the parents sh +
  • + + + + +
  • @@ -70,6 +91,9 @@ exports[`RoleDropdown for file shares custom permissions inherits the parents sh
  • +
  • + +
  • @@ -94,6 +118,12 @@ exports[`RoleDropdown for file shares custom permissions inherits the parents sh +
  • + + + + +
  • @@ -108,6 +138,12 @@ exports[`RoleDropdown for file shares custom permissions inherits the parents sh
  • +
  • + +
  • +
  • + +
  • @@ -124,9 +160,56 @@ exports[`RoleDropdown for file shares custom permissions inherits the parents sh `; exports[`RoleDropdown for file shares when an existing role is present does not render a button if only one role is available 1`] = ` -Viewer - - +Viewer + + + + +
  • + + + + +
  • +
  • + + + + +
  • +
  • + + + + +
  • +
    +
    + +

    Custom permissions

    + +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
    +
    + Cancel + Apply +
    +
    +
    `; exports[`RoleDropdown for file shares when an existing role is present renders a button with existing role if given for resource type file 1`] = ` diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/FileLinks.spec.js b/packages/web-app-files/tests/unit/components/SideBar/Shares/FileLinks.spec.js index a786cbc2f1d..33260dabe1e 100644 --- a/packages/web-app-files/tests/unit/components/SideBar/Shares/FileLinks.spec.js +++ b/packages/web-app-files/tests/unit/components/SideBar/Shares/FileLinks.spec.js @@ -4,6 +4,7 @@ import GetTextPlugin from 'vue-gettext' import DesignSystem from 'owncloud-design-system' import { mount, shallowMount, createLocalVue } from '@vue/test-utils' import FileLinks from '@files/src/components/SideBar/Shares/FileLinks.vue' +import { createLocationSpaces } from '../../../../../src/router' const localVue = createLocalVue() @@ -142,7 +143,8 @@ describe('FileLinks', () => { path: '/lorem.txt', type: 'file', canShare: jest.fn(() => false), - isFolder: false + isFolder: false, + isReceivedShare: jest.fn() } }) @@ -160,7 +162,8 @@ describe('FileLinks', () => { path: '/lorem.txt', type: 'file', canShare: jest.fn(() => true), - isFolder: false + isFolder: false, + isReceivedShare: jest.fn() }, currentFileOutgoingSharesLoading = false, sharesTreeLoading = false, @@ -234,6 +237,12 @@ describe('FileLinks', () => { mocks: { $route: { params: {} + }, + $router: { + currentRoute: createLocationSpaces('some-route'), + resolve: (r) => { + return { href: r.name } + } } } }) @@ -246,6 +255,12 @@ describe('FileLinks', () => { mocks: { $route: { params: {} + }, + $router: { + currentRoute: createLocationSpaces('some-route'), + resolve: (r) => { + return { href: r.name } + } } } }) diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/FileShares.spec.js b/packages/web-app-files/tests/unit/components/SideBar/Shares/FileShares.spec.js index 8a8e5fd6e9a..038abdc8f9f 100644 --- a/packages/web-app-files/tests/unit/components/SideBar/Shares/FileShares.spec.js +++ b/packages/web-app-files/tests/unit/components/SideBar/Shares/FileShares.spec.js @@ -93,18 +93,6 @@ describe('FileShares', () => { await wrapper.vm.$nextTick() expect(spyOnCollaboratorDeleteTrigger).toHaveBeenCalledTimes(1) }) - it('reloads shares if highlighted file is changed', async () => { - const spyOnReloadShares = jest - .spyOn(FileShares.methods, '$_reloadShares') - .mockImplementation() - const wrapper = getMountedWrapper({ - user, - outgoingCollaborators: collaborators - }) - wrapper.vm.$store.commit('Files/SET_HIGHLIGHTED_FILE', { name: 'testfile2' }) - await wrapper.vm.$nextTick() - expect(spyOnReloadShares).toHaveBeenCalledTimes(1) - }) it('correctly passes the shared parent route to the collaborator list item', () => { const wrapper = getShallowMountedWrapper({ user, @@ -302,7 +290,8 @@ function getShallowMountedWrapper(data, loading = false) { return shallowMount(FileShares, { localVue, setup: () => ({ - currentStorageId: storageId + currentStorageId: storageId, + hasResharing: false }), store: createStore(data), stubs: { diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/SharesPanel.spec.js b/packages/web-app-files/tests/unit/components/SideBar/Shares/SharesPanel.spec.js new file mode 100644 index 00000000000..c31f11c18d9 --- /dev/null +++ b/packages/web-app-files/tests/unit/components/SideBar/Shares/SharesPanel.spec.js @@ -0,0 +1,49 @@ +import { createLocalVue, mount } from '@vue/test-utils' +import GetTextPlugin from 'vue-gettext' +import Vuex from 'vuex' +import DesignSystem from 'owncloud-design-system' +import SharesPanel from '@files/src/components/SideBar/Shares/SharesPanel.vue' + +const localVue = createLocalVue() +localVue.use(DesignSystem) +localVue.use(Vuex) +localVue.use(GetTextPlugin, { + translations: 'does-not-matter.json', + silent: true +}) + +jest.mock('web-pkg/src/composables/reactivity') +describe('SharesPanel', () => { + it('reloads shares if highlighted file is changed', async () => { + const spyOnReloadShares = jest.spyOn(SharesPanel.methods, '$_reloadShares').mockImplementation() + const wrapper = mount(SharesPanel, { + localVue, + store: new Vuex.Store({ + modules: { + Files: { + namespaced: true, + state: { + highlightedFile: { name: '1' } + }, + getters: { + highlightedFile: (state) => { + return state.highlightedFile + } + }, + mutations: { + SET_HIGHLIGHTED_FILE(state, file) { + state.highlightedFile = file + } + } + } + } + }), + stubs: { + 'file-shares': true + } + }) + wrapper.vm.$store.commit('Files/SET_HIGHLIGHTED_FILE', { name: '2' }) + await wrapper.vm.$nextTick() + expect(spyOnReloadShares).toHaveBeenCalledTimes(2) + }) +}) diff --git a/packages/web-app-files/tests/unit/components/SideBar/Shares/__snapshots__/FileShares.spec.js.snap b/packages/web-app-files/tests/unit/components/SideBar/Shares/__snapshots__/FileShares.spec.js.snap index b9d723d238c..406aab38ce4 100644 --- a/packages/web-app-files/tests/unit/components/SideBar/Shares/__snapshots__/FileShares.spec.js.snap +++ b/packages/web-app-files/tests/unit/components/SideBar/Shares/__snapshots__/FileShares.spec.js.snap @@ -13,7 +13,7 @@ exports[`FileShares if currentUser can not share initially renders no share perm exports[`FileShares if currentUser can share initially renders add people dialog 1`] = `

    Share with people

    - +

    You don't have permission to share this file.

    @@ -23,7 +23,7 @@ exports[`FileShares if currentUser can share initially renders add people dialog exports[`FileShares if there are collaborators present correctly passes the shared parent route to the collaborator list item 1`] = `

    Share with people

    - +

    You don't have permission to share this file.

    Shared with

    @@ -40,7 +40,7 @@ exports[`FileShares if there are collaborators present correctly passes the shar exports[`FileShares if there are collaborators present renders sharedWithLabel and sharee list 1`] = `

    Share with people

    - +

    You don't have permission to share this file.

    Shared with

    @@ -60,7 +60,7 @@ exports[`FileShares if there are collaborators present renders sharedWithLabel a exports[`FileShares if there are no collaborators does not render avatar wrapper or collaborator list 1`] = `

    Share with people

    - +

    You don't have permission to share this file.

    diff --git a/packages/web-app-files/tests/unit/store/actions.spec.js b/packages/web-app-files/tests/unit/store/actions.spec.js index 1dcffb8e5ce..63685553ea8 100644 --- a/packages/web-app-files/tests/unit/store/actions.spec.js +++ b/packages/web-app-files/tests/unit/store/actions.spec.js @@ -108,7 +108,8 @@ describe('vuex store actions', () => { client: clientMock, graphClient: graphClientMock, share: dataSet.share, - permissions: 1, + permissions: spaceRoleManager.bitmask(false), + role: spaceRoleManager, expirationDate: null }) @@ -128,7 +129,8 @@ describe('vuex store actions', () => { graphClient: graphClientMock, shareType: dataSet.shareType, storageId: dataSet.storageId, - permissions: 1, + permissions: spaceRoleManager.bitmask(false), + role: spaceRoleManager, expirationDate: null }) diff --git a/tests/acceptance/expected-failures-with-ocis-server-ocis-storage.md b/tests/acceptance/expected-failures-with-ocis-server-ocis-storage.md index 2ad1db7e321..ee8df5a4a40 100644 --- a/tests/acceptance/expected-failures-with-ocis-server-ocis-storage.md +++ b/tests/acceptance/expected-failures-with-ocis-server-ocis-storage.md @@ -44,8 +44,6 @@ Other free text and markdown formatting can be used elsewhere in the document if - [webUISharingInternalUsers/shareWithUsers.feature:276](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUISharingInternalUsers/shareWithUsers.feature#L276) - [webUISharingInternalUsers/shareWithUsers.feature:277](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUISharingInternalUsers/shareWithUsers.feature#L277) - [webUISharingInternalUsersShareWithPage/shareWithUsers.feature:140](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUISharingInternalUsersShareWithPage/shareWithUsers.feature#L140) -- [webUISharingPermissionsUsers/sharePermissionsUsers.feature:187](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUISharingPermissionsUsers/sharePermissionsUsers.feature#L196) -- [webUISharingPermissionsUsers/sharePermissionsUsers.feature:200](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUISharingPermissionsUsers/sharePermissionsUsers.feature#L209) - [webUISharingPermissionsUsers/sharePermissionsUsers.feature:214](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUISharingPermissionsUsers/sharePermissionsUsers.feature#L223) - [webUIResharing2/reshareUsers.feature:41](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUIResharing2/reshareUsers.feature#L41) - [webUIResharing2/reshareUsers.feature:69](https://github.com/owncloud/web/blob/master/tests/acceptance/features/webUIResharing2/reshareUsers.feature#L69) diff --git a/tests/acceptance/features/webUISharingPermissionToRoot/sharePermissionsUsers.feature b/tests/acceptance/features/webUISharingPermissionToRoot/sharePermissionsUsers.feature index a1621610892..ad23766ac18 100644 --- a/tests/acceptance/features/webUISharingPermissionToRoot/sharePermissionsUsers.feature +++ b/tests/acceptance/features/webUISharingPermissionToRoot/sharePermissionsUsers.feature @@ -170,25 +170,25 @@ Feature: Sharing files and folders with internal users with different permission | permissions | delete, read, update | - Scenario: User is not allowed to reshare sub-folder with more permissions - Given user "Carol" has been created with default attributes and without skeleton files in the server - And user "Brian" has shared folder "simple-folder" with user "Alice" with "read, share, delete" permissions in the server - And user "Alice" has logged in using the webUI - When the user browses to the folder "simple-folder" on the files page - And the user shares folder "simple-empty-folder" with user "Carol King" as "Custom permissions" with permissions "share, delete, update" using the webUI - Then the error message with header "Error while sharing." should be displayed on the webUI - And as "Carol" folder "simple-empty-folder" should not exist in the server - Scenario: User is not allowed to update permissions of a reshared sub-folder to higher permissions than what user has received - Given user "Carol" has been created with default attributes and without skeleton files in the server - And user "Brian" has shared folder "simple-folder" with user "Alice" with "read, share, delete, update" permissions in the server - And user "Alice" has shared folder "simple-folder" with user "Carol" with "share, delete" permissions in the server - And user "Alice" has logged in using the webUI - When the user browses to the folder "simple-folder" on the files page - And the user shares folder "simple-empty-folder" with user "Carol King" as "Custom permissions" with permissions "share, delete, update, create" using the webUI - Then the error message with header "Error while sharing." should be displayed on the webUI - And as "Carol" folder "simple-empty-folder" should not exist in the server + + + + + + + + + + + + + + + + + Scenario: User is allowed to update permissions of a reshared sub-folder within the permissions that the user has received diff --git a/tests/acceptance/features/webUISharingPermissionsUsers/sharePermissionsUsers.feature b/tests/acceptance/features/webUISharingPermissionsUsers/sharePermissionsUsers.feature index 41ba92b0d95..4e4f4eece1e 100644 --- a/tests/acceptance/features/webUISharingPermissionsUsers/sharePermissionsUsers.feature +++ b/tests/acceptance/features/webUISharingPermissionsUsers/sharePermissionsUsers.feature @@ -183,32 +183,32 @@ Feature: Sharing files and folders with internal users with different permission | item_type | folder | | permissions | delete, read, update | - @issue-ocis-2260 - Scenario: User is not allowed to reshare sub-folder with more permissions - Given user "Carol" has been created with default attributes and without skeleton files in the server - And user "Brian" has shared folder "simple-folder" with user "Alice" with "read, share, delete" permissions in the server - And user "Alice" has accepted the share "Shares/simple-folder" offered by user "Brian" in the server - And user "Alice" has logged in using the webUI - When the user opens folder "Shares" using the webUI - And the user opens folder "simple-folder" using the webUI - And the user shares folder "simple-empty-folder" with user "Carol King" as "Custom permissions" with permissions "share, delete, update" using the webUI - Then the error message with header "Error while sharing." should be displayed on the webUI - And user "Carol" should not have received any shares in the server - And as "Carol" folder "/Shares/simple-empty-folder" should not exist in the server - @issue-ocis-2260 - Scenario: User is not allowed to update permissions of a reshared sub-folder to higher permissions than what user has received - Given user "Carol" has been created with default attributes and without skeleton files in the server - And user "Brian" has shared folder "simple-folder" with user "Alice" with "read, share, delete, update" permissions in the server - And user "Alice" has accepted the share "Shares/simple-folder" offered by user "Brian" in the server - And user "Alice" has shared folder "/Shares/simple-folder" with user "Carol" with "share, delete" permissions in the server - And user "Alice" has logged in using the webUI - When the user opens folder "Shares" using the webUI - And the user opens folder "simple-folder" using the webUI - And the user shares folder "simple-empty-folder" with user "Carol King" as "Custom permissions" with permissions "share, delete, update, create" using the webUI - Then the error message with header "Error while sharing." should be displayed on the webUI - And user "Carol" should not have received any shares in the server - And as "Carol" folder "/Shares/simple-empty-folder" should not exist in the server + + + + + + + + + + + + + + + + + + + + + + + + + @issue-ocis-2260 Scenario: User is allowed to update permissions of a reshared sub-folder within the permissions that the user has received diff --git a/tests/acceptance/stepDefinitions/debugContext.js b/tests/acceptance/stepDefinitions/debugContext.js new file mode 100644 index 00000000000..45974137941 --- /dev/null +++ b/tests/acceptance/stepDefinitions/debugContext.js @@ -0,0 +1,5 @@ +const { When } = require('@cucumber/cucumber') + +When('pause for {int}', async function (duration) { + await new Promise((resolve) => setTimeout(resolve, duration)) +}) diff --git a/tests/e2e/cucumber/steps/app-files/resource.ts b/tests/e2e/cucumber/steps/app-files/resource.ts index 1e48b40ce0d..3d7c8b258e1 100644 --- a/tests/e2e/cucumber/steps/app-files/resource.ts +++ b/tests/e2e/cucumber/steps/app-files/resource.ts @@ -1,4 +1,5 @@ import { DataTable, When, Then } from '@cucumber/cucumber' +import path from 'path' import { World } from '../../environment' import { objects } from '../../../support' import { expect } from '@playwright/test' @@ -249,13 +250,14 @@ export const processDownload = async ( } else { expect(downloads.length).toBe(1) downloads.forEach((download) => { + const { name } = path.parse(download.suggestedFilename()) if (config.ocis) { - expect(download.suggestedFilename()).toBe('download.tar') + expect(name).toBe('download') } else { if (parentFolder) { - expect(download.suggestedFilename()).toBe(parentFolder + '.zip') + expect(name).toBe(parentFolder) } else { - expect(download.suggestedFilename()).toBe('download.zip') + expect(name).toBe('download') } } }) diff --git a/tests/e2e/support/api/graph/user.ts b/tests/e2e/support/api/graph/user.ts index be3bbb5d90e..500023588cf 100644 --- a/tests/e2e/support/api/graph/user.ts +++ b/tests/e2e/support/api/graph/user.ts @@ -26,6 +26,7 @@ export const createUser = async ({ user, admin }: { user: User; admin: User }): body, user: admin }) + checkResponseStatus(response, 'Failed while creating user') return user } diff --git a/tests/e2e/support/environment/actor/shared.ts b/tests/e2e/support/environment/actor/shared.ts index 67720f87083..2ecd519d4fb 100644 --- a/tests/e2e/support/environment/actor/shared.ts +++ b/tests/e2e/support/environment/actor/shared.ts @@ -20,7 +20,8 @@ export interface ActorOptions extends ActorsOptions { export const buildBrowserContextOptions = (options: ActorOptions): BrowserContextOptions => { const contextOptions: BrowserContextOptions = { acceptDownloads: options.context.acceptDownloads, - ignoreHTTPSErrors: true + ignoreHTTPSErrors: true, + locale: 'en-US' } if (options.context.reportVideo) { diff --git a/tests/e2e/support/objects/app-files/resource/actions.ts b/tests/e2e/support/objects/app-files/resource/actions.ts index b688e52ad49..90e0a6cbbab 100644 --- a/tests/e2e/support/objects/app-files/resource/actions.ts +++ b/tests/e2e/support/objects/app-files/resource/actions.ts @@ -48,6 +48,10 @@ export const clickResource = async ({ (resp) => resp.url().endsWith(encodeURIComponent(name)) || resp.url().endsWith(itemId) ) ]) + + // toDo: remove me + // @jannik: please have a look here what we can wait for to be sure that it's there + await new Promise((resolve) => setTimeout(resolve, 250)) } }