Compare commits

...

10 Commits

Author SHA1 Message Date
tracer 79942030fa added getSelf 2022-10-08 10:58:18 +02:00
tracer 6cca02b1cf added SodiumException 2022-10-08 10:57:56 +02:00
tracer 28a9e4ac08 renamed api_token to apikey 2022-10-08 10:56:45 +02:00
tracer 9985e2e896 removed unused enum. 2022-10-08 10:56:07 +02:00
tracer 2965e9b7ce added options for testing 2022-10-08 10:55:25 +02:00
tracer 91ea53275e improves checkWebmail 2022-10-08 10:54:55 +02:00
tracer 2e1f1ac8b1 added fileGetContents 2022-10-08 10:53:31 +02:00
tracer f66298842a initial commit 2022-10-08 10:52:16 +02:00
tracer 5d6a0b426a initial commit 2022-10-08 10:52:03 +02:00
tracer 78a1f353af initial commit 2022-10-08 10:51:54 +02:00
10 changed files with 711 additions and 410 deletions

View File

@ -7,108 +7,140 @@ use UnhandledMatchError;
error_reporting(error_level: E_ALL); error_reporting(error_level: E_ALL);
/** /**
* *
*/ */
class ApiController class ApiController
{ {
/**
* @param String $requestType
* @param String $serverName
* @param int $versionIP
* @param String $apiKey
* @param String $command
* @param String $serverType
* @param array $body
*
* @return array
*/
function sendCommand(string $requestType, string $serverName, int $versionIP, string $apiKey, string $command, string $serverType, array $body = []): array
{
$error = false;
$curl = curl_init();
try { /**
match ($serverType) { * @param String $requestType
'panel' => curl_setopt(handle: $curl, option: CURLOPT_URL, value: "https://$serverName/api/v2/" . $command), * @param String $serverName
'nameserver' => curl_setopt(handle: $curl, option: CURLOPT_URL, value: "https://$serverName/api/" . $command) * @param int $versionIP
}; * @param String $apiKey
} catch (UnhandledMatchError) { * @param String $command
echo 'Unhandled match: ' . $serverType; * @param String $serverType
} * @param array $body
*
curl_setopt(handle: $curl, option: CURLOPT_RETURNTRANSFER, value: 1); * @return array
curl_setopt(handle: $curl, option: CURLOPT_TIMEOUT_MS, value: 19999); */
curl_setopt(handle: $curl, option: CURLOPT_HTTP_VERSION, value: CURL_HTTP_VERSION_2TLS); function sendCommand(string $requestType, string $serverName, int $versionIP, string $apiKey, string $command, string $serverType, array $body = []): array
{
if ($versionIP == 4) { $error = false;
curl_setopt(handle: $curl, option: CURLOPT_IPRESOLVE, value: CURL_IPRESOLVE_V4); $curl = curl_init();
} else {
curl_setopt(handle: $curl, option: CURLOPT_IPRESOLVE, value: CURL_IPRESOLVE_V6); try {
} match ($serverType) {
'panel' => curl_setopt(handle: $curl, option: CURLOPT_URL, value: "https://$serverName/api/v2/" . $command),
curl_setopt(handle: $curl, option: CURLOPT_HTTPHEADER, value: ["X-API-Key:$apiKey"]); 'nameserver' => curl_setopt(handle: $curl, option: CURLOPT_URL, value: "https://$serverName/api/" . $command)
};
if ($requestType == "POST") { } catch (UnhandledMatchError) {
curl_setopt(handle: $curl, option: CURLOPT_POST, value: true); echo 'Unhandled match: ' . $serverType;
curl_setopt(handle: $curl, option: CURLOPT_POSTFIELDS, value: $body); }
}
if ($requestType == "PUT") { curl_setopt(handle: $curl, option: CURLOPT_RETURNTRANSFER, value: 1);
curl_setopt(handle: $curl, option: CURLOPT_CUSTOMREQUEST, value: 'PUT'); curl_setopt(handle: $curl, option: CURLOPT_TIMEOUT_MS, value: 19999);
curl_setopt(handle: $curl, option: CURLOPT_POSTFIELDS, value: json_encode(value: $body)); curl_setopt(handle: $curl, option: CURLOPT_HTTP_VERSION, value: CURL_HTTP_VERSION_2TLS);
}
if ($versionIP == 4) {
curl_setopt(handle: $curl, option: CURLOPT_CUSTOMREQUEST, value: $requestType); curl_setopt(handle: $curl, option: CURLOPT_IPRESOLVE, value: CURL_IPRESOLVE_V4);
} else {
if ($resultJSON = curl_exec(handle: $curl)) { curl_setopt(handle: $curl, option: CURLOPT_IPRESOLVE, value: CURL_IPRESOLVE_V6);
$httpResponse = curl_getinfo(handle: $curl)['http_code']; }
switch ($httpResponse) { curl_setopt(handle: $curl, option: CURLOPT_HTTPHEADER, value: ["X-API-Key:$apiKey"]);
case 200:
$apiResult = json_decode(json: $resultJSON); if ($requestType == "POST") {
if ($command == "ping") { curl_setopt(handle: $curl, option: CURLOPT_POST, value: true);
if ($apiResult->response == "pong") { curl_setopt(handle: $curl, option: CURLOPT_POSTFIELDS, value: $body);
$result = $apiResult->response; }
} else { if ($requestType == "PUT") {
$result = $apiResult; curl_setopt(handle: $curl, option: CURLOPT_CUSTOMREQUEST, value: 'PUT');
} curl_setopt(handle: $curl, option: CURLOPT_POSTFIELDS, value: json_encode(value: $body));
} else { }
$result = $resultJSON;
} curl_setopt(handle: $curl, option: CURLOPT_CUSTOMREQUEST, value: $requestType);
break;
case 400: if ($resultJSON = curl_exec(handle: $curl)) {
$result = $resultJSON; $httpResponse = curl_getinfo(handle: $curl)['http_code'];
break;
case 401: switch ($httpResponse) {
$result = 'Missing or wrong API Key'; case 200:
$error = true; $apiResult = json_decode(json: $resultJSON);
break; if ($command == "ping") {
case 404: if ($apiResult->response == "pong") {
$result = '404 Not Found'; $result = $apiResult->response;
break; } else {
case 500: $result = $apiResult;
$result = 'server error'; }
break; } else {
default: $result = $resultJSON;
$result = 'Unhandled error: ' . $httpResponse; }
} break;
} else { case 400:
$error = true; $result = $resultJSON;
$result = curl_error(handle: $curl); break;
} case 401:
$result = 'Missing or wrong API Key';
$info = curl_getinfo(handle: $curl); $error = true;
$responseTime = $info['total_time']; break;
case 404:
curl_close(handle: $curl); $result = '404 Not Found';
return [ break;
'responseTime' => $responseTime, case 500:
'error' => $error, $result = 'server error';
'data' => $result, break;
'header' => $httpResponse ?? '' default:
]; $result = 'Unhandled error: ' . $httpResponse;
} }
} else {
$error = true;
$result = curl_error(handle: $curl);
}
$info = curl_getinfo(handle: $curl);
$responseTime = $info['total_time'];
curl_close(handle: $curl);
return [
'responseTime' => $responseTime,
'error' => $error,
'data' => $result,
'header' => $httpResponse ?? ''
];
}
function fileGetContents(string $url, int $versionIP): ?array
{
$curl = curl_init(url: $url);
$options = array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_ENCODING => "",
CURLOPT_AUTOREFERER => true,
CURLOPT_CONNECTTIMEOUT => 120,
CURLOPT_TIMEOUT => 120,
CURLOPT_MAXREDIRS => 10,
);
if ($versionIP == 4) {
curl_setopt(handle: $curl, option: CURLOPT_IPRESOLVE, value: CURL_IPRESOLVE_V4);
} else {
curl_setopt(handle: $curl, option: CURLOPT_IPRESOLVE, value: CURL_IPRESOLVE_V6);
}
curl_setopt_array(handle: $curl, options: $options);
$content = curl_exec(handle: $curl);
$error = curl_errno(handle: $curl);
$errorMessage = curl_error(handle: $curl);
$header = curl_getinfo(handle: $curl);
curl_close(handle: $curl);
$header['error'] = $error;
$header['errorMessage'] = $errorMessage;
$header['content'] = $content;
return $header;
}
} }

