feat(sharing): Make share permission in bundled edit configurable

Add config option shareapi_bundle_reshare_with_edit to include reshare
permission in "Allow editing" bundle. Default is true to maintain
backward compatibility.

Signed-off-by: nfebe <fenn25.fn@gmail.com>
Signed-off-by: Carl Schwan <carlschwan@kde.org>
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
This commit is contained in:
nfebe 2026-01-22 18:22:07 +01:00 committed by Andy Scherzinger
parent dc63c92faa
commit b7dceb0b17
22 changed files with 264 additions and 39 deletions

View file

@ -8,6 +8,7 @@
namespace OCA\Files_Sharing;
use OC\Core\AppInfo\ConfigLexicon;
use OCA\Files_Sharing\Config\ConfigLexicon as SharingConfigLexicon;
use OCP\App\IAppManager;
use OCP\Capabilities\ICapability;
use OCP\Constants;
@ -77,6 +78,7 @@ class Capabilities implements ICapability {
* },
* },
* default_permissions?: int,
* exclude_reshare_from_edit?: bool,
* federation: array{
* outgoing: bool,
* incoming: bool,
@ -159,6 +161,7 @@ class Capabilities implements ICapability {
$res['group']['enabled'] = $this->shareManager->allowGroupSharing();
$res['group']['expire_date']['enabled'] = true;
$res['default_permissions'] = (int)$this->config->getAppValue('core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL);
$res['exclude_reshare_from_edit'] = $this->appConfig->getValueBool('files_sharing', SharingConfigLexicon::EXCLUDE_RESHARE_FROM_EDIT);
}
//Federated sharing

View file

@ -23,6 +23,7 @@ use OCP\Config\ValueType;
class ConfigLexicon implements ILexicon {
public const SHOW_FEDERATED_AS_INTERNAL = 'show_federated_shares_as_internal';
public const SHOW_FEDERATED_TO_TRUSTED_AS_INTERNAL = 'show_federated_shares_to_trusted_servers_as_internal';
public const EXCLUDE_RESHARE_FROM_EDIT = 'shareapi_exclude_reshare_from_edit';
public function getStrictness(): Strictness {
return Strictness::IGNORE;
@ -32,6 +33,7 @@ class ConfigLexicon implements ILexicon {
return [
new Entry(self::SHOW_FEDERATED_AS_INTERNAL, ValueType::BOOL, false, 'shows federated shares as internal shares', true),
new Entry(self::SHOW_FEDERATED_TO_TRUSTED_AS_INTERNAL, ValueType::BOOL, false, 'shows federated shares to trusted servers as internal shares', true),
new Entry(self::EXCLUDE_RESHARE_FROM_EDIT, ValueType::BOOL, false, 'Exclude reshare permission from "Allow editing" bundled permissions'),
];
}

View file

@ -189,6 +189,9 @@
"type": "integer",
"format": "int64"
},
"exclude_reshare_from_edit": {
"type": "boolean"
},
"federation": {
"type": "object",
"required": [

View file

@ -41,7 +41,7 @@ import DropdownIcon from 'vue-material-design-icons/TriangleSmallDown.vue'
import IconTune from 'vue-material-design-icons/Tune.vue'
import {
ATOMIC_PERMISSIONS,
BUNDLED_PERMISSIONS,
getBundledPermissions,
} from '../lib/SharePermissionsToolBox.js'
import ShareDetails from '../mixins/ShareDetails.js'
import SharesMixin from '../mixins/SharesMixin.js'
@ -93,14 +93,19 @@ export default {
return t('files_sharing', 'Custom permissions')
},
bundledPermissions() {
return getBundledPermissions(this.config.excludeReshareFromEdit)
},
preSelectedOption() {
// We remove the share permission for the comparison as it is not relevant for bundled permissions.
const permissionsWithoutShare = this.share.permissions & ~ATOMIC_PERMISSIONS.SHARE
if (permissionsWithoutShare === BUNDLED_PERMISSIONS.READ_ONLY) {
const basePermissions = getBundledPermissions(true)
if (permissionsWithoutShare === basePermissions.READ_ONLY) {
return this.canViewText
} else if (permissionsWithoutShare === BUNDLED_PERMISSIONS.ALL || permissionsWithoutShare === BUNDLED_PERMISSIONS.ALL_FILE) {
} else if (permissionsWithoutShare === basePermissions.ALL || permissionsWithoutShare === basePermissions.ALL_FILE) {
return this.canEditText
} else if (permissionsWithoutShare === BUNDLED_PERMISSIONS.FILE_DROP) {
} else if (permissionsWithoutShare === basePermissions.FILE_DROP) {
return this.fileDropText
}
@ -140,14 +145,14 @@ export default {
dropDownPermissionValue() {
switch (this.selectedOption) {
case this.canEditText:
return this.isFolder ? BUNDLED_PERMISSIONS.ALL : BUNDLED_PERMISSIONS.ALL_FILE
return this.isFolder ? this.bundledPermissions.ALL : this.bundledPermissions.ALL_FILE
case this.fileDropText:
return BUNDLED_PERMISSIONS.FILE_DROP
return this.bundledPermissions.FILE_DROP
case this.customPermissionsText:
return 'custom'
case this.canViewText:
default:
return BUNDLED_PERMISSIONS.READ_ONLY
return this.bundledPermissions.READ_ONLY
}
},
},

View file

@ -12,12 +12,29 @@ export const ATOMIC_PERMISSIONS = {
SHARE: 16,
}
export const BUNDLED_PERMISSIONS = {
const BUNDLED_PERMISSIONS = {
READ_ONLY: ATOMIC_PERMISSIONS.READ,
UPLOAD_AND_UPDATE: ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.DELETE,
FILE_DROP: ATOMIC_PERMISSIONS.CREATE,
ALL: ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.DELETE,
ALL_FILE: ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.READ,
ALL: ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.DELETE | ATOMIC_PERMISSIONS.SHARE,
ALL_FILE: ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.SHARE,
}
/**
* Get bundled permissions based on config.
*
* @param {boolean} excludeShare - Whether to exclude SHARE permission from ALL and ALL_FILE bundles.
* @return {object}
*/
export function getBundledPermissions(excludeShare = false) {
if (excludeShare) {
return {
...BUNDLED_PERMISSIONS,
ALL: BUNDLED_PERMISSIONS.ALL & ~ATOMIC_PERMISSIONS.SHARE,
ALL_FILE: BUNDLED_PERMISSIONS.ALL_FILE & ~ATOMIC_PERMISSIONS.SHARE,
}
}
return BUNDLED_PERMISSIONS
}
/**

View file

@ -6,21 +6,23 @@ import { describe, expect, test } from 'vitest'
import {
addPermissions,
ATOMIC_PERMISSIONS,
BUNDLED_PERMISSIONS,
canTogglePermissions,
getBundledPermissions,
hasPermissions,
permissionsSetIsValid,
subtractPermissions,
togglePermissions,
} from '../lib/SharePermissionsToolBox.js'
const BUNDLED_PERMISSIONS = getBundledPermissions()
describe('SharePermissionsToolBox', () => {
test('Adding permissions', () => {
expect(addPermissions(ATOMIC_PERMISSIONS.NONE, ATOMIC_PERMISSIONS.NONE)).toBe(ATOMIC_PERMISSIONS.NONE)
expect(addPermissions(ATOMIC_PERMISSIONS.NONE, ATOMIC_PERMISSIONS.READ)).toBe(ATOMIC_PERMISSIONS.READ)
expect(addPermissions(ATOMIC_PERMISSIONS.READ, ATOMIC_PERMISSIONS.READ)).toBe(ATOMIC_PERMISSIONS.READ)
expect(addPermissions(ATOMIC_PERMISSIONS.READ, ATOMIC_PERMISSIONS.UPDATE)).toBe(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE)
expect(addPermissions(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE, ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.DELETE)).toBe(BUNDLED_PERMISSIONS.ALL)
expect(addPermissions(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE, ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.DELETE | ATOMIC_PERMISSIONS.SHARE)).toBe(BUNDLED_PERMISSIONS.ALL)
expect(addPermissions(BUNDLED_PERMISSIONS.ALL, ATOMIC_PERMISSIONS.READ)).toBe(BUNDLED_PERMISSIONS.ALL)
expect(addPermissions(BUNDLED_PERMISSIONS.ALL, ATOMIC_PERMISSIONS.NONE)).toBe(BUNDLED_PERMISSIONS.ALL)
})
@ -32,7 +34,7 @@ describe('SharePermissionsToolBox', () => {
expect(subtractPermissions(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE, ATOMIC_PERMISSIONS.UPDATE)).toBe(ATOMIC_PERMISSIONS.READ)
expect(subtractPermissions(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE, ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.DELETE)).toBe(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE)
expect(subtractPermissions(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE, ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.DELETE)).toBe(ATOMIC_PERMISSIONS.READ)
expect(subtractPermissions(BUNDLED_PERMISSIONS.ALL, ATOMIC_PERMISSIONS.READ)).toBe(ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.DELETE)
expect(subtractPermissions(BUNDLED_PERMISSIONS.ALL, ATOMIC_PERMISSIONS.READ)).toBe(ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.DELETE | ATOMIC_PERMISSIONS.SHARE)
})
test('Has permissions', () => {
@ -45,8 +47,8 @@ describe('SharePermissionsToolBox', () => {
})
test('Toggle permissions', () => {
expect(togglePermissions(BUNDLED_PERMISSIONS.ALL, BUNDLED_PERMISSIONS.UPLOAD_AND_UPDATE)).toBe(ATOMIC_PERMISSIONS.NONE)
expect(togglePermissions(BUNDLED_PERMISSIONS.ALL, BUNDLED_PERMISSIONS.FILE_DROP)).toBe(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.DELETE)
expect(togglePermissions(BUNDLED_PERMISSIONS.ALL, BUNDLED_PERMISSIONS.UPLOAD_AND_UPDATE)).toBe(ATOMIC_PERMISSIONS.SHARE)
expect(togglePermissions(BUNDLED_PERMISSIONS.ALL, BUNDLED_PERMISSIONS.FILE_DROP)).toBe(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.DELETE | ATOMIC_PERMISSIONS.SHARE)
expect(togglePermissions(BUNDLED_PERMISSIONS.ALL, ATOMIC_PERMISSIONS.NONE)).toBe(BUNDLED_PERMISSIONS.ALL)
expect(togglePermissions(ATOMIC_PERMISSIONS.NONE, BUNDLED_PERMISSIONS.ALL)).toBe(BUNDLED_PERMISSIONS.ALL)
expect(togglePermissions(ATOMIC_PERMISSIONS.READ, BUNDLED_PERMISSIONS.ALL)).toBe(BUNDLED_PERMISSIONS.ALL)
@ -76,4 +78,70 @@ describe('SharePermissionsToolBox', () => {
expect(canTogglePermissions(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.UPDATE, ATOMIC_PERMISSIONS.CREATE)).toBe(true)
expect(canTogglePermissions(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.DELETE, ATOMIC_PERMISSIONS.CREATE)).toBe(true)
})
test('Get bundled permissions with SHARE included (default)', () => {
const permissions = getBundledPermissions()
expect(permissions.READ_ONLY).toBe(BUNDLED_PERMISSIONS.READ_ONLY)
expect(permissions.FILE_DROP).toBe(BUNDLED_PERMISSIONS.FILE_DROP)
expect(permissions.UPLOAD_AND_UPDATE).toBe(BUNDLED_PERMISSIONS.UPLOAD_AND_UPDATE)
expect(permissions.ALL).toBe(BUNDLED_PERMISSIONS.ALL)
expect(permissions.ALL_FILE).toBe(BUNDLED_PERMISSIONS.ALL_FILE)
expect(permissions.ALL).toBe(31)
expect(permissions.ALL_FILE).toBe(19)
expect(hasPermissions(permissions.ALL, ATOMIC_PERMISSIONS.SHARE)).toBe(true)
expect(hasPermissions(permissions.ALL_FILE, ATOMIC_PERMISSIONS.SHARE)).toBe(true)
})
test('Get bundled permissions without SHARE (excludeShare=true)', () => {
const permissions = getBundledPermissions(true)
expect(permissions.READ_ONLY).toBe(BUNDLED_PERMISSIONS.READ_ONLY)
expect(permissions.FILE_DROP).toBe(BUNDLED_PERMISSIONS.FILE_DROP)
expect(permissions.UPLOAD_AND_UPDATE).toBe(BUNDLED_PERMISSIONS.UPLOAD_AND_UPDATE)
expect(permissions.ALL).toBe(BUNDLED_PERMISSIONS.ALL & ~ATOMIC_PERMISSIONS.SHARE)
expect(permissions.ALL_FILE).toBe(BUNDLED_PERMISSIONS.ALL_FILE & ~ATOMIC_PERMISSIONS.SHARE)
expect(permissions.ALL).toBe(15)
expect(permissions.ALL_FILE).toBe(3)
expect(hasPermissions(permissions.ALL, ATOMIC_PERMISSIONS.SHARE)).toBe(false)
expect(hasPermissions(permissions.ALL_FILE, ATOMIC_PERMISSIONS.SHARE)).toBe(false)
})
test('Operations with bundled permissions including SHARE', () => {
const permissionsWithShare = getBundledPermissions(false)
const permissionsWithoutShare = getBundledPermissions(true)
// Adding permissions to ALL with SHARE should preserve SHARE
expect(addPermissions(permissionsWithShare.ALL, ATOMIC_PERMISSIONS.READ)).toBe(permissionsWithShare.ALL)
// Subtracting READ from ALL with SHARE should leave UPDATE | CREATE | DELETE | SHARE
expect(subtractPermissions(permissionsWithShare.ALL, ATOMIC_PERMISSIONS.READ))
.toBe(ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.CREATE | ATOMIC_PERMISSIONS.DELETE | ATOMIC_PERMISSIONS.SHARE)
// Toggle UPLOAD_AND_UPDATE from ALL with SHARE should leave only SHARE
expect(togglePermissions(permissionsWithShare.ALL, BUNDLED_PERMISSIONS.UPLOAD_AND_UPDATE))
.toBe(ATOMIC_PERMISSIONS.SHARE)
// Toggle FILE_DROP from ALL with SHARE
expect(togglePermissions(permissionsWithShare.ALL, BUNDLED_PERMISSIONS.FILE_DROP))
.toBe(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.DELETE | ATOMIC_PERMISSIONS.SHARE)
// BUNDLED_PERMISSIONS.ALL already includes SHARE
expect(BUNDLED_PERMISSIONS.ALL).toBe(permissionsWithShare.ALL)
// Subtracting SHARE from ALL with SHARE should equal ALL without SHARE
expect(subtractPermissions(permissionsWithShare.ALL, ATOMIC_PERMISSIONS.SHARE)).toBe(permissionsWithoutShare.ALL)
})
test('Operations with bundled permissions for files including SHARE', () => {
const permissionsWithShare = getBundledPermissions(false)
const permissionsWithoutShare = getBundledPermissions(true)
// ALL_FILE with SHARE should be READ | UPDATE | SHARE
expect(permissionsWithShare.ALL_FILE).toBe(ATOMIC_PERMISSIONS.READ | ATOMIC_PERMISSIONS.UPDATE | ATOMIC_PERMISSIONS.SHARE)
// Subtracting SHARE from ALL_FILE with SHARE should equal ALL_FILE without SHARE
expect(subtractPermissions(permissionsWithShare.ALL_FILE, ATOMIC_PERMISSIONS.SHARE)).toBe(permissionsWithoutShare.ALL_FILE)
// BUNDLED_PERMISSIONS.ALL_FILE already includes SHARE
expect(BUNDLED_PERMISSIONS.ALL_FILE).toBe(permissionsWithShare.ALL_FILE)
})
})

View file

@ -12,7 +12,7 @@ import PQueue from 'p-queue'
import { fetchNode } from '../../../files/src/services/WebdavClient.ts'
import {
ATOMIC_PERMISSIONS,
BUNDLED_PERMISSIONS,
getBundledPermissions,
} from '../lib/SharePermissionsToolBox.js'
import Share from '../models/Share.ts'
import Config from '../services/ConfigService.ts'
@ -138,11 +138,12 @@ export default {
return this.config.isDefaultInternalExpireDateEnforced
},
hasCustomPermissions() {
const basePermissions = getBundledPermissions(true)
const bundledPermissions = [
BUNDLED_PERMISSIONS.ALL,
BUNDLED_PERMISSIONS.ALL_FILE,
BUNDLED_PERMISSIONS.READ_ONLY,
BUNDLED_PERMISSIONS.FILE_DROP,
basePermissions.ALL,
basePermissions.ALL_FILE,
basePermissions.READ_ONLY,
basePermissions.FILE_DROP,
]
const permissionsWithoutShare = this.share.permissions & ~ATOMIC_PERMISSIONS.SHARE
return !bundledPermissions.includes(permissionsWithoutShare)

View file

@ -53,6 +53,7 @@ type FileSharingCapabilities = {
}
}
default_permissions: number
exclude_reshare_from_edit: boolean
federation: {
outgoing: boolean
incoming: boolean
@ -103,6 +104,13 @@ export default class Config {
return this._capabilities.files_sharing?.default_permissions
}
/**
* Should SHARE permission be excluded from "Allow editing" bundled permissions
*/
get excludeReshareFromEdit(): boolean {
return this._capabilities.files_sharing?.exclude_reshare_from_edit === true
}
/**
* Is public upload allowed on link shares ?
* This covers File request and Full upload/edit option.

View file

@ -331,7 +331,7 @@ import SidebarTabExternalAction from '../components/SidebarTabExternal/SidebarTa
import SidebarTabExternalActionLegacy from '../components/SidebarTabExternal/SidebarTabExternalActionLegacy.vue'
import {
ATOMIC_PERMISSIONS,
BUNDLED_PERMISSIONS,
getBundledPermissions,
hasPermissions,
} from '../lib/SharePermissionsToolBox.js'
import ShareRequests from '../mixins/ShareRequests.js'
@ -390,12 +390,11 @@ export default {
data() {
return {
writeNoteToRecipientIsChecked: false,
sharingPermission: BUNDLED_PERMISSIONS.ALL.toString(),
revertSharingPermission: BUNDLED_PERMISSIONS.ALL.toString(),
sharingPermission: getBundledPermissions().ALL.toString(),
revertSharingPermission: getBundledPermissions().ALL.toString(),
setCustomPermissions: false,
passwordError: false,
advancedSectionAccordionExpanded: false,
bundledPermissions: BUNDLED_PERMISSIONS,
isFirstComponentLoad: true,
test: false,
creating: false,
@ -443,6 +442,10 @@ export default {
}
},
bundledPermissions() {
return getBundledPermissions(this.config.excludeReshareFromEdit)
},
allPermissions() {
return this.isFolder ? this.bundledPermissions.ALL.toString() : this.bundledPermissions.ALL_FILE.toString()
},
@ -1022,9 +1025,10 @@ export default {
if (this.isNewShare) {
const defaultPermissions = this.config.defaultPermissions
const permissionsWithoutShare = defaultPermissions & ~ATOMIC_PERMISSIONS.SHARE
if (permissionsWithoutShare === BUNDLED_PERMISSIONS.READ_ONLY
|| permissionsWithoutShare === BUNDLED_PERMISSIONS.ALL
|| permissionsWithoutShare === BUNDLED_PERMISSIONS.ALL_FILE) {
const basePermissions = getBundledPermissions(true)
if (permissionsWithoutShare === basePermissions.READ_ONLY
|| permissionsWithoutShare === basePermissions.ALL
|| permissionsWithoutShare === basePermissions.ALL_FILE) {
this.sharingPermission = permissionsWithoutShare.toString()
} else {
this.sharingPermission = 'custom'
@ -1075,9 +1079,9 @@ export default {
this.share.permissions = sharePermissionsSet
}
if (!this.isFolder && this.share.permissions === BUNDLED_PERMISSIONS.ALL) {
if (!this.isFolder && this.share.permissions === this.bundledPermissions.ALL) {
// It's not possible to create an existing file.
this.share.permissions = BUNDLED_PERMISSIONS.ALL_FILE
this.share.permissions = this.bundledPermissions.ALL_FILE
}
if (!this.writeNoteToRecipientIsChecked) {
this.share.note = ''

View file

@ -0,0 +1,111 @@
/*!
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { User } from '@nextcloud/e2e-test-server/cypress'
import { openSharingPanel } from './FilesSharingUtils.ts'
describe('files_sharing: Share permissions bundle configuration', () => {
let alice: User
let bob: User
before(() => {
cy.createRandomUser().then(($user) => {
alice = $user
})
cy.createRandomUser().then(($user) => {
bob = $user
})
})
beforeEach(() => {
cy.runOccCommand('config:app:delete files_sharing shareapi_exclude_reshare_from_edit')
})
after(() => {
cy.runOccCommand('config:app:delete files_sharing shareapi_exclude_reshare_from_edit')
})
/**
* Helper to create a user share and select "Allow editing"
*/
function createUserShareWithEdit(itemName: string) {
openSharingPanel(itemName)
cy.get('#app-sidebar-vue').within(() => {
cy.intercept('GET', '**/apps/files_sharing/api/v1/sharees?*').as('shareeSearch')
cy.findByRole('combobox', { name: /Search for internal recipients/i })
.type(`{selectAll}${bob.userId}`)
cy.wait('@shareeSearch')
})
cy.get(`[user="${bob.userId}"]`).click()
// Select "Allow editing" permission bundle
cy.get('[data-cy-files-sharing-share-permissions-bundle]').should('be.visible')
cy.get('[data-cy-files-sharing-share-permissions-bundle="upload-edit"]').click()
cy.intercept('POST', '**/ocs/v2.php/apps/files_sharing/api/v1/shares').as('createShare')
cy.findByRole('button', { name: 'Save share' }).click()
return cy.wait('@createShare')
}
describe('Default behavior (SHARE included in edit)', () => {
it('Creates user share with "Allow editing" with SHARE permission for folders', () => {
const folderName = 'test-folder-with-share'
cy.mkdir(alice, `/${folderName}`)
cy.login(alice)
cy.visit('/apps/files')
createUserShareWithEdit(folderName).should(({ response }) => {
// Verify permission value is 31 (ALL with SHARE: READ=1 + UPDATE=2 + CREATE=4 + DELETE=8 + SHARE=16)
expect(response?.body?.ocs?.data?.permissions).to.equal(31)
})
})
it('Creates user share with "Allow editing" with SHARE permission for files', () => {
const fileName = 'test-file-with-share.txt'
cy.uploadContent(alice, new Blob(['content']), 'text/plain', `/${fileName}`)
cy.login(alice)
cy.visit('/apps/files')
createUserShareWithEdit(fileName).should(({ response }) => {
// Verify permission value is 19 (ALL_FILE with SHARE: READ=1 + UPDATE=2 + SHARE=16)
expect(response?.body?.ocs?.data?.permissions).to.equal(19)
})
})
})
describe('With SHARE excluded from edit (config enabled)', () => {
beforeEach(() => {
cy.runOccCommand('config:app:set --value yes files_sharing shareapi_exclude_reshare_from_edit')
})
it('Creates user share with "Allow editing" without SHARE permission for folders', () => {
const folderName = 'test-folder-no-share'
cy.mkdir(alice, `/${folderName}`)
cy.login(alice)
cy.visit('/apps/files')
createUserShareWithEdit(folderName).should(({ response }) => {
// Verify permission value is 15 (ALL without SHARE: READ=1 + UPDATE=2 + CREATE=4 + DELETE=8)
expect(response?.body?.ocs?.data?.permissions).to.equal(15)
})
})
it('Creates user share with "Allow editing" without SHARE permission for files', () => {
const fileName = 'test-file-no-share.txt'
cy.uploadContent(alice, new Blob(['content']), 'text/plain', `/${fileName}`)
cy.login(alice)
cy.visit('/apps/files')
createUserShareWithEdit(fileName).should(({ response }) => {
// Verify permission value is 3 (ALL_FILE without SHARE: READ=1 + UPDATE=2)
expect(response?.body?.ocs?.data?.permissions).to.equal(3)
})
})
})
})

2
dist/8577-8577.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/8577-8577.js.map vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/8577-8577.js.map.license vendored Symbolic link
View file

@ -0,0 +1 @@
8577-8577.js.license

2
dist/9429-9429.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
9429-9429.js.license

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -2332,6 +2332,9 @@
"type": "integer",
"format": "int64"
},
"exclude_reshare_from_edit": {
"type": "boolean"
},
"federation": {
"type": "object",
"required": [