|
27 | 27 | use OC\Files\Filesystem; |
28 | 28 | use OC\Files\View; |
29 | 29 | use OC\TagManager; |
| 30 | +use OC\User\NoUserException; |
30 | 31 | use OCA\Activity\BackgroundJob\RemoteActivity; |
31 | 32 | use OCA\Activity\Extension\Files; |
32 | 33 | use OCA\Activity\Extension\Files_Sharing; |
33 | 34 | use OCP\Activity\IManager; |
| 35 | +use OCP\AppFramework\QueryException; |
34 | 36 | use OCP\Files\Config\ICachedMountFileInfo; |
35 | 37 | use OCP\Files\Config\ICachedMountInfo; |
36 | 38 | use OCP\Files\Config\IUserMountCache; |
37 | 39 | use OCP\Files\IRootFolder; |
38 | 40 | use OCP\Files\Mount\IMountPoint; |
39 | 41 | use OCP\Files\Node; |
40 | 42 | use OCP\Files\NotFoundException; |
| 43 | +use OCP\Files\NotPermittedException; |
41 | 44 | use OCP\IConfig; |
42 | 45 | use OCP\IDBConnection; |
43 | 46 | use OCP\IGroup; |
@@ -234,14 +237,7 @@ protected function addNotificationsForFileAction($filePath, $activityType, $subj |
234 | 237 | } |
235 | 238 |
|
236 | 239 | if ($this->config->getSystemValueBool('activity_use_cached_mountpoints', false)) { |
237 | | - $mountsForFile = $this->userMountCache->getMountsForFileId($fileId); |
238 | | - $affectedUserIds = array_map(function (ICachedMountInfo $mount) { |
239 | | - return $mount->getUser()->getUID(); |
240 | | - }, $mountsForFile); |
241 | | - $affectedPaths = array_map(function (ICachedMountFileInfo $mount) { |
242 | | - return $this->getVisiblePath($mount->getPath()); |
243 | | - }, $mountsForFile); |
244 | | - $affectedUsers = array_combine($affectedUserIds, $affectedPaths); |
| 240 | + $affectedUsers = $this->getAffectedUsersFromCachedMounts($fileId); |
245 | 241 | } else { |
246 | 242 | $affectedUsers = $accessList['users']; |
247 | 243 | } |
@@ -1219,4 +1215,157 @@ protected function commitActivityTransaction(bool $shouldFlush): void { |
1219 | 1215 | } |
1220 | 1216 | $this->connection->commit(); |
1221 | 1217 | } |
| 1218 | + |
| 1219 | + |
| 1220 | + /** |
| 1221 | + * @param int $fileId |
| 1222 | + * |
| 1223 | + * @return array |
| 1224 | + */ |
| 1225 | + private function getAffectedUsersFromCachedMounts(int $fileId): array { |
| 1226 | + $affectedUsers = $cachedMounts = []; |
| 1227 | + $mountsForFile = $this->userMountCache->getMountsForFileId($fileId); |
| 1228 | + foreach ($mountsForFile as $mount) { |
| 1229 | + $affectedUsers[$mount->getUser()->getUID()] = $this->getVisiblePath($mount->getPath()); |
| 1230 | + $cachedMounts[] = [ |
| 1231 | + 'userId' => $mount->getUser()->getUID(), |
| 1232 | + 'provider' => str_replace('\\\\', '\\', $mount->getMountProvider()), |
| 1233 | + 'path' => $mount->getPath(), |
| 1234 | + 'visiblePath' => $this->getVisiblePath($mount->getPath()), |
| 1235 | + 'storageId' => $mount->getStorageId() |
| 1236 | + ]; |
| 1237 | + } |
| 1238 | + |
| 1239 | + $unrelatedUsers = $this->getUnrelatedUsers($fileId, $cachedMounts); |
| 1240 | + |
| 1241 | + return array_filter( |
| 1242 | + $affectedUsers, function ($userId) use ($unrelatedUsers) { |
| 1243 | + return !in_array($userId, $unrelatedUsers); |
| 1244 | + }, |
| 1245 | + ARRAY_FILTER_USE_KEY |
| 1246 | + ); |
| 1247 | + } |
| 1248 | + |
| 1249 | + |
| 1250 | + /** |
| 1251 | + * returns an array of users that have confirmed no access to fileId |
| 1252 | + * |
| 1253 | + * @param int $fileId |
| 1254 | + * @param array $cachedMounts |
| 1255 | + * |
| 1256 | + * @return string[] list of unrelated userIds |
| 1257 | + */ |
| 1258 | + private function getUnrelatedUsers(int $fileId, array $cachedMounts): array { |
| 1259 | + /** @var \OCA\GroupFolders\ACL\RuleManager $ruleManager */ |
| 1260 | + try { |
| 1261 | + $ruleManager = \OC::$server->get(\OCA\GroupFolders\ACL\RuleManager::class); |
| 1262 | + } catch (\Exception $e) { |
| 1263 | + return []; // if we have no access to RuleManager, we cannot filter unrelated users |
| 1264 | + } |
| 1265 | + |
| 1266 | + /** @var \OCA\GroupFolders\ACL\Rule[] $rules */ |
| 1267 | + $rules = $knownRules = []; |
| 1268 | + foreach ($cachedMounts as $cachedMount) { |
| 1269 | + // we are only interested in filtering GroupFolders ACL |
| 1270 | + if ($cachedMount['provider'] !== 'OCA\GroupFolders\Mount\MountProvider') { |
| 1271 | + continue; |
| 1272 | + } |
| 1273 | + |
| 1274 | + // caching rules based on storage id |
| 1275 | + $storageId = $cachedMount['storageId']; |
| 1276 | + if (!array_key_exists($storageId, $knownRules)) { |
| 1277 | + $knownRules[$storageId] = []; |
| 1278 | + } |
| 1279 | + |
| 1280 | + // caching rules based on storage+path to file |
| 1281 | + if (!array_key_exists($cachedMount['visiblePath'], $knownRules[$storageId])) { |
| 1282 | + $path = $cachedMount['path']; |
| 1283 | + |
| 1284 | + // we need mountPoint and folderId to generate the correct path |
| 1285 | + try { |
| 1286 | + $node = $this->rootFolder->get($path); |
| 1287 | + $mountPoint = $node->getMountPoint(); |
| 1288 | + $folderId = $mountPoint->getFolderId(); |
| 1289 | + $path = substr($path, strlen($mountPoint->getMountPoint())); |
| 1290 | + } catch (\Exception $e) { |
| 1291 | + // in case of issue during the process, we can imagine the user have no access to the file |
| 1292 | + $usersToCheck[] = $cachedMount['userId']; |
| 1293 | + continue; // we'll catch rules on next user with access to the file |
| 1294 | + } |
| 1295 | + |
| 1296 | + // we generate a list of path from top level of group folder to the file itself to get all rules |
| 1297 | + $paths = ['__groupfolders/' . $folderId]; |
| 1298 | + while ($path !== '') { |
| 1299 | + $paths[] = '__groupfolders/' . $folderId . '/' . $path; |
| 1300 | + $path = dirname($path); |
| 1301 | + if ($path === '.' || $path === '/') { |
| 1302 | + $path = ''; |
| 1303 | + } |
| 1304 | + } |
| 1305 | + |
| 1306 | + // we might already know the rules for some path of the list |
| 1307 | + $paths = array_filter($paths, function (string $path) use ($knownRules, $storageId): bool { |
| 1308 | + if (array_key_exists($path, $knownRules[$storageId])) { |
| 1309 | + return false; |
| 1310 | + } |
| 1311 | + |
| 1312 | + return true; |
| 1313 | + }); |
| 1314 | + |
| 1315 | + // we get and store the rules for each path from the list |
| 1316 | + $rulesPerPath = $ruleManager->getAllRulesForPaths($storageId, $paths); |
| 1317 | + foreach (array_keys($rulesPerPath) as $path) { |
| 1318 | + $rules = array_merge($rules, $rulesPerPath[$path]); |
| 1319 | + } |
| 1320 | + |
| 1321 | + $knownRules[$storageId][$cachedMount['visiblePath']] = true; |
| 1322 | + } |
| 1323 | + } |
| 1324 | + |
| 1325 | + // using each rules that disable read permission to generate a list of users |
| 1326 | + // that might not have access to fileId |
| 1327 | + foreach ($rules as $rule) { |
| 1328 | + if (($rule->getMask() & 1) === 0 |
| 1329 | + || ($rule->getPermissions() & 1) !== 0) { |
| 1330 | + continue; // not interested of rules with 'mask' not including read capability (1), or if 'permission' does |
| 1331 | + } |
| 1332 | + |
| 1333 | + $mapping = $rule->getUserMapping(); |
| 1334 | + $id = $mapping->getId(); |
| 1335 | + |
| 1336 | + // if mapping is about user |
| 1337 | + if ($mapping->getType() === 'user' && !in_array($id, $usersToCheck)) { |
| 1338 | + $usersToCheck[] = $id; |
| 1339 | + } |
| 1340 | + |
| 1341 | + // if mapping is about group |
| 1342 | + if ($mapping->getType() === 'group') { |
| 1343 | + $group = $this->groupManager->get($id); |
| 1344 | + if ($group === null) { |
| 1345 | + continue; |
| 1346 | + } |
| 1347 | + $userIds = array_map(function (IUser $user): string { |
| 1348 | + return $user->getUID(); |
| 1349 | + }, $group->getUsers()); |
| 1350 | + |
| 1351 | + // merge current user list with members of the group |
| 1352 | + $usersToCheck = array_values(array_unique(array_merge($usersToCheck, $userIds))); |
| 1353 | + } |
| 1354 | + } |
| 1355 | + |
| 1356 | + // now that we have a list of 'unstable' users, we confirm they have no access to the file |
| 1357 | + $filteredUsers = []; |
| 1358 | + foreach ($usersToCheck as $userId) { |
| 1359 | + try { |
| 1360 | + if (count($this->rootFolder->getUserFolder($userId)->getById($fileId)) > 0) { |
| 1361 | + continue; // looks like userId can access the file, checking next one |
| 1362 | + } |
| 1363 | + } catch (\Exception $e) { |
| 1364 | + } |
| 1365 | + |
| 1366 | + $filteredUsers[] = $userId; |
| 1367 | + } |
| 1368 | + |
| 1369 | + return $filteredUsers; |
| 1370 | + } |
1222 | 1371 | } |
0 commit comments