Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/files_sharing/lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -77,6 +78,7 @@ public function __construct(
* },
* },
* default_permissions?: int,
* exclude_reshare_from_edit?: bool,
* federation: array{
* outgoing: bool,
* incoming: bool,
Expand Down Expand Up @@ -159,6 +161,7 @@ public function getCapabilities() {
$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
Expand Down
2 changes: 2 additions & 0 deletions apps/files_sharing/lib/Config/ConfigLexicon.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
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;
Expand All @@ -32,6 +33,7 @@ public function getAppConfigs(): array {
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'),
];
}

Expand Down
3 changes: 3 additions & 0 deletions apps/files_sharing/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@
"type": "integer",
"format": "int64"
},
"exclude_reshare_from_edit": {
"type": "boolean"
},
"federation": {
"type": "object",
"required": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}
},
},
Expand Down
23 changes: 20 additions & 3 deletions apps/files_sharing/src/lib/SharePermissionsToolBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

/**
Expand Down
78 changes: 73 additions & 5 deletions apps/files_sharing/src/lib/SharePermissionsToolBox.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
Expand 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', () => {
Expand All @@ -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)
Expand Down Expand Up @@ -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)
})
})
11 changes: 6 additions & 5 deletions apps/files_sharing/src/mixins/SharesMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions apps/files_sharing/src/services/ConfigService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type FileSharingCapabilities = {
}
}
default_permissions: number
exclude_reshare_from_edit: boolean
federation: {
outgoing: boolean
incoming: boolean
Expand Down Expand Up @@ -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.
Expand Down
22 changes: 13 additions & 9 deletions apps/files_sharing/src/views/SharingDetailsTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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()
},
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -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 = ''
Expand Down
Loading
Loading