4747namespace OCA \Files_Sharing \Controller ;
4848
4949use Exception ;
50- use OC \Files \FileInfo ;
5150use OC \Files \Storage \Wrapper \Wrapper ;
5251use OCA \Files \Helper ;
5352use OCA \Files_Sharing \Exceptions \SharingRightsException ;
6463use OCP \AppFramework \OCSController ;
6564use OCP \AppFramework \QueryException ;
6665use OCP \Constants ;
66+ use OCP \Files \File ;
6767use OCP \Files \Folder ;
6868use OCP \Files \InvalidPathException ;
6969use 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 === '' ) {
0 commit comments