Skip to content

Commit d78ae7d

Browse files
committed
apply ACL groupfolders
Signed-off-by: Maxence Lange <[email protected]>
1 parent 8564068 commit d78ae7d

File tree

1 file changed

+157
-8
lines changed

1 file changed

+157
-8
lines changed

lib/FilesHooks.php

Lines changed: 157 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,20 @@
2727
use OC\Files\Filesystem;
2828
use OC\Files\View;
2929
use OC\TagManager;
30+
use OC\User\NoUserException;
3031
use OCA\Activity\BackgroundJob\RemoteActivity;
3132
use OCA\Activity\Extension\Files;
3233
use OCA\Activity\Extension\Files_Sharing;
3334
use OCP\Activity\IManager;
35+
use OCP\AppFramework\QueryException;
3436
use OCP\Files\Config\ICachedMountFileInfo;
3537
use OCP\Files\Config\ICachedMountInfo;
3638
use OCP\Files\Config\IUserMountCache;
3739
use OCP\Files\IRootFolder;
3840
use OCP\Files\Mount\IMountPoint;
3941
use OCP\Files\Node;
4042
use OCP\Files\NotFoundException;
43+
use OCP\Files\NotPermittedException;
4144
use OCP\IConfig;
4245
use OCP\IDBConnection;
4346
use OCP\IGroup;
@@ -234,14 +237,7 @@ protected function addNotificationsForFileAction($filePath, $activityType, $subj
234237
}
235238

236239
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);
245241
} else {
246242
$affectedUsers = $accessList['users'];
247243
}
@@ -1219,4 +1215,157 @@ protected function commitActivityTransaction(bool $shouldFlush): void {
12191215
}
12201216
$this->connection->commit();
12211217
}
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+
}
12221371
}

0 commit comments

Comments
 (0)