@@ -564,17 +564,37 @@ private function hideDisabledUserShares(): bool {
564564 * @return list<Share>
565565 */
566566 private function list (ShareAccessContext $ accessContext , ?string $ filterShareID , ?string $ filterSourceTypeClass , ?string $ filterSourceTypeValue , ?string $ lastShareID , ?int $ limit ): array {
567+ if ($ filterSourceTypeClass ) {
568+ $ filterSourceType = $ this ->registry ->getSourceTypes ()[$ filterSourceTypeClass ] ?? null ;
569+ if ($ filterSourceType === null ) {
570+ throw new RuntimeException ('The source type is not registered: ' . $ filterSourceTypeClass );
571+ }
572+ } else {
573+ $ filterSourceType = null ;
574+ }
575+
567576 /** @var array<class-string<IShareRecipientType>, list<string>> $recipientTypeValues */
568577 $ recipientTypeValues = [];
569578
570579 /** @var list<IQueryBuilder> $queries */
571580 $ queries = [];
572581 if ($ accessContext ->overrideChecks ) {
573582 $ queries [] = $ this ->connection ->getQueryBuilder ();
583+ $ userHasDirectAccess = false ;
574584 } else {
585+ if ($ filterSourceType && $ filterSourceTypeValue !== null && $ accessContext ->currentUser instanceof IUser) {
586+ $ userHasDirectAccess = $ filterSourceType ->userHasDirectSharingAccessToSource ($ accessContext ->currentUser , $ filterSourceTypeValue );
587+ } else {
588+ $ userHasDirectAccess = false ;
589+ }
590+
575591 if ($ accessContext ->currentUser instanceof IUser) {
576592 $ qb = $ this ->connection ->getQueryBuilder ();
577- $ qb ->where ($ qb ->expr ()->eq ('s.owner_user_id ' , $ qb ->createNamedParameter ($ accessContext ->currentUser ->getUID ())));
593+ // if the access user has "direct share access" we don't filter by owner, but instead validate that all share sources are accessible
594+ if (!$ userHasDirectAccess ) {
595+ $ qb ->where ($ qb ->expr ()->eq ('s.owner_user_id ' , $ qb ->createNamedParameter ($ accessContext ->currentUser ->getUID ())));
596+ }
597+
578598 $ queries [] = $ qb ;
579599 }
580600
@@ -586,7 +606,8 @@ private function list(ShareAccessContext $accessContext, ?string $filterShareID,
586606 }
587607
588608 // Do not add a query if no recipients matched, otherwise all shares will be returned.
589- if ($ recipientTypeValues !== []) {
609+ // If the user has "direct" access, we already get all the shares, so no need to run an extra query for recipients
610+ if ($ recipientTypeValues !== [] && !$ userHasDirectAccess ) {
590611 $ qb = $ this ->connection ->getQueryBuilder ();
591612 $ qb ->innerJoin ('s ' , 'sharing_share_recipients ' , 'sr ' , $ qb ->expr ()->andX (
592613 $ qb ->expr ()->eq ('s.state ' , $ qb ->createNamedParameter (ShareState::Active->value )),
@@ -636,7 +657,7 @@ private function list(ShareAccessContext $accessContext, ?string $filterShareID,
636657 $ qb ->andWhere ($ qb ->expr ()->eq ('s.id ' , $ qb ->createNamedParameter ($ filterShareID )));
637658 }
638659
639- if ($ filterSourceTypeClass !== null ) {
660+ if ($ filterSourceType !== null ) {
640661 $ sourceTypeFilters = [
641662 $ qb ->expr ()->eq ('s.id ' , 'ss.share_id ' ),
642663 $ qb ->expr ()->eq ('ss.source_class_id ' , $ qb ->createNamedParameter ($ this ->classMapper ->getClassId ($ filterSourceTypeClass ), IQueryBuilder::PARAM_INT )),
@@ -830,7 +851,9 @@ private function list(ShareAccessContext $accessContext, ?string $filterShareID,
830851 if ($ share ['owner ' ]->isCurrentUser ($ accessContext )) {
831852 continue ;
832853 }
833-
854+ if ($ userHasDirectAccess ) {
855+ continue ;
856+ }
834857 $ isAnyMatchingRecipient = false ;
835858 foreach ($ share ['recipients ' ] as &$ recipient ) {
836859 $ isMatchingRecipient = false ;
@@ -991,6 +1014,23 @@ private function list(ShareAccessContext $accessContext, ?string $filterShareID,
9911014 $ share ['permissions ' ],
9921015 ), $ shares );
9931016
1017+ // when listing shares for a source, we also return any non-owned share if the user has "direct" access to the source
1018+ // but we do need to validate that the user has "direct" access to *all* of the sources in the share, not just one
1019+ if (!$ accessContext ->overrideChecks && $ filterSourceType && $ filterSourceTypeValue !== null && $ accessContext ->currentUser instanceof IUser) {
1020+ $ shares = array_filter ($ shares , function (Share $ share ) use ($ accessContext ): bool {
1021+ if (!$ share ->owner ->isCurrentUser ($ accessContext ) && count ($ share ->sources ) > 1 ) {
1022+ foreach ($ share ->sources as $ source ) {
1023+ $ sourceType = $ this ->registry ->getSourceTypes ()[$ source ->class ];
1024+ if (!$ sourceType ->userHasDirectSharingAccessToSource ($ accessContext ->currentUser , $ source ->value )) {
1025+ return false ;
1026+ }
1027+ }
1028+ }
1029+
1030+ return true ;
1031+ });
1032+ }
1033+
9941034 if (!$ accessContext ->overrideChecks ) {
9951035 $ filterPropertyTypes = array_filter ($ registryPropertyTypes , static fn (ISharePropertyType $ propertyType ): bool => $ propertyType instanceof ISharePropertyTypeFilter);
9961036 if ($ filterPropertyTypes !== []) {
0 commit comments