View File

@ -1745,7 +1745,7 @@ class CLIController
$quiet = $this->configController->getConfig(configKey: 'quiet'); $quiet = $this->configController->getConfig(configKey: 'quiet');
$verbose = $this->configController->getConfig(configKey: 'verbose'); $verbose = $this->configController->getConfig(configKey: 'verbose');
if (empty($this->arguments[1])) { if (empty($this->arguments[1])) {
if (!$quiet) { if (!$quiet) {
echo COLOR_DEFAULT . 'You need to supply a domain name.' . PHP_EOL; echo COLOR_DEFAULT . 'You need to supply a domain name.' . PHP_EOL;
} }
@ -1755,7 +1755,7 @@ class CLIController
} }
if (!$quiet) { if (!$quiet) {
echo COLOR_DEFAULT . 'Checking domain ' . COLOR_YELLOW . $domainName . COLOR_DEFAULT . PHP_EOL; echo COLOR_DEFAULT . 'Checking domain ' . COLOR_YELLOW . $domainName . COLOR_DEFAULT . '.' . PHP_EOL;
} }
if (!$domain = $this->domainRepository->findByName(name: $domainName)) { if (!$domain = $this->domainRepository->findByName(name: $domainName)) {
@ -1780,8 +1780,8 @@ class CLIController
$webmailDomain = 'webmail.' . $domainName; $webmailDomain = 'webmail.' . $domainName;
if (!empty($panel->getAAAA())) { if (!empty($panel->getAAAA())) {
if ($verbose) { if (!$quiet && $verbose) {
echo 'Check using IPv6: ' . COLOR_YELLOW . $panel->getAaaa() . COLOR_DEFAULT . PHP_EOL; echo 'Check using IPv6: ' . COLOR_YELLOW . $panel->getAaaa() . '.' . COLOR_DEFAULT . PHP_EOL;
} }
$result = $this->apiController->sendCommand( $result = $this->apiController->sendCommand(
requestType: 'GET', requestType: 'GET',
@ -1791,7 +1791,7 @@ class CLIController
command: 'domains/name/' . $webmailDomain, command: 'domains/name/' . $webmailDomain,
serverType: 'panel'); serverType: 'panel');
} else { } else {
if ($verbose) { if (!$quiet && $verbose) {
echo 'Check using IPv4: ' . COLOR_YELLOW . $panel->getA() . COLOR_DEFAULT . PHP_EOL; echo 'Check using IPv4: ' . COLOR_YELLOW . $panel->getA() . COLOR_DEFAULT . PHP_EOL;
} }
$result = $this->apiController->sendCommand( $result = $this->apiController->sendCommand(
@ -1800,24 +1800,70 @@ class CLIController
versionIP: 4, versionIP: 4,
apiKey: $decryptedKey, apiKey: $decryptedKey,
command: 'domains/name/' . $webmailDomain, command: 'domains/name/' . $webmailDomain,
serverType: 'panel' ); serverType: 'panel');
} }
if ($result['header'] === 404) { if ($result['header'] === 404) {
if (!$quiet) { if (!$quiet) {
echo 'The domain ' . COLOR_YELLOW . $webmailDomain . COLOR_DEFAULT . ' doesn\'t exist.' . PHP_EOL; echo 'The domain ' . COLOR_YELLOW . $webmailDomain . COLOR_DEFAULT . " doesn't exist." . PHP_EOL;
} }
exit(1); exit(1);
} else { } else {
if(!$quiet) { if (!$quiet) {
echo 'Found ' . COLOR_YELLOW . $webmailDomain . COLOR_DEFAULT . '.' . PHP_EOL; echo 'Found ' . COLOR_YELLOW . $webmailDomain . COLOR_DEFAULT . '.' . PHP_EOL;
} }
} }
if ($v4 = dns_get_record(hostname: $webmailDomain, type: DNS_A)[0]) {
if (!$quiet) {
echo "Found IPv4 entry: " . COLOR_YELLOW . $v4['ip'] . COLOR_DEFAULT . '.' .PHP_EOL;
}
$v4Test = $this->apiController->fileGetContents(url: $webmailDomain, versionIP: 4);
if ($v4Test['error']) {
if (!$quiet) {
echo 'There was an error: ' . COLOR_YELLOW . $v4Test['errorMessage'] . COLOR_DEFAULT . '.';
}
exit(1);
} else {
if (!$quiet) {
echo 'Successfully connected to webserver via ' . COLOR_YELLOW . 'IPv4' . COLOR_DEFAULT . '.' . PHP_EOL;
}
}
} else {
if (!$quiet) {
echo "Found no IPv4 entry for " . COLOR_YELLOW . $webmailDomain . COLOR_DEFAULT . PHP_EOL;
}
}
if ($v6 = dns_get_record(hostname: $webmailDomain, type: DNS_AAAA)[0]) {
if (!$quiet) {
echo "Found IPv6 entry: " . COLOR_YELLOW . $v6['ipv6'] . COLOR_DEFAULT . '.' . PHP_EOL;
}
$v6Test = $this->apiController->fileGetContents(url: $webmailDomain, versionIP: 6);
if ($v6Test['error']) {
if (!$quiet) {
echo 'There was an error: ' . COLOR_YELLOW . $v6Test['errorMessage'] . COLOR_DEFAULT . '.';
}
exit(1);
} else {
if (!$quiet) {
echo 'Successfully connected to webserver via ' . COLOR_YELLOW . 'IPv6' . COLOR_DEFAULT . '.' . PHP_EOL;
}
}
} else {
if (!$quiet) {
echo "Found no IPv6 entry for " . COLOR_YELLOW . $webmailDomain . COLOR_DEFAULT . '.' . PHP_EOL;
}
}
// TODO check that at least IPv4 or IP6 exists?
$domainData = json_decode(json: $result['data']); $domainData = json_decode(json: $result['data']);
$apacheData = $domainData->apache; $apacheData = $domainData->apache;
$httpDirectives = $apacheData->http_directives;
$httpsDirectives = $apacheData->https_directives . PHP_EOL; $httpsDirectives = $apacheData->https_directives . PHP_EOL;
if (!str_contains(haystack: $httpsDirectives, needle: '# bindAPI - webmailer')) { if (!str_contains(haystack: $httpsDirectives, needle: '# bindAPI - webmailer')) {
@ -1825,25 +1871,54 @@ class CLIController
echo 'Generated config is missing.' . PHP_EOL; echo 'Generated config is missing.' . PHP_EOL;
} }
exit(1); exit(1);
} else {
if (!$quiet) {
echo 'Generated config is valid.' . PHP_EOL;
}
exit(0);
} }
} }
public function webmailCreate() /**
* @return void
*/
public function webmailCreate(): bool
{ {
// TODO
/*
$webmailConfig = '# bindAPI - webmailer' . PHP_EOL; $webmailConfig = '# bindAPI - webmailer' . PHP_EOL;
$webmailConfig .= 'SSLProxyEngine On' . PHP_EOL; $webmailConfig .= 'SSLProxyEngine On' . PHP_EOL;
$webmailConfig .= 'ProxyPass /.well-known/ !' . PHP_EOL; $webmailConfig .= 'ProxyPass /.well-known/ !' . PHP_EOL;
$webmailConfig .= 'ProxyPass "/" "https://' . $panel->getName() . '/webmail/"' . PHP_EOL; $webmailConfig .= 'ProxyPass "/" "https://webmail' . $panel->getName() . '"' . PHP_EOL;
$webmailConfig .= '## bindAPI - webmailer' . PHP_EOL; $webmailConfig .= '## bindAPI - webmailer' . PHP_EOL;
echo $webmailConfig; echo $webmailConfig;
//$httpsDirectives += $w //$httpsDirectives += $w
*/
} }
private function checkMail(): void
{
}
private function checksVersion(): void
{
}
private function dynDnyUpdate(): void
{
}
private function dynDnsDelete(): void
{
}
private function webmailDelete(): void
{
}
} }

