From 890bcc9434c347e9e9d1874d1b629c3775aa6d4e Mon Sep 17 00:00:00 2001 From: Jean-Yves <7360784+docjyJ@users.noreply.github.com> Date: Tue, 24 Sep 2024 19:43:53 +0200 Subject: [PATCH 1/7] use space for indent Signed-off-by: Jean-Yves <7360784+docjyJ@users.noreply.github.com> --- php/psalm.xml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/php/psalm.xml b/php/psalm.xml index 59601afad4e..1d05fb822ed 100644 --- a/php/psalm.xml +++ b/php/psalm.xml @@ -1,17 +1,17 @@ - - - - - + + + + + From d674ba4f6e2172b6ff3d1f5785534e444c4bd40c Mon Sep 17 00:00:00 2001 From: Jean-Yves <7360784+docjyJ@users.noreply.github.com> Date: Tue, 24 Sep 2024 19:51:18 +0200 Subject: [PATCH 2/7] Typesafe ConfigurationController.php Signed-off-by: Jean-Yves <7360784+docjyJ@users.noreply.github.com> --- .../Controller/ConfigurationController.php | 167 ++++++------------ 1 file changed, 54 insertions(+), 113 deletions(-) diff --git a/php/src/Controller/ConfigurationController.php b/php/src/Controller/ConfigurationController.php index 835d7b6294f..1d0d13179d0 100644 --- a/php/src/Controller/ConfigurationController.php +++ b/php/src/Controller/ConfigurationController.php @@ -21,123 +21,64 @@ public function __construct( public function SetConfig(Request $request, Response $response, array $args) : Response { try { - if (isset($request->getParsedBody()['domain'])) { - $domain = $request->getParsedBody()['domain'] ?? ''; - $this->configurationManager->SetDomain($domain); - } - - if (isset($request->getParsedBody()['current-master-password']) || isset($request->getParsedBody()['new-master-password'])) { - $currentMasterPassword = $request->getParsedBody()['current-master-password'] ?? ''; - $newMasterPassword = $request->getParsedBody()['new-master-password'] ?? ''; - $this->configurationManager->ChangeMasterPassword($currentMasterPassword, $newMasterPassword); - } - - if (isset($request->getParsedBody()['borg_backup_host_location'])) { - $location = $request->getParsedBody()['borg_backup_host_location'] ?? ''; - $this->configurationManager->SetBorgBackupHostLocation($location); - } - - if (isset($request->getParsedBody()['borg_restore_host_location']) || isset($request->getParsedBody()['borg_restore_password'])) { - $restoreLocation = $request->getParsedBody()['borg_restore_host_location'] ?? ''; - $borgPassword = $request->getParsedBody()['borg_restore_password'] ?? ''; - $this->configurationManager->SetBorgRestoreHostLocationAndPassword($restoreLocation, $borgPassword); - } - - if (isset($request->getParsedBody()['daily_backup_time'])) { - if (isset($request->getParsedBody()['automatic_updates'])) { - $enableAutomaticUpdates = true; - } else { - $enableAutomaticUpdates = false; - } - if (isset($request->getParsedBody()['success_notification'])) { - $successNotification = true; - } else { - $successNotification = false; - } - $dailyBackupTime = $request->getParsedBody()['daily_backup_time'] ?? ''; - $this->configurationManager->SetDailyBackupTime($dailyBackupTime, $enableAutomaticUpdates, $successNotification); - } - - if (isset($request->getParsedBody()['delete_daily_backup_time'])) { - $this->configurationManager->DeleteDailyBackupTime(); - } - - if (isset($request->getParsedBody()['additional_backup_directories'])) { - $additionalBackupDirectories = $request->getParsedBody()['additional_backup_directories'] ?? ''; - $this->configurationManager->SetAdditionalBackupDirectories($additionalBackupDirectories); - } - - if (isset($request->getParsedBody()['delete_timezone'])) { - $this->configurationManager->DeleteTimezone(); - } - - if (isset($request->getParsedBody()['timezone'])) { - $timezone = $request->getParsedBody()['timezone'] ?? ''; - $this->configurationManager->SetTimezone($timezone); - } - - if (isset($request->getParsedBody()['options-form'])) { - if (isset($request->getParsedBody()['collabora']) && isset($request->getParsedBody()['onlyoffice'])) { - throw new InvalidSettingConfigurationException("Collabora and Onlyoffice are not allowed to be enabled at the same time!"); - } - if (isset($request->getParsedBody()['clamav'])) { - $this->configurationManager->SetClamavEnabledState(1); - } else { - $this->configurationManager->SetClamavEnabledState(0); - } - if (isset($request->getParsedBody()['onlyoffice'])) { - $this->configurationManager->SetOnlyofficeEnabledState(1); - } else { - $this->configurationManager->SetOnlyofficeEnabledState(0); - } - if (isset($request->getParsedBody()['collabora'])) { - $this->configurationManager->SetCollaboraEnabledState(1); - } else { - $this->configurationManager->SetCollaboraEnabledState(0); - } - if (isset($request->getParsedBody()['talk'])) { - $this->configurationManager->SetTalkEnabledState(1); - } else { - $this->configurationManager->SetTalkEnabledState(0); + $body = $request->getParsedBody(); + if (is_array($body)) { + if (is_string($body['domain'])) + $this->configurationManager->SetDomain($body['domain']); + + $currentMasterPassword = is_string($body['current-master-password']) ? $body['current-master-password'] : null; + $newMasterPassword = is_string($body['new-master-password']) ? $body['new-master-password'] : null; + if ($currentMasterPassword !== null || $newMasterPassword !== null) + $this->configurationManager->ChangeMasterPassword($currentMasterPassword ?? '', $newMasterPassword ?? ''); + + if (is_string($body['borg_backup_host_location'])) + $this->configurationManager->SetBorgBackupHostLocation($body['borg_backup_host_location']); + + $borgRestoreHostLocation = is_string($body['borg_restore_host_location']) ? $body['borg_restore_host_location'] : null; + $borgRestorePassword = is_string($body['borg_restore_password']) ? $body['borg_restore_password'] : null; + if ($borgRestoreHostLocation !== null || $borgRestorePassword !== null) + $this->configurationManager->SetBorgRestoreHostLocationAndPassword($borgRestoreHostLocation ?? '', $borgRestorePassword ?? ''); + + if (is_string($body['daily_backup_time'])) + $this->configurationManager->SetDailyBackupTime( + $body['daily_backup_time'], + isset($body['automatic_updates']), + isset($body['success_notification'])); + + if (isset($body['delete_daily_backup_time'])) + $this->configurationManager->DeleteDailyBackupTime(); + + if (is_string($body['additional_backup_directories'])) + $this->configurationManager->SetAdditionalBackupDirectories($body['additional_backup_directories']); + + if (isset($body['delete_timezone'])) + $this->configurationManager->DeleteTimezone(); + + if (is_string($body['timezone'])) + $this->configurationManager->SetTimezone($body['timezone']); + + if (isset($body['options-form'])) { + if (isset($body['collabora']) && isset($body['onlyoffice'])) + throw new InvalidSettingConfigurationException("Collabora and Onlyoffice are not allowed to be enabled at the same time!"); + $this->configurationManager->SetClamavEnabledState(isset($body['clamav']) ? 1 : 0); + $this->configurationManager->SetOnlyofficeEnabledState(isset($body['onlyoffice']) ? 1 : 0); + $this->configurationManager->SetCollaboraEnabledState(isset($body['collabora']) ? 1 : 0); + $this->configurationManager->SetTalkEnabledState(isset($body['talk']) ? 1 : 0); + $this->configurationManager->SetTalkRecordingEnabledState(isset($body['talk-recording']) ? 1 : 0); + $this->configurationManager->SetImaginaryEnabledState(isset($body['imaginary']) ? 1 : 0); + $this->configurationManager->SetFulltextsearchEnabledState(isset($body['fulltextsearch']) ? 1 : 0); + $this->configurationManager->SetDockerSocketProxyEnabledState(isset($body['docker-socket-proxy']) ? 1 : 0); + $this->configurationManager->SetWhiteboardEnabledState(isset($body['whiteboard']) ? 1 : 0); } - if (isset($request->getParsedBody()['talk-recording'])) { - $this->configurationManager->SetTalkRecordingEnabledState(1); - } else { - $this->configurationManager->SetTalkRecordingEnabledState(0); - } - if (isset($request->getParsedBody()['imaginary'])) { - $this->configurationManager->SetImaginaryEnabledState(1); - } else { - $this->configurationManager->SetImaginaryEnabledState(0); - } - if (isset($request->getParsedBody()['fulltextsearch'])) { - $this->configurationManager->SetFulltextsearchEnabledState(1); - } else { - $this->configurationManager->SetFulltextsearchEnabledState(0); - } - if (isset($request->getParsedBody()['docker-socket-proxy'])) { - $this->configurationManager->SetDockerSocketProxyEnabledState(1); - } else { - $this->configurationManager->SetDockerSocketProxyEnabledState(0); - } - if (isset($request->getParsedBody()['whiteboard'])) { - $this->configurationManager->SetWhiteboardEnabledState(1); - } else { - $this->configurationManager->SetWhiteboardEnabledState(0); - } - } - if (isset($request->getParsedBody()['delete_collabora_dictionaries'])) { - $this->configurationManager->DeleteCollaboraDictionaries(); - } + if (isset($body['delete_collabora_dictionaries'])) + $this->configurationManager->DeleteCollaboraDictionaries(); - if (isset($request->getParsedBody()['collabora_dictionaries'])) { - $collaboraDictionaries = $request->getParsedBody()['collabora_dictionaries'] ?? ''; - $this->configurationManager->SetCollaboraDictionaries($collaboraDictionaries); - } + if (is_string($body['collabora_dictionaries'])) + $this->configurationManager->SetCollaboraDictionaries($body['collabora_dictionaries']); - if (isset($request->getParsedBody()['delete_borg_backup_host_location'])) { - $this->configurationManager->DeleteBorgBackupHostLocation(); + if (isset($body['delete_borg_backup_host_location'])) + $this->configurationManager->DeleteBorgBackupHostLocation(); } return $response->withStatus(201)->withHeader('Location', '/'); From 56aa613dfb5a387314dd7435121cc97c74805342 Mon Sep 17 00:00:00 2001 From: Jean-Yves <7360784+docjyJ@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:12:16 +0200 Subject: [PATCH 3/7] Clean code and use const Signed-off-by: Jean-Yves <7360784+docjyJ@users.noreply.github.com> --- php/src/Auth/AuthManager.php | 16 ++++++++-------- php/src/Auth/PasswordGenerator.php | 20 ++++++++++++-------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/php/src/Auth/AuthManager.php b/php/src/Auth/AuthManager.php index a4bc96b6d3c..f3775146400 100644 --- a/php/src/Auth/AuthManager.php +++ b/php/src/Auth/AuthManager.php @@ -4,25 +4,25 @@ use AIO\Data\ConfigurationManager; use AIO\Data\DataConst; -use \DateTime; +use DateTime; class AuthManager { private const string SESSION_KEY = 'aio_authenticated'; - private ConfigurationManager $configurationManager; - public function __construct(ConfigurationManager $configurationManager) { - $this->configurationManager = $configurationManager; + public function __construct( + private readonly ConfigurationManager $configurationManager + ) { } - public function CheckCredentials(string $password) : bool { + public function CheckCredentials(string $password): bool { return hash_equals($this->configurationManager->GetPassword(), $password); } - public function CheckToken(string $token) : bool { + public function CheckToken(string $token): bool { return hash_equals($this->configurationManager->GetToken(), $token); } - public function SetAuthState(bool $isLoggedIn) : void { + public function SetAuthState(bool $isLoggedIn): void { if (!$this->IsAuthenticated() && $isLoggedIn === true) { $date = new DateTime(); @@ -40,7 +40,7 @@ public function SetAuthState(bool $isLoggedIn) : void { $_SESSION[self::SESSION_KEY] = $isLoggedIn; } - public function IsAuthenticated() : bool { + public function IsAuthenticated(): bool { return isset($_SESSION[self::SESSION_KEY]) && $_SESSION[self::SESSION_KEY] === true; } } diff --git a/php/src/Auth/PasswordGenerator.php b/php/src/Auth/PasswordGenerator.php index 6e9ee9a182a..4c3e28194c6 100644 --- a/php/src/Auth/PasswordGenerator.php +++ b/php/src/Auth/PasswordGenerator.php @@ -2,11 +2,11 @@ namespace AIO\Auth; -use AIO\Data\ConfigurationManager; +use Random\RandomException; -class PasswordGenerator -{ - private array $words = [ +class PasswordGenerator { + + private const array WORDS = [ 'abacus', 'abdomen', 'abdominal', @@ -7785,14 +7785,18 @@ class PasswordGenerator 'zoom', ]; - public function GeneratePassword(int $length) : string { + + /** + * @throws RandomException + */ + public function GeneratePassword(int $length): string { $password = ''; - for($i = 0; $i < $length; $i ++) { - if($password !== '') { + for ($i = 0; $i < $length; $i++) { + if ($password !== '') { $password = $password . ' '; } - $password = $password . $this->words[random_int(0, 7775)]; + $password = $password . PasswordGenerator::WORDS[random_int(0, 7775)]; } return $password; From c36ca0a4a310ea1639e80d82ea8e2bc6b3818251 Mon Sep 17 00:00:00 2001 From: Jean-Yves <7360784+docjyJ@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:52:22 +0200 Subject: [PATCH 4/7] Clean ConfigurationController.php Signed-off-by: Jean-Yves <7360784+docjyJ@users.noreply.github.com> --- php/src/Controller/ConfigurationController.php | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/php/src/Controller/ConfigurationController.php b/php/src/Controller/ConfigurationController.php index 1d0d13179d0..f56cd3362ee 100644 --- a/php/src/Controller/ConfigurationController.php +++ b/php/src/Controller/ConfigurationController.php @@ -2,24 +2,19 @@ namespace AIO\Controller; -use AIO\ContainerDefinitionFetcher; use AIO\Data\ConfigurationManager; use AIO\Data\InvalidSettingConfigurationException; -use AIO\Docker\DockerActionManager; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; -class ConfigurationController -{ - private ConfigurationManager $configurationManager; +readonly class ConfigurationController { public function __construct( - ConfigurationManager $configurationManager + private ConfigurationManager $configurationManager ) { - $this->configurationManager = $configurationManager; } - public function SetConfig(Request $request, Response $response, array $args) : Response { + public function SetConfig(Request $request, Response $response, array $args): Response { try { $body = $request->getParsedBody(); if (is_array($body)) { From 5187ea6c35931690d78ecb9e4d75f1d1e599aee1 Mon Sep 17 00:00:00 2001 From: Jean-Yves <7360784+docjyJ@users.noreply.github.com> Date: Tue, 24 Sep 2024 21:35:06 +0200 Subject: [PATCH 5/7] Refactor Signed-off-by: Jean-Yves <7360784+docjyJ@users.noreply.github.com> --- php/src/Auth/PasswordGenerator.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/php/src/Auth/PasswordGenerator.php b/php/src/Auth/PasswordGenerator.php index 4c3e28194c6..726a58709ee 100644 --- a/php/src/Auth/PasswordGenerator.php +++ b/php/src/Auth/PasswordGenerator.php @@ -7786,9 +7786,7 @@ class PasswordGenerator { ]; - /** - * @throws RandomException - */ + /** @throws RandomException */ public function GeneratePassword(int $length): string { $password = ''; From 1e4f32f3cb58b42c205ab5444bc7a7c0a1559916 Mon Sep 17 00:00:00 2001 From: Jean-Yves <7360784+docjyJ@users.noreply.github.com> Date: Tue, 24 Sep 2024 21:48:10 +0200 Subject: [PATCH 6/7] Auth readonly class Signed-off-by: Jean-Yves <7360784+docjyJ@users.noreply.github.com> --- php/src/Auth/AuthManager.php | 4 ++-- php/src/Auth/PasswordGenerator.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/php/src/Auth/AuthManager.php b/php/src/Auth/AuthManager.php index f3775146400..200942cbbf0 100644 --- a/php/src/Auth/AuthManager.php +++ b/php/src/Auth/AuthManager.php @@ -6,11 +6,11 @@ use AIO\Data\DataConst; use DateTime; -class AuthManager { +readonly class AuthManager { private const string SESSION_KEY = 'aio_authenticated'; public function __construct( - private readonly ConfigurationManager $configurationManager + private ConfigurationManager $configurationManager ) { } diff --git a/php/src/Auth/PasswordGenerator.php b/php/src/Auth/PasswordGenerator.php index 726a58709ee..57df7d43306 100644 --- a/php/src/Auth/PasswordGenerator.php +++ b/php/src/Auth/PasswordGenerator.php @@ -4,7 +4,7 @@ use Random\RandomException; -class PasswordGenerator { +readonly class PasswordGenerator { private const array WORDS = [ 'abacus', From f7dfdf346a8f009dcb5899f9a59c1886209123f6 Mon Sep 17 00:00:00 2001 From: Jean-Yves <7360784+docjyJ@users.noreply.github.com> Date: Thu, 26 Sep 2024 01:01:59 +0200 Subject: [PATCH 7/7] Wip Signed-off-by: Jean-Yves <7360784+docjyJ@users.noreply.github.com> --- php/src/Auth/AuthManager.php | 14 +- php/src/Auth/PasswordGenerator.php | 4 +- php/src/ContainerDefinitionFetcher.php | 58 +- .../Controller/ConfigurationController.php | 52 +- php/src/Controller/DockerController.php | 70 +- php/src/Data/ConfigFile.php | 620 +++++++++++++ php/src/Data/ConfigurationManager.php | 838 ++---------------- php/src/Data/Setup.php | 28 +- php/src/Docker/DockerActionManager.php | 128 +-- 9 files changed, 844 insertions(+), 968 deletions(-) create mode 100644 php/src/Data/ConfigFile.php diff --git a/php/src/Auth/AuthManager.php b/php/src/Auth/AuthManager.php index 200942cbbf0..48296ff8a1e 100644 --- a/php/src/Auth/AuthManager.php +++ b/php/src/Auth/AuthManager.php @@ -4,22 +4,22 @@ use AIO\Data\ConfigurationManager; use AIO\Data\DataConst; +use AIO\Data\InvalidSettingConfigurationException; use DateTime; readonly class AuthManager { private const string SESSION_KEY = 'aio_authenticated'; - public function __construct( - private ConfigurationManager $configurationManager - ) { - } - + /** @throws InvalidSettingConfigurationException */ public function CheckCredentials(string $password): bool { - return hash_equals($this->configurationManager->GetPassword(), $password); + $config = ConfigurationManager::loadConfigFile(); + return hash_equals($config->GetPassword(), $password); } + /** @throws InvalidSettingConfigurationException */ public function CheckToken(string $token): bool { - return hash_equals($this->configurationManager->GetToken(), $token); + $config = ConfigurationManager::loadConfigFile(); + return hash_equals($config->GetToken(), $token); } public function SetAuthState(bool $isLoggedIn): void { diff --git a/php/src/Auth/PasswordGenerator.php b/php/src/Auth/PasswordGenerator.php index 57df7d43306..d614306ee16 100644 --- a/php/src/Auth/PasswordGenerator.php +++ b/php/src/Auth/PasswordGenerator.php @@ -4,7 +4,7 @@ use Random\RandomException; -readonly class PasswordGenerator { +class PasswordGenerator { private const array WORDS = [ 'abacus', @@ -7787,7 +7787,7 @@ /** @throws RandomException */ - public function GeneratePassword(int $length): string { + static function GeneratePassword(int $length): string { $password = ''; for ($i = 0; $i < $length; $i++) { diff --git a/php/src/ContainerDefinitionFetcher.php b/php/src/ContainerDefinitionFetcher.php index 1cd4b17cd0e..73946561e17 100644 --- a/php/src/ContainerDefinitionFetcher.php +++ b/php/src/ContainerDefinitionFetcher.php @@ -16,15 +16,12 @@ class ContainerDefinitionFetcher { - private ConfigurationManager $configurationManager; private \DI\Container $container; public function __construct( - ConfigurationManager $configurationManager, \DI\Container $container ) { - $this->configurationManager = $configurationManager; $this->container = $container; } @@ -47,9 +44,10 @@ public function GetContainerById(string $id): Container private function GetDefinition(): array { $data = json_decode(file_get_contents(__DIR__ . '/../containers.json'), true); + $config = ConfigurationManager::loadConfigFile(); $additionalContainerNames = []; - foreach ($this->configurationManager->GetEnabledCommunityContainers() as $communityContainer) { + foreach ($config->aioCommunityContainers as $communityContainer) { if ($communityContainer !== '') { $path = DataConst::GetCommunityContainersDirectory() . '/' . $communityContainer . '/' . $communityContainer . '.json'; $additionalData = json_decode(file_get_contents($path), true); @@ -64,46 +62,46 @@ private function GetDefinition(): array $containers = []; foreach ($data['aio_services_v1'] as $entry) { if ($entry['container_name'] === 'nextcloud-aio-clamav') { - if (!$this->configurationManager->isClamavEnabled()) { + if (!$config->isClamavEnabled()) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-onlyoffice') { - if (!$this->configurationManager->isOnlyofficeEnabled()) { + if (!$config->isOnlyofficeEnabled()) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-collabora') { - if (!$this->configurationManager->isCollaboraEnabled()) { + if (!$config->isCollaboraEnabled()) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-talk') { - if (!$this->configurationManager->isTalkEnabled()) { + if (!$config->talkEnabled) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-talk-recording') { - if (!$this->configurationManager->isTalkRecordingEnabled()) { + if (!$config->isTalkRecordingEnabled()) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-imaginary') { - if (!$this->configurationManager->isImaginaryEnabled()) { + if (!$config->imaginaryEnabled) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-fulltextsearch') { - if (!$this->configurationManager->isFulltextsearchEnabled()) { + if (!$config->fulltextsearchEnabled) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-docker-socket-proxy') { - if (!$this->configurationManager->isDockerSocketProxyEnabled()) { + if (!$config->dockerSocketProxyEnabled) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-whiteboard') { - if (!$this->configurationManager->isWhiteboardEnabled()) { + if (!$config->whiteboardEnabled) { continue; } } $ports = new ContainerPorts(); if (isset($entry['ports'])) { - foreach ($entry['ports'] as $value) { + foreach ($entry['ports'] as $value) { $ports->AddPort( new ContainerPort( $value['port_number'], @@ -118,34 +116,34 @@ private function GetDefinition(): array if (isset($entry['volumes'])) { foreach ($entry['volumes'] as $value) { if($value['source'] === '%BORGBACKUP_HOST_LOCATION%') { - $value['source'] = $this->configurationManager->GetBorgBackupHostLocation(); + $value['source'] = $config->getBorgLocation(); if($value['source'] === '') { continue; } } if($value['source'] === '%NEXTCLOUD_MOUNT%') { - $value['source'] = $this->configurationManager->GetNextcloudMount(); + $value['source'] = $config->nextcloudMount; if($value['source'] === '') { continue; } } elseif ($value['source'] === '%NEXTCLOUD_DATADIR%') { - $value['source'] = $this->configurationManager->GetNextcloudDatadirMount(); + $value['source'] = $config->nextcloudDatadir; if ($value['source'] === '') { continue; } } elseif ($value['source'] === '%WATCHTOWER_DOCKER_SOCKET_PATH%') { - $value['source'] = $this->configurationManager->GetDockerSocketPath(); + $value['source'] = $config->dockerSocketPath; if($value['source'] === '') { continue; } } elseif ($value['source'] === '%NEXTCLOUD_TRUSTED_CACERTS_DIR%') { - $value['source'] = $this->configurationManager->GetTrustedCacertsDir(); + $value['source'] = $config->trustedCacertsDir; if($value['source'] === '') { continue; } } if ($value['destination'] === '%NEXTCLOUD_MOUNT%') { - $value['destination'] = $this->configurationManager->GetNextcloudMount(); + $value['destination'] = $config->nextcloudMount; if($value['destination'] === '') { continue; } @@ -173,46 +171,46 @@ private function GetDefinition(): array } foreach ($valueDependsOn as $value) { if ($value === 'nextcloud-aio-clamav') { - if (!$this->configurationManager->isClamavEnabled()) { + if (!$config->isClamavEnabled()) { continue; } } elseif ($value === 'nextcloud-aio-onlyoffice') { - if (!$this->configurationManager->isOnlyofficeEnabled()) { + if (!$config->isOnlyofficeEnabled()) { continue; } } elseif ($value === 'nextcloud-aio-collabora') { - if (!$this->configurationManager->isCollaboraEnabled()) { + if (!$config->isCollaboraEnabled()) { continue; } } elseif ($value === 'nextcloud-aio-talk') { - if (!$this->configurationManager->isTalkEnabled()) { + if (!$config->talkEnabled) { continue; } } elseif ($value === 'nextcloud-aio-talk-recording') { - if (!$this->configurationManager->isTalkRecordingEnabled()) { + if (!$config->isTalkRecordingEnabled()) { continue; } } elseif ($value === 'nextcloud-aio-imaginary') { - if (!$this->configurationManager->isImaginaryEnabled()) { + if (!$config->imaginaryEnabled) { continue; } } elseif ($value === 'nextcloud-aio-fulltextsearch') { - if (!$this->configurationManager->isFulltextsearchEnabled()) { + if (!$config->fulltextsearchEnabled) { continue; } } elseif ($value === 'nextcloud-aio-docker-socket-proxy') { - if (!$this->configurationManager->isDockerSocketProxyEnabled()) { + if (!$config->dockerSocketProxyEnabled) { continue; } } elseif ($value === 'nextcloud-aio-whiteboard') { - if (!$this->configurationManager->isWhiteboardEnabled()) { + if (!$config->whiteboardEnabled) { continue; } } $dependsOn[] = $value; } } - + $variables = new ContainerEnvironmentVariables(); if (isset($entry['environment'])) { foreach ($entry['environment'] as $value) { diff --git a/php/src/Controller/ConfigurationController.php b/php/src/Controller/ConfigurationController.php index f56cd3362ee..18b30f5def4 100644 --- a/php/src/Controller/ConfigurationController.php +++ b/php/src/Controller/ConfigurationController.php @@ -8,72 +8,70 @@ use Psr\Http\Message\ServerRequestInterface as Request; readonly class ConfigurationController { - - public function __construct( - private ConfigurationManager $configurationManager - ) { - } - public function SetConfig(Request $request, Response $response, array $args): Response { try { $body = $request->getParsedBody(); if (is_array($body)) { + $config = ConfigurationManager::loadConfigFile(); + if (is_string($body['domain'])) - $this->configurationManager->SetDomain($body['domain']); + $config->setDomain($body['domain']); $currentMasterPassword = is_string($body['current-master-password']) ? $body['current-master-password'] : null; $newMasterPassword = is_string($body['new-master-password']) ? $body['new-master-password'] : null; if ($currentMasterPassword !== null || $newMasterPassword !== null) - $this->configurationManager->ChangeMasterPassword($currentMasterPassword ?? '', $newMasterPassword ?? ''); + $config->changeMasterPassword($currentMasterPassword ?? '', $newMasterPassword ?? ''); if (is_string($body['borg_backup_host_location'])) - $this->configurationManager->SetBorgBackupHostLocation($body['borg_backup_host_location']); + $config->setBorgLocation($body['borg_backup_host_location']); $borgRestoreHostLocation = is_string($body['borg_restore_host_location']) ? $body['borg_restore_host_location'] : null; $borgRestorePassword = is_string($body['borg_restore_password']) ? $body['borg_restore_password'] : null; if ($borgRestoreHostLocation !== null || $borgRestorePassword !== null) - $this->configurationManager->SetBorgRestoreHostLocationAndPassword($borgRestoreHostLocation ?? '', $borgRestorePassword ?? ''); + $config->setBorgRestoreLocationAndPassword($borgRestoreHostLocation ?? '', $borgRestorePassword ?? ''); if (is_string($body['daily_backup_time'])) - $this->configurationManager->SetDailyBackupTime( + ConfigurationManager::SetDailyBackupTime( $body['daily_backup_time'], isset($body['automatic_updates']), isset($body['success_notification'])); if (isset($body['delete_daily_backup_time'])) - $this->configurationManager->DeleteDailyBackupTime(); + $config->deleteTimezone(); if (is_string($body['additional_backup_directories'])) - $this->configurationManager->SetAdditionalBackupDirectories($body['additional_backup_directories']); + ConfigurationManager::SetAdditionalBackupDirectories($body['additional_backup_directories']); if (isset($body['delete_timezone'])) - $this->configurationManager->DeleteTimezone(); + $config->DeleteTimezone(); if (is_string($body['timezone'])) - $this->configurationManager->SetTimezone($body['timezone']); + $config->SetTimezone($body['timezone']); if (isset($body['options-form'])) { if (isset($body['collabora']) && isset($body['onlyoffice'])) throw new InvalidSettingConfigurationException("Collabora and Onlyoffice are not allowed to be enabled at the same time!"); - $this->configurationManager->SetClamavEnabledState(isset($body['clamav']) ? 1 : 0); - $this->configurationManager->SetOnlyofficeEnabledState(isset($body['onlyoffice']) ? 1 : 0); - $this->configurationManager->SetCollaboraEnabledState(isset($body['collabora']) ? 1 : 0); - $this->configurationManager->SetTalkEnabledState(isset($body['talk']) ? 1 : 0); - $this->configurationManager->SetTalkRecordingEnabledState(isset($body['talk-recording']) ? 1 : 0); - $this->configurationManager->SetImaginaryEnabledState(isset($body['imaginary']) ? 1 : 0); - $this->configurationManager->SetFulltextsearchEnabledState(isset($body['fulltextsearch']) ? 1 : 0); - $this->configurationManager->SetDockerSocketProxyEnabledState(isset($body['docker-socket-proxy']) ? 1 : 0); - $this->configurationManager->SetWhiteboardEnabledState(isset($body['whiteboard']) ? 1 : 0); + $config->enableClamav(isset($body['clamav'])); + $config->enableOnlyoffice(isset($body['onlyoffice'])); + $config->enableCollabora(isset($body['collabora'])); + $config->talkEnabled = isset($body['talk']); + $config->enableTalkRecording(isset($body['talk-recording'])); + $config->imaginaryEnabled = isset($body['imaginary']); + $config->fulltextsearchEnabled = isset($body['fulltextsearch']); + $config->dockerSocketProxyEnabled = isset($body['docker-socket-proxy']); + $config->whiteboardEnabled = isset($body['whiteboard']); } if (isset($body['delete_collabora_dictionaries'])) - $this->configurationManager->DeleteCollaboraDictionaries(); + $config->DeleteCollaboraDictionaries(); if (is_string($body['collabora_dictionaries'])) - $this->configurationManager->SetCollaboraDictionaries($body['collabora_dictionaries']); + $config->SetCollaboraDictionaries($body['collabora_dictionaries']); if (isset($body['delete_borg_backup_host_location'])) - $this->configurationManager->DeleteBorgBackupHostLocation(); + $config->deleteBorgLocation(); + + ConfigurationManager::storeConfigFile($config); } return $response->withStatus(201)->withHeader('Location', '/'); diff --git a/php/src/Controller/DockerController.php b/php/src/Controller/DockerController.php index 2d5367386dd..5d8e82d4b61 100644 --- a/php/src/Controller/DockerController.php +++ b/php/src/Controller/DockerController.php @@ -14,16 +14,13 @@ class DockerController private DockerActionManager $dockerActionManager; private ContainerDefinitionFetcher $containerDefinitionFetcher; private const string TOP_CONTAINER = 'nextcloud-aio-apache'; - private ConfigurationManager $configurationManager; public function __construct( DockerActionManager $dockerActionManager, ContainerDefinitionFetcher $containerDefinitionFetcher, - ConfigurationManager $configurationManager ) { $this->dockerActionManager = $dockerActionManager; $this->containerDefinitionFetcher = $containerDefinitionFetcher; - $this->configurationManager = $configurationManager; } private function PerformRecursiveContainerStart(string $id, bool $pullImage = true) : void { @@ -48,7 +45,7 @@ private function PerformRecursiveContainerStart(string $id, bool $pullImage = tr } } - // Check if docker hub is reachable in order to make sure that we do not try to pull an image if it is down + // Check if docker hub is reachable in order to make sure that we do not try to pull an image if it is down // and try to mitigate issues that are arising due to that if ($pullImage) { if (!$this->dockerActionManager->isDockerHubReachable($container)) { @@ -91,9 +88,9 @@ public function StartBackupContainerBackup(Request $request, Response $response, } public function startBackup() : void { - $config = $this->configurationManager->GetConfig(); - $config['backup-mode'] = 'backup'; - $this->configurationManager->WriteConfig($config); + $config = ConfigurationManager::loadConfigFile(); + $config->setBackupMode('backup'); + ConfigurationManager::storeConfigFile($config); $id = self::TOP_CONTAINER; $this->PerformRecursiveContainerStop($id); @@ -108,19 +105,19 @@ public function StartBackupContainerCheck(Request $request, Response $response, } public function checkBackup() : void { - $config = $this->configurationManager->GetConfig(); - $config['backup-mode'] = 'check'; - $this->configurationManager->WriteConfig($config); + $config = ConfigurationManager::loadConfigFile(); + $config->setBackupMode('check'); + ConfigurationManager::storeConfigFile($config); $id = 'nextcloud-aio-borgbackup'; $this->PerformRecursiveContainerStart($id); } public function StartBackupContainerRestore(Request $request, Response $response, array $args) : Response { - $config = $this->configurationManager->GetConfig(); - $config['backup-mode'] = 'restore'; - $config['selected-restore-time'] = $request->getParsedBody()['selected_restore_time'] ?? ''; - $this->configurationManager->WriteConfig($config); + $config = ConfigurationManager::loadConfigFile(); + $config->setBackupMode('restore'); + $config->setSelectedRestoreTime($request->getParsedBody()['selected_restore_time'] ?? ''); + ConfigurationManager::storeConfigFile($config); $id = self::TOP_CONTAINER; $this->PerformRecursiveContainerStop($id); @@ -132,26 +129,26 @@ public function StartBackupContainerRestore(Request $request, Response $response } public function StartBackupContainerCheckRepair(Request $request, Response $response, array $args) : Response { - $config = $this->configurationManager->GetConfig(); - $config['backup-mode'] = 'check-repair'; - $this->configurationManager->WriteConfig($config); + $config = ConfigurationManager::loadConfigFile(); + $config->setBackupMode('check-repair'); + ConfigurationManager::storeConfigFile($config); $id = 'nextcloud-aio-borgbackup'; $this->PerformRecursiveContainerStart($id); // Restore to backup check which is needed to make the UI logic work correctly - $config = $this->configurationManager->GetConfig(); - $config['backup-mode'] = 'check'; - $this->configurationManager->WriteConfig($config); + $config = ConfigurationManager::loadConfigFile(); + $config->setBackupMode('check'); + ConfigurationManager::storeConfigFile($config); return $response->withStatus(201)->withHeader('Location', '/'); } public function StartBackupContainerTest(Request $request, Response $response, array $args) : Response { - $config = $this->configurationManager->GetConfig(); - $config['backup-mode'] = 'test'; - $config['instance_restore_attempt'] = 0; - $this->configurationManager->WriteConfig($config); + $config = ConfigurationManager::loadConfigFile(); + $config->setBackupMode('test'); + $config->instanceRestoreAttempt = 0; + ConfigurationManager::storeConfigFile($config); $id = self::TOP_CONTAINER; $this->PerformRecursiveContainerStop($id); @@ -175,17 +172,14 @@ public function StartContainer(Request $request, Response $response, array $args if (isset($request->getParsedBody()['install_latest_major'])) { $installLatestMajor = 30; } else { - $installLatestMajor = ""; + $installLatestMajor = 0; } - $config = $this->configurationManager->GetConfig(); - // set AIO_URL - $config['AIO_URL'] = $host . ':' . $port; - // set wasStartButtonClicked - $config['wasStartButtonClicked'] = 1; - // set install_latest_major - $config['install_latest_major'] = $installLatestMajor; - $this->configurationManager->WriteConfig($config); + $config = ConfigurationManager::loadConfigFile(); + $config->aioUrl = $host . ':' . $port; + $config->wasStartButtonClicked = true; + $config->installLatestMajor = $installLatestMajor; + ConfigurationManager::storeConfigFile($config); // Start container $this->startTopContainer(true); @@ -198,10 +192,9 @@ public function StartContainer(Request $request, Response $response, array $args } public function startTopContainer(bool $pullImage) : void { - $config = $this->configurationManager->GetConfig(); - // set AIO_TOKEN - $config['AIO_TOKEN'] = bin2hex(random_bytes(24)); - $this->configurationManager->WriteConfig($config); + $config = ConfigurationManager::loadConfigFile(); + $config->setToken(bin2hex(random_bytes(24))); + ConfigurationManager::storeConfigFile($config); // Stop domaincheck since apache would not be able to start otherwise $this->StopDomaincheckContainer(); @@ -250,7 +243,8 @@ public function stopTopContainer() : void { public function StartDomaincheckContainer() : void { # Don't start if domain is already set - if ($this->configurationManager->GetDomain() !== '' || $this->configurationManager->wasStartButtonClicked()) { + $config = ConfigurationManager::loadConfigFile(); + if ($config->getDomain() !== '' || $config->wasStartButtonClicked) { return; } diff --git a/php/src/Data/ConfigFile.php b/php/src/Data/ConfigFile.php new file mode 100644 index 00000000000..74f4874cb71 --- /dev/null +++ b/php/src/Data/ConfigFile.php @@ -0,0 +1,620 @@ + + * @throws JsonException + */ +function _map_str_str(array $json, int|string $key): array { + return array_filter(_object($json[$key]), fn($k, $v) => is_string($k) && is_string($v), ARRAY_FILTER_USE_BOTH); +} + + +class ConfigFile implements JsonSerializable { + ////////////////////// + /// Object Methods /// + ////////////////////// + readonly bool $x64Platform; + private bool $clamav, $talkRecording, $onlyoffice, $collabora; + + // Bool keys + const string KEY_CLAMAV = 'isClamavEnabled'; + const string KEY_DOCKER_SOCKET_PROXY = 'isDockerSocketProxyEnabled'; + const string KEY_WHITEBOARD = 'isWhiteboardEnabled'; + const string KEY_IMAGINARY = 'isImaginaryEnabled'; + const string KEY_FULLTEXTSEARCH = 'isFulltextsearchEnabled'; + const string KEY_ONLYOFFICE = 'isOnlyofficeEnabled'; + const string KEY_COLLABORA = 'isCollaboraEnabled'; + const string KEY_TALK = 'isTalkEnabled'; + const string KEY_TALK_RECORDING = 'isTalkRecordingEnabled'; + const string KEY_START_BUTTON_CLICKED = 'wasStartButtonClicked'; + const string KEY_INSTALL_LATEST_MAJOR = 'shouldLatestMajorGetInstalled'; + // Readonly keys + const string KEY_APACHE_PORT = 'apache_port'; + const string KEY_TALK_PORT = 'talk_port'; + const string KEY_NEXTCLOUD_MOUNT = 'nextcloud_mount'; + const string KEY_NEXTCLOUD_DATADIR = 'nextcloud_datadir'; + const string KEY_NEXTCLOUD_UPLOAD_LIMIT = 'nextcloud_upload_limit'; + const string KEY_NEXTCLOUD_MEMORY_LIMIT = 'nextcloud_memory_limit'; + const string KEY_NEXTCLOUD_MAX_TIME = 'nextcloud_max_time'; + const string KEY_BORG_RETENTION_POLICY = 'borg_retention_policy'; + const string KEY_DOCKER_SOCKET_PATH = 'docker_socket_path'; + const string KEY_TRUSTED_CACERTS_DIR = 'trusted_cacerts_dir'; + const string KEY_NEXTCLOUD_ADDITIONAL_APKS = 'nextcloud_additional_apks'; + const string KEY_NEXTCLOUD_ADDITIONAL_PHP_EXTENSIONS = 'nextcloud_additional_php_extensions'; + const string KEY_APACHE_IP_BINDING = 'apache_ip_binding'; + const string KEY_AIO_DISABLE_BACKUP_SECTION = 'aio_disable_backup_section'; + const string KEY_AIO_COMMUNITY_CONTAINERS = 'aio_community_containers'; + const string KEY_NEXTCLOUD_ENABLE_DRI_DEVICE = 'nextcloud_enable_dri_device'; + const string KEY_COLLABORA_SECCOMP_DISABLED = 'collabora_seccomp_disabled'; + const string KEY_NEXTCLOUD_KEEP_DISABLED_APPS = 'nextcloud_keep_disabled_apps'; + // String keys + const string KEY_INSTANCE_RESTORE_ATTEMPT = 'instance_restore_attempt'; + const string KEY_BORG_LOCATION = 'borg_backup_host_location'; + const string KEY_BORG_PASSWORD = 'borg_restore_password'; + const string KEY_BACKUP_MODE = 'backup-mode'; + const string KEY_SELECTED_RESTORE_TIME = 'selected-restore-time'; + // Other keys + const string KEY_PASSWORD = 'password'; + const string KEY_TOKEN = 'AIO_TOKEN'; + const string KEY_DOMAIN = 'domain'; + const string KEY_AIO_URL = 'AIO_URL'; + const string KEY_TIMEZONE = 'timezone'; + const string KEY_COLLABORA_DICTIONARIES = 'collabora_dictionaries'; + const string KEY_SECRETS = 'secrets'; + + + private function __construct( + // Bool data + bool $clamavEnabled, + public bool $dockerSocketProxyEnabled, + public bool $whiteboardEnabled, + public bool $imaginaryEnabled, + public bool $fulltextsearchEnabled, + bool $onlyofficeEnabled, + bool $collaboraEnabled, + public bool $talkEnabled, + bool $talkRecordingEnabled, + public bool $wasStartButtonClicked, + // Readonly data + public int $apachePort, + public int $talkPort, + public string $nextcloudMount, + public string $nextcloudDatadir, + public string $nextcloudUploadLimit, + public string $nextcloudMemoryLimit, + public string $nextcloudMaxTime, + public string $borgRetentionPolicy, + public string $dockerSocketPath, + public string $trustedCacertsDir, + public string $nextcloudAdditionalApks, + public string $nextcloudAdditionalPhpExtensions, + public string $apacheIpBinding, + public bool $aioDisableBackupSection, + public bool $nextcloudEnableDriDevice, + public bool $nextcloudKeepDisabledApps, + private bool $collaboraSeccompDisabled, + /** @var list $aioCommunityContainers */ + public array $aioCommunityContainers, + + // Backup data + public int $instanceRestoreAttempt, + private string $borgLocation, + private string $borgPassword, + private string $backupMode, + private string $selectedRestoreTime, + // Other data + public int $installLatestMajor, + private string $password, + private string $token, + private string $domain, + public string $aioUrl, + private string $timezone, + private string $collaboraDictionaries, + /** @var array $secrets */ + private array $secrets = [], + ) { + $this->x64Platform = php_uname('m') === 'x86_64'; + $this->clamav = $clamavEnabled && $this->x64Platform; + $this->talkRecording = $talkRecordingEnabled && $this->talkEnabled; + $this->collabora = $collaboraEnabled; + $this->onlyoffice = $onlyofficeEnabled && !$this->collabora; + } + + /** @throws InvalidSettingConfigurationException */ + static function parse(mixed $data): self { + try { + $json = _object($data); + return new self( + // Bool data + clamavEnabled: _bool($json, self::KEY_CLAMAV), + dockerSocketProxyEnabled: _bool($json, self::KEY_DOCKER_SOCKET_PROXY), + whiteboardEnabled: _bool($json, self::KEY_WHITEBOARD), + imaginaryEnabled: _bool($json, self::KEY_IMAGINARY), + fulltextsearchEnabled: _bool($json, self::KEY_FULLTEXTSEARCH), + onlyofficeEnabled: _bool($json, self::KEY_ONLYOFFICE), + collaboraEnabled: _bool($json, self::KEY_COLLABORA), + talkEnabled: _bool($json, self::KEY_TALK), + talkRecordingEnabled: _bool($json, self::KEY_TALK_RECORDING), + wasStartButtonClicked: _bool($json, self::KEY_START_BUTTON_CLICKED), + // Readonly data + apachePort: _env_int('APACHE_PORT', $json, self::KEY_APACHE_PORT, 443), + talkPort: _env_int('TALK_PORT', $json, self::KEY_TALK_PORT, 3478), + nextcloudMount: _env_string('NEXTCLOUD_MOUNT', $json, self::KEY_NEXTCLOUD_MOUNT), + nextcloudDatadir: _env_string('NEXTCLOUD_DATADIR', $json, self::KEY_NEXTCLOUD_DATADIR, 'nextcloud_aio_nextcloud_data'), + nextcloudUploadLimit: _env_string('NEXTCLOUD_UPLOAD_LIMIT', $json, self::KEY_NEXTCLOUD_UPLOAD_LIMIT, '10G'), + nextcloudMemoryLimit: _env_string('NEXTCLOUD_MEMORY_LIMIT', $json, self::KEY_NEXTCLOUD_MEMORY_LIMIT, '512M'), + nextcloudMaxTime: _env_string('NEXTCLOUD_MAX_TIME', $json, self::KEY_NEXTCLOUD_MAX_TIME, '3600'), + borgRetentionPolicy: _env_string('BORG_RETENTION_POLICY', $json, self::KEY_BORG_RETENTION_POLICY, '--keep-within=7d --keep-weekly=4 --keep-monthly=6'), + dockerSocketPath: _env_string('DOCKER_SOCKET_PATH', $json, self::KEY_DOCKER_SOCKET_PATH, '/var/run/docker.sock'), + trustedCacertsDir: _env_string('TRUSTED_CACERTS_DIR', $json, self::KEY_TRUSTED_CACERTS_DIR), + nextcloudAdditionalApks: _env_string('NEXTCLOUD_ADDITIONAL_APKS', $json, self::KEY_NEXTCLOUD_ADDITIONAL_APKS, 'imagemagick'), + nextcloudAdditionalPhpExtensions: _env_string('NEXTCLOUD_ADDITIONAL_PHP_EXTENSIONS', $json, self::KEY_NEXTCLOUD_ADDITIONAL_PHP_EXTENSIONS, 'imagick'), + apacheIpBinding: _env_string('APACHE_IP_BINDING', $json, self::KEY_APACHE_IP_BINDING), + aioDisableBackupSection: _env_bool('AIO_DISABLE_BACKUP_SECTION', $json, self::KEY_AIO_DISABLE_BACKUP_SECTION), + nextcloudEnableDriDevice: _env_bool('NEXTCLOUD_ENABLE_DRI_DEVICE', $json, self::KEY_NEXTCLOUD_ENABLE_DRI_DEVICE), + nextcloudKeepDisabledApps: _env_bool('NEXTCLOUD_KEEP_DISABLED_APPS', $json, self::KEY_NEXTCLOUD_KEEP_DISABLED_APPS), + collaboraSeccompDisabled: _env_bool('COLLABORA_SECCOMP_DISABLED', $json, self::KEY_COLLABORA_SECCOMP_DISABLED), + aioCommunityContainers: _env_string('AIO_COMMUNITY_CONTAINERS', $json, self::KEY_AIO_COMMUNITY_CONTAINERS), + // Backup data + instanceRestoreAttempt: _int($json, self::KEY_INSTANCE_RESTORE_ATTEMPT), + borgLocation: _string($json, self::KEY_BORG_LOCATION), + borgPassword: _string($json, self::KEY_BORG_PASSWORD), + backupMode: _string($json, self::KEY_BACKUP_MODE), + selectedRestoreTime: _string($json, self::KEY_SELECTED_RESTORE_TIME), + // Other data + installLatestMajor: _int($json, self::KEY_INSTALL_LATEST_MAJOR), + password: _string($json, self::KEY_PASSWORD), + token: _string($json, self::KEY_TOKEN), + domain: _string($json, self::KEY_DOMAIN), + aioUrl: _string($json, self::KEY_AIO_URL), + timezone: _string($json, self::KEY_TIMEZONE), + collaboraDictionaries: _string($json, self::KEY_COLLABORA_DICTIONARIES), + secrets: _map_str_str($json, self::KEY_SECRETS), + ); + + } catch (JsonException $e) { + throw new InvalidSettingConfigurationException('Failed to parse JSON data', previous: $e); + } + } + + static function blank(string $password): self { + return new self( + // Bool data + clamavEnabled: false, + dockerSocketProxyEnabled: false, + whiteboardEnabled: false, + imaginaryEnabled: false, + fulltextsearchEnabled: false, + onlyofficeEnabled: false, + collaboraEnabled: false, + talkEnabled: false, + talkRecordingEnabled: false, + wasStartButtonClicked: false, + // Readonly data + apachePort: 0, + talkPort: 0, + nextcloudMount: '', + nextcloudDatadir: '', + nextcloudUploadLimit: '', + nextcloudMemoryLimit: '', + nextcloudMaxTime: '', + borgRetentionPolicy: '', + dockerSocketPath: '', + trustedCacertsDir: '', + nextcloudAdditionalApks: '', + nextcloudAdditionalPhpExtensions: '', + apacheIpBinding: '', + aioDisableBackupSection: false, + nextcloudEnableDriDevice: false, + nextcloudKeepDisabledApps: false, + collaboraSeccompDisabled: false, + aioCommunityContainers: [], + // Backup data + instanceRestoreAttempt: 0, + borgLocation: '', + borgPassword: '', + backupMode: '', + selectedRestoreTime: '', + // Other data + installLatestMajor: 0, + password: $password, + token: '', + domain: '', + aioUrl: '', + timezone: '', + collaboraDictionaries: '', + secrets: [], + ); + } + + function jsonSerialize(): array { + $json = []; + // Bool data + if ($this->clamav) $json[self::KEY_CLAMAV] = true; + if ($this->dockerSocketProxyEnabled) $json[self::KEY_DOCKER_SOCKET_PROXY] = true; + if ($this->whiteboardEnabled) $json[self::KEY_WHITEBOARD] = true; + if ($this->imaginaryEnabled) $json[self::KEY_IMAGINARY] = true; + if ($this->fulltextsearchEnabled) $json[self::KEY_FULLTEXTSEARCH] = true; + if ($this->onlyoffice) $json[self::KEY_ONLYOFFICE] = true; + if ($this->collabora) $json[self::KEY_COLLABORA] = true; + if ($this->talkEnabled) $json[self::KEY_TALK] = true; + if ($this->talkRecording) $json[self::KEY_TALK_RECORDING] = true; + if ($this->wasStartButtonClicked) $json[self::KEY_START_BUTTON_CLICKED] = true; + // Backup data + if (!empty($this->instanceRestoreAttempt)) $json[self::KEY_INSTANCE_RESTORE_ATTEMPT] = $this->instanceRestoreAttempt; + if (!empty($this->borgLocation)) $json[self::KEY_BORG_LOCATION] = $this->borgLocation; + if (!empty($this->borgPassword)) $json[self::KEY_BORG_PASSWORD] = $this->borgPassword; + if (!empty($this->backupMode)) $json[self::KEY_BACKUP_MODE] = $this->backupMode; + if (!empty($this->selectedRestoreTime)) $json[self::KEY_SELECTED_RESTORE_TIME] = $this->selectedRestoreTime; + // Other data + if (!empty($this->password)) $json[self::KEY_PASSWORD] = $this->password; + if (!empty($this->token)) $json[self::KEY_TOKEN] = $this->token; + if (!empty($this->domain)) $json[self::KEY_DOMAIN] = $this->domain; + if (!empty($this->aioUrl)) $json[self::KEY_AIO_URL] = $this->aioUrl; + if (!empty($this->timezone)) $json[self::KEY_TIMEZONE] = $this->timezone; + if (!empty($this->collaboraDictionaries)) $json[self::KEY_COLLABORA_DICTIONARIES] = $this->collaboraDictionaries; + if (!empty($this->secrets)) $json[self::KEY_SECRETS] = $this->secrets; + + return $json; + } + + function overwrite(string $key, string $value): void { + switch ($key) { + case self::KEY_APACHE_IP_BINDING: + $this->apacheIpBinding = $value; + break; + case self::KEY_APACHE_PORT: + if (is_numeric($value)) $this->apachePort = intval($value); + break; + case self::KEY_NEXTCLOUD_MEMORY_LIMIT: + $this->nextcloudMemoryLimit = $value; + break; + default: + } + + } + ///////////////////////// + /// Bool data Methods /// + ///////////////////////// + function isClamavEnabled(): bool { + return $this->clamav; + } + + function enableClamav(bool $clamav): void { + if ($this->x64Platform) $this->clamav = $clamav; + } + + function isCollaboraEnabled(): bool { + return $this->collabora; + } + + function enableCollabora(bool $collabora): void { + $this->collabora = $collabora; + $this->onlyoffice = false; + } + + function isOnlyofficeEnabled(): bool { + return $this->onlyoffice; + } + + function enableOnlyoffice(bool $onlyoffice): void { + if ($this->collabora) + $this->onlyoffice = $onlyoffice; + } + + function isTalkRecordingEnabled(): bool { + return $this->talkRecording; + } + + function enableTalkRecording(bool $talkRecording): void { + if ($this->talkEnabled) + $this->talkRecording = $talkRecording; + } + + ///////////////////////////// + /// Readonly data Methods /// + ///////////////////////////// + function getCollaboraSeccompPolicy(): string { + return $this->collaboraSeccompDisabled + ? '--o:security.seccomp=false' + : '--o:security.seccomp=true'; + } + + function getApacheMaxSize(): int { + return intval(rtrim($this->nextcloudUploadLimit, 'G')) * 1024 * 1024 * 1024; + } + + /////////////////////////// + /// Backup data Methods /// + /////////////////////////// + function getBorgLocation(): string { + return $this->borgLocation; + } + + /** @throws InvalidSettingConfigurationException */ + function setBorgLocation(string $location): void { + if ($location !== 'nextcloud_aio_backupdir' && (!str_starts_with($location, '/') || str_ends_with($location, '/'))) + throw new InvalidSettingConfigurationException("The path must start with '/', and must not end with '/'!"); + $this->borgLocation = $location; + } + + function deleteBorgLocation(): void { + $this->borgLocation = ''; + } + + function getBorgPassword(): string { + return $this->borgPassword; + } + + /** @throws InvalidSettingConfigurationException */ + function setBorgRestoreLocationAndPassword(string $location, string $password): void { + if ($password === '') + throw new InvalidSettingConfigurationException("Please enter the password!"); + $this->setBorgLocation($location); + $this->password = $password; + $this->instanceRestoreAttempt = 1; + } + + function getBackupMode(): string { + return $this->backupMode; + } + + function setBackupMode(string $backupMode): void { + $this->backupMode = $backupMode; + } + + function getSelectedRestoreTime(): string { + return $this->selectedRestoreTime; + } + + function setSelectedRestoreTime(string $selectedRestoreTime): void { + $this->selectedRestoreTime = $selectedRestoreTime; + } + + ///////////////////////// + /// Other data Methods // + ///////////////////////// + function getPassword(): string { + return $this->password; + } + + /** @throws InvalidSettingConfigurationException */ + function changeMasterPassword(string $currentPassword, string $newPassword): void { + if ($currentPassword === '') + throw new InvalidSettingConfigurationException("Please enter your current password."); + if ($currentPassword !== $this->password) + throw new InvalidSettingConfigurationException("The entered current password is not correct."); + if ($newPassword === '') + throw new InvalidSettingConfigurationException("Please enter a new password."); + if (strlen($newPassword) < 24) + throw new InvalidSettingConfigurationException("New passwords must be >= 24 digits."); + if (!preg_match("#^[a-zA-Z0-9 ]+$#", $newPassword)) + throw new InvalidSettingConfigurationException('Not allowed characters in the new password.'); + + $this->password = $newPassword; + } + + function getToken(): string { + return $this->token; + } + + function setToken(string $token): void { + $this->token = $token; + } + + function getDomain(): string { + return $this->domain; + } + + /* @throws InvalidSettingConfigurationException */ + function setDomain(string $domain): void { + // Validate that at least one dot is contained + if (!str_contains($domain, '.')) { + throw new InvalidSettingConfigurationException("Domain must contain at least one dot!"); + } + + // Validate that no slashes are contained + if (str_contains($domain, '/')) { + throw new InvalidSettingConfigurationException("Domain must not contain slashes!"); + } + + // Validate that no colons are contained + if (str_contains($domain, ':')) { + throw new InvalidSettingConfigurationException("Domain must not contain colons!"); + } + + // Validate domain + if (filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false) { + throw new InvalidSettingConfigurationException("Domain is not a valid domain!"); + } + + // Validate that it is not an IP-address + if (filter_var($domain, FILTER_VALIDATE_IP)) { + throw new InvalidSettingConfigurationException("Please enter a domain and not an IP-address!"); + } +// TODO +// +// // Skip domain validation if opted in to do so +// if (!$this->shouldDomainValidationBeSkipped()) { +// +// $dnsRecordIP = gethostbyname($domain); +// if ($dnsRecordIP === $domain) { +// $dnsRecordIP = ''; +// } +// +// if (empty($dnsRecordIP)) { +// $record = dns_get_record($domain, DNS_AAAA); +// if (isset($record[0]['ipv6']) && !empty($record[0]['ipv6'])) { +// $dnsRecordIP = $record[0]['ipv6']; +// } +// } +// +// // Validate IP +// if (!filter_var($dnsRecordIP, FILTER_VALIDATE_IP)) { +// throw new InvalidSettingConfigurationException("DNS config is not set for this domain or the domain is not a valid domain! (It was found to be set to '" . $dnsRecordIP . "')"); +// } +// +// // Get the apache port +// $port = $this->GetApachePort(); +// +// if (!filter_var($dnsRecordIP, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { +// if ($port === '443') { +// throw new InvalidSettingConfigurationException("It seems like the ip-address of the domain is set to an internal or reserved ip-address. This is not supported. (It was found to be set to '" . $dnsRecordIP . "'). Please set it to a public ip-address so that the domain validation can work!"); +// } else { +// error_log("It seems like the ip-address of " . $domain . " is set to an internal or reserved ip-address. (It was found to be set to '" . $dnsRecordIP . "')"); +// } +// } +// +// // Check if port 443 is open +// $connection = @fsockopen($domain, 443, $errno, $errstr, 10); +// if ($connection) { +// fclose($connection); +// } else { +// throw new InvalidSettingConfigurationException("The domain is not reachable on Port 443 from within this container. Have you opened port 443/tcp in your router/firewall? If yes is the problem most likely that the router or firewall forbids local access to your domain. You can work around that by setting up a local DNS-server."); +// } +// +// // Get Instance ID +// $instanceID = $this->GetAndGenerateSecret('INSTANCE_ID'); +// +// // set protocol +// if ($port !== '443') { +// $protocol = 'https://'; +// } else { +// $protocol = 'http://'; +// } +// +// // Check if response is correct +// $ch = curl_init(); +// $testUrl = $protocol . $domain . ':443'; +// curl_setopt($ch, CURLOPT_URL, $testUrl); +// curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); +// curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); +// curl_setopt($ch, CURLOPT_TIMEOUT, 10); +// $response = (string)curl_exec($ch); +// # Get rid of trailing \n +// $response = str_replace("\n", "", $response); +// +// if ($response !== $instanceID) { +// error_log('The response of the connection attempt to "' . $testUrl . '" was: ' . $response); +// error_log('Expected was: ' . $instanceID); +// error_log('The error message was: ' . curl_error($ch)); +// $notice = "Domain does not point to this server or the reverse proxy is not configured correctly. See the mastercontainer logs for more details. ('sudo docker logs -f nextcloud-aio-mastercontainer')"; +// if ($port === '443') { +// $notice .= " If you should be using Cloudflare, make sure to disable the Cloudflare Proxy feature as it might block the domain validation. Same for any other firewall or service that blocks unencrypted access on port 443."; +// } else { +// error_log('Please follow https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md#6-how-to-debug-things in order to debug things!'); +// } +// throw new InvalidSettingConfigurationException($notice); +// } +// } + + // Write domain + $this->domain = $domain; + // Reset the borg restore password when setting the domain + $this->borgPassword = ''; + } + + function getBaseDN(): string { + $domain = $this->getDomain(); + if ($domain === "") { + return ""; + } + return 'dc=' . implode(',dc=', explode('.', $domain)); + } + + function getTimezone(): string { + return $this->timezone; + } + + /* @throws InvalidSettingConfigurationException */ + function setTimezone(string $timezone): void { + if ($timezone === "") + throw new InvalidSettingConfigurationException("The timezone must not be empty!"); + if (!preg_match("#^[a-zA-Z0-9_\-/+]+$#", $timezone)) + throw new InvalidSettingConfigurationException("The entered timezone does not seem to be a valid timezone!"); + $this->timezone = $timezone; + } + + function deleteTimezone(): void { + $this->timezone = ''; + } + + function getCollaboraDictionaries(): string { + return $this->collaboraDictionaries; + } + + /** @throws InvalidSettingConfigurationException */ + function setCollaboraDictionaries(string $collaboraDictionaries): void { + if ($collaboraDictionaries === "") + throw new InvalidSettingConfigurationException("The dictionaries must not be empty!"); + if (!preg_match("#^[a-zA-Z_ ]+$#", $collaboraDictionaries)) + throw new InvalidSettingConfigurationException("The entered dictionaries do not seem to be a valid!"); + $this->collaboraDictionaries = $collaboraDictionaries; + } + + function deleteCollaboraDictionaries(): void { + $this->collaboraDictionaries = ''; + } + + function getSecret(string $key): ?string { + return $this->secrets[$key] ?? null; + } + + function setSecret(string $key, string $data): void { + $this->secrets[$key] = $data; + } +} diff --git a/php/src/Data/ConfigurationManager.php b/php/src/Data/ConfigurationManager.php index 527904fbfed..15eecbc3d52 100644 --- a/php/src/Data/ConfigurationManager.php +++ b/php/src/Data/ConfigurationManager.php @@ -4,76 +4,50 @@ use AIO\Auth\PasswordGenerator; use AIO\Controller\DockerController; +use JsonException; +use Random\RandomException; -class ConfigurationManager -{ - public function GetConfig() : array - { - if(file_exists(DataConst::GetConfigFile())) - { - $configContent = file_get_contents(DataConst::GetConfigFile()); - return json_decode($configContent, true, 512, JSON_THROW_ON_ERROR); - } - - return []; - } - - public function GetPassword() : string { - return $this->GetConfig()['password']; - } - - public function GetToken() : string { - return $this->GetConfig()['AIO_TOKEN']; +readonly class ConfigurationManager { + /* @throws InvalidSettingConfigurationException */ + static function loadConfigFile(): ConfigFile { + return ConfigFile::parse(file_get_contents(DataConst::GetConfigFile())); } - public function SetPassword(string $password) : void { - $config = $this->GetConfig(); - $config['password'] = $password; - $this->WriteConfig($config); - } - - public function GetAndGenerateSecret(string $secretId) : string { - $config = $this->GetConfig(); - if(!isset($config['secrets'][$secretId])) { - $config['secrets'][$secretId] = bin2hex(random_bytes(24)); - $this->WriteConfig($config); + /** + * @throws InvalidSettingConfigurationException + * @throws RandomException + */ + static function GetAndGenerateSecret(string $secretId): string { + $config = self::loadConfigFile(); + $secret = $config->getSecret($secretId); + if ($secret === null) { + $secret = bin2hex(random_bytes(24)); + $config->setSecret($secretId, $secret); + self::storeConfigFile($config); } if ($secretId === 'BORGBACKUP_PASSWORD' && !file_exists(DataConst::GetBackupSecretFile())) { - $this->DoubleSafeBackupSecret($config['secrets'][$secretId]); + self::DoubleSafeBackupSecret($secret); } - return $config['secrets'][$secretId]; + return $secret; } - public function GetSecret(string $secretId) : string { - $config = $this->GetConfig(); - if(!isset($config['secrets'][$secretId])) { - $config['secrets'][$secretId] = ""; - } - - return $config['secrets'][$secretId]; - } - - private function DoubleSafeBackupSecret(string $borgBackupPassword) : void { + private static function DoubleSafeBackupSecret(string $borgBackupPassword): void { file_put_contents(DataConst::GetBackupSecretFile(), $borgBackupPassword); } - public function hasBackupRunOnce() : bool { - if (!file_exists(DataConst::GetBackupKeyFile())) { - return false; - } else { - return true; - } + public function hasBackupRunOnce(): bool { + return file_exists(DataConst::GetBackupKeyFile()); } - public function GetLastBackupTime() : string { + public function GetLastBackupTime(): string { if (!file_exists(DataConst::GetBackupArchivesList())) { return ''; } - + $content = file_get_contents(DataConst::GetBackupArchivesList()); - if ($content === '') { + if ($content === '' || $content === false) { return ''; } @@ -91,15 +65,15 @@ public function GetLastBackupTime() : string { if ($lastBackupTime === "") { return ''; } - + return $lastBackupTime; } - public function GetBackupTimes() : array { + public function GetBackupTimes(): array { if (!file_exists(DataConst::GetBackupArchivesList())) { return []; } - + $content = file_get_contents(DataConst::GetBackupArchivesList()); if ($content === '') { return []; @@ -107,443 +81,30 @@ public function GetBackupTimes() : array { $backupLines = explode("\n", $content); $backupTimes = []; - foreach($backupLines as $lines) { + foreach ($backupLines as $lines) { if ($lines !== "") { $backupTimesTemp = explode(',', $lines); - $backupTimes[] = $backupTimesTemp[1]; + $backupTimes[] = $backupTimesTemp[1]; } } // Reverse the array to list newest backup first - $backupTimes = array_reverse($backupTimes); - - return $backupTimes; - } - - public function wasStartButtonClicked() : bool { - if (isset($this->GetConfig()['wasStartButtonClicked'])) { - return true; - } else { - return false; - } - } - - public function isx64Platform() : bool { - if (php_uname('m') === 'x86_64') { - return true; - } else { - return false; - } - } - - public function isClamavEnabled() : bool { - if (!$this->isx64Platform()) { - return false; - } - - $config = $this->GetConfig(); - if (isset($config['isClamavEnabled']) && $config['isClamavEnabled'] === 1) { - return true; - } else { - return false; - } - } - - public function isDockerSocketProxyEnabled() : bool { - $config = $this->GetConfig(); - if (isset($config['isDockerSocketProxyEnabled']) && $config['isDockerSocketProxyEnabled'] === 1) { - return true; - } else { - return false; - } - } - - public function SetDockerSocketProxyEnabledState(int $value) : void { - $config = $this->GetConfig(); - $config['isDockerSocketProxyEnabled'] = $value; - $this->WriteConfig($config); - } - - public function isWhiteboardEnabled() : bool { - $config = $this->GetConfig(); - if (isset($config['isWhiteboardEnabled']) && $config['isWhiteboardEnabled'] === 1) { - return true; - } else { - return false; - } - } - - public function SetWhiteboardEnabledState(int $value) : void { - $config = $this->GetConfig(); - $config['isWhiteboardEnabled'] = $value; - $this->WriteConfig($config); - } - - public function SetClamavEnabledState(int $value) : void { - $config = $this->GetConfig(); - $config['isClamavEnabled'] = $value; - $this->WriteConfig($config); - } - - public function isImaginaryEnabled() : bool { - $config = $this->GetConfig(); - if (isset($config['isImaginaryEnabled']) && $config['isImaginaryEnabled'] === 0) { - return false; - } else { - return true; - } - } - - public function SetImaginaryEnabledState(int $value) : void { - $config = $this->GetConfig(); - $config['isImaginaryEnabled'] = $value; - $this->WriteConfig($config); - } - - public function isFulltextsearchEnabled() : bool { - $config = $this->GetConfig(); - if (isset($config['isFulltextsearchEnabled']) && $config['isFulltextsearchEnabled'] === 1) { - return true; - } else { - return false; - } - } - - public function SetFulltextsearchEnabledState(int $value) : void { - $config = $this->GetConfig(); - $config['isFulltextsearchEnabled'] = $value; - $this->WriteConfig($config); - } - - public function isOnlyofficeEnabled() : bool { - $config = $this->GetConfig(); - if (isset($config['isOnlyofficeEnabled']) && $config['isOnlyofficeEnabled'] === 1) { - return true; - } else { - return false; - } - } - - public function SetOnlyofficeEnabledState(int $value) : void { - $config = $this->GetConfig(); - $config['isOnlyofficeEnabled'] = $value; - $this->WriteConfig($config); - } - - public function isCollaboraEnabled() : bool { - $config = $this->GetConfig(); - if (isset($config['isCollaboraEnabled']) && $config['isCollaboraEnabled'] === 0) { - return false; - } else { - return true; - } - } - - public function SetCollaboraEnabledState(int $value) : void { - $config = $this->GetConfig(); - $config['isCollaboraEnabled'] = $value; - $this->WriteConfig($config); - } - - public function isTalkEnabled() : bool { - $config = $this->GetConfig(); - if (isset($config['isTalkEnabled']) && $config['isTalkEnabled'] === 0) { - return false; - } else { - return true; - } - } - - public function SetTalkEnabledState(int $value) : void { - $config = $this->GetConfig(); - $config['isTalkEnabled'] = $value; - $this->WriteConfig($config); - } - - public function isTalkRecordingEnabled() : bool { - if (!$this->isTalkEnabled()) { - return false; - } - $config = $this->GetConfig(); - if (isset($config['isTalkRecordingEnabled']) && $config['isTalkRecordingEnabled'] === 1) { - return true; - } else { - return false; - } - } - - public function SetTalkRecordingEnabledState(int $value) : void { - if (!$this->isTalkEnabled()) { - $value = 0; - } - $config = $this->GetConfig(); - $config['isTalkRecordingEnabled'] = $value; - $this->WriteConfig($config); - } - - /** - * @throws InvalidSettingConfigurationException - */ - public function SetDomain(string $domain) : void { - // Validate that at least one dot is contained - if (!str_contains($domain, '.')) { - throw new InvalidSettingConfigurationException("Domain must contain at least one dot!"); - } - - // Validate that no slashes are contained - if (str_contains($domain, '/')) { - throw new InvalidSettingConfigurationException("Domain must not contain slashes!"); - } - - // Validate that no colons are contained - if (str_contains($domain, ':')) { - throw new InvalidSettingConfigurationException("Domain must not contain colons!"); - } - - // Validate domain - if (filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false) { - throw new InvalidSettingConfigurationException("Domain is not a valid domain!"); - } - - // Validate that it is not an IP-address - if(filter_var($domain, FILTER_VALIDATE_IP)) { - throw new InvalidSettingConfigurationException("Please enter a domain and not an IP-address!"); - } - - // Skip domain validation if opted in to do so - if (!$this->shouldDomainValidationBeSkipped()) { - - $dnsRecordIP = gethostbyname($domain); - if ($dnsRecordIP === $domain) { - $dnsRecordIP = ''; - } - - if (empty($dnsRecordIP)) { - $record = dns_get_record($domain, DNS_AAAA); - if (isset($record[0]['ipv6']) && !empty($record[0]['ipv6'])) { - $dnsRecordIP = $record[0]['ipv6']; - } - } - - // Validate IP - if (!filter_var($dnsRecordIP, FILTER_VALIDATE_IP)) { - throw new InvalidSettingConfigurationException("DNS config is not set for this domain or the domain is not a valid domain! (It was found to be set to '" . $dnsRecordIP . "')"); - } - - // Get the apache port - $port = $this->GetApachePort(); - - if (!filter_var($dnsRecordIP, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { - if ($port === '443') { - throw new InvalidSettingConfigurationException("It seems like the ip-address of the domain is set to an internal or reserved ip-address. This is not supported. (It was found to be set to '" . $dnsRecordIP . "'). Please set it to a public ip-address so that the domain validation can work!"); - } else { - error_log("It seems like the ip-address of " . $domain . " is set to an internal or reserved ip-address. (It was found to be set to '" . $dnsRecordIP . "')"); - } - } - - // Check if port 443 is open - $connection = @fsockopen($domain, 443, $errno, $errstr, 10); - if ($connection) { - fclose($connection); - } else { - throw new InvalidSettingConfigurationException("The domain is not reachable on Port 443 from within this container. Have you opened port 443/tcp in your router/firewall? If yes is the problem most likely that the router or firewall forbids local access to your domain. You can work around that by setting up a local DNS-server."); - } - - // Get Instance ID - $instanceID = $this->GetAndGenerateSecret('INSTANCE_ID'); - - // set protocol - if ($port !== '443') { - $protocol = 'https://'; - } else { - $protocol = 'http://'; - } - - // Check if response is correct - $ch = curl_init(); - $testUrl = $protocol . $domain . ':443'; - curl_setopt($ch, CURLOPT_URL, $testUrl); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); - curl_setopt($ch, CURLOPT_TIMEOUT, 10); - $response = (string)curl_exec($ch); - # Get rid of trailing \n - $response = str_replace("\n", "", $response); - - if ($response !== $instanceID) { - error_log('The response of the connection attempt to "' . $testUrl . '" was: ' . $response); - error_log('Expected was: ' . $instanceID); - error_log('The error message was: ' . curl_error($ch)); - $notice = "Domain does not point to this server or the reverse proxy is not configured correctly. See the mastercontainer logs for more details. ('sudo docker logs -f nextcloud-aio-mastercontainer')"; - if ($port === '443') { - $notice .= " If you should be using Cloudflare, make sure to disable the Cloudflare Proxy feature as it might block the domain validation. Same for any other firewall or service that blocks unencrypted access on port 443."; - } else { - error_log('Please follow https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md#6-how-to-debug-things in order to debug things!'); - } - throw new InvalidSettingConfigurationException($notice); - } - } - - // Write domain - $config = $this->GetConfig(); - $config['domain'] = $domain; - // Reset the borg restore password when setting the domain - $config['borg_restore_password'] = ''; - $this->WriteConfig($config); - } - - public function GetDomain() : string { - $config = $this->GetConfig(); - if(!isset($config['domain'])) { - $config['domain'] = ''; - } - - return $config['domain']; - } - - public function GetBaseDN() : string { - $domain = $this->GetDomain(); - if ($domain === "") { - return ""; - } - return 'dc=' . implode(',dc=', explode('.', $domain)); - } - - public function GetBackupMode() : string { - $config = $this->GetConfig(); - if(!isset($config['backup-mode'])) { - $config['backup-mode'] = ''; - } - - return $config['backup-mode']; - } - - public function GetSelectedRestoreTime() : string { - $config = $this->GetConfig(); - if(!isset($config['selected-restore-time'])) { - $config['selected-restore-time'] = ''; - } - - return $config['selected-restore-time']; - } - - public function GetAIOURL() : string { - $config = $this->GetConfig(); - if(!isset($config['AIO_URL'])) { - $config['AIO_URL'] = ''; - } - - return $config['AIO_URL']; - } - - /** - * @throws InvalidSettingConfigurationException - */ - public function SetBorgBackupHostLocation(string $location) : void { - $isValidPath = false; - if (str_starts_with($location, '/') && !str_ends_with($location, '/')) { - $isValidPath = true; - } elseif ($location === 'nextcloud_aio_backupdir') { - $isValidPath = true; - } - - if (!$isValidPath) { - throw new InvalidSettingConfigurationException("The path must start with '/', and must not end with '/'!"); - } - - - $config = $this->GetConfig(); - $config['borg_backup_host_location'] = $location; - $this->WriteConfig($config); - } - - public function DeleteBorgBackupHostLocation() : void { - $config = $this->GetConfig(); - $config['borg_backup_host_location'] = ''; - $this->WriteConfig($config); - } - - /** - * @throws InvalidSettingConfigurationException - */ - public function SetBorgRestoreHostLocationAndPassword(string $location, string $password) : void { - if ($location === '') { - throw new InvalidSettingConfigurationException("Please enter a path!"); - } - - $isValidPath = false; - if (str_starts_with($location, '/') && !str_ends_with($location, '/')) { - $isValidPath = true; - } elseif ($location === 'nextcloud_aio_backupdir') { - $isValidPath = true; - } - - if (!$isValidPath) { - throw new InvalidSettingConfigurationException("The path must start with '/', and must not end with '/'!"); - } - - if ($password === '') { - throw new InvalidSettingConfigurationException("Please enter the password!"); - } - - $config = $this->GetConfig(); - $config['borg_backup_host_location'] = $location; - $config['borg_restore_password'] = $password; - $config['instance_restore_attempt'] = 1; - $this->WriteConfig($config); + return array_reverse($backupTimes); } /** * @throws InvalidSettingConfigurationException */ - public function ChangeMasterPassword(string $currentPassword, string $newPassword) : void { - if ($currentPassword === '') { - throw new InvalidSettingConfigurationException("Please enter your current password."); - } - - if ($currentPassword !== $this->GetPassword()) { - throw new InvalidSettingConfigurationException("The entered current password is not correct."); - } - - if ($newPassword === '') { - throw new InvalidSettingConfigurationException("Please enter a new password."); - } - - if (strlen($newPassword) < 24) { - throw new InvalidSettingConfigurationException("New passwords must be >= 24 digits."); - } - - if (!preg_match("#^[a-zA-Z0-9 ]+$#", $newPassword)) { - throw new InvalidSettingConfigurationException('Not allowed characters in the new password.'); - } - - // All checks pass so set the password - $this->SetPassword($newPassword); - } - - public function GetApachePort() : string { - $envVariableName = 'APACHE_PORT'; - $configName = 'apache_port'; - $defaultValue = '443'; - return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); - } - - public function GetTalkPort() : string { - $envVariableName = 'TALK_PORT'; - $configName = 'talk_port'; - $defaultValue = '3478'; - return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); - } - - /** - * @throws InvalidSettingConfigurationException - */ - public function WriteConfig(array $config) : void { - if(!is_dir(DataConst::GetDataDirectory())) { + public static function storeConfigFile(ConfigFile $config): void { + if (!is_dir(DataConst::GetDataDirectory())) { throw new InvalidSettingConfigurationException(DataConst::GetDataDirectory() . " does not exist! Something was set up falsely!"); } $df = disk_free_space(DataConst::GetDataDirectory()); - $content = json_encode($config, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR); + try { + $content = json_encode($config, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + throw new InvalidSettingConfigurationException('Failed to encode JSON data', previous: $e); + } $size = strlen($content) + 10240; if ($df !== false && (int)$df < $size) { throw new InvalidSettingConfigurationException(DataConst::GetDataDirectory() . " does not have enough space for writing the config file! Not writing it back!"); @@ -551,161 +112,11 @@ public function WriteConfig(array $config) : void { file_put_contents(DataConst::GetConfigFile(), $content); } - private function GetEnvironmentalVariableOrConfig(string $envVariableName, string $configName, string $defaultValue) : string { - $envVariableOutput = getenv($envVariableName); - if ($envVariableOutput === false) { - $config = $this->GetConfig(); - if (!isset($config[$configName]) || $config[$configName] === '') { - $config[$configName] = $defaultValue; - } - return $config[$configName]; - } - if(file_exists(DataConst::GetConfigFile())) { - $config = $this->GetConfig(); - if (!isset($config[$configName])) { - $config[$configName] = ''; - } - if ($envVariableOutput !== $config[$configName]) { - $config[$configName] = $envVariableOutput; - $this->WriteConfig($config); - } - } - return $envVariableOutput; - } - - public function GetBorgBackupHostLocation() : string { - $config = $this->GetConfig(); - if(!isset($config['borg_backup_host_location'])) { - $config['borg_backup_host_location'] = ''; - } - - return $config['borg_backup_host_location']; - } - - public function GetBorgRestorePassword() : string { - $config = $this->GetConfig(); - if(!isset($config['borg_restore_password'])) { - $config['borg_restore_password'] = ''; - } - - return $config['borg_restore_password']; - } - - public function isInstanceRestoreAttempt() : bool { - $config = $this->GetConfig(); - if(!isset($config['instance_restore_attempt'])) { - $config['instance_restore_attempt'] = ''; - } - - if ($config['instance_restore_attempt'] === 1) { - return true; - } - return false; - } - - public function GetBorgBackupMode() : string { - $config = $this->GetConfig(); - if(!isset($config['backup-mode'])) { - $config['backup-mode'] = ''; - } - - return $config['backup-mode']; - } - - public function GetNextcloudMount() : string { - $envVariableName = 'NEXTCLOUD_MOUNT'; - $configName = 'nextcloud_mount'; - $defaultValue = ''; - return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); - } - - public function GetNextcloudDatadirMount() : string { - $envVariableName = 'NEXTCLOUD_DATADIR'; - $configName = 'nextcloud_datadir'; - $defaultValue = 'nextcloud_aio_nextcloud_data'; - return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); - } - - public function GetNextcloudUploadLimit() : string { - $envVariableName = 'NEXTCLOUD_UPLOAD_LIMIT'; - $configName = 'nextcloud_upload_limit'; - $defaultValue = '10G'; - return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); - } - - public function GetNextcloudMemoryLimit() : string { - $envVariableName = 'NEXTCLOUD_MEMORY_LIMIT'; - $configName = 'nextcloud_memory_limit'; - $defaultValue = '512M'; - return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); - } - - public function GetApacheMaxSize() : int { - $uploadLimit = (int)rtrim($this->GetNextcloudUploadLimit(), 'G'); - return $uploadLimit * 1024 * 1024 * 1024; - } - - public function GetNextcloudMaxTime() : string { - $envVariableName = 'NEXTCLOUD_MAX_TIME'; - $configName = 'nextcloud_max_time'; - $defaultValue = '3600'; - return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); - } - - public function GetBorgRetentionPolicy() : string { - $envVariableName = 'BORG_RETENTION_POLICY'; - $configName = 'borg_retention_policy'; - $defaultValue = '--keep-within=7d --keep-weekly=4 --keep-monthly=6'; - return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); - } - - public function GetDockerSocketPath() : string { - $envVariableName = 'WATCHTOWER_DOCKER_SOCKET_PATH'; - $configName = 'docker_socket_path'; - $defaultValue = '/var/run/docker.sock'; - return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); - } - - public function GetTrustedCacertsDir() : string { - $envVariableName = 'NEXTCLOUD_TRUSTED_CACERTS_DIR'; - $configName = 'trusted_cacerts_dir'; - $defaultValue = ''; - return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); - } - - public function GetNextcloudAdditionalApks() : string { - $envVariableName = 'NEXTCLOUD_ADDITIONAL_APKS'; - $configName = 'nextcloud_additional_apks'; - $defaultValue = 'imagemagick'; - return trim($this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue)); - } - - public function GetNextcloudAdditionalPhpExtensions() : string { - $envVariableName = 'NEXTCLOUD_ADDITIONAL_PHP_EXTENSIONS'; - $configName = 'nextcloud_additional_php_extensions'; - $defaultValue = 'imagick'; - return trim($this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue)); - } - - public function GetCollaboraSeccompPolicy() : string { - $defaultString = '--o:security.seccomp='; - if ($this->GetCollaboraSeccompDisabledState() !== 'true') { - return $defaultString . 'true'; - } - return $defaultString . 'false'; - } - - private function GetCollaboraSeccompDisabledState() : string { - $envVariableName = 'COLLABORA_SECCOMP_DISABLED'; - $configName = 'collabora_seccomp_disabled'; - $defaultValue = 'false'; - return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); - } /** * @throws InvalidSettingConfigurationException */ - public function SetDailyBackupTime(string $time, bool $enableAutomaticUpdates, bool $successNotification) : void { + public static function SetDailyBackupTime(string $time, bool $enableAutomaticUpdates, bool $successNotification): void { if ($time === "") { throw new InvalidSettingConfigurationException("The daily backup time must not be empty!"); } @@ -713,7 +124,7 @@ public function SetDailyBackupTime(string $time, bool $enableAutomaticUpdates, b if (!preg_match("#^[0-1][0-9]:[0-5][0-9]$#", $time) && !preg_match("#^2[0-3]:[0-5][0-9]$#", $time)) { throw new InvalidSettingConfigurationException("You did not enter a correct time! One correct example is '04:00'!"); } - + if ($enableAutomaticUpdates === false) { $time .= PHP_EOL . 'automaticUpdatesAreNotEnabled'; } else { @@ -727,7 +138,7 @@ public function SetDailyBackupTime(string $time, bool $enableAutomaticUpdates, b file_put_contents(DataConst::GetDailyBackupTimeFile(), $time); } - public function GetDailyBackupTime() : string { + public function GetDailyBackupTime(): string { if (!file_exists(DataConst::GetDailyBackupTimeFile())) { return ''; } @@ -736,7 +147,7 @@ public function GetDailyBackupTime() : string { return $dailyBackupFileArray[0]; } - public function areAutomaticUpdatesEnabled() : bool { + static function areAutomaticUpdatesEnabled(): bool { if (!file_exists(DataConst::GetDailyBackupTimeFile())) { return false; } @@ -749,19 +160,17 @@ public function areAutomaticUpdatesEnabled() : bool { } } - public function DeleteDailyBackupTime() : void { + public function DeleteDailyBackupTime(): void { if (file_exists(DataConst::GetDailyBackupTimeFile())) { unlink(DataConst::GetDailyBackupTimeFile()); } } - /** - * @throws InvalidSettingConfigurationException - */ - public function SetAdditionalBackupDirectories(string $additionalBackupDirectories) : void { + /** @throws InvalidSettingConfigurationException */ + public static function SetAdditionalBackupDirectories(string $additionalBackupDirectories): void { $additionalBackupDirectoriesArray = explode("\n", $additionalBackupDirectories); $validDirectories = ''; - foreach($additionalBackupDirectoriesArray as $entry) { + foreach ($additionalBackupDirectoriesArray as $entry) { // Trim all unwanted chars on both sites $entry = trim($entry); if ($entry !== "") { @@ -779,175 +188,38 @@ public function SetAdditionalBackupDirectories(string $additionalBackupDirectori } } - public function shouldLatestMajorGetInstalled() : bool { - $config = $this->GetConfig(); - if(!isset($config['install_latest_major'])) { - $config['install_latest_major'] = ''; - } - return $config['install_latest_major'] !== ''; - } - - public function GetAdditionalBackupDirectoriesString() : string { - if (!file_exists(DataConst::GetAdditionalBackupDirectoriesFile())) { - return ''; - } - $additionalBackupDirectories = file_get_contents(DataConst::GetAdditionalBackupDirectoriesFile()); - return $additionalBackupDirectories; + static function GetAdditionalBackupDirectoriesString(): string { + return file_exists(DataConst::GetAdditionalBackupDirectoriesFile()) + ? file_get_contents(DataConst::GetAdditionalBackupDirectoriesFile()) + : ''; } - public function GetAdditionalBackupDirectoriesArray() : array { - $additionalBackupDirectories = $this->GetAdditionalBackupDirectoriesString(); + static function GetAdditionalBackupDirectoriesArray(): array { + $additionalBackupDirectories = self::GetAdditionalBackupDirectoriesString(); $additionalBackupDirectoriesArray = explode("\n", $additionalBackupDirectories); $additionalBackupDirectoriesArray = array_unique($additionalBackupDirectoriesArray, SORT_REGULAR); return $additionalBackupDirectoriesArray; } - public function isDailyBackupRunning() : bool { + static function isDailyBackupRunning(): bool { if (file_exists(DataConst::GetDailyBackupBlockFile())) { return true; } return false; } - public function GetTimezone() : string { - $config = $this->GetConfig(); - if(!isset($config['timezone'])) { - $config['timezone'] = ''; - } - - return $config['timezone']; - } - - /** - * @throws InvalidSettingConfigurationException - */ - public function SetTimezone(string $timezone) : void { - if ($timezone === "") { - throw new InvalidSettingConfigurationException("The timezone must not be empty!"); - } - - if (!preg_match("#^[a-zA-Z0-9_\-\/\+]+$#", $timezone)) { - throw new InvalidSettingConfigurationException("The entered timezone does not seem to be a valid timezone!"); - } - - $config = $this->GetConfig(); - $config['timezone'] = $timezone; - $this->WriteConfig($config); - } - - public function DeleteTimezone() : void { - $config = $this->GetConfig(); - $config['timezone'] = ''; - $this->WriteConfig($config); - } - - public function shouldDomainValidationBeSkipped() : bool { + public function shouldDomainValidationBeSkipped(): bool { if (getenv('SKIP_DOMAIN_VALIDATION') !== false) { return true; } return false; } - public function GetNextcloudStartupApps() : string { + static function GetNextcloudStartupApps(): string { $apps = getenv('NEXTCLOUD_STARTUP_APPS'); if (is_string($apps)) { return trim($apps); } return 'deck twofactor_totp tasks calendar contacts notes'; } - - public function GetCollaboraDictionaries() : string { - $config = $this->GetConfig(); - if(!isset($config['collabora_dictionaries'])) { - $config['collabora_dictionaries'] = ''; - } - - return $config['collabora_dictionaries']; - } - - /** - * @throws InvalidSettingConfigurationException - */ - public function SetCollaboraDictionaries(string $CollaboraDictionaries) : void { - if ($CollaboraDictionaries === "") { - throw new InvalidSettingConfigurationException("The dictionaries must not be empty!"); - } - - if (!preg_match("#^[a-zA-Z_ ]+$#", $CollaboraDictionaries)) { - throw new InvalidSettingConfigurationException("The entered dictionaries do not seem to be a valid!"); - } - - $config = $this->GetConfig(); - $config['collabora_dictionaries'] = $CollaboraDictionaries; - $this->WriteConfig($config); - } - - public function DeleteCollaboraDictionaries() : void { - $config = $this->GetConfig(); - $config['collabora_dictionaries'] = ''; - $this->WriteConfig($config); - } - - public function GetApacheIPBinding() : string { - $envVariableName = 'APACHE_IP_BINDING'; - $configName = 'apache_ip_binding'; - $defaultValue = ''; - return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); - } - - private function GetDisableBackupSection() : string { - $envVariableName = 'AIO_DISABLE_BACKUP_SECTION'; - $configName = 'disable_backup_section'; - $defaultValue = ''; - return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); - } - - public function isBackupSectionEnabled() : bool { - if ($this->GetDisableBackupSection() === 'true') { - return false; - } else { - return true; - } - } - - private function GetCommunityContainers() : string { - $envVariableName = 'AIO_COMMUNITY_CONTAINERS'; - $configName = 'aio_community_containers'; - $defaultValue = ''; - return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); - } - - public function GetEnabledCommunityContainers() : array { - return explode(' ', $this->GetCommunityContainers()); - } - - private function GetEnabledDriDevice() : string { - $envVariableName = 'NEXTCLOUD_ENABLE_DRI_DEVICE'; - $configName = 'nextcloud_enable_dri_device'; - $defaultValue = ''; - return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); - } - - public function isDriDeviceEnabled() : bool { - if ($this->GetEnabledDriDevice() === 'true') { - return true; - } else { - return false; - } - } - - private function GetKeepDisabledApps() : string { - $envVariableName = 'NEXTCLOUD_KEEP_DISABLED_APPS'; - $configName = 'nextcloud_keep_disabled_apps'; - $defaultValue = ''; - return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); - } - - public function shouldDisabledAppsGetRemoved() : bool { - if ($this->GetKeepDisabledApps() === 'true') { - return false; - } else { - return true; - } - } } diff --git a/php/src/Data/Setup.php b/php/src/Data/Setup.php index 2ab87e36e85..8c25616c7a3 100644 --- a/php/src/Data/Setup.php +++ b/php/src/Data/Setup.php @@ -3,30 +3,24 @@ namespace AIO\Data; use AIO\Auth\PasswordGenerator; +use Random\RandomException; -class Setup -{ - private PasswordGenerator $passwordGenerator; - private ConfigurationManager $configurationManager; - - public function __construct( - PasswordGenerator $passwordGenerator, - ConfigurationManager $configurationManager) { - $this->passwordGenerator = $passwordGenerator; - $this->configurationManager = $configurationManager; - } - - public function Setup() : string { - if(!$this->CanBeInstalled()) { +readonly class Setup { + /** + * @throws InvalidSettingConfigurationException + * @throws RandomException + */ + static function Setup(): string { + if (!self::CanBeInstalled()) { return ''; } - $password = $this->passwordGenerator->GeneratePassword(8); - $this->configurationManager->SetPassword($password); + $password = PasswordGenerator::GeneratePassword(8); + ConfigurationManager::storeConfigFile(ConfigFile::blank($password)); return $password; } - public function CanBeInstalled() : bool { + static function CanBeInstalled(): bool { return !file_exists(DataConst::GetConfigFile()); } } diff --git a/php/src/Docker/DockerActionManager.php b/php/src/Docker/DockerActionManager.php index 6bb7f70ee75..d53c1b07524 100644 --- a/php/src/Docker/DockerActionManager.php +++ b/php/src/Docker/DockerActionManager.php @@ -12,6 +12,7 @@ use AIO\Container\State\VersionDifferentState; use AIO\Container\State\StoppedState; use AIO\Container\State\VersionEqualState; +use AIO\Data\ConfigFile; use AIO\Data\ConfigurationManager; use GuzzleHttp\Exception\RequestException; use AIO\ContainerDefinitionFetcher; @@ -21,16 +22,13 @@ class DockerActionManager { private const string API_VERSION = 'v1.41'; private \GuzzleHttp\Client $guzzleClient; - private ConfigurationManager $configurationManager; private ContainerDefinitionFetcher $containerDefinitionFetcher; private DockerHubManager $dockerHubManager; public function __construct( - ConfigurationManager $configurationManager, ContainerDefinitionFetcher $containerDefinitionFetcher, DockerHubManager $dockerHubManager ) { - $this->configurationManager = $configurationManager; $this->containerDefinitionFetcher = $containerDefinitionFetcher; $this->dockerHubManager = $dockerHubManager; $this->guzzleClient = new \GuzzleHttp\Client( @@ -130,12 +128,13 @@ public function GetContainerStartingState(Container $container) : IContainerStat return new ImageDoesNotExistState(); } + $config = ConfigurationManager::loadConfigFile(); $containerName = $container->GetIdentifier(); $internalPort = $container->GetInternalPort(); if($internalPort === '%APACHE_PORT%') { - $internalPort = $this->configurationManager->GetApachePort(); + $internalPort = $config->apachePort; } elseif($internalPort === '%TALK_PORT%') { - $internalPort = $this->configurationManager->GetTalkPort(); + $internalPort = $config->talkPort; } if ($internalPort !== "" && $internalPort !== 'host') { @@ -221,11 +220,13 @@ public function CreateVolumes(Container $container): void } public function CreateContainer(Container $container) : void { + $config = ConfigurationManager::loadConfigFile(); + $volumes = []; foreach ($container->GetVolumes()->GetVolumes() as $volume) { // // NEXTCLOUD_MOUNT gets added via bind-mount later on // if ($container->GetIdentifier() === 'nextcloud-aio-nextcloud') { - // if ($volume->name === $this->configurationManager->GetNextcloudMount()) { + // if ($volume->name === $config->GetNextcloudMount()) { // continue; // } // } @@ -249,17 +250,15 @@ public function CreateContainer(Container $container) : void { } foreach($container->GetSecrets() as $secret) { - $this->configurationManager->GetAndGenerateSecret($secret); + ConfigurationManager::GetAndGenerateSecret($secret); } $aioVariables = $container->GetAioVariables()->GetVariables(); foreach($aioVariables as $variable) { - $config = $this->configurationManager->GetConfig(); $variableArray = explode('=', $variable); - $config[$variableArray[0]] = $variableArray[1]; - $this->configurationManager->WriteConfig($config); - sleep(1); + $config->overwrite($variableArray[0], $variableArray[1]); } + if(!empty($aioVariables)) ConfigurationManager::storeConfigFile($config); $envs = $container->GetEnvironmentVariables()->GetVariables(); // Special thing for the nextcloud container @@ -269,8 +268,8 @@ public function CreateContainer(Container $container) : void { foreach($envs as $key => $env) { // TODO: This whole block below is a hack and needs to get reworked in order to support multiple substitutions per line by default for all envs if (str_starts_with($env, 'extra_params=')) { - $env = str_replace('%COLLABORA_SECCOMP_POLICY%', $this->configurationManager->GetCollaboraSeccompPolicy(), $env); - $env = str_replace('%NC_DOMAIN%', $this->configurationManager->GetDomain(), $env); + $env = str_replace('%COLLABORA_SECCOMP_POLICY%', $config->getCollaboraSeccompPolicy(), $env); + $env = str_replace('%NC_DOMAIN%', $config->getDomain(), $env); $envs[$key] = $env; continue; } @@ -282,130 +281,130 @@ public function CreateContainer(Container $container) : void { $replacements = array(); if($out[1] === 'NC_DOMAIN') { - $replacements[1] = $this->configurationManager->GetDomain(); + $replacements[1] = $config->GetDomain(); } elseif($out[1] === 'NC_BASE_DN') { - $replacements[1] = $this->configurationManager->GetBaseDN(); + $replacements[1] = $config->GetBaseDN(); } elseif ($out[1] === 'AIO_TOKEN') { - $replacements[1] = $this->configurationManager->GetToken(); + $replacements[1] = $config->GetToken(); } elseif ($out[1] === 'BORGBACKUP_MODE') { - $replacements[1] = $this->configurationManager->GetBackupMode(); + $replacements[1] = $config->GetBackupMode(); } elseif ($out[1] === 'AIO_URL') { - $replacements[1] = $this->configurationManager->GetAIOURL(); + $replacements[1] = $config->aioUrl; } elseif ($out[1] === 'SELECTED_RESTORE_TIME') { - $replacements[1] = $this->configurationManager->GetSelectedRestoreTime(); + $replacements[1] = $config->GetSelectedRestoreTime(); } elseif ($out[1] === 'APACHE_PORT') { - $replacements[1] = $this->configurationManager->GetApachePort(); + $replacements[1] = $config->apachePort; } elseif ($out[1] === 'TALK_PORT') { - $replacements[1] = $this->configurationManager->GetTalkPort(); + $replacements[1] = $config->talkPort; } elseif ($out[1] === 'NEXTCLOUD_MOUNT') { - $replacements[1] = $this->configurationManager->GetNextcloudMount(); + $replacements[1] = $config->nextcloudMount; } elseif ($out[1] === 'BACKUP_RESTORE_PASSWORD') { - $replacements[1] = $this->configurationManager->GetBorgRestorePassword(); + $replacements[1] = $config->getBorgPassword(); } elseif ($out[1] === 'CLAMAV_ENABLED') { - if ($this->configurationManager->isClamavEnabled()) { + if ($config->isClamavEnabled()) { $replacements[1] = 'yes'; } else { $replacements[1] = ''; } } elseif ($out[1] === 'TALK_RECORDING_ENABLED') { - if ($this->configurationManager->isTalkRecordingEnabled()) { + if ($config->isTalkRecordingEnabled()) { $replacements[1] = 'yes'; } else { $replacements[1] = ''; } } elseif ($out[1] === 'ONLYOFFICE_ENABLED') { - if ($this->configurationManager->isOnlyofficeEnabled()) { + if ($config->isOnlyofficeEnabled()) { $replacements[1] = 'yes'; } else { $replacements[1] = ''; } } elseif ($out[1] === 'COLLABORA_ENABLED') { - if ($this->configurationManager->isCollaboraEnabled()) { + if ($config->isCollaboraEnabled()) { $replacements[1] = 'yes'; } else { $replacements[1] = ''; } } elseif ($out[1] === 'TALK_ENABLED') { - if ($this->configurationManager->isTalkEnabled()) { + if ($config->talkEnabled) { $replacements[1] = 'yes'; } else { $replacements[1] = ''; } } elseif ($out[1] === 'UPDATE_NEXTCLOUD_APPS') { - if ($this->configurationManager->isDailyBackupRunning() && $this->configurationManager->areAutomaticUpdatesEnabled()) { + if (ConfigurationManager::isDailyBackupRunning() && ConfigurationManager::areAutomaticUpdatesEnabled()) { $replacements[1] = 'yes'; } else { $replacements[1] = ''; } } elseif ($out[1] === 'TIMEZONE') { - if ($this->configurationManager->GetTimezone() === '') { + if ($config->GetTimezone() === '') { $replacements[1] = 'Etc/UTC'; } else { - $replacements[1] = $this->configurationManager->GetTimezone(); + $replacements[1] = $config->getTimezone(); } } elseif ($out[1] === 'COLLABORA_DICTIONARIES') { - if ($this->configurationManager->GetCollaboraDictionaries() === '') { + if ($config->getCollaboraDictionaries() === '') { $replacements[1] = 'de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru'; } else { - $replacements[1] = $this->configurationManager->GetCollaboraDictionaries(); + $replacements[1] = $config->getCollaboraDictionaries(); } } elseif ($out[1] === 'IMAGINARY_ENABLED') { - if ($this->configurationManager->isImaginaryEnabled()) { + if ($config->imaginaryEnabled) { $replacements[1] = 'yes'; } else { $replacements[1] = ''; } } elseif ($out[1] === 'FULLTEXTSEARCH_ENABLED') { - if ($this->configurationManager->isFulltextsearchEnabled()) { + if ($config->fulltextsearchEnabled) { $replacements[1] = 'yes'; } else { $replacements[1] = ''; } } elseif ($out[1] === 'DOCKER_SOCKET_PROXY_ENABLED') { - if ($this->configurationManager->isDockerSocketProxyEnabled()) { + if ($config->dockerSocketProxyEnabled) { $replacements[1] = 'yes'; } else { $replacements[1] = ''; } } elseif ($out[1] === 'NEXTCLOUD_UPLOAD_LIMIT') { - $replacements[1] = $this->configurationManager->GetNextcloudUploadLimit(); + $replacements[1] = $config->nextcloudUploadLimit; } elseif ($out[1] === 'NEXTCLOUD_MEMORY_LIMIT') { - $replacements[1] = $this->configurationManager->GetNextcloudMemoryLimit(); + $replacements[1] = $config->nextcloudMemoryLimit; } elseif ($out[1] === 'NEXTCLOUD_MAX_TIME') { - $replacements[1] = $this->configurationManager->GetNextcloudMaxTime(); + $replacements[1] = $config->nextcloudMaxTime; } elseif ($out[1] === 'BORG_RETENTION_POLICY') { - $replacements[1] = $this->configurationManager->GetBorgRetentionPolicy(); + $replacements[1] = $config->borgRetentionPolicy; } elseif ($out[1] === 'NEXTCLOUD_TRUSTED_CACERTS_DIR') { - $replacements[1] = $this->configurationManager->GetTrustedCacertsDir(); + $replacements[1] = $config->trustedCacertsDir; } elseif ($out[1] === 'ADDITIONAL_DIRECTORIES_BACKUP') { - if ($this->configurationManager->GetAdditionalBackupDirectoriesString() !== '') { + if (ConfigurationManager::GetAdditionalBackupDirectoriesString() !== '') { $replacements[1] = 'yes'; } else { $replacements[1] = ''; } } elseif ($out[1] === 'BORGBACKUP_HOST_LOCATION') { - $replacements[1] = $this->configurationManager->GetBorgBackupHostLocation(); + $replacements[1] = $config->getBorgLocation(); } elseif ($out[1] === 'APACHE_MAX_SIZE') { - $replacements[1] = $this->configurationManager->GetApacheMaxSize(); + $replacements[1] = $config->GetApacheMaxSize(); } elseif ($out[1] === 'COLLABORA_SECCOMP_POLICY') { - $replacements[1] = $this->configurationManager->GetCollaboraSeccompPolicy(); + $replacements[1] = $config->GetCollaboraSeccompPolicy(); } elseif ($out[1] === 'NEXTCLOUD_STARTUP_APPS') { - $replacements[1] = $this->configurationManager->GetNextcloudStartupApps(); + $replacements[1] = ConfigurationManager::GetNextcloudStartupApps(); } elseif ($out[1] === 'NEXTCLOUD_ADDITIONAL_APKS') { - $replacements[1] = $this->configurationManager->GetNextcloudAdditionalApks(); + $replacements[1] = $config->nextcloudAdditionalApks; } elseif ($out[1] === 'NEXTCLOUD_ADDITIONAL_PHP_EXTENSIONS') { - $replacements[1] = $this->configurationManager->GetNextcloudAdditionalPhpExtensions(); + $replacements[1] = $config->nextcloudAdditionalPhpExtensions; } elseif ($out[1] === 'INSTALL_LATEST_MAJOR') { - if ($this->configurationManager->shouldLatestMajorGetInstalled()) { + if ($config->installLatestMajor) { $replacements[1] = 'yes'; } else { $replacements[1] = ''; } } elseif ($out[1] === 'REMOVE_DISABLED_APPS') { - if ($this->configurationManager->shouldDisabledAppsGetRemoved()) { - $replacements[1] = 'yes'; - } else { + if ($config->nextcloudKeepDisabledApps) { $replacements[1] = ''; + } else { + $replacements[1] = 'yes'; } // Allow to get local ip-address of database container which allows to talk to it even in host mode (the container that requires this needs to be started first then) } elseif ($out[1] === 'AIO_DATABASE_HOST') { @@ -413,18 +412,18 @@ public function CreateContainer(Container $container) : void { // Allow to get local ip-address of caddy container and add it to trusted proxies automatically } elseif ($out[1] === 'CADDY_IP_ADDRESS') { $replacements[1] = ''; - $communityContainers = $this->configurationManager->GetEnabledCommunityContainers(); + $communityContainers = $config->aioCommunityContainers; if (in_array('caddy', $communityContainers, true)) { $replacements[1] = gethostbyname('nextcloud-aio-caddy'); } } elseif ($out[1] === 'WHITEBOARD_ENABLED') { - if ($this->configurationManager->isWhiteboardEnabled()) { + if ($config->whiteboardEnabled) { $replacements[1] = 'yes'; } else { $replacements[1] = ''; } } else { - $secret = $this->configurationManager->GetSecret($out[1]); + $secret = $config->GetSecret($out[1]); if ($secret === "") { throw new \Exception("The secret " . $out[1] . " is empty. Cannot substitute its value. Please check if it is defined in secrets of containers.json."); } @@ -449,13 +448,13 @@ public function CreateContainer(Container $container) : void { $port = $value->port; $protocol = $value->protocol; if ($port === '%APACHE_PORT%') { - $port = $this->configurationManager->GetApachePort(); + $port = $config->apachePort; // Do not expose udp if AIO is in reverse proxy mode if ($port !== '443' && $protocol === 'udp') { continue; } } else if ($port === '%TALK_PORT%') { - $port = $this->configurationManager->GetTalkPort(); + $port = $config->talkPort; } $portWithProtocol = $port . '/' . $protocol; $exposedPorts[$portWithProtocol] = null; @@ -471,17 +470,17 @@ public function CreateContainer(Container $container) : void { $port = $value->port; $protocol = $value->protocol; if ($port === '%APACHE_PORT%') { - $port = $this->configurationManager->GetApachePort(); + $port = $config->apachePort; // Do not expose udp if AIO is in reverse proxy mode if ($port !== '443' && $protocol === 'udp') { continue; } } else if ($port === '%TALK_PORT%') { - $port = $this->configurationManager->GetTalkPort(); + $port = $config->talkPort; } $ipBinding = $value->ipBinding; if ($ipBinding === '%APACHE_IP_BINDING%') { - $ipBinding = $this->configurationManager->GetApacheIPBinding(); + $ipBinding = $config->apacheIpBinding; // Do not expose if AIO is in internal network mode if ($ipBinding === '@INTERNAL') { continue; @@ -499,7 +498,7 @@ public function CreateContainer(Container $container) : void { $devices = []; foreach($container->GetDevices() as $device) { - if ($device === '/dev/dri' && ! $this->configurationManager->isDriDeviceEnabled()) { + if ($device === '/dev/dri' && ! $config->nextcloudEnableDriDevice) { continue; } $devices[] = ["PathOnHost" => $device, "PathInContainer" => $device, "CgroupPermissions" => "rwm"]; @@ -555,7 +554,7 @@ public function CreateContainer(Container $container) : void { $mounts[] = ["Type" => "volume", "Source" => $additionalBackupVolumes, "Target" => "/nextcloud_aio_volumes/" . $additionalBackupVolumes, "ReadOnly" => false]; } } - foreach ($this->configurationManager->GetAdditionalBackupDirectoriesArray() as $additionalBackupDirectories) { + foreach (ConfigurationManager::GetAdditionalBackupDirectoriesArray() as $additionalBackupDirectories) { if ($additionalBackupDirectories !== '') { if (!str_starts_with($additionalBackupDirectories, '/')) { $mounts[] = ["Type" => "volume", "Source" => $additionalBackupDirectories, "Target" => "/docker_volumes/" . $additionalBackupDirectories, "ReadOnly" => true]; @@ -571,7 +570,7 @@ public function CreateContainer(Container $container) : void { // // Special things for the nextcloud container which should not be exposed in the containers.json // } elseif ($container->GetIdentifier() === 'nextcloud-aio-nextcloud') { // foreach ($container->GetVolumes()->GetVolumes() as $volume) { - // if ($volume->name !== $this->configurationManager->GetNextcloudMount()) { + // if ($volume->name !== $config->GetNextcloudMount()) { // continue; // } // $mounts[] = ["Type" => "bind", "Source" => $volume->name, "Target" => $volume->mountPoint, "ReadOnly" => !$volume->isWritable, "BindOptions" => [ "Propagation" => "rshared"]]; @@ -652,7 +651,8 @@ private function isContainerUpdateAvailable(string $id) : string public function isAnyUpdateAvailable() : bool { // return early if instance is not installed - if (!$this->configurationManager->wasStartButtonClicked()) { + $config = ConfigurationManager::loadConfigFile(); + if (!$config->wasStartButtonClicked) { return false; } $id = 'nextcloud-aio-apache';