diff --git a/TODO b/TODO index 5fa864f..e58d1c7 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,6 @@ -check:config *validate if all required fields are set, sanity checks. API Endpoint cleanup -check keytype of panel/bindApi +check keytype of panel +check keytype of 1bindApi check:configkey => update config.json more UNIT tests diff --git a/bin/console b/bin/console index c275c21..6984e0c 100755 --- a/bin/console +++ b/bin/console @@ -10,11 +10,11 @@ if (php_sapi_name() !== 'cli') { // check php version (must be >= 8.1) /** @noinspection PhpArgumentWithoutNamedIdentifierInspection */ -if (version_compare(PHP_VERSION, '8.1.0', '<')) { - echo 'This application requires PHP 8.1 or newer. You are running ' . PHP_VERSION . PHP_EOL; - echo 'If you are using KeyHelp, use keyhelp-php81 ' . $argv[0] . ' instead.' . PHP_EOL; +if (version_compare(PHP_VERSION, '8.2.0', '<')) { + echo 'This application requires PHP 8.2 or newer. You are running ' . PHP_VERSION . PHP_EOL; + echo 'If you are using KeyHelp, use keyhelp-php82 ' . $argv[0] . ' instead.' . PHP_EOL; exit; } /** @noinspection PhpArgumentWithoutNamedIdentifierInspection */ - require dirname(__DIR__, 1) . '/src/Util/Console.php'; + require dirname(__DIR__, 1) . '/src/Utilities/Console.php'; diff --git a/composer.json b/composer.json index 16ad6aa..6f369b2 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "tracer/bindapi", "description": "manage Bind9 client zones for KeyHelp", "version": "1.1.1", - "build_number": "379", + "build_number": "380", "authors": [ { "name": "Micha Espey", diff --git a/src/Controller/CLIController.php b/src/Controller/CLIController.php index 185a4c4..2038031 100644 --- a/src/Controller/CLIController.php +++ b/src/Controller/CLIController.php @@ -318,13 +318,13 @@ class CLIController $this->commandGroupContainer = (new CommandGroupContainer()) ->addCommandGroup(commandGroup: $apikeyGroup) - ->addCommandGroup($cronGroup) - ->addCommandGroup($panelsGroup) - ->addCommandGroup($domainsGroup) - ->addCommandGroup($nameserversGroup) - ->addCommandGroup($dyndnsGroup) - ->addCommandGroup($checkGroup) - ->addCommandGroup($migrationsGroup); + ->addCommandGroup(commandGroup: $cronGroup) + ->addCommandGroup(commandGroup: $panelsGroup) + ->addCommandGroup(commandGroup: $domainsGroup) + ->addCommandGroup(commandGroup: $nameserversGroup) + ->addCommandGroup(commandGroup: $dyndnsGroup) + ->addCommandGroup(commandGroup: $checkGroup) + ->addCommandGroup(commandGroup: $migrationsGroup); // ->addCommandGroup(commandGroup: (new CommandGroup(name: 'webmail', description: 'manage webmail setup')) @@ -369,6 +369,8 @@ class CLIController $this->logger->debug(message: "runCommand()"); + $this->checkConfig(); + if (str_contains(haystack: $arguments[0], needle: ':')) { [$command, $subcommand] = explode(separator: ':', string: $arguments[0]); } else { @@ -792,12 +794,12 @@ class CLIController break; case 401: if (!$this->quiet) { - echo Color::RED . ' Error: ' . Colors::YELLOW . $result['data'] . Colors::DEFAULT . PHP_EOL; + echo Colors::RED . ' Error: ' . Colors::YELLOW . $result['data'] . Colors::DEFAULT . PHP_EOL; } exit(1); case 404: if (!$this->quiet) { - echo Color::RED . ' ' . $result['header'] . Colors::DEFAULT; + echo Colors::RED . ' ' . $result['header'] . Colors::DEFAULT; } if (!empty($this->arguments['fix']) && $this->arguments['fix'] == 'yes') { if (!$this->quiet) { @@ -2310,7 +2312,7 @@ const VERSION = '{$versionSting}'; { $phinx = new PhinxApplication(); - $phinx->add(new GenerateCommand()); + $phinx->add(command: new GenerateCommand()); $arguments = [ 'command' => 'generate', @@ -2436,4 +2438,49 @@ const VERSION = '{$versionSting}'; echo 'Created new bootsrap.php' . PHP_EOL; } } + + private function checkConfig(): void + { + echo 'checking config.' . PHP_EOL; + $error = []; + $env = $this->configController->getConfig(configKey: 'env'); + switch ($env) { + case 'prod': + $configFile = $this->baseDir . 'config.json'; + break; + case 'test': + $configFile = $this->baseDir . 'config.json.test'; + break; + case 'dev': + // currently both dev and test use config.json + $configFile = $this->baseDir . 'config.json'; + break; + default: + if (!$this->quiet) { + echo Colors::RED . 'Error: ' . Colors::DEFAULT . 'unknown environment: env = ' . Colors::YELLOW . $env . Colors::DEFAULT . PHP_EOL; + } + exit(1); + } + + if (!$this->quiet) { + echo Colors::DEFAULT . 'Checking config file ' . Colors::YELLOW . $configFile . Colors::DEFAULT . '.' . PHP_EOL; + } + + $config = file_get_contents(filename: $configFile); + $configValues = json_decode(json: $config); + + // db settings are mandatory, but already checked while invoking DatabaseConnection. + $encryptionKey = $configValues->encryptionKey; + if (strlen(string: $encryptionKey) != 64) { + echo Colors::RED . 'Error: ' . Colors::DEFAULT . 'This is no valid encryption key.' . PHP_EOL; + echo 'Run ' . Colors::YELLOW . './bin/console check:generatekey ' . Colors::DEFAULT . 'to create a new one' . PHP_EOL; + exit(1); + } + try { + $binKey = sodium_hex2bin(string: $encryptionKey); + } catch (SodiumException $exception) { + echo Colors::RED . 'Error: ' . Colors::YELLOW . $exception->getMessage() . Colors::DEFAULT; + exit(1); + } + } } diff --git a/src/Controller/ConfigController.php b/src/Controller/ConfigController.php index 3dbd40a..728e15f 100644 --- a/src/Controller/ConfigController.php +++ b/src/Controller/ConfigController.php @@ -47,19 +47,24 @@ class ConfigController $this->config = json_decode(json: $configJSON, associative: true); if (!ConfigController::$missingEncryptionShown) { - if ($this->config['encryptionKey'] === '1bad::babe') { + if (!isset($this->config['encryptionKey']) || ($this->config['encryptionKey'] === '1bad::babe')) { ConfigController::$missingEncryptionShown = true; if (!$this->quiet) { echo Colors::RED . 'Error: ' . Colors::DEFAULT . 'No encryption key, please run ' . Colors::YELLOW . './bin/console check:generatekey' . Colors::DEFAULT . PHP_EOL; } + exit(1); } } } - public function getConfig(string $configKey): string + public function getConfig(string $configKey): ?string { - return $this->config[$configKey]; + if (isset($this->config[$configKey])) { + return $this->config[$configKey]; + } else { + return null; + } } } diff --git a/src/Controller/EncryptionController.php b/src/Controller/EncryptionController.php index 6b85a0b..72c5c37 100644 --- a/src/Controller/EncryptionController.php +++ b/src/Controller/EncryptionController.php @@ -58,7 +58,7 @@ class EncryptionController $plain = sodium_crypto_secretbox_open(ciphertext: $ciphertext, nonce: $nonce, key: $binKey); if ($plain === false) { - throw new Exception(message: ' Incorrect key.'); + throw new Exception(message: ' Incorrect key.' . PHP_EOL); } sodium_memzero(string: $ciphertext); sodium_memzero(string: $key); diff --git a/src/Provider/DatabaseConnection.php b/src/Provider/DatabaseConnection.php index 79e1802..2ceb3c7 100644 --- a/src/Provider/DatabaseConnection.php +++ b/src/Provider/DatabaseConnection.php @@ -18,21 +18,39 @@ class DatabaseConnection { private PDO $dbConnection; - const TABLE_PREFIX = ''; - const TABLE_DOMAINS = self::TABLE_PREFIX . "domains"; + const TABLE_PREFIX = ''; + const TABLE_DOMAINS = self::TABLE_PREFIX . "domains"; const TABLE_NAMESERVERS = self::TABLE_PREFIX . "nameservers"; - const TABLE_PANELS = self::TABLE_PREFIX . "panels"; - const TABLE_APIKEYS = self::TABLE_PREFIX . "apikeys"; - const TABLE_DYNDNS = self::TABLE_PREFIX . "dyndns"; - const TABLE_SETTINGS = self::TABLE_PREFIX . 'config'; + const TABLE_PANELS = self::TABLE_PREFIX . "panels"; + const TABLE_APIKEYS = self::TABLE_PREFIX . "apikeys"; + const TABLE_DYNDNS = self::TABLE_PREFIX . "dyndns"; + const TABLE_SETTINGS = self::TABLE_PREFIX . 'config'; public function __construct(private readonly ConfigController $configController) { - $dbHost = $this->configController->getConfig(configKey: 'dbHost'); - $dbPort = $this->configController->getConfig(configKey: 'dbPort'); - $dbDatabase = $this->configController->getConfig(configKey: 'dbDatabase'); - $dbUser = $this->configController->getConfig(configKey: 'dbUser'); - $dbPassword = $this->configController->getConfig(configKey: 'dbPassword'); + $errors = []; + if (!$dbHost = $this->configController->getConfig(configKey: 'dbHost')) { + $errors[] = Colors::RED . 'Error: ' . Colors::DEFAULT . 'Missing config: dbHost' . PHP_EOL; + } + if (!$dbPort = $this->configController->getConfig(configKey: 'dbPort')) { + $errors[] = Colors::RED . 'Error: ' . Colors::DEFAULT . 'Missing config: dbPort}' . PHP_EOL; + } + if (!$dbDatabase = $this->configController->getConfig(configKey: 'dbDatabase')) { + $errors[] = Colors::RED . 'Error: ' . Colors::DEFAULT . 'Missing config: dbDatabase' . PHP_EOL; + } + if (!$dbUser = $this->configController->getConfig(configKey: 'dbUser')) { + $errors[] = Colors::RED . 'Error: ' . Colors::DEFAULT . 'Missing config: dbUser' . PHP_EOL; + } + if (!$dbPassword = $this->configController->getConfig(configKey: 'dbPassword')) { + $errors[] = Colors::RED . 'Error: ' . Colors::DEFAULT . 'Missing config: dbPassword' . PHP_EOL; + } + + if ($errors) { + foreach ($errors as $error) { + echo $error; + } + exit(1); + } try { $this->dbConnection = new PDO( @@ -72,9 +90,10 @@ class DatabaseConnection } } } + function generatePassword(int $length = 8): string { - $chars = '23456789bcdfhkmnprstvzBCDFHJKLMNPRSTVZ'; + $chars = '23456789bcdfhkmnprstvzBCDFHJKLMNPRSTVZ'; $shuffled = str_shuffle(string: $chars); return mb_substr(string: $shuffled, start: 0, length: $length); } diff --git a/src/Util/Console.php b/src/Utilities/Console.php similarity index 96% rename from src/Util/Console.php rename to src/Utilities/Console.php index 79c7060..fe7fb0e 100644 --- a/src/Util/Console.php +++ b/src/Utilities/Console.php @@ -1,11 +1,12 @@