diff --git a/.env.example b/.env.example index 62ac2ebeb3..4b5dd8d3ea 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,3 @@ -# -# Remember to run "php artisan optimize" after changing this file -# - APP_KEY= #DB_HOST= diff --git a/LibreNMS/ComposerHelper.php b/LibreNMS/ComposerHelper.php index 9557555905..7290954152 100644 --- a/LibreNMS/ComposerHelper.php +++ b/LibreNMS/ComposerHelper.php @@ -26,6 +26,7 @@ namespace LibreNMS; use Composer\Script\Event; +use LibreNMS\Exceptions\FileWriteFailedException; use LibreNMS\Util\EnvHelper; class ComposerHelper @@ -78,13 +79,6 @@ class ComposerHelper */ private static function populateEnv() { - $install = false; - if (!file_exists('.env')) { - copy('.env.example', '.env'); - self::exec('php artisan key:generate'); - $install = true; - } - $config = [ 'db_host' => '', 'db_port' => '', @@ -99,19 +93,23 @@ class ComposerHelper @include 'config.php'; - EnvHelper::writeEnv([ - 'NODE_ID' => uniqid(), - 'DB_HOST' => $config['db_host'], - 'DB_PORT' => $config['db_port'], - 'DB_USERNAME' => $config['db_user'], - 'DB_PASSWORD' => $config['db_pass'], - 'DB_DATABASE' => $config['db_name'], - 'DB_SOCKET' => $config['db_socket'], - 'APP_URL' => $config['base_url'], - 'LIBRENMS_USER' => $config['user'], - 'LIBRENMS_GROUP' => $config['group'], - 'INSTALL' => $install, - ]); + try { + EnvHelper::init(); + EnvHelper::writeEnv([ + 'NODE_ID' => uniqid(), + 'DB_HOST' => $config['db_host'], + 'DB_PORT' => $config['db_port'], + 'DB_USERNAME' => $config['db_user'], + 'DB_PASSWORD' => $config['db_pass'], + 'DB_DATABASE' => $config['db_name'], + 'DB_SOCKET' => $config['db_socket'], + 'APP_URL' => $config['base_url'], + 'LIBRENMS_USER' => $config['user'], + 'LIBRENMS_GROUP' => $config['group'], + ]); + } catch (FileWriteFailedException $exception) { + echo $exception->getMessage() . PHP_EOL; + } } private static function setPermissions() diff --git a/LibreNMS/Exceptions/FileWriteFailedException.php b/LibreNMS/Exceptions/FileWriteFailedException.php index cb599e1d6d..637528c730 100644 --- a/LibreNMS/Exceptions/FileWriteFailedException.php +++ b/LibreNMS/Exceptions/FileWriteFailedException.php @@ -29,8 +29,31 @@ use Throwable; class FileWriteFailedException extends \Exception { + protected $file; + public function __construct($file, $code = 0, Throwable $previous = null) { + $this->file = $file; parent::__construct("Failed to write file: $file", $code, $previous); } + +// /** +// * Render the exception into an HTTP or JSON response. +// * +// * @param \Illuminate\Http\Request +// * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse +// */ +// public function render(\Illuminate\Http\Request $request) +// { +// $title = trans('exceptions.file_write_failed.title'); +// $message = trans('exceptions.file_write_failed.message', ['file' => $this->file]); +// +// return $request->wantsJson() ? response()->json([ +// 'status' => 'error', +// 'message' => "$title: $message", +// ]) : response()->view('errors.generic', [ +// 'title' => $title, +// 'content' => $message, +// ]); +// } } diff --git a/LibreNMS/Util/EnvHelper.php b/LibreNMS/Util/EnvHelper.php index 933e8a5599..943acaecee 100644 --- a/LibreNMS/Util/EnvHelper.php +++ b/LibreNMS/Util/EnvHelper.php @@ -25,6 +25,8 @@ namespace LibreNMS\Util; +use Artisan; +use ErrorException; use LibreNMS\Exceptions\FileWriteFailedException; class EnvHelper @@ -37,49 +39,30 @@ class EnvHelper * @param array $unset Remove the given KEYS from the config * @param string $file * @return string + * @throws \LibreNMS\Exceptions\FileWriteFailedException */ public static function writeEnv($settings, $unset = [], $file = '.env') { - $original_content = file_get_contents($file); + try { + $original_content = file_get_contents($file); - $new_content = self::setEnv($original_content, $settings, $unset); + $new_content = self::setEnv($original_content, $settings, $unset); - // only write if the content has changed - if ($new_content !== $original_content) { - file_put_contents($file, $new_content); - } - - return $new_content; - } - - /** - * Set a setting in .env file. - * Will only set non-empty unset variables - * - * @param array $settings KEY => value list of settings - * @param array $unset Remove the given KEYS from the config - * @param string $file - * @return string - * @throws \LibreNMS\Exceptions\FileWriteFailedException - */ - public static function tryWriteEnv($settings, $unset = [], $file = '.env') - { - $original_content = file_get_contents($file); - - $new_content = self::setEnv($original_content, $settings, $unset); - - // only write if the content has changed - if ($new_content !== $original_content) { - if(!file_put_contents($file, $new_content)) { - throw new FileWriteFailedException($file); + // only write if the content has changed + if ($new_content !== $original_content) { + if (!file_put_contents($file, $new_content)) { + throw new FileWriteFailedException($file); + } } - } - return $new_content; + return $new_content; + } catch (ErrorException $e) { + throw new FileWriteFailedException($file, 0, $e); + } } /** - * Set a setting in .env file. + * Set a setting in .env file content. * Will only set non-empty unset variables * * @param string $content @@ -120,6 +103,36 @@ class EnvHelper return self::fixComments($content); } + /** + * Copy the example .env file and set APP_KEY + * + * @return bool|string + * @throws \LibreNMS\Exceptions\FileWriteFailedException + */ + public static function init() + { + $env_file = base_path('.env'); + try { + if (!file_exists($env_file)) { + Artisan::call('key:generate', ['--show' => true]); + $key = trim(Artisan::output()); + config(['app.key' => $key]); + + copy(base_path('.env.example'), $env_file); + self::writeEnv([ + 'APP_KEY' => $key, + 'INSTALL' => !file_exists(base_path('config.php')) ? 'true' : false, // if both .env and config.php are missing, assume install is needed + ], [], $env_file); + + return $key; + } + + return false; + } catch (ErrorException $e) { + throw new FileWriteFailedException($env_file, 0, $e); + } + } + /** * Fix .env with # in them without a space before it * diff --git a/app/Http/Controllers/Install/FinalizeController.php b/app/Http/Controllers/Install/FinalizeController.php index 600ef77fcb..3fbf7e0576 100644 --- a/app/Http/Controllers/Install/FinalizeController.php +++ b/app/Http/Controllers/Install/FinalizeController.php @@ -79,7 +79,7 @@ class FinalizeController extends InstallationController implements InstallerStep { $this->configureDatabase(); $connection = config('database.default', $this->connection); - return EnvHelper::tryWriteEnv([ + return EnvHelper::writeEnv([ 'NODE_ID' => uniqid(), 'DB_HOST' => config("database.connections.$connection.host"), 'DB_PORT' => config("database.connections.$connection.port"), diff --git a/app/Http/Controllers/Install/MakeUserController.php b/app/Http/Controllers/Install/MakeUserController.php index 06f3222953..fd32a3df26 100644 --- a/app/Http/Controllers/Install/MakeUserController.php +++ b/app/Http/Controllers/Install/MakeUserController.php @@ -28,6 +28,7 @@ namespace App\Http\Controllers\Install; use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Arr; +use LibreNMS\DB\Eloquent; use LibreNMS\Interfaces\InstallerStep; class MakeUserController extends InstallationController implements InstallerStep @@ -76,7 +77,7 @@ class MakeUserController extends InstallationController implements InstallerStep public function complete(): bool { - return User::adminOnly()->exists(); + return Eloquent::isConnected() && User::adminOnly()->exists(); } public function enabled(): bool diff --git a/app/Http/Middleware/CheckInstalled.php b/app/Http/Middleware/CheckInstalled.php index 3a45ac1d89..7e2e39ede6 100644 --- a/app/Http/Middleware/CheckInstalled.php +++ b/app/Http/Middleware/CheckInstalled.php @@ -27,6 +27,7 @@ namespace App\Http\Middleware; use Closure; use Illuminate\Auth\Access\AuthorizationException; +use LibreNMS\Util\EnvHelper; class CheckInstalled { @@ -39,11 +40,17 @@ class CheckInstalled */ public function handle($request, Closure $next) { + config(['app.debug' => true]); $installed = !config('librenms.install') && file_exists(base_path('.env')); $is_install_route = $request->is('install*'); + // further middleware will fail without an app key, init one + if (empty(config('app.key'))) { + config(['app.key' => EnvHelper::init()]); + } + if (!$installed && !$is_install_route) { - // no config.php does so let's redirect to the install + // redirect to install if not installed return redirect()->route('install'); } elseif ($installed && $is_install_route) { throw new AuthorizationException('This should only be called during install'); diff --git a/resources/lang/en/exceptions.php b/resources/lang/en/exceptions.php index 8582b9b489..1a0fdc2e69 100644 --- a/resources/lang/en/exceptions.php +++ b/resources/lang/en/exceptions.php @@ -7,6 +7,10 @@ return [ 'title' => 'It is unsafe to run Dusk in production', 'message' => 'Run ":command" to remove Dusk or if you are a developer set the appropriate APP_ENV' ], + 'file_write_failed' => [ + 'title' => 'Error: Could not write to file', + 'message' => 'Failed to write to file (:file). Please check permissions and SELinux/AppArmor if applicable.' + ], 'ldap_missing' => [ 'title' => 'PHP LDAP support missing', 'message' => 'PHP does not support LDAP, please install or enable the PHP LDAP extension'