Skip to content

Commit 547dc41

Browse files
Merge pull request #50504 from nextcloud/backport/50270/stable29
2 parents a733523 + 3d50853 commit 547dc41

File tree

7 files changed

+386
-286
lines changed

7 files changed

+386
-286
lines changed

apps/files_sharing/lib/Controller/ShareAPIController.php

+95-101
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
namespace OCA\Files_Sharing\Controller;
4848

4949
use Exception;
50-
use OC\Files\FileInfo;
5150
use OC\Files\Storage\Wrapper\Wrapper;
5251
use OCA\Files\Helper;
5352
use OCA\Files_Sharing\Exceptions\SharingRightsException;
@@ -64,6 +63,7 @@
6463
use OCP\AppFramework\OCSController;
6564
use OCP\AppFramework\QueryException;
6665
use OCP\Constants;
66+
use OCP\Files\File;
6767
use OCP\Files\Folder;
6868
use OCP\Files\InvalidPathException;
6969
use OCP\Files\IRootFolder;
@@ -566,8 +566,8 @@ public function deleteShare(string $id): DataResponse {
566566
* @param string|null $path Path of the share
567567
* @param int|null $permissions Permissions for the share
568568
* @param int $shareType Type of the share
569-
* @param string|null $shareWith The entity this should be shared with
570-
* @param string $publicUpload If public uploading is allowed
569+
* @param ?string $shareWith The entity this should be shared with
570+
* @param 'true'|'false'|null $publicUpload If public uploading is allowed (deprecated)
571571
* @param string $password Password for the share
572572
* @param string|null $sendPasswordByTalk Send the password for the share over Talk
573573
* @param ?string $expireDate The expiry date of the share in the user's timezone at 00:00.
@@ -590,7 +590,7 @@ public function createShare(
590590
?int $permissions = null,
591591
int $shareType = -1,
592592
?string $shareWith = null,
593-
string $publicUpload = 'false',
593+
?string $publicUpload = null,
594594
string $password = '',
595595
?string $sendPasswordByTalk = null,
596596
?string $expireDate = null,
@@ -599,17 +599,7 @@ public function createShare(
599599
?string $attributes = null
600600
): DataResponse {
601601
$share = $this->shareManager->newShare();
602-
603-
if ($permissions === null) {
604-
if ($shareType === IShare::TYPE_LINK
605-
|| $shareType === IShare::TYPE_EMAIL) {
606-
607-
// to keep legacy default behaviour, we ignore the setting below for link shares
608-
$permissions = Constants::PERMISSION_READ;
609-
} else {
610-
$permissions = (int)$this->config->getAppValue('core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL);
611-
}
612-
}
602+
$hasPublicUpload = $this->getLegacyPublicUpload($publicUpload);
613603

614604
// Verify path
615605
if ($path === null) {
@@ -628,7 +618,7 @@ public function createShare(
628618
// combine all permissions to determine if the user can share this file
629619
$nodes = $userFolder->getById($node->getId());
630620
foreach ($nodes as $nodeById) {
631-
/** @var FileInfo $fileInfo */
621+
/** @var \OC\Files\FileInfo $fileInfo */
632622
$fileInfo = $node->getFileInfo();
633623
$fileInfo['permissions'] |= $nodeById->getPermissions();
634624
}
@@ -641,17 +631,23 @@ public function createShare(
641631
throw new OCSNotFoundException($this->l->t('Could not create share'));
642632
}
643633

644-
if ($permissions < 0 || $permissions > Constants::PERMISSION_ALL) {
645-
throw new OCSNotFoundException($this->l->t('Invalid permissions'));
634+
// Set permissions
635+
if ($shareType === IShare::TYPE_LINK || $shareType === IShare::TYPE_EMAIL) {
636+
$permissions = $this->getLinkSharePermissions($permissions, $hasPublicUpload);
637+
$this->validateLinkSharePermissions($node, $permissions, $hasPublicUpload);
638+
} else {
639+
// Use default permissions only for non-link shares to keep legacy behavior
640+
if ($permissions === null) {
641+
$permissions = (int)$this->config->getAppValue('core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL);
642+
}
643+
// Non-link shares always require read permissions (link shares could be file drop)
644+
$permissions |= Constants::PERMISSION_READ;
646645
}
647646

648-
// Shares always require read permissions
649-
$permissions |= Constants::PERMISSION_READ;
650-
651-
if ($node instanceof \OCP\Files\File) {
652-
// Single file shares should never have delete or create permissions
653-
$permissions &= ~Constants::PERMISSION_DELETE;
654-
$permissions &= ~Constants::PERMISSION_CREATE;
647+
// For legacy reasons the API allows to pass PERMISSIONS_ALL even for single file shares (I look at you Talk)
648+
if ($node instanceof File) {
649+
// if this is a single file share we remove the DELETE and CREATE permissions
650+
$permissions = $permissions & ~(Constants::PERMISSION_DELETE | Constants::PERMISSION_CREATE);
655651
}
656652

657653
/**
@@ -712,28 +708,7 @@ public function createShare(
712708
throw new OCSNotFoundException($this->l->t('Public link sharing is disabled by the administrator'));
713709
}
714710

715-
if ($publicUpload === 'true') {
716-
// Check if public upload is allowed
717-
if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
718-
throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
719-
}
720-
721-
// Public upload can only be set for folders
722-
if ($node instanceof \OCP\Files\File) {
723-
throw new OCSNotFoundException($this->l->t('Public upload is only possible for publicly shared folders'));
724-
}
725-
726-
$permissions = Constants::PERMISSION_READ |
727-
Constants::PERMISSION_CREATE |
728-
Constants::PERMISSION_UPDATE |
729-
Constants::PERMISSION_DELETE;
730-
}
731-
732-
// TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
733-
if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
734-
$permissions |= Constants::PERMISSION_SHARE;
735-
}
736-
711+
$this->validateLinkSharePermissions($node, $permissions, $hasPublicUpload);
737712
$share->setPermissions($permissions);
738713

739714
// Set password
@@ -979,6 +954,71 @@ public function getShares(
979954
return new DataResponse($shares);
980955
}
981956

957+
private function getLinkSharePermissions(?int $permissions, ?bool $legacyPublicUpload): int {
958+
$permissions = $permissions ?? Constants::PERMISSION_READ;
959+
960+
// Legacy option handling
961+
if ($legacyPublicUpload !== null) {
962+
$permissions = $legacyPublicUpload
963+
? (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
964+
: Constants::PERMISSION_READ;
965+
}
966+
967+
// TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
968+
if ($this->hasPermission($permissions, Constants::PERMISSION_READ)
969+
&& $this->shareManager->outgoingServer2ServerSharesAllowed()) {
970+
$permissions |= Constants::PERMISSION_SHARE;
971+
}
972+
973+
return $permissions;
974+
}
975+
976+
/**
977+
* Helper to check for legacy "publicUpload" handling.
978+
* If the value is set to `true` or `false` then true or false are returned.
979+
* Otherwise null is returned to indicate that the option was not (or wrong) set.
980+
*
981+
* @param null|string $legacyPublicUpload The value of `publicUpload`
982+
*/
983+
private function getLegacyPublicUpload(?string $legacyPublicUpload): ?bool {
984+
if ($legacyPublicUpload === 'true') {
985+
return true;
986+
} elseif ($legacyPublicUpload === 'false') {
987+
return false;
988+
}
989+
// Not set at all
990+
return null;
991+
}
992+
993+
/**
994+
* For link and email shares validate that only allowed combinations are set.
995+
*
996+
* @throw OCSBadRequestException If permission combination is invalid.
997+
* @throw OCSForbiddenException If public upload was forbidden by the administrator.
998+
*/
999+
private function validateLinkSharePermissions(Node $node, int $permissions, ?bool $legacyPublicUpload): void {
1000+
if ($legacyPublicUpload && ($node instanceof File)) {
1001+
throw new OCSBadRequestException($this->l->t('Public upload is only possible for publicly shared folders'));
1002+
}
1003+
1004+
// We need at least READ or CREATE (file drop)
1005+
if (!$this->hasPermission($permissions, Constants::PERMISSION_READ)
1006+
&& !$this->hasPermission($permissions, Constants::PERMISSION_CREATE)) {
1007+
throw new OCSBadRequestException($this->l->t('Share must at least have READ or CREATE permissions'));
1008+
}
1009+
1010+
// UPDATE and DELETE require a READ permission
1011+
if (!$this->hasPermission($permissions, Constants::PERMISSION_READ)
1012+
&& ($this->hasPermission($permissions, Constants::PERMISSION_UPDATE) || $this->hasPermission($permissions, Constants::PERMISSION_DELETE))) {
1013+
throw new OCSBadRequestException($this->l->t('Share must have READ permission if UPDATE or DELETE permission is set'));
1014+
}
1015+
1016+
// Check if public uploading was disabled
1017+
if ($this->hasPermission($permissions, Constants::PERMISSION_CREATE)
1018+
&& !$this->shareManager->shareApiLinkAllowPublicUpload()) {
1019+
throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
1020+
}
1021+
}
9821022

9831023
/**
9841024
* @param string $viewer
@@ -1161,7 +1201,6 @@ private function hasPermission(int $permissionsSet, int $permissionsToCheck): bo
11611201
return ($permissionsSet & $permissionsToCheck) === $permissionsToCheck;
11621202
}
11631203

1164-
11651204
/**
11661205
* @NoAdminRequired
11671206
*
@@ -1236,7 +1275,7 @@ public function updateShare(
12361275
$this->checkInheritedAttributes($share);
12371276

12381277
/**
1239-
* expirationdate, password and publicUpload only make sense for link shares
1278+
* expiration date, password and publicUpload only make sense for link shares
12401279
*/
12411280
if ($share->getShareType() === IShare::TYPE_LINK
12421281
|| $share->getShareType() === IShare::TYPE_EMAIL) {
@@ -1260,58 +1299,13 @@ public function updateShare(
12601299
$share->setHideDownload(false);
12611300
}
12621301

1263-
$newPermissions = null;
1264-
if ($publicUpload === 'true') {
1265-
$newPermissions = Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE;
1266-
} elseif ($publicUpload === 'false') {
1267-
$newPermissions = Constants::PERMISSION_READ;
1268-
}
1269-
1270-
if ($permissions !== null) {
1271-
$newPermissions = $permissions;
1272-
$newPermissions = $newPermissions & ~Constants::PERMISSION_SHARE;
1273-
}
1274-
1275-
if ($newPermissions !== null) {
1276-
if (!$this->hasPermission($newPermissions, Constants::PERMISSION_READ) && !$this->hasPermission($newPermissions, Constants::PERMISSION_CREATE)) {
1277-
throw new OCSBadRequestException($this->l->t('Share must at least have READ or CREATE permissions'));
1278-
}
1279-
1280-
if (!$this->hasPermission($newPermissions, Constants::PERMISSION_READ) && (
1281-
$this->hasPermission($newPermissions, Constants::PERMISSION_UPDATE) || $this->hasPermission($newPermissions, Constants::PERMISSION_DELETE)
1282-
)) {
1283-
throw new OCSBadRequestException($this->l->t('Share must have READ permission if UPDATE or DELETE permission is set'));
1284-
}
1285-
}
1286-
1287-
if (
1288-
// legacy
1289-
$newPermissions === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE) ||
1290-
// correct
1291-
$newPermissions === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
1292-
) {
1293-
if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
1294-
throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
1295-
}
1296-
1297-
if (!($share->getNode() instanceof \OCP\Files\Folder)) {
1298-
throw new OCSBadRequestException($this->l->t('Public upload is only possible for publicly shared folders'));
1299-
}
1300-
1301-
// normalize to correct public upload permissions
1302-
if ($publicUpload === 'true') {
1303-
$newPermissions = Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE;
1304-
}
1305-
}
1306-
1307-
if ($newPermissions !== null) {
1308-
// TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
1309-
if (($newPermissions & Constants::PERMISSION_READ) && $this->shareManager->outgoingServer2ServerSharesAllowed()) {
1310-
$newPermissions |= Constants::PERMISSION_SHARE;
1311-
}
1312-
1313-
$share->setPermissions($newPermissions);
1314-
$permissions = $newPermissions;
1302+
// If either manual permissions are specified or publicUpload
1303+
// then we need to also update the permissions of the share
1304+
if ($permissions !== null || $publicUpload !== null) {
1305+
$hasPublicUpload = $this->getLegacyPublicUpload($publicUpload);
1306+
$permissions = $this->getLinkSharePermissions($permissions ?? Constants::PERMISSION_READ, $hasPublicUpload);
1307+
$this->validateLinkSharePermissions($share->getNode(), $permissions, $hasPublicUpload);
1308+
$share->setPermissions($permissions);
13151309
}
13161310

13171311
if ($password === '') {

apps/files_sharing/openapi.json

+6-2
Original file line numberDiff line numberDiff line change
@@ -1762,10 +1762,14 @@
17621762
{
17631763
"name": "publicUpload",
17641764
"in": "query",
1765-
"description": "If public uploading is allowed",
1765+
"description": "If public uploading is allowed (deprecated)",
17661766
"schema": {
17671767
"type": "string",
1768-
"default": "false"
1768+
"nullable": true,
1769+
"enum": [
1770+
"true",
1771+
"false"
1772+
]
17691773
}
17701774
},
17711775
{

0 commit comments

Comments
 (0)