diff --git a/TODO b/TODO index 8a4afd0..9920c54 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,3 @@ +versions, get from weekly more UNIT tests -DB migrations diff --git a/composer.json b/composer.json index 8982a17..e8a5fcb 100644 --- a/composer.json +++ b/composer.json @@ -1,8 +1,8 @@ { "name": "24unix/bindapi", "description": "manage Bind9 DNS server via REST API", - "version": "2023.0.1", - "build_number": "344", + "version": "1.0.7", + "build_number": "345", "authors": [ { "name": "Micha Espey", diff --git a/db/migrations/20240418172308_api_key_creation_date.php b/db/migrations/20240418172308_api_key_creation_date.php new file mode 100644 index 0000000..a40a5f9 --- /dev/null +++ b/db/migrations/20240418172308_api_key_creation_date.php @@ -0,0 +1,24 @@ +table('apikeys', [ + 'id' => false, + 'primary_key' => ['id'], + 'engine' => 'InnoDB', + 'encoding' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'comment' => '', + 'row_format' => 'DYNAMIC', + ]) + ->addColumn('created_at', 'timestamp', [ + 'null' => false, + 'after' => 'apikey', + ]) + ->save(); + } +} diff --git a/db/migrations/20240419120157_unique_config_values.php b/db/migrations/20240419120157_unique_config_values.php new file mode 100644 index 0000000..0080450 --- /dev/null +++ b/db/migrations/20240419120157_unique_config_values.php @@ -0,0 +1,24 @@ +table('config', [ + 'id' => false, + 'primary_key' => ['name'], + 'engine' => 'InnoDB', + 'encoding' => 'utf8mb4', + 'collation' => 'utf8mb4_general_ci', + 'comment' => '', + 'row_format' => 'DYNAMIC', + ]) + ->addIndex(['name'], [ + 'name' => 'name', + 'unique' => true, + ]) + ->save(); + } +} diff --git a/db/migrations/schema.php b/db/migrations/schema.php index 814ceb3..347830e 100644 --- a/db/migrations/schema.php +++ b/db/migrations/schema.php @@ -129,7 +129,7 @@ return array ( 'CHARACTER_SET_NAME' => 'utf8mb4', 'COLLATION_NAME' => 'utf8mb4_general_ci', 'COLUMN_TYPE' => 'varchar(256)', - 'COLUMN_KEY' => '', + 'COLUMN_KEY' => 'PRI', 'EXTRA' => '', 'PRIVILEGES' => 'select,insert,update,references', 'COLUMN_COMMENT' => '', @@ -160,33 +160,27 @@ return array ( 'IS_GENERATED' => 'NEVER', 'GENERATION_EXPRESSION' => NULL, ), - 'fff' => - array ( - 'TABLE_CATALOG' => 'def', - 'TABLE_NAME' => 'config', - 'COLUMN_NAME' => 'fff', - 'ORDINAL_POSITION' => 3, - 'COLUMN_DEFAULT' => NULL, - 'IS_NULLABLE' => 'NO', - 'DATA_TYPE' => 'int', - 'CHARACTER_MAXIMUM_LENGTH' => NULL, - 'CHARACTER_OCTET_LENGTH' => NULL, - 'NUMERIC_PRECISION' => 10, - 'NUMERIC_SCALE' => 0, - 'DATETIME_PRECISION' => NULL, - 'CHARACTER_SET_NAME' => NULL, - 'COLLATION_NAME' => NULL, - 'COLUMN_TYPE' => 'int(11)', - 'COLUMN_KEY' => '', - 'EXTRA' => '', - 'PRIVILEGES' => 'select,insert,update,references', - 'COLUMN_COMMENT' => '', - 'IS_GENERATED' => 'NEVER', - 'GENERATION_EXPRESSION' => NULL, - ), ), 'indexes' => array ( + 'name' => + array ( + 1 => + array ( + 'Table' => 'config', + 'Non_unique' => 0, + 'Key_name' => 'name', + 'Seq_in_index' => 1, + 'Column_name' => 'name', + 'Collation' => 'A', + 'Sub_part' => NULL, + 'Packed' => NULL, + 'Null' => '', + 'Index_type' => 'BTREE', + 'Comment' => '', + 'Index_comment' => '', + ), + ), ), 'foreign_keys' => NULL, ), @@ -927,6 +921,30 @@ return array ( 'IS_GENERATED' => 'NEVER', 'GENERATION_EXPRESSION' => NULL, ), + 'created_at' => + array ( + 'TABLE_CATALOG' => 'def', + 'TABLE_NAME' => 'apikeys', + 'COLUMN_NAME' => 'created_at', + 'ORDINAL_POSITION' => 5, + 'COLUMN_DEFAULT' => NULL, + 'IS_NULLABLE' => 'NO', + 'DATA_TYPE' => 'timestamp', + 'CHARACTER_MAXIMUM_LENGTH' => NULL, + 'CHARACTER_OCTET_LENGTH' => NULL, + 'NUMERIC_PRECISION' => NULL, + 'NUMERIC_SCALE' => NULL, + 'DATETIME_PRECISION' => 0, + 'CHARACTER_SET_NAME' => NULL, + 'COLLATION_NAME' => NULL, + 'COLUMN_TYPE' => 'timestamp', + 'COLUMN_KEY' => '', + 'EXTRA' => '', + 'PRIVILEGES' => 'select,insert,update,references', + 'COLUMN_COMMENT' => '', + 'IS_GENERATED' => 'NEVER', + 'GENERATION_EXPRESSION' => NULL, + ), ), 'indexes' => array ( diff --git a/src/Controller/CLIController.php b/src/Controller/CLIController.php index d6d7c27..51b0011 100644 --- a/src/Controller/CLIController.php +++ b/src/Controller/CLIController.php @@ -26,6 +26,7 @@ use App\Repository\DomainRepository; use App\Repository\DynDNSRepository; use App\Repository\NameserverRepository; use App\Repository\PanelRepository; +use App\Repository\SettingsRepository; use Arubacao\TldChecker\Validator; use Exception; use JsonMapper; @@ -62,11 +63,38 @@ class CLIController private readonly PanelRepository $panelRepository, private readonly ConfigController $configController, private readonly EncryptionController $encryptionController, + private readonly SettingsRepository $settingsRepository, private $logger, private bool $quiet ) { $this->commandGroupContainer = (new CommandGroupContainer()) + ->addCommandGroup(commandGroup: (new CommandGroup(name: 'apikeys', description: 'API keys to access this bindAPI')) + ->addCommand(command: new Command( + name: 'list', + callback: function () { + $this->apikeysList(); + })) + ->addCommand(command: new Command( + name: 'create', + callback: function () { + $this->apikeysCreate(); + }, + mandatoryParameters: ['name'])) + ->addCommand(command: new Command( + name: 'update', + callback: function () { + $this->apikeysUpdate(); + }, + mandatoryParameters: ['ID',], + optionalParameters: ['name='])) + ->addCommand(command: new Command( + name: 'delete', + callback: function () { + $this->apikeysDelete(); + }, + mandatoryParameters: ['ID']))) + ->addCommandGroup(commandGroup: (new CommandGroup(name: 'check', description: 'health checks the system can perform')) ->addCommand(command: new Command( name: 'permissions', @@ -118,9 +146,9 @@ class CLIController ->addCommand(command: new Command( name: 'version', callback: function () { - $this->checksVersion(); + $this->checkVersion(); }, - optionalParameters: ['major:minor:patch'], + optionalParameters: ['update'], description: 'Read or set the bindApi version in the database'))) ->addCommandGroup(commandGroup: (new CommandGroup(name: 'panels', description: 'all KeyHelp systems configured')) ->addCommand(command: new Command( @@ -227,31 +255,6 @@ class CLIController $this->dynDnsDelete(); }, mandatoryParameters: ['ID']))) - ->addCommandGroup(commandGroup: (new CommandGroup(name: 'apikeys', description: 'API keys to access this bindAPI')) - ->addCommand(command: new Command( - name: 'list', - callback: function () { - $this->apikeysList(); - })) - ->addCommand(command: new Command( - name: 'create', - callback: function () { - $this->apikeysCreate(); - }, - mandatoryParameters: ['name'])) - ->addCommand(command: new Command( - name: 'update', - callback: function () { - $this->apikeysUpdate(); - }, - mandatoryParameters: ['ID',], - optionalParameters: ['name='])) - ->addCommand(command: new Command( - name: 'delete', - callback: function () { - $this->apikeysDelete(); - }, - mandatoryParameters: ['ID']))) ->addCommandGroup(commandGroup: (new CommandGroup(name: 'migrations', description: 'maintain database migrations')) ->addCommand(command: new Command( name: 'status', @@ -1155,21 +1158,18 @@ class CLIController exit(0); } - /** - * @return void - */ function apikeysList(): void { $keys = $this->apikeyRepository->findAll(); if (!empty($keys)) { - echo 'All valid API keys:' . PHP_EOL; $table = new ConsoleTable(); - $table->setHeaders(content: ['ID', 'Name', 'API key prefix']); + $table->setHeaders(content: ['ID', 'Name', 'API key prefix', 'Created at']); foreach ($keys as $key) { $row = []; $row[] = $key->getID(); $row[] = $key->getName(); $row[] = $key->getApikeyPrefix(); + $row[] = $key->getCreatedAt(); $table->addRow(data: $row); } $table->setPadding(value: 2); @@ -2002,9 +2002,49 @@ class CLIController echo 'Not yet implemented.' . PHP_EOL; } - private function checksVersion(): void + private function checkVersion(): void { - echo 'Not yet implemented.' . PHP_EOL; + $update = false; + if (isset($this->arguments[1])) { + $action = $this->arguments[1]; + if ($action !== 'update') { + if (!$this->quiet) { + echo 'The only allowed argument is "' . COLOR_YELLOW . 'update' . COLOR_DEFAULT . '.' . PHP_EOL; + } + exit(1); + } + $update = true; + } + + $composerFile = dirname(path: __DIR__, levels: 2) . DIRECTORY_SEPARATOR . 'composer.json'; + $composerJson = json_decode(json: file_get_contents(filename: $composerFile), associative: false); + + $fileVersion = $composerJson->version; + $fileBuildNumber = $composerJson->build_number; + [$major, $minor, $patch] = explode(separator: '.', string: $fileVersion); + $version = [ + 'major' => $major, + 'minor' => $minor, + 'patch' => $patch + ]; + $json = json_encode(value: ['version' => $version]); + + echo "File version:\t\t$fileVersion" . PHP_EOL; + echo "File build number:\t$fileBuildNumber" . PHP_EOL; + + if ($update) { + $this->settingsRepository->set(name: 'version', value: $json); + $this->settingsRepository->set(name: 'buildnumber', value: $fileBuildNumber); + } + $currentDBVersion = $this->settingsRepository->findByName(name: 'version'); + $dbVersion = json_decode($currentDBVersion); + + $currentDBBuildnumber= $this->settingsRepository->findByName(name: 'buildnumber'); + echo "DB version:\t\t"; + echo $dbVersion->version->major . '.'; + echo $dbVersion->version->minor . '.'; + echo $dbVersion->version->patch . PHP_EOL; + echo "DB build number:\t$currentDBBuildnumber" . PHP_EOL; } private function dynDnyUpdate(): void diff --git a/src/Controller/ConfigController.php b/src/Controller/ConfigController.php index bd65ce7..a7e2ef1 100644 --- a/src/Controller/ConfigController.php +++ b/src/Controller/ConfigController.php @@ -2,9 +2,6 @@ namespace App\Controller; -/** - * - */ class ConfigController { private array $config; diff --git a/src/Entity/Apikey.php b/src/Entity/Apikey.php index 08fd863..fdc3010 100644 --- a/src/Entity/Apikey.php +++ b/src/Entity/Apikey.php @@ -5,20 +5,16 @@ namespace App\Entity; use App\Controller\ConfigController; use App\Controller\EncryptionController; use Exception; -use SodiumException; -/** - * - */ class Apikey { - public function __construct( private int $id = 0, private string $name = '', private string $apikey = '', private string $apikeyPrefix = '', - private readonly string $passphrase = '' + private readonly string $passphrase = '', + private string $createdAt = '' ) { if ($this->passphrase) { @@ -31,82 +27,45 @@ class Apikey try { $this->apikey = $encryptionController->safeEncrypt(message: $this->passphrase, key: $encryptionKey); - } catch (Exception|SodiumException $e) { + } catch (Exception $e) { exit($e->getMessage() . PHP_EOL); } } } - /** - * @return string - */ - public function getPassphrase(): string + public function getCreatedAt(): string { - return $this->passphrase; + return $this->createdAt; } - - /** - * @return String - */ public function getApikey(): string { return $this->apikey; } - /** - * @return string - */ public function getApikeyPrefix(): string { return $this->apikeyPrefix; } - /** - * @return int - */ public function getId(): int { return $this->id; } - /** - * @param int $id - */ - public function setId(int $id): void - { - $this->id = $id; - } - - /** - * @return String - */ public function getName(): string { return $this->name; } - /** - * @param string $apikeyPrefix - */ - public function setApikeyPrefix(string $apikeyPrefix): void - { - $this->apikeyPrefix = $apikeyPrefix; - } - /** - * @param String $apiToken - */ public function setApikey(string $apikey): void { $this->apikey = $apikey; } - /** - * @param String $name - */ public function setName(string $name): void { $this->name = $name; diff --git a/src/Controller/DatabaseConnection.php b/src/Provider/DatabaseConnection.php similarity index 96% rename from src/Controller/DatabaseConnection.php rename to src/Provider/DatabaseConnection.php index ba4f3a4..3113c54 100644 --- a/src/Controller/DatabaseConnection.php +++ b/src/Provider/DatabaseConnection.php @@ -1,6 +1,6 @@ databaseConnection->getConnection()->prepare(query: $sql); $statement->execute(); - $apikeys = []; + $apiKeys = []; while ($result = $statement->fetch()) { - $apikey = new Apikey(id: $result['id'], name: $result['name'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix']); - $apikeys[] = $apikey; + $apikey = new Apikey(id: $result['id'], name: $result['name'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix'], createdAt: $result['created_at']); + $apiKeys[] = $apikey; } - return $apikeys; + return $apiKeys; } catch (PDOException $e) { exit($e->getMessage()); } diff --git a/src/Repository/DomainRepository.php b/src/Repository/DomainRepository.php index ab85776..f184018 100644 --- a/src/Repository/DomainRepository.php +++ b/src/Repository/DomainRepository.php @@ -3,7 +3,7 @@ namespace App\Repository; use App\Controller\ConfigController; -use App\Controller\DatabaseConnection; +use App\Provider\DatabaseConnection; use App\Entity\Domain; use Monolog\Logger; use PDO; diff --git a/src/Repository/DynDNSRepository.php b/src/Repository/DynDNSRepository.php index 142051f..9f4a63d 100644 --- a/src/Repository/DynDNSRepository.php +++ b/src/Repository/DynDNSRepository.php @@ -2,7 +2,7 @@ namespace App\Repository; -use App\Controller\DatabaseConnection; +use App\Provider\DatabaseConnection; use App\Entity\DynDNS; use Monolog\Logger; use PDO; diff --git a/src/Repository/NameserverRepository.php b/src/Repository/NameserverRepository.php index b73ea7c..67ad28c 100644 --- a/src/Repository/NameserverRepository.php +++ b/src/Repository/NameserverRepository.php @@ -2,7 +2,7 @@ namespace App\Repository; -use App\Controller\DatabaseConnection; +use App\Provider\DatabaseConnection; use App\Entity\Nameserver; use PDO; use PDOException; diff --git a/src/Repository/PanelRepository.php b/src/Repository/PanelRepository.php index 19e2b16..f259aed 100644 --- a/src/Repository/PanelRepository.php +++ b/src/Repository/PanelRepository.php @@ -2,7 +2,7 @@ namespace App\Repository; -use App\Controller\DatabaseConnection; +use App\Provider\DatabaseConnection; use App\Entity\Panel; use PDO; use PDOException; diff --git a/src/Repository/SettingsRepository.php b/src/Repository/SettingsRepository.php new file mode 100644 index 0000000..68ea2dc --- /dev/null +++ b/src/Repository/SettingsRepository.php @@ -0,0 +1,63 @@ +databaseConnection->getConnection()->prepare(query: $sql); + $statement->bindParam(param: ':name', var: $name); + $statement->execute(); + if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) { + return $result['value']; + } else { + return false; + } + } catch (PDOException $e) { + exit($e->getMessage()); + } + } + + public function set(string $name, string $value): int + { + + $sql = " + INSERT INTO " . DatabaseConnection::TABLE_SETTINGS . " (name, value) + VALUES (:name, :value) + ON DUPLICATE KEY UPDATE + value = :value + "; + + try { + $statement = $this->databaseConnection->getConnection()->prepare(query: $sql); + $statement->bindParam(param: ':name', var: $name); + $statement->bindParam(param: ':value', var: $value); + + $statement->execute(); + return intval(value: $this->databaseConnection->getConnection()->lastInsertId()); + } catch (PDOException $e) { + exit($e->getMessage()); + } + } + +} diff --git a/src/Controller/BindAPI.php b/src/Service/BindAPI.php similarity index 94% rename from src/Controller/BindAPI.php rename to src/Service/BindAPI.php index 25cb875..dd659fb 100755 --- a/src/Controller/BindAPI.php +++ b/src/Service/BindAPI.php @@ -1,9 +1,13 @@