|
|
|
@ -18,10 +18,12 @@ use OCP\BackgroundJob\IJobList; |
|
|
|
use OCP\DB\Exception; |
|
|
|
use OCP\EventDispatcher\IEventDispatcher; |
|
|
|
use OCP\Files\AppData\IAppDataFactory; |
|
|
|
use OCP\Files\Config\IUserMountCache; |
|
|
|
use OCP\Files\File; |
|
|
|
use OCP\Files\GenericFileException; |
|
|
|
use OCP\Files\IAppData; |
|
|
|
use OCP\Files\IRootFolder; |
|
|
|
use OCP\Files\Node; |
|
|
|
use OCP\Files\NotPermittedException; |
|
|
|
use OCP\Files\SimpleFS\ISimpleFile; |
|
|
|
use OCP\IL10N; |
|
|
|
@ -77,7 +79,7 @@ class Manager implements IManager { |
|
|
|
private \OCP\TextProcessing\IManager $textProcessingManager, |
|
|
|
private \OCP\TextToImage\IManager $textToImageManager, |
|
|
|
private \OCP\SpeechToText\ISpeechToTextManager $speechToTextManager, |
|
|
|
private \OCP\Share\IManager $shareManager, |
|
|
|
private IUserMountCache $userMountCache, |
|
|
|
) { |
|
|
|
$this->appData = $appDataFactory->get('core'); |
|
|
|
} |
|
|
|
@ -561,19 +563,8 @@ class Manager implements IManager { |
|
|
|
} |
|
|
|
} |
|
|
|
foreach ($ids as $fileId) { |
|
|
|
$node = $this->rootFolder->getFirstNodeById($fileId); |
|
|
|
if ($node === null) { |
|
|
|
$node = $this->rootFolder->getFirstNodeByIdInPath($fileId, '/' . $this->rootFolder->getAppDataDirectoryName() . '/'); |
|
|
|
if ($node === null) { |
|
|
|
throw new ValidationException('Could not find file ' . $fileId); |
|
|
|
} |
|
|
|
} |
|
|
|
/** @var array{users:array<string,array{node_id:int, node_path: string}>, remote: array<string,array{node_id:int, node_path: string}>, mail: array<string,array{node_id:int, node_path: string}>} $accessList */ |
|
|
|
$accessList = $this->shareManager->getAccessList($node, true, true); |
|
|
|
$userIds = array_map(fn ($id) => strval($id), array_keys($accessList['users'])); |
|
|
|
if (!in_array($task->getUserId(), $userIds)) { |
|
|
|
throw new UnauthorizedException('User ' . $task->getUserId() . ' does not have access to file ' . $fileId); |
|
|
|
} |
|
|
|
$this->validateFileId($fileId); |
|
|
|
$this->validateUserAccessToFile($fileId, $task->getUserId()); |
|
|
|
} |
|
|
|
// remove superfluous keys and set input
|
|
|
|
$task->setInput($this->removeSuperfluousArrayKeys($task->getInput(), $inputShape, $optionalInputShape)); |
|
|
|
@ -643,7 +634,7 @@ class Manager implements IManager { |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
public function setTaskResult(int $id, ?string $error, ?array $result): void { |
|
|
|
public function setTaskResult(int $id, ?string $error, ?array $result, bool $isUsingFileIds = false): void { |
|
|
|
// TODO: Not sure if we should rather catch the exceptions of getTask here and fail silently
|
|
|
|
$task = $this->getTask($id); |
|
|
|
if ($task->getStatus() === Task::STATUS_CANCELLED) { |
|
|
|
@ -664,7 +655,11 @@ class Manager implements IManager { |
|
|
|
$this->validateOutput($optionalOutputShape, $result, true); |
|
|
|
$output = $this->removeSuperfluousArrayKeys($result, $outputShape, $optionalOutputShape); |
|
|
|
// extract raw data and put it in files, replace it with file ids
|
|
|
|
$output = $this->encapsulateOutputFileData($output, $outputShape, $optionalOutputShape); |
|
|
|
if (!$isUsingFileIds) { |
|
|
|
$output = $this->encapsulateOutputFileData($output, $outputShape, $optionalOutputShape); |
|
|
|
} else { |
|
|
|
$output = $this->validateOutputFileIds($output, $outputShape, $optionalOutputShape); |
|
|
|
} |
|
|
|
$task->setOutput($output); |
|
|
|
$task->setProgress(1); |
|
|
|
$task->setStatus(Task::STATUS_SUCCESSFUL); |
|
|
|
@ -711,16 +706,13 @@ class Manager implements IManager { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Takes task input or output data and replaces fileIds with base64 data |
|
|
|
* Takes task input data and replaces fileIds with File objects |
|
|
|
* |
|
|
|
* @param string|null $userId |
|
|
|
* @param array<array-key, list<numeric|string>|numeric|string> $input |
|
|
|
* @param ShapeDescriptor[] ...$specs the specs |
|
|
|
* @return array<array-key, list<File|numeric|string>|numeric|string|File> |
|
|
|
* @throws GenericFileException |
|
|
|
* @throws LockedException |
|
|
|
* @throws NotPermittedException |
|
|
|
* @throws ValidationException |
|
|
|
* @throws GenericFileException|LockedException|NotPermittedException|ValidationException|UnauthorizedException |
|
|
|
*/ |
|
|
|
public function fillInputFileData(?string $userId, array $input, ...$specs): array { |
|
|
|
if ($userId !== null) { |
|
|
|
@ -738,30 +730,14 @@ class Manager implements IManager { |
|
|
|
continue; |
|
|
|
} |
|
|
|
if ($type->value < 10) { |
|
|
|
$node = $this->rootFolder->getFirstNodeById((int)$input[$key]); |
|
|
|
if ($node === null) { |
|
|
|
$node = $this->rootFolder->getFirstNodeByIdInPath((int)$input[$key], '/' . $this->rootFolder->getAppDataDirectoryName() . '/'); |
|
|
|
if (!$node instanceof File) { |
|
|
|
throw new ValidationException('File id given for key "' . $key . '" is not a file'); |
|
|
|
} |
|
|
|
} elseif (!$node instanceof File) { |
|
|
|
throw new ValidationException('File id given for key "' . $key . '" is not a file'); |
|
|
|
} |
|
|
|
// TODO: Validate if userId has access to this file
|
|
|
|
$node = $this->validateFileId((int)$input[$key]); |
|
|
|
$this->validateUserAccessToFile($input[$key], $userId); |
|
|
|
$newInputOutput[$key] = $node; |
|
|
|
} else { |
|
|
|
$newInputOutput[$key] = []; |
|
|
|
foreach ($input[$key] as $item) { |
|
|
|
$node = $this->rootFolder->getFirstNodeById((int)$item); |
|
|
|
if ($node === null) { |
|
|
|
$node = $this->rootFolder->getFirstNodeByIdInPath((int)$item, '/' . $this->rootFolder->getAppDataDirectoryName() . '/'); |
|
|
|
if (!$node instanceof File) { |
|
|
|
throw new ValidationException('File id given for key "' . $key . '" is not a file'); |
|
|
|
} |
|
|
|
} elseif (!$node instanceof File) { |
|
|
|
throw new ValidationException('File id given for key "' . $key . '" is not a file'); |
|
|
|
} |
|
|
|
// TODO: Validate if userId has access to this file
|
|
|
|
$node = $this->validateFileId((int)$item); |
|
|
|
$this->validateUserAccessToFile($item, $userId); |
|
|
|
$newInputOutput[$key][] = $node; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -851,7 +827,7 @@ class Manager implements IManager { |
|
|
|
* @throws GenericFileException |
|
|
|
* @throws LockedException |
|
|
|
* @throws NotPermittedException |
|
|
|
* @throws ValidationException |
|
|
|
* @throws ValidationException|UnauthorizedException |
|
|
|
*/ |
|
|
|
public function prepareInputData(Task $task): array { |
|
|
|
$taskTypes = $this->getAvailableTaskTypes(); |
|
|
|
@ -884,4 +860,73 @@ class Manager implements IManager { |
|
|
|
$taskEntity = \OC\TaskProcessing\Db\Task::fromPublicTask($task); |
|
|
|
$this->taskMapper->update($taskEntity); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @param array $output |
|
|
|
* @param ShapeDescriptor[] ...$specs the specs that define which keys to keep |
|
|
|
* @return array |
|
|
|
* @throws NotPermittedException |
|
|
|
*/ |
|
|
|
private function validateOutputFileIds(array $output, ...$specs): array { |
|
|
|
$newOutput = []; |
|
|
|
$spec = array_reduce($specs, fn ($carry, $spec) => $carry + $spec, []); |
|
|
|
foreach($spec as $key => $descriptor) { |
|
|
|
$type = $descriptor->getShapeType(); |
|
|
|
if (!isset($output[$key])) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
if (!in_array(EShapeType::getScalarType($type), [EShapeType::Image, EShapeType::Audio, EShapeType::Video, EShapeType::File], true)) { |
|
|
|
$newOutput[$key] = $output[$key]; |
|
|
|
continue; |
|
|
|
} |
|
|
|
if ($type->value < 10) { |
|
|
|
// Is scalar file ID
|
|
|
|
$newOutput[$key] = $this->validateFileId($output[$key]); |
|
|
|
} else { |
|
|
|
// Is list of file IDs
|
|
|
|
$newOutput = []; |
|
|
|
foreach ($output[$key] as $item) { |
|
|
|
$newOutput[$key][] = $this->validateFileId($item); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return $newOutput; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @param mixed $id |
|
|
|
* @return Node |
|
|
|
* @throws ValidationException |
|
|
|
*/ |
|
|
|
private function validateFileId(mixed $id): Node { |
|
|
|
$node = $this->rootFolder->getFirstNodeById($id); |
|
|
|
if ($node === null) { |
|
|
|
$node = $this->rootFolder->getFirstNodeByIdInPath($id, '/' . $this->rootFolder->getAppDataDirectoryName() . '/'); |
|
|
|
if ($node === null) { |
|
|
|
throw new ValidationException('Could not find file ' . $id); |
|
|
|
} elseif (!$node instanceof File) { |
|
|
|
throw new ValidationException('File with id "' . $id . '" is not a file'); |
|
|
|
} |
|
|
|
} elseif (!$node instanceof File) { |
|
|
|
throw new ValidationException('File with id "' . $id . '" is not a file'); |
|
|
|
} |
|
|
|
return $node; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @param mixed $fileId |
|
|
|
* @param string $userId |
|
|
|
* @return void |
|
|
|
* @throws UnauthorizedException |
|
|
|
*/ |
|
|
|
private function validateUserAccessToFile(mixed $fileId, ?string $userId): void { |
|
|
|
if ($userId === null) { |
|
|
|
throw new UnauthorizedException('User does not have access to file ' . $fileId); |
|
|
|
} |
|
|
|
$mounts = $this->userMountCache->getMountsForFileId($fileId); |
|
|
|
$userIds = array_map(fn ($mount) => $mount->getUser()->getUID(), $mounts); |
|
|
|
if (!in_array($userId, $userIds)) { |
|
|
|
throw new UnauthorizedException('User ' . $userId . ' does not have access to file ' . $fileId); |
|
|
|
} |
|
|
|
} |
|
|
|
} |