setName('groupfolders:permissions') ->setDescription('Configure advanced permissions for a configured Team folder') ->addArgument('folder_id', InputArgument::REQUIRED, 'Id of the folder to configure') ->addOption('enable', 'e', InputOption::VALUE_NONE, 'Enable advanced permissions for the folder') ->addOption('disable', 'd', InputOption::VALUE_NONE, 'Disable advanced permissions for the folder') ->addOption('manage-add', 'm', InputOption::VALUE_NONE, 'Add manage permission for user or group') ->addOption('manage-remove', 'r', InputOption::VALUE_NONE, 'Remove manage permission for user or group') ->addArgument('path', InputArgument::OPTIONAL, 'The path within the folder to set permissions for') ->addOption('user', 'u', InputOption::VALUE_REQUIRED, 'The user to configure the permissions for') ->addOption('group', 'g', InputOption::VALUE_REQUIRED, 'The group to configure the permissions for') ->addOption('team', 'c', InputOption::VALUE_REQUIRED, 'The circle/team to configure the permissions for') ->addOption('test', 't', InputOption::VALUE_NONE, 'Test the permissions for the set path') ->addArgument('permissions', InputArgument::IS_ARRAY + InputArgument::OPTIONAL, 'The permissions to set for the user or group as a white space separated list (ex: +read "-write"). Use "clear" to remove all permissions. Prepend the permission list with -- to allow parsing the - character.'); parent::configure(); } protected function execute(InputInterface $input, OutputInterface $output): int { $folder = $this->getFolder($input, $output); if ($folder === null) { return -1; } if ($input->getOption('enable')) { $this->folderManager->setFolderACL($folder->id, true); } elseif ($input->getOption('disable')) { $this->folderManager->setFolderACL($folder->id, false); } elseif ($input->getOption('test')) { if ($input->getOption('user') && ($input->getArgument('path'))) { $mappingId = $input->getOption('user'); $user = $this->userManager->get($mappingId); if (!$user) { $output->writeln('User not found: ' . $mappingId . ''); return -1; } $path = $input->getArgument('path'); $aclManager = $this->aclManagerFactory->getACLManager($user); if ($this->folderManager->getFolderPermissionsForUser($user, $folder->id) === 0) { $permissions = 0; } else { $permissions = $aclManager->getACLPermissionsForPath($folder->storageId, $folder->rootCacheEntry->getPath() . rtrim('/' . $path, '/')); } $permissionString = Rule::formatRulePermissions(Constants::PERMISSION_ALL, $permissions); $output->writeln($permissionString); return 0; } else { $output->writeln('--user and options needs to be set for permissions testing'); return -3; } } elseif (!$folder->acl) { $output->writeln('Advanced permissions not enabled for folder: ' . $folder->id . ''); return -2; } elseif ( !$input->getArgument('path') && !$input->getArgument('permissions') && !$input->getOption('user') && !$input->getOption('team') && !$input->getOption('group') ) { $this->printPermissions($input, $output, $folder); } elseif ($input->getOption('manage-add') && ($input->getOption('user') || $input->getOption('group') || $input->getOption('team'))) { [$mappingType, $mappingId] = $this->convertMappingOptions($input); $this->folderManager->setManageACL($folder->id, $mappingType, $mappingId, true); } elseif ($input->getOption('manage-remove') && ($input->getOption('user') || $input->getOption('group') || $input->getOption('team'))) { [$mappingType, $mappingId] = $this->convertMappingOptions($input); $this->folderManager->setManageACL($folder->id, $mappingType, $mappingId, false); } elseif (!$input->getArgument('path')) { $output->writeln(' argument has to be set when not using --enable or --disable'); return -3; } elseif (!$input->getArgument('permissions')) { $output->writeln(' argument has to be set when not using --enable or --disable'); return -3; } elseif ((int)(bool)$input->getOption('user') + (int)(bool)$input->getOption('group') + (int)(bool)$input->getOption('team') > 1) { $output->writeln('--user, --team and --group can not be used at the same time'); return -3; } elseif (!$input->getOption('user') && !$input->getOption('group') && !$input->getOption('team')) { $output->writeln('either --user, --group or --team has to be used when not using --enable or --disable'); return -3; } else { [$mappingType, $mappingId] = $this->convertMappingOptions($input); $path = $input->getArgument('path'); if (!is_string($path)) { $output->writeln(' argument has to be a string'); return -3; } $path = trim($path, '/'); $permissionStrings = $input->getArgument('permissions'); if (!is_array($permissionStrings)) { $output->writeln(' argument has to be an array'); return -3; } $mount = $this->mountProvider->getMount( FolderDefinitionWithPermissions::fromFolder($folder, $folder->rootCacheEntry, Constants::PERMISSION_ALL), '/dummy/files/' . $folder->mountPoint, ); $id = $mount->getStorage()->getCache()->getId($path); if ($id === -1) { $output->writeln('Path not found in folder: ' . $path . ''); return -1; } if ($permissionStrings === ['clear']) { $this->ruleManager->deleteRule(new Rule( new UserMapping($mappingType, $mappingId), $id, 0, 0 )); return 0; } foreach ($permissionStrings as $permission) { if (!is_string($permission)) { $output->writeln(' argument has to be an array of strings'); return -3; } if ($permission[0] !== '+' && $permission[0] !== '-') { $output->writeln('incorrect format for permissions "' . $permission . '"'); return -3; } $name = substr($permission, 1); if (!isset(Rule::PERMISSIONS_MAP[$name])) { $output->writeln('incorrect format for permissions2 "' . $permission . '"'); return -3; } } [$mask, $permissions] = $this->parsePermissions($permissionStrings); $this->ruleManager->saveRule(new Rule( new UserMapping($mappingType, $mappingId), $id, $mask, $permissions )); } return 0; } private function printPermissions(InputInterface $input, OutputInterface $output, FolderWithMappingsAndCache $folder): void { $rootPath = $folder->rootCacheEntry->getPath(); $rules = $this->ruleManager->getAllRulesForPrefix( $this->rootFolder->getMountPoint()->getNumericStorageId(), $rootPath ); $jailPathLength = strlen($rootPath) + 1; $outputFormat = $input->getOption('output'); switch ($outputFormat) { case parent::OUTPUT_FORMAT_JSON: case parent::OUTPUT_FORMAT_JSON_PRETTY: $paths = array_map(function (string $rawPath) use ($jailPathLength): string { $path = substr($rawPath, $jailPathLength); return $path ?: '/'; }, array_keys($rules)); $items = array_combine($paths, $rules); ksort($items); $output->writeln(json_encode($items, $outputFormat === parent::OUTPUT_FORMAT_JSON_PRETTY ? JSON_PRETTY_PRINT : 0)); break; default: $items = array_map(function (array $rulesForPath, string $path) use ($jailPathLength): array { /** @var Rule[] $rulesForPath */ $mappings = array_map( fn (Rule $rule): string => $rule->getUserMapping()->getType() . ': ' . $rule->getUserMapping()->getDisplayName() . ' (' . $rule->getUserMapping()->getId() . ')', $rulesForPath, ); $permissions = array_map(fn (Rule $rule): string => $rule->formatPermissions(), $rulesForPath); $formattedPath = substr($path, $jailPathLength); return [ 'path' => $formattedPath ?: '/', 'mappings' => implode("\n", $mappings), 'permissions' => implode("\n", $permissions), ]; }, $rules, array_keys($rules)); usort($items, fn (array $a, array $b): int => $a['path'] <=> $b['path']); $table = new Table($output); $table->setHeaders(['Path', 'User/Group', 'Permissions']); $table->setRows($items); $table->render(); break; } } private function convertMappingOptions(InputInterface $input): array { if ($input->getOption('user')) { return ['user', $input->getOption('user')]; } if ($input->getOption('group')) { return ['group', $input->getOption('group')]; } if ($input->getOption('team')) { return ['circle', $input->getOption('team')]; } throw new InvalidArgumentException('invalid mapping options'); } /** * @param list $permissions */ private function parsePermissions(array $permissions): array { $mask = 0; $result = 0; foreach ($permissions as $permission) { $permissionValue = Rule::PERMISSIONS_MAP[substr($permission, 1)]; $mask |= $permissionValue; if ($permission[0] === '+') { $result |= $permissionValue; } } return [$mask, $result]; } }