From 43b54fcf3984c629ba74f987b579c38be667e69f Mon Sep 17 00:00:00 2001 From: Tony Murray Date: Tue, 15 May 2018 15:07:29 -0500 Subject: [PATCH] refactor: Handle database exceptions properly (#8720) Display the errors to the user Hide the query, unless APP_DEBUG=true in .env Much easier to display output for other exceptions now, just need to add a render() function to them --- .../Exceptions/DatabaseConnectException.php | 22 ++++++++++++++++-- app/Exceptions/Handler.php | 22 ++++++++++++++++++ app/Providers/AppServiceProvider.php | 23 ++++++++----------- includes/dbFacile.php | 3 ++- includes/init.php | 5 ++-- 5 files changed, 57 insertions(+), 18 deletions(-) diff --git a/LibreNMS/Exceptions/DatabaseConnectException.php b/LibreNMS/Exceptions/DatabaseConnectException.php index b465d162ea..76e566c398 100644 --- a/LibreNMS/Exceptions/DatabaseConnectException.php +++ b/LibreNMS/Exceptions/DatabaseConnectException.php @@ -19,7 +19,7 @@ * * @package LibreNMS * @link http://librenms.org - * @copyright 2017 Tony Murray + * @copyright 2018 Tony Murray * @author Tony Murray */ @@ -27,5 +27,23 @@ namespace LibreNMS\Exceptions; class DatabaseConnectException extends \Exception { - + /** + * Render the exception into an HTTP or JSON response. + * + * @param \Illuminate\Http\Request + * @return \Illuminate\Http\Response + */ + public function render(\Illuminate\Http\Request $request) + { + if ($request->wantsJson()) { + return response()->json([ + 'status' => 'error', + 'message' => 'Error connecting to database: ' . $this->getMessage(), + ]); + } else { + $message = "

Error connecting to database.

"; + $message .= $this->getMessage(); + return response($message); + } + } } diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index a747e31b81..de4a639145 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -4,7 +4,9 @@ namespace App\Exceptions; use Exception; use Illuminate\Auth\AuthenticationException; +use Illuminate\Database\QueryException; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; +use LibreNMS\Exceptions\DatabaseConnectException; class Handler extends ExceptionHandler { @@ -44,6 +46,26 @@ class Handler extends ExceptionHandler */ public function render($request, Exception $exception) { + if ($exception instanceof QueryException) { + // connect exception, convert to our standard connection exception + if (config('app.debug')) { + // get message form PDO exception, it doesn't contain the query + $message = $exception->getMessage(); + } else { + $message = $exception->getPrevious()->getMessage(); + } + + if (in_array($exception->getCode(), [1044, 1045, 2002])) { + throw new DatabaseConnectException($message, $exception->getCode(), $exception); + } + return response('Unhandled MySQL Error [' . $exception->getCode() . "] $message"); + } + + // emulate Laravel 5.5 renderable exceptions + if (method_exists($exception, 'render')) { + return $exception->render($request); + } + return parent::render($request, $exception); } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 99dedb4f4c..346d1ef7b1 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -16,24 +16,21 @@ class AppServiceProvider extends ServiceProvider * Bootstrap any application services. * * @return void + * @throws DatabaseConnectException caught by App\Exceptions\Handler and displayed to the user */ public function boot() { // connect legacy db //FIXME this is for auth right now, remove later - try { - $db_config = config('database.connections')[config('database.default')]; - dbConnect( - $db_config['host'], - $db_config['username'], - $db_config['password'], - $db_config['database'], - $db_config['port'], - $db_config['unix_socket'] - ); - } catch (DatabaseConnectException $e) { - // ignore failures here - } + $db_config = config('database.connections')[config('database.default')]; + dbConnect( + $db_config['host'], + $db_config['username'], + $db_config['password'], + $db_config['database'], + $db_config['port'], + $db_config['unix_socket'] + ); // load config Config::load(); diff --git a/includes/dbFacile.php b/includes/dbFacile.php index f80c5ac2ce..1c74840e0c 100644 --- a/includes/dbFacile.php +++ b/includes/dbFacile.php @@ -78,7 +78,6 @@ function dbConnect($db_host = null, $db_user = '', $db_pass = '', $db_name = '', } $database_link = @mysqli_connect('p:' . $db_host, $db_user, $db_pass, null, $db_port, $db_socket); - mysqli_options($database_link, MYSQLI_OPT_LOCAL_INFILE, false); if ($database_link === false) { $error = mysqli_connect_error(); if ($error == 'No such file or directory') { @@ -87,6 +86,8 @@ function dbConnect($db_host = null, $db_user = '', $db_pass = '', $db_name = '', throw new DatabaseConnectException($error); } + mysqli_options($database_link, MYSQLI_OPT_LOCAL_INFILE, false); + $database_db = mysqli_select_db($database_link, $db_name); if (!$database_db) { $db_create_sql = "CREATE DATABASE $db_name CHARACTER SET utf8 COLLATE utf8_unicode_ci"; diff --git a/includes/init.php b/includes/init.php index 6a51d417a6..dca1d03c4b 100644 --- a/includes/init.php +++ b/includes/init.php @@ -105,10 +105,11 @@ if (!module_selected('nodb', $init_modules)) { } catch (\LibreNMS\Exceptions\DatabaseConnectException $e) { if (isCli()) { echo 'MySQL Error: ' . $e->getMessage() . PHP_EOL; + exit(2); } else { - echo "

MySQL Error

" . $e->getMessage() . "

"; + // punt to the Laravel error handler + throw $e; } - exit(2); } }