'Unauthorized']); exit; } // --- helpers --- function loadPermsFor(string $u): array { try { if (function_exists('loadUserPermissions')) { $p = loadUserPermissions($u); return is_array($p) ? $p : []; } if (class_exists('userModel') && method_exists('userModel', 'getUserPermissions')) { $all = userModel::getUserPermissions(); if (is_array($all)) { if (isset($all[$u])) return (array)$all[$u]; $lk = strtolower($u); if (isset($all[$lk])) return (array)$all[$lk]; } } } catch (Throwable $e) {} return []; } function isOwnerOrAncestorOwner(string $user, array $perms, string $folder): bool { $f = ACL::normalizeFolder($folder); // direct owner if (ACL::isOwner($user, $perms, $f)) return true; // ancestor owner while ($f !== '' && strcasecmp($f, 'root') !== 0) { $pos = strrpos($f, '/'); if ($pos === false) break; $f = substr($f, 0, $pos); if ($f === '' || strcasecmp($f, 'root') === 0) break; if (ACL::isOwner($user, $perms, $f)) return true; } return false; } /** * folder-only scope: * - Admins: always in scope * - Non folder-only accounts: always in scope * - Folder-only accounts: in scope iff: * - folder == username OR subpath of username, OR * - user is owner of this folder (or any ancestor) */ function inUserFolderScope(string $folder, string $u, array $perms, bool $isAdmin): bool { if ($isAdmin) return true; //$folderOnly = !empty($perms['folderOnly']) || !empty($perms['userFolderOnly']) || !empty($perms['UserFolderOnly']); //if (!$folderOnly) return true; $f = ACL::normalizeFolder($folder); if ($f === 'root' || $f === '') { // folder-only users cannot act on root unless they own a subfolder (handled below) return isOwnerOrAncestorOwner($u, $perms, $f); } if ($f === $u || str_starts_with($f, $u . '/')) return true; // Treat ownership as in-scope return isOwnerOrAncestorOwner($u, $perms, $f); } // --- inputs --- $folder = isset($_GET['folder']) ? trim((string)$_GET['folder']) : 'root'; // validate folder path if ($folder !== 'root') { $parts = array_filter(explode('/', trim($folder, "/\\ "))); if (empty($parts)) { http_response_code(400); echo json_encode(['error' => 'Invalid folder name.']); exit; } foreach ($parts as $seg) { if (!preg_match(REGEX_FOLDER_NAME, $seg)) { http_response_code(400); echo json_encode(['error' => 'Invalid folder name.']); exit; } } $folder = implode('/', $parts); } // --- user + flags --- $perms = loadPermsFor($username); $isAdmin = ACL::isAdmin($perms); $readOnly = !empty($perms['readOnly']); $inScope = inUserFolderScope($folder, $username, $perms, $isAdmin); // --- ACL base abilities --- $canViewBase = $isAdmin || ACL::canRead($username, $perms, $folder); $canViewOwn = $isAdmin || ACL::canReadOwn($username, $perms, $folder); $canWriteBase = $isAdmin || ACL::canWrite($username, $perms, $folder); $canShareBase = $isAdmin || ACL::canShare($username, $perms, $folder); $canManageBase = $isAdmin || ACL::canManage($username, $perms, $folder); // granular base $gCreateBase = $isAdmin || ACL::canCreate($username, $perms, $folder); $gRenameBase = $isAdmin || ACL::canRename($username, $perms, $folder); $gDeleteBase = $isAdmin || ACL::canDelete($username, $perms, $folder); $gMoveBase = $isAdmin || ACL::canMove($username, $perms, $folder); $gUploadBase = $isAdmin || ACL::canUpload($username, $perms, $folder); $gEditBase = $isAdmin || ACL::canEdit($username, $perms, $folder); $gCopyBase = $isAdmin || ACL::canCopy($username, $perms, $folder); $gExtractBase = $isAdmin || ACL::canExtract($username, $perms, $folder); $gShareFile = $isAdmin || ACL::canShareFile($username, $perms, $folder); $gShareFolder = $isAdmin || ACL::canShareFolder($username, $perms, $folder); // --- Apply scope + flags to effective UI actions --- $canView = $canViewBase && $inScope; // keep scope for folder-only $canUpload = $gUploadBase && !$readOnly && $inScope; $canCreate = $canManageBase && !$readOnly && $inScope; // Create **folder** $canRename = $canManageBase && !$readOnly && $inScope; // Rename **folder** $canDelete = $gDeleteBase && !$readOnly && $inScope; // Destination can receive items if user can create/write (or manage) here $canReceive = ($gUploadBase || $gCreateBase || $canManageBase) && !$readOnly && $inScope; // Back-compat: expose as canMoveIn (used by toolbar/context-menu/drag&drop) $canMoveIn = $canReceive; $canMoveAlias = $canMoveIn; $canEdit = $gEditBase && !$readOnly && $inScope; $canCopy = $gCopyBase && !$readOnly && $inScope; $canExtract = $gExtractBase && !$readOnly && $inScope; // Sharing respects scope; optionally also gate on readOnly $canShare = $canShareBase && $inScope; // legacy umbrella $canShareFileEff = $gShareFile && $inScope; $canShareFoldEff = $gShareFolder && $inScope; // never allow destructive ops on root $isRoot = ($folder === 'root'); if ($isRoot) { $canRename = false; $canDelete = false; $canShareFoldEff = false; $canMoveFolder = false; } if (!$isRoot) { $canMoveFolder = (ACL::canManage($username, $perms, $folder) || ACL::isOwner($username, $perms, $folder)) && !$readOnly; } $owner = null; try { $owner = FolderModel::getOwnerFor($folder); } catch (Throwable $e) {} echo json_encode([ 'user' => $username, 'folder' => $folder, 'isAdmin' => $isAdmin, 'flags' => [ //'folderOnly' => !empty($perms['folderOnly']) || !empty($perms['userFolderOnly']) || !empty($perms['UserFolderOnly']), 'readOnly' => $readOnly, ], 'owner' => $owner, // viewing 'canView' => $canView, 'canViewOwn' => $canViewOwn, // write-ish 'canUpload' => $canUpload, 'canCreate' => $canCreate, 'canRename' => $canRename, 'canDelete' => $canDelete, 'canMoveIn' => $canMoveIn, 'canMove' => $canMoveAlias, 'canMoveFolder'=> $canMoveFolder, 'canEdit' => $canEdit, 'canCopy' => $canCopy, 'canExtract' => $canExtract, // sharing 'canShare' => $canShare, // legacy 'canShareFile' => $canShareFileEff, 'canShareFolder' => $canShareFoldEff, ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);