diff --git a/lib/Middleware/InjectionMiddleware.php b/lib/Middleware/InjectionMiddleware.php index 04a2c252d9..45bd5d3122 100644 --- a/lib/Middleware/InjectionMiddleware.php +++ b/lib/Middleware/InjectionMiddleware.php @@ -144,16 +144,23 @@ public function afterException($controller, $methodName, \Exception $exception): ]; } return new JSONResponse( - $body, - $exception->getCode() === 0 - ? AppFrameworkHttp::STATUS_UNPROCESSABLE_ENTITY - : $exception->getCode() + data: $body, + statusCode: $this->getStatusCodeFromException($exception) ); case $exception instanceof PageException: - $this->initialState->provideInitialState('config', json_decode($exception->getMessage(), true)); + if ($this->isJson($exception->getMessage())) { + $this->initialState->provideInitialState('config', json_decode($exception->getMessage(), true)); + } else { + $this->initialState->provideInitialState('error', ['message' => $exception->getMessage()]); + } Util::addScript(Application::APP_ID, 'libresign-external'); - $response = new TemplateResponse(Application::APP_ID, 'external', [], TemplateResponse::RENDER_AS_BASE); + $response = new TemplateResponse( + appName: Application::APP_ID, + templateName: 'external', + renderAs: TemplateResponse::RENDER_AS_BASE, + status: $this->getStatusCodeFromException($exception) + ); $policy = new ContentSecurityPolicy(); $policy->addAllowedFrameDomain('\'self\''); @@ -164,6 +171,13 @@ public function afterException($controller, $methodName, \Exception $exception): throw $exception; } + private function getStatusCodeFromException(\Exception $exception): int { + if ($exception->getCode() === 0) { + return AppFrameworkHttp::STATUS_UNPROCESSABLE_ENTITY; + } + return $exception->getCode(); + } + protected function isJson(string $string): bool { json_decode($string); return json_last_error() === JSON_ERROR_NONE; diff --git a/tests/Unit/Middleware/InjectionMiddlewareTest.php b/tests/Unit/Middleware/InjectionMiddlewareTest.php new file mode 100644 index 0000000000..dafb3f0d0a --- /dev/null +++ b/tests/Unit/Middleware/InjectionMiddlewareTest.php @@ -0,0 +1,176 @@ + + * + * @author Vitor Mattos + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +final class InjectionMiddlewareTest extends \OCA\Libresign\Tests\Unit\TestCase { + private IRequest|MockObject $request; + private IUserSession|MockObject $userSession; + private ValidateHelper|MockObject $validateHelper; + private SignRequestMapper|MockObject $signRequestMapper; + private FileMapper|MockObject $fileMapper; + private IInitialState|MockObject $initialState; + private SignFileService|MockObject $signFileService; + private IL10N|MockObject $l10n; + private ?string $userId; + + private InitialStateService $initialStateService; + + public function setUp(): void { + $this->request = $this->createMock(IRequest::class); + $this->userSession = $this->createMock(IUserSession::class); + $this->validateHelper = $this->createMock(ValidateHelper::class); + $this->signRequestMapper = $this->createMock(SignRequestMapper::class); + $this->fileMapper = $this->createMock(FileMapper::class); + + $this->initialStateService = new InitialStateService( + $this->createMock(LoggerInterface::class), + $this->createMock(Coordinator::class), + $this->createMock(IServerContainer::class) + ); + $this->initialState = new InitialState($this->initialStateService, 'libresign'); + $this->signFileService = $this->createMock(SignFileService::class); + $this->l10n = $this->createMock(IL10N::class); + $this->userId = null; + } + + public function getInjectionMiddleware(): InjectionMiddleware { + return new InjectionMiddleware( + $this->request, + $this->userSession, + $this->validateHelper, + $this->signRequestMapper, + $this->fileMapper, + $this->initialState, + $this->signFileService, + $this->l10n, + $this->userId, + ); + } + + /** + * @dataProvider providerAfterException + */ + public function testAfterException(string $message, int $code, string $exception, callable $expected): void { + $controller = $this->createMock(Controller::class); + $methodName = 'fake'; + try { + throw new $exception($message, $code); + } catch (\Throwable $exception) { + } + $injectionMiddleware = $this->getInjectionMiddleware(); + $actual = $injectionMiddleware->afterException($controller, $methodName, $exception); + $expected($this, $message, $code, $actual); + } + + public static function providerAfterException(): array { + return [ + [ + json_encode(['action' => 100]), 1, LibresignException::class, + function (self $self, $message, int $code, $actual) { + /** @var JSONResponse $actual */ + $self->assertInstanceOf( + JSONResponse::class, + $actual, + 'The response need to be JSONResponse' + ); + $self->assertJsonStringEqualsJsonString( + $message, + json_encode($actual->getData()), + 'Invalid response json content' + ); + $self->assertEquals( + $code, + $actual->getStatus(), + 'Invalid response status code' + ); + }, + ], + [ + 'a text here', 1, LibresignException::class, + function (self $self, $message, int $code, $actual) { + /** @var JSONResponse $actual */ + $self->assertInstanceOf( + JSONResponse::class, + $actual, + 'The response need to be JSONResponse' + ); + $self->assertJsonStringEqualsJsonString( + json_encode(['message' => $message]), + json_encode($actual->getData()), + 'Invalid response json content' + ); + $self->assertEquals( + $code, + $actual->getStatus(), + 'Invalid response status code' + ); + }, + ], + [ + 'a text here', 1, PageException::class, + function (self $self, $message, int $code, $actual) { + /** @var TemplateResponse $actual */ + $self->assertInstanceOf( + TemplateResponse::class, + $actual, + 'The response need to be TemplateResponse' + ); + $states = $self->initialStateService->getInitialStates(); + $self->assertArrayHasKey('libresign-error', $states); + $self->assertJsonStringEqualsJsonString( + json_encode(['message' => $message]), + $states['libresign-error'], + 'Invalid response params content' + ); + $self->assertEquals( + $code, + $actual->getStatus(), + 'Invalid response status code' + ); + }, + ], + ]; + } +} diff --git a/tests/psalm-baseline.xml b/tests/psalm-baseline.xml index 3059db178f..db297fae1c 100644 --- a/tests/psalm-baseline.xml +++ b/tests/psalm-baseline.xml @@ -72,6 +72,11 @@ LoadSidebar + + + $response->setContentSecurityPolicy($policy) + + $this->root