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 ;
@@ -515,20 +515,21 @@ public function deleteShare(string $id): DataResponse {
515515 /**
516516 * @NoAdminRequired
517517 *
518- * @param string $path
519- * @param int $permissions
520- * @param int $shareType
521- * @param string $shareWith
522- * @param string $publicUpload
523- * @param string $password
524- * @param string $sendPasswordByTalk
525- * @param string $expireDate
526- * @param string $label
527- * @param string $attributes
518+ * @param string|null $path Path of the share
519+ * @param int|null $permissions Permissions for the share
520+ * @param int $shareType Type of the share
521+ * @param string|null $shareWith The entity this should be shared with
522+ * @param 'true'|'false'|null $publicUpload If public uploading is allowed (deprecated)
523+ * @param string $password Password for the share
524+ * @param string|null $sendPasswordByTalk Send the password for the share over Talk
525+ * @param string|null $expireDate The expiry date of the share in the user's timezone at 00:00.
526+ * If $expireDate is not supplied or set to `null`, the system default will be used.
527+ * @param string $note Note for the share
528+ * @param string $label Label for the share (only used in link and email)
529+ * @param string|null $attributes Additional attributes for the share
528530 *
529531 * @return DataResponse
530- * @throws NotFoundException
531- * @throws OCSBadRequestException
532+ * @throws OCSBadRequestException Unknown share type
532533 * @throws OCSException
533534 * @throws OCSForbiddenException
534535 * @throws OCSNotFoundException
@@ -539,8 +540,8 @@ public function createShare(
539540 string $ path = null ,
540541 int $ permissions = null ,
541542 int $ shareType = -1 ,
542- string $ shareWith = null ,
543- string $ publicUpload = ' false ' ,
543+ ? string $ shareWith = null ,
544+ ? string $ publicUpload = null ,
544545 string $ password = '' ,
545546 string $ sendPasswordByTalk = null ,
546547 string $ expireDate = null ,
@@ -549,17 +550,7 @@ public function createShare(
549550 string $ attributes = null
550551 ): DataResponse {
551552 $ share = $ this ->shareManager ->newShare ();
552-
553- if ($ permissions === null ) {
554- if ($ shareType === IShare::TYPE_LINK
555- || $ shareType === IShare::TYPE_EMAIL ) {
556-
557- // to keep legacy default behaviour, we ignore the setting below for link shares
558- $ permissions = Constants::PERMISSION_READ ;
559- } else {
560- $ permissions = (int )$ this ->config ->getAppValue ('core ' , 'shareapi_default_permissions ' , (string )Constants::PERMISSION_ALL );
561- }
562- }
553+ $ hasPublicUpload = $ this ->getLegacyPublicUpload ($ publicUpload , $ permissions );
563554
564555 // Verify path
565556 if ($ path === null ) {
@@ -578,7 +569,7 @@ public function createShare(
578569 // combine all permissions to determine if the user can share this file
579570 $ nodes = $ userFolder ->getById ($ node ->getId ());
580571 foreach ($ nodes as $ nodeById ) {
581- /** @var FileInfo $fileInfo */
572+ /** @var \OC\Files\ FileInfo $fileInfo */
582573 $ fileInfo = $ node ->getFileInfo ();
583574 $ fileInfo ['permissions ' ] |= $ nodeById ->getPermissions ();
584575 }
@@ -591,17 +582,23 @@ public function createShare(
591582 throw new OCSNotFoundException ($ this ->l ->t ('Could not create share ' ));
592583 }
593584
594- if ($ permissions < 0 || $ permissions > Constants::PERMISSION_ALL ) {
595- throw new OCSNotFoundException ($ this ->l ->t ('Invalid permissions ' ));
585+ // Set permissions
586+ if ($ shareType === IShare::TYPE_LINK || $ shareType === IShare::TYPE_EMAIL ) {
587+ $ permissions = $ this ->getLinkSharePermissions ($ permissions , $ hasPublicUpload );
588+ $ this ->validateLinkSharePermissions ($ node , $ permissions , $ hasPublicUpload );
589+ } else {
590+ // Use default permissions only for non-link shares to keep legacy behavior
591+ if ($ permissions === null ) {
592+ $ permissions = (int )$ this ->config ->getAppValue ('core ' , 'shareapi_default_permissions ' , (string )Constants::PERMISSION_ALL );
593+ }
594+ // Non-link shares always require read permissions (link shares could be file drop)
595+ $ permissions |= Constants::PERMISSION_READ ;
596596 }
597597
598- // Shares always require read permissions
599- $ permissions |= Constants::PERMISSION_READ ;
600-
601- if ($ node instanceof \OCP \Files \File) {
602- // Single file shares should never have delete or create permissions
603- $ permissions &= ~Constants::PERMISSION_DELETE ;
604- $ permissions &= ~Constants::PERMISSION_CREATE ;
598+ // For legacy reasons the API allows to pass PERMISSIONS_ALL even for single file shares (I look at you Talk)
599+ if ($ node instanceof File) {
600+ // if this is a single file share we remove the DELETE and CREATE permissions
601+ $ permissions = $ permissions & ~(Constants::PERMISSION_DELETE | Constants::PERMISSION_CREATE );
605602 }
606603
607604 /**
@@ -661,28 +658,7 @@ public function createShare(
661658 throw new OCSNotFoundException ($ this ->l ->t ('Public link sharing is disabled by the administrator ' ));
662659 }
663660
664- if ($ publicUpload === 'true ' ) {
665- // Check if public upload is allowed
666- if (!$ this ->shareManager ->shareApiLinkAllowPublicUpload ()) {
667- throw new OCSForbiddenException ($ this ->l ->t ('Public upload disabled by the administrator ' ));
668- }
669-
670- // Public upload can only be set for folders
671- if ($ node instanceof \OCP \Files \File) {
672- throw new OCSNotFoundException ($ this ->l ->t ('Public upload is only possible for publicly shared folders ' ));
673- }
674-
675- $ permissions = Constants::PERMISSION_READ |
676- Constants::PERMISSION_CREATE |
677- Constants::PERMISSION_UPDATE |
678- Constants::PERMISSION_DELETE ;
679- }
680-
681- // TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
682- if ($ this ->shareManager ->outgoingServer2ServerSharesAllowed ()) {
683- $ permissions |= Constants::PERMISSION_SHARE ;
684- }
685-
661+ $ this ->validateLinkSharePermissions ($ node , $ permissions , $ hasPublicUpload );
686662 $ share ->setPermissions ($ permissions );
687663
688664 // Set password
@@ -936,6 +912,73 @@ public function getShares(
936912 return new DataResponse ($ shares );
937913 }
938914
915+ private function getLinkSharePermissions (?int $ permissions , ?bool $ legacyPublicUpload ): int {
916+ $ permissions = $ permissions ?? Constants::PERMISSION_READ ;
917+
918+ // Legacy option handling
919+ if ($ legacyPublicUpload !== null ) {
920+ $ permissions = $ legacyPublicUpload
921+ ? (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE )
922+ : Constants::PERMISSION_READ ;
923+ }
924+
925+ // TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
926+ if ($ this ->hasPermission ($ permissions , Constants::PERMISSION_READ )
927+ && $ this ->shareManager ->outgoingServer2ServerSharesAllowed ()) {
928+ $ permissions |= Constants::PERMISSION_SHARE ;
929+ }
930+
931+ return $ permissions ;
932+ }
933+
934+ /**
935+ * Helper to check for legacy "publicUpload" handling.
936+ * If the value is set to `true` or `false` then true or false are returned.
937+ * Otherwise null is returned to indicate that the option was not (or wrong) set.
938+ *
939+ * @param null|string $legacyPublicUpload The value of `publicUpload`
940+ */
941+ private function getLegacyPublicUpload (?string $ legacyPublicUpload , ?int $ permissions ): ?bool {
942+ if ($ legacyPublicUpload === 'true ' ) {
943+ return true ;
944+ } elseif ($ legacyPublicUpload === 'false ' ) {
945+ return false ;
946+ } elseif ($ legacyPublicUpload === null && $ permissions === null ) {
947+ return false ;
948+ }
949+ // Not set at all
950+ return null ;
951+ }
952+
953+ /**
954+ * For link and email shares validate that only allowed combinations are set.
955+ *
956+ * @throw OCSBadRequestException If permission combination is invalid.
957+ * @throw OCSForbiddenException If public upload was forbidden by the administrator.
958+ */
959+ private function validateLinkSharePermissions (Node $ node , int $ permissions , ?bool $ legacyPublicUpload ): void {
960+ if ($ legacyPublicUpload && ($ node instanceof File)) {
961+ throw new OCSBadRequestException ($ this ->l ->t ('Public upload is only possible for publicly shared folders ' ));
962+ }
963+
964+ // We need at least READ or CREATE (file drop)
965+ if (!$ this ->hasPermission ($ permissions , Constants::PERMISSION_READ )
966+ && !$ this ->hasPermission ($ permissions , Constants::PERMISSION_CREATE )) {
967+ throw new OCSBadRequestException ($ this ->l ->t ('Share must at least have READ or CREATE permissions ' ));
968+ }
969+
970+ // UPDATE and DELETE require a READ permission
971+ if (!$ this ->hasPermission ($ permissions , Constants::PERMISSION_READ )
972+ && ($ this ->hasPermission ($ permissions , Constants::PERMISSION_UPDATE ) || $ this ->hasPermission ($ permissions , Constants::PERMISSION_DELETE ))) {
973+ throw new OCSBadRequestException ($ this ->l ->t ('Share must have READ permission if UPDATE or DELETE permission is set ' ));
974+ }
975+
976+ // Check if public uploading was disabled
977+ if ($ this ->hasPermission ($ permissions , Constants::PERMISSION_CREATE )
978+ && !$ this ->shareManager ->shareApiLinkAllowPublicUpload ()) {
979+ throw new OCSForbiddenException ($ this ->l ->t ('Public upload disabled by the administrator ' ));
980+ }
981+ }
939982
940983 /**
941984 * @param string $viewer
@@ -1125,7 +1168,6 @@ private function hasPermission(int $permissionsSet, int $permissionsToCheck): bo
11251168 return ($ permissionsSet & $ permissionsToCheck ) === $ permissionsToCheck ;
11261169 }
11271170
1128-
11291171 /**
11301172 * @NoAdminRequired
11311173 *
@@ -1198,7 +1240,7 @@ public function updateShare(
11981240 $ this ->checkInheritedAttributes ($ share );
11991241
12001242 /**
1201- * expirationdate , password and publicUpload only make sense for link shares
1243+ * expiration date , password and publicUpload only make sense for link shares
12021244 */
12031245 if ($ share ->getShareType () === IShare::TYPE_LINK
12041246 || $ share ->getShareType () === IShare::TYPE_EMAIL ) {
@@ -1222,58 +1264,13 @@ public function updateShare(
12221264 $ share ->setHideDownload (false );
12231265 }
12241266
1225- $ newPermissions = null ;
1226- if ($ publicUpload === 'true ' ) {
1227- $ newPermissions = Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE ;
1228- } elseif ($ publicUpload === 'false ' ) {
1229- $ newPermissions = Constants::PERMISSION_READ ;
1230- }
1231-
1232- if ($ permissions !== null ) {
1233- $ newPermissions = $ permissions ;
1234- $ newPermissions = $ newPermissions & ~Constants::PERMISSION_SHARE ;
1235- }
1236-
1237- if ($ newPermissions !== null ) {
1238- if (!$ this ->hasPermission ($ newPermissions , Constants::PERMISSION_READ ) && !$ this ->hasPermission ($ newPermissions , Constants::PERMISSION_CREATE )) {
1239- throw new OCSBadRequestException ($ this ->l ->t ('Share must at least have READ or CREATE permissions ' ));
1240- }
1241-
1242- if (!$ this ->hasPermission ($ newPermissions , Constants::PERMISSION_READ ) && (
1243- $ this ->hasPermission ($ newPermissions , Constants::PERMISSION_UPDATE ) || $ this ->hasPermission ($ newPermissions , Constants::PERMISSION_DELETE )
1244- )) {
1245- throw new OCSBadRequestException ($ this ->l ->t ('Share must have READ permission if UPDATE or DELETE permission is set ' ));
1246- }
1247- }
1248-
1249- if (
1250- // legacy
1251- $ newPermissions === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE ) ||
1252- // correct
1253- $ newPermissions === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE )
1254- ) {
1255- if (!$ this ->shareManager ->shareApiLinkAllowPublicUpload ()) {
1256- throw new OCSForbiddenException ($ this ->l ->t ('Public upload disabled by the administrator ' ));
1257- }
1258-
1259- if (!($ share ->getNode () instanceof \OCP \Files \Folder)) {
1260- throw new OCSBadRequestException ($ this ->l ->t ('Public upload is only possible for publicly shared folders ' ));
1261- }
1262-
1263- // normalize to correct public upload permissions
1264- if ($ publicUpload === 'true ' ) {
1265- $ newPermissions = Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE ;
1266- }
1267- }
1268-
1269- if ($ newPermissions !== null ) {
1270- // TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
1271- if (($ newPermissions & Constants::PERMISSION_READ ) && $ this ->shareManager ->outgoingServer2ServerSharesAllowed ()) {
1272- $ newPermissions |= Constants::PERMISSION_SHARE ;
1273- }
1274-
1275- $ share ->setPermissions ($ newPermissions );
1276- $ permissions = $ newPermissions ;
1267+ // If either manual permissions are specified or publicUpload
1268+ // then we need to also update the permissions of the share
1269+ if ($ permissions !== null || $ publicUpload !== null ) {
1270+ $ hasPublicUpload = $ this ->getLegacyPublicUpload ($ publicUpload , $ permissions );
1271+ $ permissions = $ this ->getLinkSharePermissions ($ permissions ?? Constants::PERMISSION_READ , $ hasPublicUpload );
1272+ $ this ->validateLinkSharePermissions ($ share ->getNode (), $ permissions , $ hasPublicUpload );
1273+ $ share ->setPermissions ($ permissions );
12771274 }
12781275
12791276 if ($ password === '' ) {
0 commit comments