77
88namespace OCA \Tables \Service ;
99
10+ use OCA \Tables \AppInfo \Application ;
1011use OCA \Tables \Db \Column ;
1112use OCA \Tables \Db \ColumnMapper ;
1213use OCA \Tables \Db \Row2Mapper ;
14+ use OCA \Tables \Db \TableMapper ;
1315use OCA \Tables \Db \ViewMapper ;
1416use OCA \Tables \Errors \InternalError ;
1517use OCA \Tables \Errors \NotFoundError ;
@@ -20,11 +22,17 @@ class RelationService {
2022 /** @var array<string, array> Cache for relation data */
2123 private array $ cacheRelationData = [];
2224
25+ /** @var array<string, bool> Cache for manager accessibility decisions, keyed by host table + target */
26+ private array $ cacheManagerAccess = [];
27+
2328 public function __construct (
2429 private ColumnMapper $ columnMapper ,
2530 private ViewMapper $ viewMapper ,
31+ private TableMapper $ tableMapper ,
2632 private Row2Mapper $ row2Mapper ,
2733 private ColumnService $ columnService ,
34+ private PermissionsService $ permissionsService ,
35+ private ShareService $ shareService ,
2836 private ?string $ userId ,
2937 ) {
3038 }
@@ -83,9 +91,10 @@ private function getRelationsForColumns(array $relationColumns): array {
8391 foreach ($ groupedColumns as $ target => $ columns ) {
8492 $ relationData = $ this ->getRelationDataForTarget ($ target , $ columns [0 ]);
8593
86- // Assign the same data to all columns with this target
94+ // Assign the same data to all columns with this target, but only when
95+ // the relation is still backed by a manager of the hosting table.
8796 foreach ($ columns as $ column ) {
88- $ result [$ column ->getId ()] = $ relationData ;
97+ $ result [$ column ->getId ()] = $ this -> isTargetAccessibleByManager ( $ column ) ? $ relationData : [] ;
8998 }
9099 }
91100
@@ -133,11 +142,72 @@ public function getRelationData(Column $column): array {
133142 return [];
134143 }
135144
145+ if (!$ this ->isTargetAccessibleByManager ($ column )) {
146+ return [];
147+ }
148+
136149 $ target = sprintf ('%s_%s_%s ' , $ settings ['relationType ' ], $ settings ['targetId ' ], $ settings ['labelColumn ' ]);
137150
138151 return $ this ->getRelationDataForTarget ($ target , $ column );
139152 }
140153
154+ public function isTargetAccessibleByManager (Column $ relationColumn ): bool {
155+ $ settings = $ relationColumn ->getCustomSettingsArray ();
156+ $ relationType = $ settings [Column::RELATION_TYPE ] ?? null ;
157+ $ targetId = isset ($ settings [Column::RELATION_TARGET_ID ]) ? (int )$ settings [Column::RELATION_TARGET_ID ] : null ;
158+ if (empty ($ relationType ) || empty ($ targetId )) {
159+ return false ;
160+ }
161+
162+ $ hostTableId = $ relationColumn ->getTableId ();
163+ $ cacheKey = sprintf ('%s_%s_%s ' , $ hostTableId , $ relationType , $ targetId );
164+ if (isset ($ this ->cacheManagerAccess [$ cacheKey ])) {
165+ return $ this ->cacheManagerAccess [$ cacheKey ];
166+ }
167+
168+ $ candidateUserIds = [];
169+ try {
170+ $ hostTable = $ this ->tableMapper ->find ($ hostTableId );
171+ if ($ hostTable ->getOwnership () !== null && $ hostTable ->getOwnership () !== '' ) {
172+ $ candidateUserIds [] = $ hostTable ->getOwnership ();
173+ }
174+ } catch (DoesNotExistException |\OCP \AppFramework \Db \MultipleObjectsReturnedException |\OCP \DB \Exception $ e ) {
175+ // host table gone, so nothing to expose
176+ $ this ->cacheManagerAccess [$ cacheKey ] = false ;
177+ return false ;
178+ }
179+
180+ try {
181+ $ candidateUserIds = array_unique (array_merge (
182+ $ candidateUserIds ,
183+ $ this ->shareService ->findManagerUserIds ($ hostTableId , Application::NODE_TYPE_NAME_TABLE ),
184+ ));
185+ } catch (InternalError $ e ) {
186+ // fall back to the owner only
187+ }
188+
189+ $ accessible = false ;
190+ foreach ($ candidateUserIds as $ candidateUserId ) {
191+ if ($ candidateUserId === null || $ candidateUserId === '' ) {
192+ continue ;
193+ }
194+ if ($ relationType === Application::NODE_TYPE_NAME_VIEW ) {
195+ $ canRead = $ this ->permissionsService ->canReadColumnsByViewId ($ targetId , $ candidateUserId );
196+ } elseif ($ relationType === Application::NODE_TYPE_NAME_TABLE ) {
197+ $ canRead = $ this ->permissionsService ->canReadColumnsByTableId ($ targetId , $ candidateUserId );
198+ } else {
199+ $ canRead = false ;
200+ }
201+ if ($ canRead ) {
202+ $ accessible = true ;
203+ break ;
204+ }
205+ }
206+
207+ $ this ->cacheManagerAccess [$ cacheKey ] = $ accessible ;
208+ return $ accessible ;
209+ }
210+
141211 /**
142212 * Get relation data for a specific target
143213 *
@@ -159,7 +229,7 @@ private function getRelationDataForTarget(string $target, Column $column): array
159229 return [];
160230 }
161231
162- $ isView = $ settings [Column::RELATION_TYPE ] === ' view ' ;
232+ $ isView = $ settings [Column::RELATION_TYPE ] === Application:: NODE_TYPE_NAME_VIEW ;
163233 $ targetId = $ settings [Column::RELATION_TARGET_ID ] ?? null ;
164234
165235 try {
0 commit comments