2016-01-08 09:02:15 -06:00
|
|
|
<?php
|
2018-05-11 03:08:34 -05:00
|
|
|
|
2017-10-21 01:40:00 -05:00
|
|
|
/**
|
|
|
|
* Handler.php
|
2020-01-28 01:44:57 -06:00
|
|
|
* Copyright (c) 2019 james@firefly-iii.org
|
2017-10-21 01:40:00 -05:00
|
|
|
*
|
2019-10-01 23:37:26 -05:00
|
|
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
2017-10-21 01:40:00 -05:00
|
|
|
*
|
2019-10-01 23:37:26 -05:00
|
|
|
* 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.
|
2017-10-21 01:40:00 -05:00
|
|
|
*
|
2019-10-01 23:37:26 -05:00
|
|
|
* This program is distributed in the hope that it will be useful,
|
2017-10-21 01:40:00 -05:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2019-10-01 23:37:26 -05:00
|
|
|
* GNU Affero General Public License for more details.
|
2017-10-21 01:40:00 -05:00
|
|
|
*
|
2019-10-01 23:37:26 -05:00
|
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2017-10-21 01:40:00 -05:00
|
|
|
*/
|
2017-09-14 10:40:02 -05:00
|
|
|
|
2018-05-11 03:08:34 -05:00
|
|
|
declare(strict_types=1);
|
|
|
|
|
2016-01-08 09:02:15 -06:00
|
|
|
namespace FireflyIII\Exceptions;
|
2015-02-05 21:39:52 -06:00
|
|
|
|
2017-09-12 15:14:43 -05:00
|
|
|
use ErrorException;
|
|
|
|
use FireflyIII\Jobs\MailError;
|
2018-02-17 07:14:26 -06:00
|
|
|
use Illuminate\Auth\AuthenticationException;
|
2021-10-02 06:28:56 -05:00
|
|
|
use Illuminate\Contracts\Foundation\Application;
|
2022-06-09 23:00:01 -05:00
|
|
|
use Illuminate\Database\QueryException;
|
2016-01-08 09:02:15 -06:00
|
|
|
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
2021-10-02 06:28:56 -05:00
|
|
|
use Illuminate\Http\RedirectResponse;
|
2020-07-31 05:45:02 -05:00
|
|
|
use Illuminate\Http\Request;
|
2021-10-02 06:28:56 -05:00
|
|
|
use Illuminate\Routing\Redirector;
|
2021-04-07 00:28:43 -05:00
|
|
|
use Illuminate\Session\TokenMismatchException;
|
2021-04-03 05:32:29 -05:00
|
|
|
use Illuminate\Support\Arr;
|
2018-07-15 03:00:08 -05:00
|
|
|
use Illuminate\Validation\ValidationException as LaravelValidationException;
|
2021-04-07 07:18:43 -05:00
|
|
|
use Laravel\Passport\Exceptions\OAuthServerException as LaravelOAuthException;
|
2021-05-28 16:11:12 -05:00
|
|
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
2022-09-04 06:31:46 -05:00
|
|
|
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
|
2022-12-29 12:41:57 -06:00
|
|
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
2021-04-07 23:50:00 -05:00
|
|
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
2022-06-23 02:33:43 -05:00
|
|
|
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
2021-05-28 16:11:12 -05:00
|
|
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
2020-06-06 15:25:52 -05:00
|
|
|
use Throwable;
|
2021-03-21 03:15:40 -05:00
|
|
|
|
2017-12-17 07:30:53 -06:00
|
|
|
/**
|
|
|
|
* Class Handler
|
2018-12-15 00:59:02 -06:00
|
|
|
*
|
2018-08-23 11:33:07 -05:00
|
|
|
* @codeCoverageIgnore
|
2017-12-17 07:30:53 -06:00
|
|
|
*/
|
2015-02-11 00:35:10 -06:00
|
|
|
class Handler extends ExceptionHandler
|
|
|
|
{
|
2021-04-03 05:25:35 -05:00
|
|
|
/**
|
2022-12-31 06:32:42 -06:00
|
|
|
* @var array<int, class-string<Throwable>>
|
2021-04-03 05:25:35 -05:00
|
|
|
*/
|
|
|
|
protected $dontReport
|
|
|
|
= [
|
2021-04-03 06:19:11 -05:00
|
|
|
AuthenticationException::class,
|
|
|
|
LaravelValidationException::class,
|
2021-04-04 08:53:17 -05:00
|
|
|
NotFoundHttpException::class,
|
2021-04-07 00:28:43 -05:00
|
|
|
OAuthServerException::class,
|
2021-04-07 07:18:43 -05:00
|
|
|
LaravelOAuthException::class,
|
2021-04-07 00:28:43 -05:00
|
|
|
TokenMismatchException::class,
|
2021-05-28 16:11:12 -05:00
|
|
|
HttpException::class,
|
2022-12-29 12:41:57 -06:00
|
|
|
SuspiciousOperationException::class,
|
2021-04-03 05:25:35 -05:00
|
|
|
];
|
|
|
|
|
2016-02-11 07:17:11 -06:00
|
|
|
/**
|
2017-09-12 15:14:43 -05:00
|
|
|
* Render an exception into an HTTP response.
|
2016-02-11 07:17:11 -06:00
|
|
|
*
|
2022-12-29 12:41:57 -06:00
|
|
|
* @param Request $request
|
|
|
|
* @param Throwable $e
|
2019-08-17 03:46:55 -05:00
|
|
|
*
|
2018-07-06 00:37:14 -05:00
|
|
|
* @return mixed
|
2021-05-24 01:50:17 -05:00
|
|
|
* @throws Throwable
|
2016-02-11 07:17:11 -06:00
|
|
|
*/
|
2021-04-03 06:19:11 -05:00
|
|
|
public function render($request, Throwable $e)
|
2016-02-11 07:17:11 -06:00
|
|
|
{
|
2021-04-03 06:19:11 -05:00
|
|
|
if ($e instanceof LaravelValidationException && $request->expectsJson()) {
|
2018-02-13 11:24:06 -06:00
|
|
|
// ignore it: controller will handle it.
|
2021-04-03 06:19:11 -05:00
|
|
|
return parent::render($request, $e);
|
2018-02-13 11:24:06 -06:00
|
|
|
}
|
2021-04-03 06:19:11 -05:00
|
|
|
if ($e instanceof NotFoundHttpException && $request->expectsJson()) {
|
2018-03-06 22:51:51 -06:00
|
|
|
// JSON error:
|
2018-02-11 13:45:33 -06:00
|
|
|
return response()->json(['message' => 'Resource not found', 'exception' => 'NotFoundHttpException'], 404);
|
|
|
|
}
|
2018-02-17 07:14:26 -06:00
|
|
|
|
2021-04-03 06:19:11 -05:00
|
|
|
if ($e instanceof AuthenticationException && $request->expectsJson()) {
|
2018-03-06 22:51:51 -06:00
|
|
|
// somehow Laravel handler does not catch this:
|
2018-02-17 07:14:26 -06:00
|
|
|
return response()->json(['message' => 'Unauthenticated', 'exception' => 'AuthenticationException'], 401);
|
|
|
|
}
|
|
|
|
|
2021-04-03 06:19:11 -05:00
|
|
|
if ($e instanceof OAuthServerException && $request->expectsJson()) {
|
2018-05-10 02:48:13 -05:00
|
|
|
// somehow Laravel handler does not catch this:
|
2021-04-03 06:19:11 -05:00
|
|
|
return response()->json(['message' => $e->getMessage(), 'exception' => 'OAuthServerException'], 401);
|
2018-05-10 02:48:13 -05:00
|
|
|
}
|
2022-10-30 08:24:10 -05:00
|
|
|
if ($e instanceof BadRequestHttpException) {
|
2022-06-23 02:33:43 -05:00
|
|
|
return response()->json(['message' => $e->getMessage(), 'exception' => 'BadRequestHttpException'], 400);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($e instanceof BadHttpHeaderException) {
|
|
|
|
// is always API exception.
|
|
|
|
return response()->json(['message' => $e->getMessage(), 'exception' => 'BadHttpHeaderException'], $e->statusCode);
|
|
|
|
}
|
2018-05-10 02:48:13 -05:00
|
|
|
|
2018-02-11 13:45:33 -06:00
|
|
|
if ($request->expectsJson()) {
|
2022-06-23 02:33:43 -05:00
|
|
|
$errorCode = 500;
|
2022-10-30 08:24:10 -05:00
|
|
|
$errorCode = $e instanceof MethodNotAllowedHttpException ? 405 : $errorCode;
|
2022-06-23 02:33:43 -05:00
|
|
|
|
2018-03-04 01:22:32 -06:00
|
|
|
$isDebug = config('app.debug', false);
|
2018-02-11 13:45:33 -06:00
|
|
|
if ($isDebug) {
|
|
|
|
return response()->json(
|
|
|
|
[
|
2021-04-03 06:19:11 -05:00
|
|
|
'message' => $e->getMessage(),
|
|
|
|
'exception' => get_class($e),
|
|
|
|
'line' => $e->getLine(),
|
|
|
|
'file' => $e->getFile(),
|
|
|
|
'trace' => $e->getTrace(),
|
2020-03-17 08:57:04 -05:00
|
|
|
],
|
2022-06-23 02:33:43 -05:00
|
|
|
$errorCode
|
2018-02-11 13:45:33 -06:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-03-21 03:15:40 -05:00
|
|
|
return response()->json(
|
2022-06-23 02:33:43 -05:00
|
|
|
['message' => sprintf('Internal Firefly III Exception: %s', $e->getMessage()), 'exception' => get_class($e)],
|
|
|
|
$errorCode
|
2021-03-21 03:15:40 -05:00
|
|
|
);
|
2018-02-10 02:57:56 -06:00
|
|
|
}
|
|
|
|
|
2021-04-03 06:19:11 -05:00
|
|
|
if ($e instanceof NotFoundHttpException) {
|
2019-07-31 23:22:07 -05:00
|
|
|
$handler = app(GracefulNotFoundHandler::class);
|
2020-03-17 08:57:04 -05:00
|
|
|
|
2021-04-03 06:19:11 -05:00
|
|
|
return $handler->render($request, $e);
|
2019-07-31 23:22:07 -05:00
|
|
|
}
|
2021-04-03 06:19:11 -05:00
|
|
|
if ($e instanceof FireflyException || $e instanceof ErrorException || $e instanceof OAuthServerException) {
|
2018-12-15 00:59:02 -06:00
|
|
|
$isDebug = config('app.debug');
|
2017-09-12 15:14:43 -05:00
|
|
|
|
2021-04-03 06:19:11 -05:00
|
|
|
return response()->view('errors.FireflyException', ['exception' => $e, 'debug' => $isDebug], 500);
|
2017-09-12 15:14:43 -05:00
|
|
|
}
|
2022-06-09 23:00:01 -05:00
|
|
|
// special view for database errors with extra instructions
|
2022-10-30 08:24:10 -05:00
|
|
|
if ($e instanceof QueryException) {
|
2022-06-09 23:00:01 -05:00
|
|
|
$isDebug = config('app.debug');
|
|
|
|
|
|
|
|
return response()->view('errors.DatabaseException', ['exception' => $e, 'debug' => $isDebug], 500);
|
|
|
|
}
|
|
|
|
|
|
|
|
//var_dump($e);exit;
|
2017-09-12 15:14:43 -05:00
|
|
|
|
2021-04-03 06:19:11 -05:00
|
|
|
return parent::render($request, $e);
|
2015-02-11 00:35:10 -06:00
|
|
|
}
|
2016-09-15 23:40:45 -05:00
|
|
|
|
|
|
|
/**
|
2017-09-12 15:14:43 -05:00
|
|
|
* Report or log an exception.
|
2016-10-06 22:44:21 -05:00
|
|
|
*
|
2022-12-29 12:41:57 -06:00
|
|
|
* @param Throwable $e
|
2017-11-17 22:46:19 -06:00
|
|
|
*
|
2021-03-21 03:15:40 -05:00
|
|
|
* @return void
|
2021-04-03 06:19:11 -05:00
|
|
|
* @throws Throwable
|
2017-12-22 11:32:43 -06:00
|
|
|
*
|
2016-09-15 23:40:45 -05:00
|
|
|
*/
|
2021-04-03 05:25:35 -05:00
|
|
|
public function report(Throwable $e)
|
2016-09-15 23:40:45 -05:00
|
|
|
{
|
2018-12-15 00:59:02 -06:00
|
|
|
$doMailError = config('firefly.send_error_message');
|
2021-04-03 05:32:29 -05:00
|
|
|
if ($this->shouldntReportLocal($e) || !$doMailError) {
|
2021-04-03 05:25:35 -05:00
|
|
|
parent::report($e);
|
2021-04-03 05:32:29 -05:00
|
|
|
|
2021-04-03 05:25:35 -05:00
|
|
|
return;
|
2017-09-12 15:14:43 -05:00
|
|
|
}
|
2021-04-03 05:25:35 -05:00
|
|
|
$userData = [
|
|
|
|
'id' => 0,
|
|
|
|
'email' => 'unknown@example.com',
|
|
|
|
];
|
|
|
|
if (auth()->check()) {
|
|
|
|
$userData['id'] = auth()->user()->id;
|
|
|
|
$userData['email'] = auth()->user()->email;
|
|
|
|
}
|
2022-03-06 09:03:52 -06:00
|
|
|
|
2023-01-02 23:48:53 -06:00
|
|
|
$headers = request()->headers->all();
|
2022-03-06 09:03:52 -06:00
|
|
|
|
2021-04-03 05:25:35 -05:00
|
|
|
$data = [
|
|
|
|
'class' => get_class($e),
|
|
|
|
'errorMessage' => $e->getMessage(),
|
|
|
|
'time' => date('r'),
|
|
|
|
'stackTrace' => $e->getTraceAsString(),
|
|
|
|
'file' => $e->getFile(),
|
|
|
|
'line' => $e->getLine(),
|
|
|
|
'code' => $e->getCode(),
|
|
|
|
'version' => config('firefly.version'),
|
|
|
|
'url' => request()->fullUrl(),
|
|
|
|
'userAgent' => request()->userAgent(),
|
|
|
|
'json' => request()->acceptsJson(),
|
2022-04-03 05:38:17 -05:00
|
|
|
'method' => request()->method(),
|
2022-03-06 09:03:52 -06:00
|
|
|
'headers' => $headers,
|
2021-04-03 05:25:35 -05:00
|
|
|
];
|
|
|
|
|
|
|
|
// create job that will mail.
|
|
|
|
$ipAddress = request()->ip() ?? '0.0.0.0';
|
2022-12-29 12:41:57 -06:00
|
|
|
$job = new MailError($userData, (string)config('firefly.site_owner'), $ipAddress, $data);
|
2021-04-03 05:25:35 -05:00
|
|
|
dispatch($job);
|
|
|
|
|
|
|
|
parent::report($e);
|
2016-09-15 23:40:45 -05:00
|
|
|
}
|
2021-04-03 05:32:29 -05:00
|
|
|
|
|
|
|
/**
|
2022-12-29 12:41:57 -06:00
|
|
|
* @param Throwable $e
|
2021-04-03 05:32:29 -05:00
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
private function shouldntReportLocal(Throwable $e): bool
|
|
|
|
{
|
|
|
|
return !is_null(
|
|
|
|
Arr::first(
|
2022-10-30 08:24:10 -05:00
|
|
|
$this->dontReport,
|
|
|
|
function ($type) use ($e) {
|
|
|
|
return $e instanceof $type;
|
|
|
|
}
|
2021-04-03 05:32:29 -05:00
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2021-10-02 06:28:56 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert a validation exception into a response.
|
|
|
|
*
|
2022-12-29 12:41:57 -06:00
|
|
|
* @param Request $request
|
|
|
|
* @param LaravelValidationException $exception
|
2021-10-02 06:28:56 -05:00
|
|
|
*
|
|
|
|
* @return Application|RedirectResponse|Redirector
|
|
|
|
*/
|
|
|
|
protected function invalid($request, LaravelValidationException $exception): Application|RedirectResponse|Redirector
|
|
|
|
{
|
|
|
|
// protect against open redirect when submitting invalid forms.
|
2021-10-02 23:05:30 -05:00
|
|
|
$previous = app('steam')->getSafePreviousUrl();
|
2021-10-02 06:28:56 -05:00
|
|
|
$redirect = $this->getRedirectUrl($exception);
|
|
|
|
|
|
|
|
return redirect($redirect ?? $previous)
|
|
|
|
->withInput(Arr::except($request->input(), $this->dontFlash))
|
|
|
|
->withErrors($exception->errors(), $request->input('_error_bag', $exception->errorBag));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Only return the redirectTo property from the exception if it is a valid URL. Return NULL otherwise.
|
|
|
|
*
|
2022-12-29 12:41:57 -06:00
|
|
|
* @param LaravelValidationException $exception
|
2021-10-02 06:28:56 -05:00
|
|
|
*
|
|
|
|
* @return string|null
|
|
|
|
*/
|
|
|
|
private function getRedirectUrl(LaravelValidationException $exception): ?string
|
|
|
|
{
|
|
|
|
if (null === $exception->redirectTo) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
$safe = route('index');
|
|
|
|
$previous = $exception->redirectTo;
|
|
|
|
$previousHost = parse_url($previous, PHP_URL_HOST);
|
|
|
|
$safeHost = parse_url($safe, PHP_URL_HOST);
|
|
|
|
|
|
|
|
return null !== $previousHost && $previousHost === $safeHost ? $previous : $safe;
|
|
|
|
}
|
2015-02-05 21:39:52 -06:00
|
|
|
}
|