Simplify and clean up exception handling.

This commit is contained in:
Buster Neece 2024-01-25 19:28:57 -06:00
parent f379464937
commit 1c9d9fb5f7
No known key found for this signature in database
22 changed files with 64 additions and 85 deletions

View File

@ -239,7 +239,7 @@ final class Auth
$user = $this->getUser(); $user = $this->getUser();
if (!($user instanceof User)) { if (!($user instanceof User)) {
throw new NotLoggedInException(); throw NotLoggedInException::create();
} }
if ($user->verifyTwoFactor($otp)) { if ($user->verifyTwoFactor($otp)) {

View File

@ -42,7 +42,7 @@ final class ListenerAuthAction implements SingleActionInterface
] ]
); );
throw new PermissionDeniedException(); throw PermissionDeniedException::create();
} }
} }

View File

@ -218,7 +218,7 @@ final class RemotesController extends AbstractStationApiCrudController
$record = parent::getRecord($request, $params); $record = parent::getRecord($request, $params);
if ($record instanceof StationRemote && !$record->isEditable()) { if ($record instanceof StationRemote && !$record->isEditable()) {
throw new PermissionDeniedException('This record cannot be edited.'); throw PermissionDeniedException::create();
} }
return $record; return $record;

View File

@ -220,7 +220,7 @@ final class SetupController
// If past "register" step, require login. // If past "register" step, require login.
$auth = $request->getAuth(); $auth = $request->getAuth();
if (!$auth->isLoggedIn()) { if (!$auth->isLoggedIn()) {
throw new NotLoggedInException(); throw NotLoggedInException::create();
} }
// Step 2: Set up Station // Step 2: Set up Station

View File

