diff options
Diffstat (limited to 'core/Controller')
-rw-r--r-- | core/Controller/AppPasswordController.php | 2 | ||||
-rw-r--r-- | core/Controller/AvatarController.php | 4 | ||||
-rw-r--r-- | core/Controller/CSRFTokenController.php | 2 | ||||
-rw-r--r-- | core/Controller/ClientFlowLoginController.php | 9 | ||||
-rw-r--r-- | core/Controller/ClientFlowLoginV2Controller.php | 4 | ||||
-rw-r--r-- | core/Controller/ContactsMenuController.php | 1 | ||||
-rw-r--r-- | core/Controller/GuestAvatarController.php | 1 | ||||
-rw-r--r-- | core/Controller/LoginController.php | 4 | ||||
-rw-r--r-- | core/Controller/LostController.php | 4 | ||||
-rw-r--r-- | core/Controller/NavigationController.php | 1 | ||||
-rw-r--r-- | core/Controller/OCJSController.php | 1 | ||||
-rw-r--r-- | core/Controller/OCSController.php | 1 | ||||
-rw-r--r-- | core/Controller/PreviewController.php | 7 | ||||
-rw-r--r-- | core/Controller/TaskProcessingApiController.php | 86 | ||||
-rw-r--r-- | core/Controller/TwoFactorChallengeController.php | 5 | ||||
-rw-r--r-- | core/Controller/WalledGardenController.php | 1 | ||||
-rw-r--r-- | core/Controller/WhatsNewController.php | 1 |
17 files changed, 100 insertions, 34 deletions
diff --git a/core/Controller/AppPasswordController.php b/core/Controller/AppPasswordController.php index 41a45926ba7..e5edc165bf5 100644 --- a/core/Controller/AppPasswordController.php +++ b/core/Controller/AppPasswordController.php @@ -77,7 +77,7 @@ class AppPasswordController extends OCSController { $password = null; } - $userAgent = $this->request->getHeader('USER_AGENT'); + $userAgent = $this->request->getHeader('user-agent'); $token = $this->random->generate(72, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS); diff --git a/core/Controller/AvatarController.php b/core/Controller/AvatarController.php index 4e7a2f8714a..b577b2fd460 100644 --- a/core/Controller/AvatarController.php +++ b/core/Controller/AvatarController.php @@ -193,8 +193,8 @@ class AvatarController extends Controller { } } elseif (!is_null($files)) { if ( - $files['error'][0] === 0 && - is_uploaded_file($files['tmp_name'][0]) + $files['error'][0] === 0 + && is_uploaded_file($files['tmp_name'][0]) ) { if ($files['size'][0] > 20 * 1024 * 1024) { return new JSONResponse( diff --git a/core/Controller/CSRFTokenController.php b/core/Controller/CSRFTokenController.php index 4fdd669e144..edf7c26e94c 100644 --- a/core/Controller/CSRFTokenController.php +++ b/core/Controller/CSRFTokenController.php @@ -34,6 +34,8 @@ class CSRFTokenController extends Controller { * * 200: CSRF token returned * 403: Strict cookie check failed + * + * @NoTwoFactorRequired */ #[PublicPage] #[NoCSRFRequired] diff --git a/core/Controller/ClientFlowLoginController.php b/core/Controller/ClientFlowLoginController.php index affb60f2b2e..4464af890c4 100644 --- a/core/Controller/ClientFlowLoginController.php +++ b/core/Controller/ClientFlowLoginController.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later @@ -17,6 +18,7 @@ use OCP\AppFramework\Http\Attribute\FrontpageRoute; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\Attribute\OpenAPI; +use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired; use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\Attribute\UseSession; use OCP\AppFramework\Http\ContentSecurityPolicy; @@ -64,7 +66,7 @@ class ClientFlowLoginController extends Controller { } private function getClientName(): string { - $userAgent = $this->request->getHeader('USER_AGENT'); + $userAgent = $this->request->getHeader('user-agent'); return $userAgent !== '' ? $userAgent : 'unknown'; } @@ -108,8 +110,8 @@ class ClientFlowLoginController extends Controller { $this->appName, 'error', [ - 'errors' => - [ + 'errors' + => [ [ 'error' => 'Access Forbidden', 'hint' => 'Invalid request', @@ -214,6 +216,7 @@ class ClientFlowLoginController extends Controller { #[NoAdminRequired] #[UseSession] + #[PasswordConfirmationRequired(strict: false)] #[FrontpageRoute(verb: 'POST', url: '/login/flow')] public function generateAppPassword( string $stateToken, diff --git a/core/Controller/ClientFlowLoginV2Controller.php b/core/Controller/ClientFlowLoginV2Controller.php index e21a0cb250d..8c0c1e8179d 100644 --- a/core/Controller/ClientFlowLoginV2Controller.php +++ b/core/Controller/ClientFlowLoginV2Controller.php @@ -19,6 +19,7 @@ use OCP\AppFramework\Http\Attribute\FrontpageRoute; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\Attribute\OpenAPI; +use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired; use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\Attribute\UseSession; use OCP\AppFramework\Http\JSONResponse; @@ -228,6 +229,7 @@ class ClientFlowLoginV2Controller extends Controller { #[NoAdminRequired] #[UseSession] + #[PasswordConfirmationRequired(strict: false)] #[FrontpageRoute(verb: 'POST', url: '/login/v2/grant')] public function generateAppPassword(?string $stateToken): Response { if ($stateToken === null) { @@ -291,7 +293,7 @@ class ClientFlowLoginV2Controller extends Controller { #[OpenAPI(scope: OpenAPI::SCOPE_DEFAULT)] public function init(): JSONResponse { // Get client user agent - $userAgent = $this->request->getHeader('USER_AGENT'); + $userAgent = $this->request->getHeader('user-agent'); $tokens = $this->loginFlowV2Service->createTokens($userAgent); diff --git a/core/Controller/ContactsMenuController.php b/core/Controller/ContactsMenuController.php index f4ded1ed42b..d90ee8a1c61 100644 --- a/core/Controller/ContactsMenuController.php +++ b/core/Controller/ContactsMenuController.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/core/Controller/GuestAvatarController.php b/core/Controller/GuestAvatarController.php index 818b25a0c80..711158e0708 100644 --- a/core/Controller/GuestAvatarController.php +++ b/core/Controller/GuestAvatarController.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/core/Controller/LoginController.php b/core/Controller/LoginController.php index 7e8afd9f083..5a21d27898f 100644 --- a/core/Controller/LoginController.php +++ b/core/Controller/LoginController.php @@ -93,8 +93,8 @@ class LoginController extends Controller { $this->session->close(); if ( - $this->request->getServerProtocol() === 'https' && - !$this->request->isUserAgent([Request::USER_AGENT_CHROME, Request::USER_AGENT_ANDROID_MOBILE_CHROME]) + $this->request->getServerProtocol() === 'https' + && !$this->request->isUserAgent([Request::USER_AGENT_CHROME, Request::USER_AGENT_ANDROID_MOBILE_CHROME]) ) { $response->addHeader('Clear-Site-Data', '"cache", "storage"'); } diff --git a/core/Controller/LostController.php b/core/Controller/LostController.php index f940a3cfeee..d956f3427f2 100644 --- a/core/Controller/LostController.php +++ b/core/Controller/LostController.php @@ -64,7 +64,7 @@ class LostController extends Controller { private Defaults $defaults, private IL10N $l10n, private IConfig $config, - protected string $from, + protected string $defaultMailAddress, private IManager $encryptionManager, private IMailer $mailer, private LoggerInterface $logger, @@ -281,7 +281,7 @@ class LostController extends Controller { try { $message = $this->mailer->createMessage(); $message->setTo([$email => $user->getDisplayName()]); - $message->setFrom([$this->from => $this->defaults->getName()]); + $message->setFrom([$this->defaultMailAddress => $this->defaults->getName()]); $message->useTemplate($emailTemplate); $this->mailer->send($message); } catch (Exception $e) { diff --git a/core/Controller/NavigationController.php b/core/Controller/NavigationController.php index 5fc929b4eb4..017061ef979 100644 --- a/core/Controller/NavigationController.php +++ b/core/Controller/NavigationController.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/core/Controller/OCJSController.php b/core/Controller/OCJSController.php index 176558b013d..ea372b43b2e 100644 --- a/core/Controller/OCJSController.php +++ b/core/Controller/OCJSController.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/core/Controller/OCSController.php b/core/Controller/OCSController.php index b05ddd0e298..fb0280479c4 100644 --- a/core/Controller/OCSController.php +++ b/core/Controller/OCSController.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/core/Controller/PreviewController.php b/core/Controller/PreviewController.php index ea90be31078..aac49c06d57 100644 --- a/core/Controller/PreviewController.php +++ b/core/Controller/PreviewController.php @@ -152,15 +152,12 @@ class PreviewController extends Controller { // Is this header is set it means our UI is doing a preview for no-download shares // we check a header so we at least prevent people from using the link directly (obfuscation) - $isNextcloudPreview = $this->request->getHeader('X-NC-Preview') === 'true'; + $isNextcloudPreview = $this->request->getHeader('x-nc-preview') === 'true'; $storage = $node->getStorage(); if ($isNextcloudPreview === false && $storage->instanceOfStorage(ISharedStorage::class)) { /** @var ISharedStorage $storage */ $share = $storage->getShare(); - $attributes = $share->getAttributes(); - // No "allow preview" header set, so we must check if - // the share has not explicitly disabled download permissions - if ($attributes?->getAttribute('permissions', 'download') === false) { + if (!$share->canSeeContent()) { return new DataResponse([], Http::STATUS_FORBIDDEN); } } diff --git a/core/Controller/TaskProcessingApiController.php b/core/Controller/TaskProcessingApiController.php index cf62b4f6b6b..90a0e9ba14a 100644 --- a/core/Controller/TaskProcessingApiController.php +++ b/core/Controller/TaskProcessingApiController.php @@ -20,12 +20,12 @@ use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\Attribute\UserRateLimit; -use OCP\AppFramework\Http\DataDownloadResponse; use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\Http\StreamResponse; use OCP\AppFramework\OCSController; use OCP\Files\File; -use OCP\Files\GenericFileException; use OCP\Files\IAppData; +use OCP\Files\IMimeTypeDetector; use OCP\Files\IRootFolder; use OCP\Files\NotPermittedException; use OCP\IL10N; @@ -56,6 +56,7 @@ class TaskProcessingApiController extends OCSController { private ?string $userId, private IRootFolder $rootFolder, private IAppData $appData, + private IMimeTypeDetector $mimeTypeDetector, ) { parent::__construct($appName, $request); } @@ -302,7 +303,7 @@ class TaskProcessingApiController extends OCSController { * * @param int $taskId The id of the task * @param int $fileId The file id of the file to retrieve - * @return DataDownloadResponse<Http::STATUS_OK, string, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}> + * @return StreamResponse<Http::STATUS_OK, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}> * * 200: File content returned * 404: Task or file not found @@ -310,12 +311,14 @@ class TaskProcessingApiController extends OCSController { #[NoAdminRequired] #[NoCSRFRequired] #[ApiRoute(verb: 'GET', url: '/tasks/{taskId}/file/{fileId}', root: '/taskprocessing')] - public function getFileContents(int $taskId, int $fileId): DataDownloadResponse|DataResponse { + public function getFileContents(int $taskId, int $fileId): StreamResponse|DataResponse { try { $task = $this->taskProcessingManager->getUserTask($taskId, $this->userId); return $this->getFileContentsInternal($task, $fileId); } catch (NotFoundException) { return new DataResponse(['message' => $this->l->t('Not found')], Http::STATUS_NOT_FOUND); + } catch (LockedException) { + return new DataResponse(['message' => $this->l->t('Node is locked')], Http::STATUS_INTERNAL_SERVER_ERROR); } catch (Exception) { return new DataResponse(['message' => $this->l->t('Internal error')], Http::STATUS_INTERNAL_SERVER_ERROR); } @@ -326,19 +329,21 @@ class TaskProcessingApiController extends OCSController { * * @param int $taskId The id of the task * @param int $fileId The file id of the file to retrieve - * @return DataDownloadResponse<Http::STATUS_OK, string, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}> + * @return StreamResponse<Http::STATUS_OK, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}> * * 200: File content returned * 404: Task or file not found */ #[ExAppRequired] #[ApiRoute(verb: 'GET', url: '/tasks_provider/{taskId}/file/{fileId}', root: '/taskprocessing')] - public function getFileContentsExApp(int $taskId, int $fileId): DataDownloadResponse|DataResponse { + public function getFileContentsExApp(int $taskId, int $fileId): StreamResponse|DataResponse { try { $task = $this->taskProcessingManager->getTask($taskId); return $this->getFileContentsInternal($task, $fileId); } catch (NotFoundException) { return new DataResponse(['message' => $this->l->t('Not found')], Http::STATUS_NOT_FOUND); + } catch (LockedException) { + return new DataResponse(['message' => $this->l->t('Node is locked')], Http::STATUS_INTERNAL_SERVER_ERROR); } catch (Exception) { return new DataResponse(['message' => $this->l->t('Internal error')], Http::STATUS_INTERNAL_SERVER_ERROR); } @@ -381,12 +386,11 @@ class TaskProcessingApiController extends OCSController { /** * @throws NotPermittedException * @throws NotFoundException - * @throws GenericFileException * @throws LockedException * - * @return DataDownloadResponse<Http::STATUS_OK, string, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}> + * @return StreamResponse<Http::STATUS_OK, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR|Http::STATUS_NOT_FOUND, array{message: string}, array{}> */ - private function getFileContentsInternal(Task $task, int $fileId): DataDownloadResponse|DataResponse { + private function getFileContentsInternal(Task $task, int $fileId): StreamResponse|DataResponse { $ids = $this->extractFileIdsFromTask($task); if (!in_array($fileId, $ids)) { return new DataResponse(['message' => $this->l->t('Not found')], Http::STATUS_NOT_FOUND); @@ -403,7 +407,25 @@ class TaskProcessingApiController extends OCSController { } elseif (!$node instanceof File) { throw new NotFoundException('Node is not a file'); } - return new DataDownloadResponse($node->getContent(), $node->getName(), $node->getMimeType()); + + $contentType = $node->getMimeType(); + if (function_exists('mime_content_type')) { + $mimeType = mime_content_type($node->fopen('rb')); + if ($mimeType !== false) { + $mimeType = $this->mimeTypeDetector->getSecureMimeType($mimeType); + if ($mimeType !== 'application/octet-stream') { + $contentType = $mimeType; + } + } + } + + $response = new StreamResponse($node->fopen('rb')); + $response->addHeader( + 'Content-Disposition', + 'attachment; filename="' . rawurldecode($node->getName()) . '"' + ); + $response->addHeader('Content-Type', $contentType); + return $response; } /** @@ -553,23 +575,51 @@ class TaskProcessingApiController extends OCSController { #[ApiRoute(verb: 'GET', url: '/tasks_provider/next', root: '/taskprocessing')] public function getNextScheduledTask(array $providerIds, array $taskTypeIds): DataResponse { try { + $providerIdsBasedOnTaskTypesWithNull = array_unique(array_map(function ($taskTypeId) { + try { + return $this->taskProcessingManager->getPreferredProvider($taskTypeId)->getId(); + } catch (Exception) { + return null; + } + }, $taskTypeIds)); + + $providerIdsBasedOnTaskTypes = array_filter($providerIdsBasedOnTaskTypesWithNull, fn ($providerId) => $providerId !== null); + // restrict $providerIds to providers that are configured as preferred for the passed task types - $providerIds = array_values(array_intersect(array_unique(array_map(fn ($taskTypeId) => $this->taskProcessingManager->getPreferredProvider($taskTypeId)->getId(), $taskTypeIds)), $providerIds)); + $possibleProviderIds = array_values(array_intersect($providerIdsBasedOnTaskTypes, $providerIds)); + // restrict $taskTypeIds to task types that can actually be run by one of the now restricted providers - $taskTypeIds = array_values(array_filter($taskTypeIds, fn ($taskTypeId) => in_array($this->taskProcessingManager->getPreferredProvider($taskTypeId)->getId(), $providerIds, true))); - if (count($providerIds) === 0 || count($taskTypeIds) === 0) { + $possibleTaskTypeIds = array_values(array_filter($taskTypeIds, function ($taskTypeId) use ($possibleProviderIds) { + try { + $providerForTaskType = $this->taskProcessingManager->getPreferredProvider($taskTypeId)->getId(); + } catch (Exception) { + // no provider found for task type + return false; + } + return in_array($providerForTaskType, $possibleProviderIds, true); + })); + + if (count($possibleProviderIds) === 0 || count($possibleTaskTypeIds) === 0) { throw new NotFoundException(); } $taskIdsToIgnore = []; while (true) { - $task = $this->taskProcessingManager->getNextScheduledTask($taskTypeIds, $taskIdsToIgnore); - $provider = $this->taskProcessingManager->getPreferredProvider($task->getTaskTypeId()); - if (in_array($provider->getId(), $providerIds, true)) { - if ($this->taskProcessingManager->lockTask($task)) { - break; + // Until we find a task whose task type is set to be provided by the providers requested with this request + // Or no scheduled task is found anymore (given the taskIds to ignore) + $task = $this->taskProcessingManager->getNextScheduledTask($possibleTaskTypeIds, $taskIdsToIgnore); + try { + $provider = $this->taskProcessingManager->getPreferredProvider($task->getTaskTypeId()); + if (in_array($provider->getId(), $possibleProviderIds, true)) { + if ($this->taskProcessingManager->lockTask($task)) { + break; + } } + } catch (Exception) { + // There is no provider set for the task type of this task + // proceed to ignore this task } + $taskIdsToIgnore[] = (int)$task->getId(); } diff --git a/core/Controller/TwoFactorChallengeController.php b/core/Controller/TwoFactorChallengeController.php index ef0f420fc82..4791139bb12 100644 --- a/core/Controller/TwoFactorChallengeController.php +++ b/core/Controller/TwoFactorChallengeController.php @@ -25,6 +25,7 @@ use OCP\IRequest; use OCP\ISession; use OCP\IURLGenerator; use OCP\IUserSession; +use OCP\Util; use Psr\Log\LoggerInterface; #[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)] @@ -89,6 +90,7 @@ class TwoFactorChallengeController extends Controller { 'logout_url' => $this->getLogoutUrl(), 'hasSetupProviders' => !empty($setupProviders), ]; + Util::addScript('core', 'twofactor-request-token'); return new StandaloneTemplateResponse($this->appName, 'twofactorselectchallenge', $data, 'guest'); } @@ -141,6 +143,7 @@ class TwoFactorChallengeController extends Controller { if ($provider instanceof IProvidesCustomCSP) { $response->setContentSecurityPolicy($provider->getCSP()); } + Util::addScript('core', 'twofactor-request-token'); return $response; } @@ -204,6 +207,7 @@ class TwoFactorChallengeController extends Controller { 'redirect_url' => $redirect_url, ]; + Util::addScript('core', 'twofactor-request-token'); return new StandaloneTemplateResponse($this->appName, 'twofactorsetupselection', $data, 'guest'); } @@ -235,6 +239,7 @@ class TwoFactorChallengeController extends Controller { 'template' => $tmpl->fetchPage(), ]; $response = new StandaloneTemplateResponse($this->appName, 'twofactorsetupchallenge', $data, 'guest'); + Util::addScript('core', 'twofactor-request-token'); return $response; } diff --git a/core/Controller/WalledGardenController.php b/core/Controller/WalledGardenController.php index b55e90675a1..d0bc0665534 100644 --- a/core/Controller/WalledGardenController.php +++ b/core/Controller/WalledGardenController.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/core/Controller/WhatsNewController.php b/core/Controller/WhatsNewController.php index b3bb7becbac..af8c3d4853b 100644 --- a/core/Controller/WhatsNewController.php +++ b/core/Controller/WhatsNewController.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later |