mirror of
https://github.com/nextcloud/server.git
synced 2026-02-18 18:28:50 -05:00
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:
parent
dc63c92faa
commit
b7dceb0b17
22 changed files with 264 additions and 39 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -189,6 +189,9 @@
|
|||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"exclude_reshare_from_edit": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"federation": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 = ''
|
||||
|
|
|
|||
111
cypress/e2e/files_sharing/share-permissions-bundle.cy.ts
Normal file
111
cypress/e2e/files_sharing/share-permissions-bundle.cy.ts
Normal 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
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
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
1
dist/8577-8577.js.map.license
vendored
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
8577-8577.js.license
|
||||
2
dist/9429-9429.js
vendored
2
dist/9429-9429.js
vendored
File diff suppressed because one or more lines are too long
1
dist/9429-9429.js.map
vendored
1
dist/9429-9429.js.map
vendored
File diff suppressed because one or more lines are too long
1
dist/9429-9429.js.map.license
vendored
1
dist/9429-9429.js.map.license
vendored
|
|
@ -1 +0,0 @@
|
|||
9429-9429.js.license
|
||||
4
dist/files_sharing-files_sharing_tab.js
vendored
4
dist/files_sharing-files_sharing_tab.js
vendored
File diff suppressed because one or more lines are too long
2
dist/files_sharing-files_sharing_tab.js.map
vendored
2
dist/files_sharing-files_sharing_tab.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/files_sharing-init.js
vendored
4
dist/files_sharing-init.js
vendored
File diff suppressed because one or more lines are too long
2
dist/files_sharing-init.js.map
vendored
2
dist/files_sharing-init.js.map
vendored
File diff suppressed because one or more lines are too long
|
|
@ -2332,6 +2332,9 @@
|
|||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"exclude_reshare_from_edit": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"federation": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
|
|
|||
Loading…
Reference in a new issue