@ -12,7 +12,7 @@ final class BootstrapException extends Exception
{ {
public function __construct( public function __construct(
string $message = '', string $message = '',
int $code = 0, int $code = 500,
Throwable $previous = null, Throwable $previous = null,
Level $loggerLevel = Level::Alert Level $loggerLevel = Level::Alert
) { ) {

View File

@ -12,7 +12,7 @@ final class CannotCompleteActionException extends Exception
{ {
public function __construct( public function __construct(
string $message = 'Cannot complete action.', string $message = 'Cannot complete action.',
int $code = 0, int $code = 500,
Throwable $previous = null, Throwable $previous = null,
Level $loggerLevel = Level::Info Level $loggerLevel = Level::Info
) { ) {

View File

@ -14,7 +14,7 @@ final class CannotProcessMediaException extends Exception
public function __construct( public function __construct(
string $message = 'Cannot process media file.', string $message = 'Cannot process media file.',
int $code = 0, int $code = 500,
Throwable $previous = null, Throwable $previous = null,
Level $loggerLevel = Level::Warning Level $loggerLevel = Level::Warning
) { ) {

View File

@ -12,7 +12,7 @@ final class CsrfValidationException extends Exception
{ {
public function __construct( public function __construct(
string $message = 'CSRF Validation Error', string $message = 'CSRF Validation Error',
int $code = 0, int $code = 500,
Throwable $previous = null, Throwable $previous = null,
Level $loggerLevel = Level::Info Level $loggerLevel = Level::Info
) { ) {

View File

@ -12,7 +12,7 @@ final class InvalidRequestAttribute extends Exception
{ {
public function __construct( public function __construct(
string $message = 'Invalid request attribute.', string $message = 'Invalid request attribute.',
int $code = 0, int $code = 500,
Throwable $previous = null, Throwable $previous = null,
Level $loggerLevel = Level::Info Level $loggerLevel = Level::Info
) { ) {

View File

@ -12,7 +12,7 @@ final class NoFileUploadedException extends Exception
{ {
public function __construct( public function __construct(
string $message = 'No file was uploaded.', string $message = 'No file was uploaded.',
int $code = 0, int $code = 400,
Throwable $previous = null, Throwable $previous = null,
Level $loggerLevel = Level::Info Level $loggerLevel = Level::Info
) { ) {

View File

@ -12,10 +12,17 @@ final class NotLoggedInException extends Exception
{ {
public function __construct( public function __construct(
string $message = 'Not logged in.', string $message = 'Not logged in.',
int $code = 0, int $code = 403,
Throwable $previous = null, Throwable $previous = null,
Level $loggerLevel = Level::Debug Level $loggerLevel = Level::Debug
) { ) {
parent::__construct($message, $code, $previous, $loggerLevel); parent::__construct($message, $code, $previous, $loggerLevel);
} }
public static function create(): self
{
return new self(
__('You must be logged in to access this page.')
);
}
} }

View File

@ -12,10 +12,17 @@ final class PermissionDeniedException extends Exception
{ {
public function __construct( public function __construct(
string $message = 'Permission denied.', string $message = 'Permission denied.',
int $code = 0, int $code = 403,
Throwable $previous = null, Throwable $previous = null,
Level $loggerLevel = Level::Info Level $loggerLevel = Level::Info
) { ) {
parent::__construct($message, $code, $previous, $loggerLevel); parent::__construct($message, $code, $previous, $loggerLevel);
} }
public static function create(): self
{
return new self(
__('You do not have permission to access this portion of the site.')
);
}
} }

View File

@ -12,7 +12,7 @@ final class RateLimitExceededException extends Exception
{ {
public function __construct( public function __construct(
string $message = 'You have exceeded the rate limit for this application.', string $message = 'You have exceeded the rate limit for this application.',
int $code = 0, int $code = 429,
Throwable $previous = null, Throwable $previous = null,
Level $loggerLevel = Level::Info Level $loggerLevel = Level::Info
) { ) {

View File

@ -12,7 +12,7 @@ final class StationUnsupportedException extends Exception
{ {
public function __construct( public function __construct(
string $message = 'This feature is not currently supported on this station.', string $message = 'This feature is not currently supported on this station.',
int $code = 0, int $code = 500,
Throwable $previous = null, Throwable $previous = null,
Level $loggerLevel = Level::Info Level $loggerLevel = Level::Info
) { ) {

View File

@ -12,7 +12,7 @@ final class StorageLocationFullException extends Exception
{ {
public function __construct( public function __construct(
string $message = 'Storage location is full.', string $message = 'Storage location is full.',
int $code = 0, int $code = 500,
Throwable $previous = null, Throwable $previous = null,
Level $loggerLevel = Level::Info Level $loggerLevel = Level::Info
) { ) {

View File

@ -12,7 +12,7 @@ final class AlreadyRunningException extends SupervisorException
{ {
public function __construct( public function __construct(
string $message = 'Process was already running.', string $message = 'Process was already running.',
int $code = 0, int $code = 500,
Throwable $previous = null, Throwable $previous = null,
Level $loggerLevel = Level::Info Level $loggerLevel = Level::Info
) { ) {

View File

@ -12,7 +12,7 @@ final class NotRunningException extends SupervisorException
{ {
public function __construct( public function __construct(
string $message = 'Process was not running yet.', string $message = 'Process was not running yet.',
int $code = 0, int $code = 500,
Throwable $previous = null, Throwable $previous = null,
Level $loggerLevel = Level::Info Level $loggerLevel = Level::Info
) { ) {

View File

@ -15,7 +15,7 @@ final class ValidationException extends Exception
public function __construct( public function __construct(
string $message = 'Validation error.', string $message = 'Validation error.',
int $code = 0, int $code = 400,
Throwable $previous = null, Throwable $previous = null,
Level $loggerLevel = Level::Info Level $loggerLevel = Level::Info
) { ) {

View File

@ -68,6 +68,19 @@ final class ErrorHandler extends SlimErrorHandler
return parent::__invoke($request, $exception, $displayErrorDetails, $logErrors, $logErrorDetails); return parent::__invoke($request, $exception, $displayErrorDetails, $logErrors, $logErrorDetails);
} }
protected function determineStatusCode(): int
{
if ($this->method === 'OPTIONS') {
return 200;
}
if ($this->exception instanceof Exception || $this->exception instanceof HttpException) {
return $this->exception->getCode();
}
return 500;
}
private function shouldReturnJson(ServerRequestInterface $req): bool private function shouldReturnJson(ServerRequestInterface $req): bool
{ {
$xhr = $req->getHeaderLine('X-Requested-With') === 'XMLHttpRequest'; $xhr = $req->getHeaderLine('X-Requested-With') === 'XMLHttpRequest';
@ -120,12 +133,13 @@ final class ErrorHandler extends SlimErrorHandler
return parent::respond(); return parent::respond();
} }
/** @var Response $response */
$response = $this->responseFactory->createResponse($this->statusCode);
// Special handling for cURL requests. // Special handling for cURL requests.
$ua = $this->request->getHeaderLine('User-Agent'); $ua = $this->request->getHeaderLine('User-Agent');
if (false !== stripos($ua, 'curl') || false !== stripos($ua, 'Liquidsoap')) { if (false !== stripos($ua, 'curl') || false !== stripos($ua, 'Liquidsoap')) {
$response = $this->responseFactory->createResponse($this->statusCode);
$response->getBody()->write( $response->getBody()->write(
sprintf( sprintf(
'Error: %s on %s L%s', 'Error: %s on %s L%s',
@ -138,42 +152,12 @@ final class ErrorHandler extends SlimErrorHandler
return $response; return $response;
} }
if ($this->exception instanceof HttpException) { if ($this->returnJson) {
/** @var Response $response */ $apiResponse = Error::fromException($this->exception, $this->showDetailed);
$response = $this->responseFactory->createResponse($this->exception->getCode()); return $response->withJson($apiResponse);
if ($this->returnJson) {
$apiResponse = Error::fromException($this->exception, $this->showDetailed);
return $response->withJson($apiResponse);
}
$view = $this->view->withRequest($this->request);
try {
return $view->renderToResponse(
$response,
'system/error_http',
[
'exception' => $this->exception,
]
);
} catch (Throwable) {
return parent::respond();
}
} }
if ($this->exception instanceof NotLoggedInException) { if ($this->exception instanceof NotLoggedInException) {
/** @var Response $response */
$response = $this->responseFactory->createResponse(403);
if ($this->returnJson) {
$error = Error::fromException($this->exception);
$error->code = 403;
$error->message = __('You must be logged in to access this page.');
return $response->withJson($error);
}
// Redirect to login page for not-logged-in users. // Redirect to login page for not-logged-in users.
$sessionPersistence = $this->injectSession->getSessionPersistence($this->request); $sessionPersistence = $this->injectSession->getSessionPersistence($this->request);
@ -181,54 +165,33 @@ final class ErrorHandler extends SlimErrorHandler
$session = $sessionPersistence->initializeSessionFromRequest($this->request); $session = $sessionPersistence->initializeSessionFromRequest($this->request);
$flash = new Flash($session); $flash = new Flash($session);
$flash->error(__('You must be logged in to access this page.')); $flash->error($this->exception->getMessage());
// Set referrer for login redirection. // Set referrer for login redirection.
$session->set('login_referrer', $this->request->getUri()->getPath()); $session->set('login_referrer', $this->request->getUri()->getPath());
/** @var Response $response */
$response = $sessionPersistence->persistSession($session, $response); $response = $sessionPersistence->persistSession($session, $response);
/** @var Response $response */
return $response->withRedirect($this->router->named('account:login')); return $response->withRedirect($this->router->named('account:login'));
} }
if ($this->exception instanceof PermissionDeniedException) { if ($this->exception instanceof PermissionDeniedException) {
/** @var Response $response */
$response = $this->responseFactory->createResponse(403);
if ($this->returnJson) {
$error = Error::fromException($this->exception);
$error->code = 403;
$error->message = __('You do not have permission to access this portion of the site.');
return $response->withJson($error);
}
$sessionPersistence = $this->injectSession->getSessionPersistence($this->request); $sessionPersistence = $this->injectSession->getSessionPersistence($this->request);
/** @var Session $session */ /** @var Session $session */
$session = $sessionPersistence->initializeSessionFromRequest($this->request); $session = $sessionPersistence->initializeSessionFromRequest($this->request);
$flash = new Flash($session); $flash = new Flash($session);
$flash->error( $flash->error($this->exception->getMessage());
__('You do not have permission to access this portion of the site.'),
);
/** @var Response $response */
$response = $sessionPersistence->persistSession($session, $response); $response = $sessionPersistence->persistSession($session, $response);
// Bounce back to homepage for permission-denied users. // Bounce back to homepage for permission-denied users.
/** @var Response $response */
return $response->withRedirect($this->router->named('home')); return $response->withRedirect($this->router->named('home'));
} }
/** @var Response $response */
$response = $this->responseFactory->createResponse(500);
if ($this->returnJson) {
$apiResponse = Error::fromException($this->exception, $this->showDetailed);
return $response->withJson($apiResponse);
}
if ($this->showDetailed && class_exists(Run::class)) { if ($this->showDetailed && class_exists(Run::class)) {
// Register error-handler. // Register error-handler.
$handler = new PrettyPageHandler(); $handler = new PrettyPageHandler();
@ -246,12 +209,14 @@ final class ErrorHandler extends SlimErrorHandler
return $response->write($run->handleException($this->exception)); return $response->write($run->handleException($this->exception));
} }
$view = $this->view->withRequest($this->request);
try { try {
$view = $this->view->withRequest($this->request);
return $view->renderToResponse( return $view->renderToResponse(
$response, $response,
'system/error_general', ($this->exception instanceof HttpException)
? 'system/error_http'
: 'system/error_general',
[ [
'exception' => $this->exception, 'exception' => $this->exception,
] ]

View File

@ -33,12 +33,12 @@ final class Permissions extends AbstractMiddleware
try { try {
$user = $request->getUser(); $user = $request->getUser();
} catch (Exception) { } catch (Exception) {
throw new PermissionDeniedException(); throw PermissionDeniedException::create();
} }
$acl = $request->getAcl(); $acl = $request->getAcl();
if (!$acl->userAllowed($user, $this->action, $stationId)) { if (!$acl->userAllowed($user, $this->action, $stationId)) {
throw new PermissionDeniedException(); throw PermissionDeniedException::create();
} }
return $handler->handle($request); return $handler->handle($request);

View File

@ -20,7 +20,7 @@ final class RequireLogin extends AbstractMiddleware
try { try {
$request->getUser(); $request->getUser();
} catch (Exception) { } catch (Exception) {
throw new NotLoggedInException(); throw NotLoggedInException::create();
} }
return $handler->handle($request); return $handler->handle($request);

View File

@ -22,7 +22,7 @@ class Frontend_PublicCest extends CestAbstract
$this->em->flush(); $this->em->flush();
$I->amOnPage('/public/' . $testStation->getId()); $I->amOnPage('/public/' . $testStation->getId());
$I->seeResponseCodeIs(500); $I->seeResponseCodeIs(404);
// Enable public pages // Enable public pages
$testStation = $this->getTestStation(); $testStation = $this->getTestStation();