4545namespace OCA \Files_Sharing \Controller ;
4646
4747use Exception ;
48- use OC \Files \FileInfo ;
4948use OC \Files \Storage \Wrapper \Wrapper ;
5049use OCA \Files \Helper ;
5150use OCA \Files_Sharing \Exceptions \SharingRightsException ;
6059use OCP \AppFramework \OCSController ;
6160use OCP \AppFramework \QueryException ;
6261use OCP \Constants ;
62+ use OCP \Files \File ;
6363use OCP \Files \Folder ;
6464use OCP \Files \InvalidPathException ;
6565use OCP \Files \IRootFolder ;
@@ -549,20 +549,21 @@ public function deleteShare(string $id): DataResponse {
549549 /**
550550 * @NoAdminRequired
551551 *
552- * @param string $path
553- * @param int $permissions
554- * @param int $shareType
555- * @param string $shareWith
556- * @param string $publicUpload
557- * @param string $password
558- * @param string $sendPasswordByTalk
559- * @param string $expireDate
560- * @param string $label
561- * @param string $attributes
552+ * @param string|null $path Path of the share
553+ * @param int|null $permissions Permissions for the share
554+ * @param int $shareType Type of the share
555+ * @param string|null $shareWith The entity this should be shared with
556+ * @param 'true'|'false'|null $publicUpload If public uploading is allowed (deprecated)
557+ * @param string $password Password for the share
558+ * @param string|null $sendPasswordByTalk Send the password for the share over Talk
559+ * @param string $expireDate The expiry date of the share in the user's timezone at 00:00.
560+ * If $expireDate is not supplied or set to `null`, the system default will be used.
561+ * @param string $note Note for the share
562+ * @param string $label Label for the share (only used in link and email)
563+ * @param string|null $attributes Additional attributes for the share
562564 *
563565 * @return DataResponse
564- * @throws NotFoundException
565- * @throws OCSBadRequestException
566+ * @throws OCSBadRequestException Unknown share type
566567 * @throws OCSException
567568 * @throws OCSForbiddenException
568569 * @throws OCSNotFoundException
@@ -573,8 +574,8 @@ public function createShare(
573574 string $ path = null ,
574575 int $ permissions = null ,
575576 int $ shareType = -1 ,
576- string $ shareWith = null ,
577- string $ publicUpload = ' false ' ,
577+ ? string $ shareWith = null ,
578+ ? string $ publicUpload = null ,
578579 string $ password = '' ,
579580 string $ sendPasswordByTalk = null ,
580581 string $ expireDate = '' ,
@@ -583,16 +584,7 @@ public function createShare(
583584 string $ attributes = null
584585 ): DataResponse {
585586 $ share = $ this ->shareManager ->newShare ();
586-
587- if ($ permissions === null ) {
588- if ($ shareType === IShare::TYPE_LINK
589- || $ shareType === IShare::TYPE_EMAIL ) {
590- // to keep legacy default behaviour, we ignore the setting below for link shares
591- $ permissions = Constants::PERMISSION_READ ;
592- } else {
593- $ permissions = (int )$ this ->config ->getAppValue ('core ' , 'shareapi_default_permissions ' , (string )Constants::PERMISSION_ALL );
594- }
595- }
587+ $ hasPublicUpload = $ this ->getLegacyPublicUpload ($ publicUpload , $ permissions );
596588
597589 // Verify path
598590 if ($ path === null ) {
@@ -611,7 +603,7 @@ public function createShare(
611603 // combine all permissions to determine if the user can share this file
612604 $ nodes = $ userFolder ->getById ($ node ->getId ());
613605 foreach ($ nodes as $ nodeById ) {
614- /** @var FileInfo $fileInfo */
606+ /** @var \OC\Files\ FileInfo $fileInfo */
615607 $ fileInfo = $ node ->getFileInfo ();
616608 $ fileInfo ['permissions ' ] |= $ nodeById ->getPermissions ();
617609 }
@@ -624,17 +616,23 @@ public function createShare(
624616 throw new OCSNotFoundException ($ this ->l ->t ('Could not create share ' ));
625617 }
626618
627- if ($ permissions < 0 || $ permissions > Constants::PERMISSION_ALL ) {
628- throw new OCSNotFoundException ($ this ->l ->t ('Invalid permissions ' ));
619+ // Set permissions
620+ if ($ shareType === IShare::TYPE_LINK || $ shareType === IShare::TYPE_EMAIL ) {
621+ $ permissions = $ this ->getLinkSharePermissions ($ permissions , $ hasPublicUpload );
622+ $ this ->validateLinkSharePermissions ($ node , $ permissions , $ hasPublicUpload );
623+ } else {
624+ // Use default permissions only for non-link shares to keep legacy behavior
625+ if ($ permissions === null ) {
626+ $ permissions = (int )$ this ->config ->getAppValue ('core ' , 'shareapi_default_permissions ' , (string )Constants::PERMISSION_ALL );
627+ }
628+ // Non-link shares always require read permissions (link shares could be file drop)
629+ $ permissions |= Constants::PERMISSION_READ ;
629630 }
630631
631- // Shares always require read permissions
632- $ permissions |= Constants::PERMISSION_READ ;
633-
634- if ($ node instanceof \OCP \Files \File) {
635- // Single file shares should never have delete or create permissions
636- $ permissions &= ~Constants::PERMISSION_DELETE ;
637- $ permissions &= ~Constants::PERMISSION_CREATE ;
632+ // For legacy reasons the API allows to pass PERMISSIONS_ALL even for single file shares (I look at you Talk)
633+ if ($ node instanceof File) {
634+ // if this is a single file share we remove the DELETE and CREATE permissions
635+ $ permissions = $ permissions & ~(Constants::PERMISSION_DELETE | Constants::PERMISSION_CREATE );
638636 }
639637
640638 /**
@@ -678,28 +676,7 @@ public function createShare(
678676 throw new OCSNotFoundException ($ this ->l ->t ('Public link sharing is disabled by the administrator ' ));
679677 }
680678
681- if ($ publicUpload === 'true ' ) {
682- // Check if public upload is allowed
683- if (!$ this ->shareManager ->shareApiLinkAllowPublicUpload ()) {
684- throw new OCSForbiddenException ($ this ->l ->t ('Public upload disabled by the administrator ' ));
685- }
686-
687- // Public upload can only be set for folders
688- if ($ node instanceof \OCP \Files \File) {
689- throw new OCSNotFoundException ($ this ->l ->t ('Public upload is only possible for publicly shared folders ' ));
690- }
691-
692- $ permissions = Constants::PERMISSION_READ |
693- Constants::PERMISSION_CREATE |
694- Constants::PERMISSION_UPDATE |
695- Constants::PERMISSION_DELETE ;
696- }
697-
698- // TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
699- if ($ this ->shareManager ->outgoingServer2ServerSharesAllowed ()) {
700- $ permissions |= Constants::PERMISSION_SHARE ;
701- }
702-
679+ $ this ->validateLinkSharePermissions ($ node , $ permissions , $ hasPublicUpload );
703680 $ share ->setPermissions ($ permissions );
704681
705682 // Set password
@@ -980,6 +957,73 @@ public function getShares(
980957 return new DataResponse ($ shares );
981958 }
982959
960+ private function getLinkSharePermissions (?int $ permissions , ?bool $ legacyPublicUpload ): int {
961+ $ permissions = $ permissions ?? Constants::PERMISSION_READ ;
962+
963+ // Legacy option handling
964+ if ($ legacyPublicUpload !== null ) {
965+ $ permissions = $ legacyPublicUpload
966+ ? (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE )
967+ : Constants::PERMISSION_READ ;
968+ }
969+
970+ // TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
971+ if ($ this ->hasPermission ($ permissions , Constants::PERMISSION_READ )
972+ && $ this ->shareManager ->outgoingServer2ServerSharesAllowed ()) {
973+ $ permissions |= Constants::PERMISSION_SHARE ;
974+ }
975+
976+ return $ permissions ;
977+ }
978+
979+ /**
980+ * Helper to check for legacy "publicUpload" handling.
981+ * If the value is set to `true` or `false` then true or false are returned.
982+ * Otherwise null is returned to indicate that the option was not (or wrong) set.
983+ *
984+ * @param null|string $legacyPublicUpload The value of `publicUpload`
985+ */
986+ private function getLegacyPublicUpload (?string $ legacyPublicUpload , ?int $ permissions ): ?bool {
987+ if ($ legacyPublicUpload === 'true ' ) {
988+ return true ;
989+ } elseif ($ legacyPublicUpload === 'false ' ) {
990+ return false ;
991+ } elseif ($ legacyPublicUpload === null && $ permissions === null ) {
992+ return false ;
993+ }
994+ // Not set at all
995+ return null ;
996+ }
997+
998+ /**
999+ * For link and email shares validate that only allowed combinations are set.
1000+ *
1001+ * @throw OCSBadRequestException If permission combination is invalid.
1002+ * @throw OCSForbiddenException If public upload was forbidden by the administrator.
1003+ */
1004+ private function validateLinkSharePermissions (Node $ node , int $ permissions , ?bool $ legacyPublicUpload ): void {
1005+ if ($ legacyPublicUpload && ($ node instanceof File)) {
1006+ throw new OCSBadRequestException ($ this ->l ->t ('Public upload is only possible for publicly shared folders ' ));
1007+ }
1008+
1009+ // We need at least READ or CREATE (file drop)
1010+ if (!$ this ->hasPermission ($ permissions , Constants::PERMISSION_READ )
1011+ && !$ this ->hasPermission ($ permissions , Constants::PERMISSION_CREATE )) {
1012+ throw new OCSBadRequestException ($ this ->l ->t ('Share must at least have READ or CREATE permissions ' ));
1013+ }
1014+
1015+ // UPDATE and DELETE require a READ permission
1016+ if (!$ this ->hasPermission ($ permissions , Constants::PERMISSION_READ )
1017+ && ($ this ->hasPermission ($ permissions , Constants::PERMISSION_UPDATE ) || $ this ->hasPermission ($ permissions , Constants::PERMISSION_DELETE ))) {
1018+ throw new OCSBadRequestException ($ this ->l ->t ('Share must have READ permission if UPDATE or DELETE permission is set ' ));
1019+ }
1020+
1021+ // Check if public uploading was disabled
1022+ if ($ this ->hasPermission ($ permissions , Constants::PERMISSION_CREATE )
1023+ && !$ this ->shareManager ->shareApiLinkAllowPublicUpload ()) {
1024+ throw new OCSForbiddenException ($ this ->l ->t ('Public upload disabled by the administrator ' ));
1025+ }
1026+ }
9831027
9841028 /**
9851029 * @param string $viewer
@@ -1165,7 +1209,6 @@ private function hasPermission(int $permissionsSet, int $permissionsToCheck): bo
11651209 return ($ permissionsSet & $ permissionsToCheck ) === $ permissionsToCheck ;
11661210 }
11671211
1168-
11691212 /**
11701213 * @NoAdminRequired
11711214 *
@@ -1238,7 +1281,7 @@ public function updateShare(
12381281 $ this ->checkInheritedAttributes ($ share );
12391282
12401283 /**
1241- * expirationdate , password and publicUpload only make sense for link shares
1284+ * expiration date , password and publicUpload only make sense for link shares
12421285 */
12431286 if ($ share ->getShareType () === IShare::TYPE_LINK
12441287 || $ share ->getShareType () === IShare::TYPE_EMAIL ) {
@@ -1261,58 +1304,13 @@ public function updateShare(
12611304 $ share ->setHideDownload (false );
12621305 }
12631306
1264- $ newPermissions = null ;
1265- if ($ publicUpload === 'true ' ) {
1266- $ newPermissions = Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE ;
1267- } elseif ($ publicUpload === 'false ' ) {
1268- $ newPermissions = Constants::PERMISSION_READ ;
1269- }
1270-
1271- if ($ permissions !== null ) {
1272- $ newPermissions = $ permissions ;
1273- $ newPermissions = $ newPermissions & ~Constants::PERMISSION_SHARE ;
1274- }
1275-
1276- if ($ newPermissions !== null ) {
1277- if (!$ this ->hasPermission ($ newPermissions , Constants::PERMISSION_READ ) && !$ this ->hasPermission ($ newPermissions , Constants::PERMISSION_CREATE )) {
1278- throw new OCSBadRequestException ($ this ->l ->t ('Share must at least have READ or CREATE permissions ' ));
1279- }
1280-
1281- if (!$ this ->hasPermission ($ newPermissions , Constants::PERMISSION_READ ) && (
1282- $ this ->hasPermission ($ newPermissions , Constants::PERMISSION_UPDATE ) || $ this ->hasPermission ($ newPermissions , Constants::PERMISSION_DELETE )
1283- )) {
1284- throw new OCSBadRequestException ($ this ->l ->t ('Share must have READ permission if UPDATE or DELETE permission is set ' ));
1285- }
1286- }
1287-
1288- if (
1289- // legacy
1290- $ newPermissions === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE ) ||
1291- // correct
1292- $ newPermissions === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE )
1293- ) {
1294- if (!$ this ->shareManager ->shareApiLinkAllowPublicUpload ()) {
1295- throw new OCSForbiddenException ($ this ->l ->t ('Public upload disabled by the administrator ' ));
1296- }
1297-
1298- if (!($ share ->getNode () instanceof \OCP \Files \Folder)) {
1299- throw new OCSBadRequestException ($ this ->l ->t ('Public upload is only possible for publicly shared folders ' ));
1300- }
1301-
1302- // normalize to correct public upload permissions
1303- if ($ publicUpload === 'true ' ) {
1304- $ newPermissions = Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE ;
1305- }
1306- }
1307-
1308- if ($ newPermissions !== null ) {
1309- // TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
1310- if (($ newPermissions & Constants::PERMISSION_READ ) && $ this ->shareManager ->outgoingServer2ServerSharesAllowed ()) {
1311- $ newPermissions |= Constants::PERMISSION_SHARE ;
1312- }
1313-
1314- $ share ->setPermissions ($ newPermissions );
1315- $ permissions = $ newPermissions ;
1307+ // If either manual permissions are specified or publicUpload
1308+ // then we need to also update the permissions of the share
1309+ if ($ permissions !== null || $ publicUpload !== null ) {
1310+ $ hasPublicUpload = $ this ->getLegacyPublicUpload ($ publicUpload , $ permissions );
1311+ $ permissions = $ this ->getLinkSharePermissions ($ permissions ?? Constants::PERMISSION_READ , $ hasPublicUpload );
1312+ $ this ->validateLinkSharePermissions ($ share ->getNode (), $ permissions , $ hasPublicUpload );
1313+ $ share ->setPermissions ($ permissions );
13161314 }
13171315
13181316 if ($ password === '' ) {
0 commit comments