From f55632efb7bf78c0bc23143759beb11c547619ca Mon Sep 17 00:00:00 2001 From: Pascal Wengerter Date: Tue, 28 Jun 2022 14:35:31 +0200 Subject: [PATCH] Migrate deny-acl UI code from CERNbox --- .../enhancement-deny-subfolder-share | 5 ++++ .../Shares/Collaborators/RoleDropdown.vue | 15 +++++++++--- .../web-app-files/src/helpers/resources.ts | 4 ++++ .../web-client/src/helpers/resource/types.ts | 1 + .../src/helpers/share/permission.ts | 2 ++ packages/web-client/src/helpers/share/role.ts | 23 +++++++++++++++---- packages/web-pkg/src/constants/dav.ts | 1 + 7 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 changelog/unreleased/enhancement-deny-subfolder-share diff --git a/changelog/unreleased/enhancement-deny-subfolder-share b/changelog/unreleased/enhancement-deny-subfolder-share new file mode 100644 index 00000000000..3b03a10d83f --- /dev/null +++ b/changelog/unreleased/enhancement-deny-subfolder-share @@ -0,0 +1,5 @@ +Enhancement: Deny subfolders inside share + +Subfolders within non-link shares can now be denied for certain share receivers if the backend is capabable of negative ACLs. + +https://github.com/owncloud/web/pull/7190 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 469f56c9710..651112482be 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 @@ -170,6 +170,8 @@ export default defineComponent({ inviteLabel() { if (this.selectedRole.hasCustomPermissions) { return this.$gettext('Invite with custom permissions') + } else if (this.selectedRole.permissions().includes(SharePermissions.deny)) { + return this.$gettext('Deny access') } else { return this.$gettextInterpolate(this.$gettext('Invite as %{ name }'), { name: this.$gettext(this.selectedRole.inlineLabel) || '' @@ -187,7 +189,7 @@ export default defineComponent({ }, availableRoles() { if (this.resourceIsSpace) { - return SpacePeopleShareRoles.list() + return SpacePeopleShareRoles.list(this.resource.canDeny()) } if (this.incomingParentShare.value && this.resourceIsSharable) { @@ -199,7 +201,11 @@ export default defineComponent({ ) } - return PeopleShareRoles.list(this.resource.isFolder, this.allowCustomSharing !== false) + return PeopleShareRoles.list( + this.resource.isFolder, + this.allowCustomSharing !== false, + this.resource.canDeny() + ) }, availablePermissions() { if (this.incomingParentShare.value && this.resourceIsSharable) { @@ -234,7 +240,10 @@ export default defineComponent({ } else if (this.resourceIsSpace) { this.selectedRole = SpacePeopleShareRoles.list()[0] } else { - this.selectedRole = PeopleShareRoles.list(this.resource.isFolder)[0] + this.selectedRole = PeopleShareRoles.list( + this.resource.isFolder, + this.resource.canDeny() + )[0] } if (this.selectedRole.hasCustomPermissions) { diff --git a/packages/web-app-files/src/helpers/resources.ts b/packages/web-app-files/src/helpers/resources.ts index 9a80d19b48f..0d56874313f 100644 --- a/packages/web-app-files/src/helpers/resources.ts +++ b/packages/web-app-files/src/helpers/resources.ts @@ -107,6 +107,9 @@ export function buildResource(resource): Resource { isReceivedShare: function () { return this.permissions.indexOf(DavPermission.Shared) >= 0 }, + canDeny: function () { + return this.permissions.indexOf(DavPermission.Deny) >= 0 + }, getDomSelector: () => extractDomSelector(id) } } @@ -269,6 +272,7 @@ export function buildSharedResource( resource.canShare = () => true resource.canRename = () => true resource.canBeDeleted = () => true + resource.canDeny = () => SharePermissions.deny.enabled(share.permissions) } resource.extension = extractExtensionFromFile(resource) diff --git a/packages/web-client/src/helpers/resource/types.ts b/packages/web-client/src/helpers/resource/types.ts index 3e0d80f0862..8717024786b 100644 --- a/packages/web-client/src/helpers/resource/types.ts +++ b/packages/web-client/src/helpers/resource/types.ts @@ -34,6 +34,7 @@ export interface Resource { canRename?(): boolean canBeDeleted?(): boolean canBeRestored?(): boolean + canDeny?(): boolean isReceivedShare?(): boolean isMounted?(): boolean diff --git a/packages/web-client/src/helpers/share/permission.ts b/packages/web-client/src/helpers/share/permission.ts index 71332cfb58d..e11be0ac15c 100644 --- a/packages/web-client/src/helpers/share/permission.ts +++ b/packages/web-client/src/helpers/share/permission.ts @@ -61,6 +61,8 @@ export abstract class SharePermissions { static readonly share = new SharePermission('share', SharePermissionBit.Share, $gettext('Share')) + static readonly deny = new SharePermission('deny', 64, $gettext('Deny')) + static permissionsToBitmask(permissions: SharePermission[]): number { return (permissions || []).reduce((b: number, p: SharePermission) => b | p.bit, 0) } diff --git a/packages/web-client/src/helpers/share/role.ts b/packages/web-client/src/helpers/share/role.ts index 875e7c64383..7d071e2e751 100644 --- a/packages/web-client/src/helpers/share/role.ts +++ b/packages/web-client/src/helpers/share/role.ts @@ -185,6 +185,14 @@ export const peopleRoleCustomFolder = new CustomShareRole( SharePermissions.share ] ) +export const peopleRoleDenyFolder = new PeopleShareRole( + 'deny', + true, + $gettext('Deny'), + $gettext('deny'), + 'user -unfollow', + [SharePermissions.deny] +) export const linkRoleInternalFile = new LinkShareRole( 'none', false, @@ -288,7 +296,8 @@ export abstract class SpacePeopleShareRoles { } static getByBitmask(bitmask: number): ShareRole { - return this.all.find((r) => r.bitmask(true) === bitmask) + return this.all // Retrieve all possible options always, even if deny is not enabled + .find((r) => r.bitmask(true) === bitmask) } } @@ -302,8 +311,11 @@ export abstract class PeopleShareRoles { static readonly allWithCustom = [...this.all, peopleRoleCustomFile, peopleRoleCustomFolder] - static list(isFolder: boolean, hasCustom = true): ShareRole[] { - return (hasCustom ? this.allWithCustom : this.all).filter((r) => r.folder === isFolder) + static list(isFolder: boolean, hasCustom = true, canDeny = false): ShareRole[] { + return [ + ...(hasCustom ? this.allWithCustom : this.all), + ...(canDeny ? [peopleRoleDenyFolder] : []) + ].filter((r) => r.folder === isFolder) } static custom(isFolder: boolean): ShareRole { @@ -311,7 +323,7 @@ export abstract class PeopleShareRoles { } static getByBitmask(bitmask: number, isFolder: boolean, allowSharing: boolean): ShareRole { - const role = this.allWithCustom + const role = [...this.allWithCustom, peopleRoleDenyFolder] // Retrieve all possible options always, even if deny is not enabled .filter((r) => !r.hasCustomPermissions) .find((r) => r.folder === isFolder && r.bitmask(allowSharing) === bitmask) return role || this.custom(isFolder) @@ -401,7 +413,8 @@ const shareRoleDescriptions = { [peopleRoleEditorFolder.bitmask(false)]: $gettext('Upload, edit, delete, download and preview'), [peopleRoleEditorFolder.bitmask(true)]: $gettext( 'Upload, edit, delete, download, preview and share' - ) + ), + [peopleRoleDenyFolder.bitmask(false)]: $gettext('Deny access') } /** diff --git a/packages/web-pkg/src/constants/dav.ts b/packages/web-pkg/src/constants/dav.ts index 11c7dd10f10..718211d230c 100644 --- a/packages/web-pkg/src/constants/dav.ts +++ b/packages/web-pkg/src/constants/dav.ts @@ -8,6 +8,7 @@ export abstract class DavPermission { static readonly Updateable: string = 'NV' static readonly FileUpdateable: string = 'W' static readonly FolderCreateable: string = 'CK' + static readonly Deny: string = 'Z' } export abstract class DavProperty {