View File

@ -2,7 +2,7 @@
namespace App\Controller; namespace App\Controller;
error_reporting(error_level: E_ALL); //error_reporting(error_level: E_ALL);
use PDO; use PDO;
@ -15,31 +15,32 @@ class DatabaseConnection
{ {
private PDO $dbConnection; private PDO $dbConnection;
const TABLE_PREFIX = ''; const TABLE_PREFIX = '';
const TABLE_DOMAINS = self::TABLE_PREFIX . "domains"; const TABLE_DOMAINS = self::TABLE_PREFIX . "domains";
const TABLE_NAMESERVERS = self::TABLE_PREFIX . "nameservers"; const TABLE_NAMESERVERS = self::TABLE_PREFIX . "nameservers";
const TABLE_PANELS = self::TABLE_PREFIX . "panels"; const TABLE_PANELS = self::TABLE_PREFIX . "panels";
const TABLE_APIKEYS = self::TABLE_PREFIX . "apikeys"; const TABLE_APIKEYS = self::TABLE_PREFIX . "apikeys";
const TABLE_DYNDNS = self::TABLE_PREFIX . "dyndns"; const TABLE_DYNDNS = self::TABLE_PREFIX . "dyndns";
public function __construct(private readonly ConfigController $configController) public function __construct(private readonly ConfigController $configController)
{ {
$dbHost = $this->configController->getConfig(configKey: 'dbHost'); $dbHost = $this->configController->getConfig(configKey: 'dbHost');
$dbPort = $this->configController->getConfig(configKey: 'dbPort'); $dbPort = $this->configController->getConfig(configKey: 'dbPort');
$dbDatabase = $this->configController->getConfig(configKey: 'dbDatabase'); $dbDatabase = $this->configController->getConfig(configKey: 'dbDatabase');
$dbUser = $this->configController->getConfig(configKey: 'dbUser'); $dbUser = $this->configController->getConfig(configKey: 'dbUser');
$dbPassword = $this->configController->getConfig(configKey: 'dbPassword'); $dbPassword = $this->configController->getConfig(configKey: 'dbPassword');
$this->dbConnection = new PDO(
dsn: "mysql:host=$dbHost;port=$dbPort;charset=utf8mb4;dbname=$dbDatabase",
username: $dbUser,
password: $dbPassword
);
if (!$this->configController->getConfig(configKey: 'test')) { if (!$this->configController->getConfig(configKey: 'test')) {
// TODO create config => encryption key // TODO create config => encryption key
try { try {
$this->dbConnection = new PDO(
dsn: "mysql:host=$dbHost;port=$dbPort;charset=utf8mb4;dbname=$dbDatabase",
username: $dbUser,
password: $dbPassword
);
$sql = "SHOW TABLES"; $sql = "SHOW TABLES";
$statement = $this->dbConnection->prepare(query: $sql); $statement = $this->dbConnection->prepare(query: $sql);
$statement->execute(); $statement->execute();
$result = $statement->fetch(); $result = $statement->fetch();
@ -47,7 +48,7 @@ class DatabaseConnection
// ALTER TABLE `domains` ADD `panel_id` INT NULL AFTER `id`; // ALTER TABLE `domains` ADD `panel_id` INT NULL AFTER `id`;
echo 'Error: Cannot find tables.' . PHP_EOL; echo 'Error: Cannot find tables.' . PHP_EOL;
if (confirm(message: 'Should I try to create them?')) { if (confirm(message: 'Should I try to create them?')) {
$sql = " $sql = "
CREATE TABLE `apikeys` ( CREATE TABLE `apikeys` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
@ -58,7 +59,7 @@ class DatabaseConnection
$statement = $this->dbConnection->prepare(query: $sql); $statement = $this->dbConnection->prepare(query: $sql);
$statement->execute(); $statement->execute();
$sql = " $sql = "
CREATE TABLE `domains` ( CREATE TABLE `domains` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
@ -68,7 +69,7 @@ class DatabaseConnection
$statement = $this->dbConnection->prepare(query: $sql); $statement = $this->dbConnection->prepare(query: $sql);
$statement->execute(); $statement->execute();
$sql = " $sql = "
CREATE TABLE `nameservers` ( CREATE TABLE `nameservers` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
@ -80,7 +81,7 @@ class DatabaseConnection
$statement = $this->dbConnection->prepare(query: $sql); $statement = $this->dbConnection->prepare(query: $sql);
$statement->execute(); $statement->execute();
$sql = " $sql = "
CREATE TABLE `panels` ( CREATE TABLE `panels` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
@ -133,7 +134,7 @@ class DatabaseConnection
*/ */
function generatePassword(int $length = 8): string function generatePassword(int $length = 8): string
{ {
$chars = '23456789bcdfhkmnprstvzBCDFHJKLMNPRSTVZ'; $chars = '23456789bcdfhkmnprstvzBCDFHJKLMNPRSTVZ';
$shuffled = str_shuffle(string: $chars); $shuffled = str_shuffle(string: $chars);
return mb_substr(string: $shuffled, start: 0, length: $length); return mb_substr(string: $shuffled, start: 0, length: $length);
} }

View File

@ -2,10 +2,6 @@
namespace App\Entity; namespace App\Entity;
use App\Enums\PanelType;
/** /**
* *
*/ */

View File

@ -4,8 +4,8 @@ namespace App\Repository;
error_reporting(error_level: E_ALL); error_reporting(error_level: E_ALL);
use App\Controller\DatabaseConnection; use App\Controller\DatabaseConnection;
use App\Controller\EncryptionController;
use App\Entity\Apikey; use App\Entity\Apikey;
use Exception;
use PDO; use PDO;
use PDOException; use PDOException;
@ -14,7 +14,7 @@ use PDOException;
*/ */
class ApikeyRepository class ApikeyRepository
{ {
public function __construct(private readonly DatabaseConnection $databaseConnection) public function __construct(private readonly DatabaseConnection $databaseConnection, EncryptionController $encryptionController)
{} {}
@ -23,8 +23,9 @@ class ApikeyRepository
*/ */
public function findAll(): bool|array public function findAll(): bool|array
{ {
$sql = " $sql = "
SELECT id, name, api_token_prefix, api_token SELECT id, name, apikey_prefix, apikey
FROM " . DatabaseConnection::TABLE_APIKEYS; FROM " . DatabaseConnection::TABLE_APIKEYS;
try { try {
@ -34,7 +35,7 @@ class ApikeyRepository
$apikeys = []; $apikeys = [];
while ($result = $statement->fetch()) { while ($result = $statement->fetch()) {
$apikey = new Apikey(name: $result['name'], apiTokenPrefix: $result['api_token_prefix'], apiToken: $result['api_token'], id: $result['id']); $apikey = new Apikey(id: $result['id'], name: $result['name'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix']);
$apikeys[] = $apikey; $apikeys[] = $apikey;
} }
return $apikeys; return $apikeys;
@ -47,12 +48,12 @@ class ApikeyRepository
/** /**
* @param Int $id * @param Int $id
* *
* @return \App\Entity\Apikey|bool * @return Apikey|bool
*/ */
public function findByID(Int $id): Apikey|bool public function findByID(Int $id): Apikey|bool
{ {
$sql = " $sql = "
SELECT id, name, api_token_prefix, api_token SELECT id, name, apikey_prefix, apikey
FROM " . DatabaseConnection::TABLE_APIKEYS . " FROM " . DatabaseConnection::TABLE_APIKEYS . "
WHERE id = :id; WHERE id = :id;
"; ";
@ -62,7 +63,7 @@ class ApikeyRepository
$statement->bindParam(param: ':id', var: $id); $statement->bindParam(param: ':id', var: $id);
$statement->execute(); $statement->execute();
if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) { if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
return new Apikey(name: $result['name'], apiTokenPrefix: $result['api_token_prefix'], apiToken: $result['api_token'], id: $result['id']); return new Apikey(id: $result['id'], name: $result['name'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix']);
} else { } else {
return false; return false;
} }
@ -75,21 +76,21 @@ class ApikeyRepository
/** /**
* @param String $prefix * @param String $prefix
* *
* @return \App\Entity\Apikey|bool * @return Apikey|bool
*/ */
public function findByPrefix(String $prefix): Apikey|bool public function findByPrefix(String $prefix): Apikey|bool
{ {
$sql = " $sql = "
SELECT id, name, api_token_prefix, api_token SELECT id, name, apikey_prefix, apikey
FROM " . DatabaseConnection::TABLE_APIKEYS . " FROM " . DatabaseConnection::TABLE_APIKEYS . "
WHERE api_token_prefix = :prefix"; WHERE apikey_prefix = :prefix";
try { try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql); $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->bindParam(param: ':prefix', var: $prefix); $statement->bindParam(param: ':prefix', var: $prefix);
$statement->execute(); $statement->execute();
if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) { if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
return new Apikey(name: $result['name'], apiTokenPrefix: $result['api_token_prefix'], apiToken: $result['api_token'], id: $result['id']); return new Apikey(id: $result['id'], name: $result['name'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix']);
} else { } else {
return false; return false;
} }
@ -97,56 +98,51 @@ class ApikeyRepository
exit($e->getMessage()); exit($e->getMessage());
} }
} }
/** /**
* @return array|void * @param Apikey $apikey
*/ * @return int
public function create(String $name = '') */
{ public function insert(ApiKey $apikey): int
$tokenPrefix = uniqid(); {
$result['tokenPrefix'] = $tokenPrefix;
try { $name = $apikey->getName();
$key = bin2hex(string: random_bytes(length: 24)); $apikeyPrefix = $apikey->getApikeyPrefix();
$result['key'] = $key; $apikeyValue = $apikey->getApikey();
} catch (Exception $e) {
echo $e->getMessage() . PHP_EOL;
exit(1);
}
$token = password_hash(password: $tokenPrefix . '.' . $key, algo: PASSWORD_ARGON2ID);
$sql = " $sql = "
INSERT INTO " . DatabaseConnection::TABLE_APIKEYS . " (name, api_token_prefix, api_token) INSERT INTO " . DatabaseConnection::TABLE_APIKEYS . " (name, apikey_prefix, apikey)
VALUES (:name, :token_prefix, :token)"; VALUES (:name, :apikey_prefix, :apikey)";
try { try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql); $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->bindParam(param: ':token_prefix', var: $tokenPrefix); $statement->bindParam(param: ':name', var: $name);
$statement->bindParam(param: ':token', var: $token); $statement->bindParam(param: ':apikey_prefix', var: $apikeyPrefix);
$statement->bindParam(param: ':name', var: $name); $statement->bindParam(param: ':apikey', var: $apikeyValue);
$statement->execute(); $statement->execute();
$result['row'] = $this->databaseConnection->getConnection()->lastInsertId(); return intval(value: $this->databaseConnection->getConnection()->lastInsertId());
return $result;
} catch (PDOException $e) { } catch (PDOException $e) {
exit($e->getMessage()); exit($e->getMessage());
} }
} }
/** /**
* @param Int $id * @param Apikey $apikey
* @param String $name * @return false|int
* */
* @return false|int public function update(Apikey $apikey): bool|int
*/
public function update(int $id, string $name): bool|int
{ {
$current = $this->findByID(id: $id); $id = $apikey->getId();
$name = $apikey->getName();
if (empty($name)) {
$name = $current['name']; $current = $this->findByID(id: $id);
}
if (empty($name)) {
$name = $current->getName();
}
$sql = " $sql = "
UPDATE " . DatabaseConnection::TABLE_APIKEYS . " SET UPDATE " . DatabaseConnection::TABLE_APIKEYS . " SET
name = :name name = :name

View File

@ -6,249 +6,257 @@ use App\Controller\DatabaseConnection;
use App\Entity\Nameserver; use App\Entity\Nameserver;
use PDO; use PDO;
use PDOException; use PDOException;
use SodiumException;
/** /**
* *
*/ */
class NameserverRepository class NameserverRepository
{ {
public function __construct(private readonly DatabaseConnection $databaseConnection) public function __construct(private readonly DatabaseConnection $databaseConnection)
{ {
} // no body
}
/**
* @return array /**
*/ * @return array|null
public function findAll(): array */
{ public function findAll(): ?array
$nameservers = []; {
$sql = " $nameservers = [];
SELECT id, name, a, aaaa, apikey $sql = "
SELECT id, name, a, aaaa, apikey, apikey_prefix
FROM " . DatabaseConnection::TABLE_NAMESERVERS . " FROM " . DatabaseConnection::TABLE_NAMESERVERS . "
ORDER BY name"; ORDER BY name";
try { try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql); $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->execute(); $statement->execute();
while ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) { while ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
$nameserver = new Nameserver(name: $result['name'], id: $result['id'], a: $result['a'], aaaa: $result['aaaa'], apikey: $result['apikey']); $nameserver = new Nameserver(name: $result['name'], id: $result['id'], a: $result['a'], aaaa: $result['aaaa'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix']);
$nameservers[] = $nameserver; $nameservers[] = $nameserver;
} }
return $nameservers; return $nameservers;
} catch (PDOException $e) { } catch (PDOException $e) {
exit($e->getMessage()); exit($e->getMessage());
} }
} }
/** /**
* @return \App\Entity\Nameserver * @return Nameserver|null
*/ */
public function findFirst(): Nameserver public function findFirst(): ?Nameserver
{ {
$nameservers = []; $sql = "
$sql = " SELECT id, name, a, aaaa, apikey, apikey_prefix
SELECT id, name, a, aaaa, apikey
FROM " . DatabaseConnection::TABLE_NAMESERVERS . " FROM " . DatabaseConnection::TABLE_NAMESERVERS . "
ORDER BY name"; ORDER BY name";
try { try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql); $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->execute(); $statement->execute();
$result = $statement->fetch(mode: PDO::FETCH_ASSOC); $result = $statement->fetch(mode: PDO::FETCH_ASSOC);
return new Nameserver(name: $result['name'], id: $result['id'], a: $result['a'], aaaa: $result['aaaa'], apikey: $result['apikey']); return new Nameserver(name: $result['name'], id: $result['id'], a: $result['a'], aaaa: $result['aaaa'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix']);
} catch (PDOException $e) { } catch (PDOException $e) {
exit($e->getMessage()); exit($e->getMessage());
} }
} }
/** /**
* @param int $id * @param int $id
* *
* @return null|\App\Entity\Nameserver * @return null|Nameserver
*/ */
public function findByID(int $id): ?Nameserver public function findByID(int $id): ?Nameserver
{ {
$sql = " $sql = "
SELECT id, name, a, aaaa, apikey SELECT id, name, a, aaaa, apikey, apikey_prefix
FROM . " . DatabaseConnection::TABLE_NAMESERVERS . " FROM . " . DatabaseConnection::TABLE_NAMESERVERS . "
WHERE id = :id"; WHERE id = :id";
try { try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql); $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->bindParam(param: ':id', var: $id); $statement->bindParam(param: ':id', var: $id);
$statement->execute(); $statement->execute();
if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) { if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
return new Nameserver(name: $result['name'], a: $result['a'], aaaa: $result['aaaa'], apikey: $result['apikey']); return new Nameserver(name: $result['name'], a: $result['a'], aaaa: $result['aaaa'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix']);
} else { } else {
return null; return null;
} }
} catch (PDOException $e) { } catch (PDOException $e) {
exit($e->getMessage()); exit($e->getMessage());
} }
} }
/** /**
* @param String $name * @param String $name
* *
* @return \App\Entity\Nameserver|bool * @return Nameserver|null
*/ */
public function findByName(string $name): Nameserver|bool public function findByName(string $name): ?Nameserver
{ {
$sql = " $sql = "
SELECT id, name, a, aaaa, apikey SELECT id, name, a, aaaa, apikey, apikey_prefix
FROM " . DatabaseConnection::TABLE_NAMESERVERS . " FROM " . DatabaseConnection::TABLE_NAMESERVERS . "
WHERE name = :name"; WHERE name = :name";
try { try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql); $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->bindParam(param: ':name', var: $name); $statement->bindParam(param: ':name', var: $name);
$statement->execute(); $statement->execute();
if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) { if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
return new Nameserver(name: $result['name'], a: $result['a'], aaaa: $result['aaaa']); return new Nameserver(name: $result['name'], a: $result['a'], aaaa: $result['aaaa'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix']);
} else { } else {
return false; return null;
} }
} catch (PDOException $e) { } catch (PDOException $e) {
exit($e->getMessage()); exit($e->getMessage());
} }
} }
/** /**
* @param \App\Entity\Nameserver $nameserver * @param Nameserver $nameserver
* *
* @return string|false * @return int|null
*/ */
public function insert(Nameserver $nameserver): bool|string public function insert(Nameserver $nameserver): ?int
{ {
$sql = " $name = $nameserver->getName();
INSERT INTO " . DatabaseConnection::TABLE_NAMESERVERS . " (name, a, aaaa, apikey) $a = $nameserver->getA();
VALUES (:name, :a, :aaaa, :apikey)"; $aaaa = $nameserver->getAaaa();
$apikey = $nameserver->getApikey();
try { $apikeyPrefix = $nameserver->getApikeyPrefix();
$name = $nameserver->getName();
$a = $nameserver->getA();
$aaaa = $nameserver->getAaaa(); $sql = "
$apikey = $nameserver->getApikey(); INSERT INTO " . DatabaseConnection::TABLE_NAMESERVERS . " (name, a, aaaa, apikey, apikey_prefix)
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql); VALUES (:name, :a, :aaaa, :apikey, :apikey_prefix)";
$statement->bindParam(param: ':name', var: $name);
$statement->bindParam(param: ':a', var: $a); try {
$statement->bindParam(param: ':aaaa', var: $aaaa); $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->bindParam(param: ':apikey', var: $apikey); $statement->bindParam(param: ':name', var: $name);
$statement->execute(); $statement->bindParam(param: ':a', var: $a);
$statement->bindParam(param: ':aaaa', var: $aaaa);
return $this->databaseConnection->getConnection()->lastInsertId(); $statement->bindParam(param: ':apikey', var: $apikey);
} catch (PDOException $e) { $statement->bindParam(param: ':apikey_prefix', var: $apikeyPrefix);
exit($e->getMessage()); $statement->execute();
}
} return intval(value: $this->databaseConnection->getConnection()->lastInsertId());
} catch (PDOException $e) {
exit($e->getMessage() . PHP_EOL);
/** }
* @param Int $id }
* @param String $name
* @param String $a
* @param String $aaaa /**
* @param String $apikey * @param Nameserver $nameserver
* * @return false|int
* @return false|int */
*/ public function update(Nameserver $nameserver): bool|int
public function update(int $id, string $name, string $a, string $aaaa, string $apikey): bool|int {
{ $id = $nameserver->getId();
$current = $this->findByID(id: $id); $name = $nameserver->getName();
$a = $nameserver->getA();
/* doesn't work $aaaa = $nameserver->getAaaa();
$statement = " $apikey = $nameserver->getApikey();
INSERT INTO domains(id, name, a, aaaa) $apikeyPrefix = $nameserver->getApikeyPrefix();
VALUES(:id, :name, :a, :aaaa) $passphrase = $nameserver->getPassphrase();
ON DUPLICATE KEY UPDATE
name=COALESCE(VALUES(name), :name), $current = $this->findByID(id: $id);
a=COALESCE(:a, a),
aaaa=COALESCE(:aaaa, aaaa)";
*/ if (empty($name)) {
$name = $current->getName();
if (empty($name)) { }
$name = $current->getName(); if (empty($a)) {
} $a = $current->getA();
if (empty($a)) { }
$a = $current->getA(); if (empty($aaaa)) {
} $aaaa = $current->getAaaa();
if (empty($aaaa)) { }
$aaaa = $current->getAaaa();
} if (empty($passphrase)) {
if (empty($apikey)) { $apikey = $current->getApikey();
$apikey = $current->getApikey(); $apikeyPrefix = $current->getApikeyPrefix();
} }
$sql = "
$sql = "
UPDATE " . DatabaseConnection::TABLE_NAMESERVERS . " SET UPDATE " . DatabaseConnection::TABLE_NAMESERVERS . " SET
name = :name, name = :name,
a = :a, a = :a,
aaaa = :aaaa, aaaa = :aaaa,
apikey = :apikey apikey = :apikey,
apikey_prefix = :apikey_prefix
WHERE id = :id"; WHERE id = :id";
try { try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql); $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->bindParam(param: 'id', var: $id); $statement->bindParam(param: 'id', var: $id);
$statement->bindParam(param: 'name', var: $name); $statement->bindParam(param: 'name', var: $name);
$statement->bindParam(param: 'a', var: $a); $statement->bindParam(param: 'a', var: $a);
$statement->bindParam(param: 'aaaa', var: $aaaa); $statement->bindParam(param: 'aaaa', var: $aaaa);
$statement->bindParam(param: 'apikey', var: $apikey); $statement->bindParam(param: 'apikey', var: $apikey);
$statement->execute(); $statement->bindParam(param: 'apikey_prefix', var: $apikeyPrefix);
$statement->execute();
return $statement->rowCount(); try {
} catch (PDOException $e) { sodium_memzero(string: $apikey);
echo $e->getMessage(); } catch(SodiumException $e) {
return false; die($e->getMessage() . PHP_EOL);
} }
} return intval(value: $statement->rowCount());
} catch (PDOException $e) {
echo $e->getMessage();
/** return false;
* @param $id }
* }
* @return int
*/
public function delete($id): int /**
{ * @param $id
$sql = " *
* @return int|null
*/
public function delete($id): ?int
{
$sql = "
DELETE FROM " . DatabaseConnection::TABLE_NAMESERVERS . " DELETE FROM " . DatabaseConnection::TABLE_NAMESERVERS . "
WHERE id = :id"; WHERE id = :id";
try { try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql); $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->bindParam(param: 'id', var: $id); $statement->bindParam(param: 'id', var: $id);
$statement->execute(); $statement->execute();
return $statement->rowCount(); return $statement->rowCount();
} catch (PDOException $e) { } catch (PDOException $e) {
exit($e->getMessage()); exit($e->getMessage());
} }
} }
/** /**
* @param String $field * @param String $field
* *
* @return int * @return int
*/ */
public function getLongestEntry(string $field): int public function getLongestEntry(string $field): int
{ {
$sql = " $sql = "
SELECT MAX(LENGTH(" . $field . ")) as length FROM " . DatabaseConnection::TABLE_NAMESERVERS; SELECT MAX(LENGTH(" . $field . ")) as length FROM " . DatabaseConnection::TABLE_NAMESERVERS;
try { try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql); $statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->execute(); $statement->execute();
$result = $statement->fetch(); $result = $statement->fetch();
return $result['length']; return $result['length'];
} catch (PDOException $e) { } catch (PDOException $e) {
exit($e->getMessage()); exit($e->getMessage());
} }
} }
} }

View File

@ -257,4 +257,16 @@ class PanelRepository
exit($e->getMessage()); exit($e->getMessage());
} }
} }
public function getSelf(): ?Panel
{
$panels = $this->findAll();
foreach ($panels as $panel) {
if ($panel->getSelf() === 'yes') {
return $panel;
}
}
return null;
}
} }

View File

@ -0,0 +1,124 @@
<?php
namespace Unit\Repository;
use App\Controller\ConfigController;
use App\Controller\DomainController;
use App\Entity\Domain;
use App\Repository\DomainRepository;
use DI\Container;
use DI\ContainerBuilder;
use Exception;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use PHPUnit\Framework\TestCase;
use Unit\Controller\BindApiControllerTest;
use function DI\autowire;
/**
*
*/
class DomainRepositoryTest extends BindApiControllerTest
{
private string $localZoneFile;
private string $localZonesDir;
private string $namedConfLocalFile;
/**
* @param int|string $dataName
*
* @throws Exception
* @internal This method is not covered by the backward compatibility promise for PHPUnit
*/
public function __construct(?string $name = null, array $data = [], $dataName = '')
{
parent::__construct(name: $name, data: $data, dataName: $dataName);
}
public function setUp(): void
{
$this->logger->info(message: 'Started DomainRepositoryTest');
}
public function tearDown(): void
{
$this->logger->info(message: 'Finished DomainRepositoryTest');
}
/**
*/
public function testInsert()
{
self::assertEquals(expected: true, actual: true);
/*
$domain = new Domain(name: 'inserttest.org', panel: 'keyhelp.lab.24unix.net');
$this->domainRepository->insert(domain: $domain);
$this->domainController->createSlaveZoneFile(domain: $domain);
// now get the persisted domain with id
$domainTest = $this->domainRepository->findByName(name: 'inserttest.org');
$this->assertIsNotBool(actual: $domainTest);
$this->assertEquals(expected: 'inserttest.org', actual: $domainTest->getName());
if ($namedConfLocal = file_get_contents(filename: $this->namedConfLocalFile)) {
$this->assertStringContainsString(needle: $this->localZoneFile, haystack: $namedConfLocal);
} else {
$this->fail(message: 'No permissions: ' . $this->namedConfLocalFile);
}
$this->assertNotFalse(condition: fileperms(filename: $this->localZoneFile));
$localZones = file_get_contents(filename: $this->localZoneFile);
$this->assertStringContainsString(needle: $domainTest->getName(), haystack: $localZones);
$zoneFile = $this->localZonesDir . $domain->getName();
$this->assertFileExists(filename: $zoneFile);
// clean up
$this->domainRepository->delete(domain: $domainTest);
*/
}
/**
*/
public function testDelete()
{
self::assertEquals(expected: true, actual: true);
/*
$domain = new Domain(name: 'inserttest.org', panel: 'keyhelp.lab.24unix.net');
$this->domainRepository->insert(domain: $domain);
$this->domainController->createSlaveZoneFile(domain: $domain);
$domainTest = $this->domainRepository->findByName(name: 'inserttest.org');
$this->assertIsNotBool(actual: $domainTest);
$this->assertEquals(expected: 'inserttest.org', actual: $domainTest->getName());
// domain is valid and created
// now delete and check for cleanup
$this->domainRepository->delete(domain: $domainTest);
$this->domainController->deleteZone(domain: $domainTest);
// check zone is removed
$this->assertNotFalse(condition: fileperms(filename: $this->localZoneFile));
$localZones = file_get_contents(filename: $this->localZoneFile);
$this->assertStringNotContainsString(needle: $domainTest->getName(), haystack: $localZones);
$zoneFile = $this->localZonesDir . $domain->getName();
$this->assertFileDoesNotExist(filename: $zoneFile);
*/
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace Unit\Repository;
use App\Entity\Nameserver;
use Unit\Controller\BindApiControllerTest;
/**
*
*/
class NameserverRepositoryTest extends BindApiControllerTest
{
/**
*/
public function testInsert()
{
$nameserver = new Nameserver(name: 'inserttest.org', a: '1.2.3.4', aaaa: '1bad::babe');
$this->nameserverRepository->insert(nameserver: $nameserver);
$nameserverTest = $this->nameserverRepository->findByName(name: 'inserttest.org');
$this->assertIsNotBool(actual: $nameserver);
$this->assertEquals(expected: 'inserttest.org', actual: $nameserverTest->getName());
// clean up
$this->nameserverRepository->delete(id: $nameserverTest->getId());
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace Unit\Repository;
use App\Controller\BindApiTestController;
use PHPUnit\Framework\TestCase;
use Unit\Controller\BindApiControllerTest;
/**
*
*/
class PanelRepositoryTest extends BindApiControllerTest
{
public function setUp(): void
{
}
public function tearDown(): void
{
}
public function testInsert()
{
self::assertEquals(expected: true, actual: true);
}
}