diff --git a/application/clicommands/CheckCommand.php b/application/clicommands/CheckCommand.php index 838428e8..0c369d9c 100644 --- a/application/clicommands/CheckCommand.php +++ b/application/clicommands/CheckCommand.php @@ -9,6 +9,7 @@ use DateTimeInterface; use Icinga\Application\Logger; use Icinga\Module\X509\Command; +use Icinga\Module\X509\Common\Database; use Icinga\Module\X509\Model\X509Certificate; use Icinga\Module\X509\Model\X509Target; use ipl\Sql\Expression; @@ -67,8 +68,7 @@ public function hostAction() exit(3); } - $conn = $this->getDb(); - $targets = X509Target::on($conn)->with([ + $targets = X509Target::on(Database::get())->with([ 'chain', 'chain.certificate', 'chain.certificate.issuer_certificate' diff --git a/application/clicommands/CleanupCommand.php b/application/clicommands/CleanupCommand.php index 55870f53..748648cd 100644 --- a/application/clicommands/CleanupCommand.php +++ b/application/clicommands/CleanupCommand.php @@ -9,6 +9,7 @@ use Icinga\Application\Logger; use Icinga\Module\X509\CertificateUtils; use Icinga\Module\X509\Command; +use Icinga\Module\X509\Common\Database; use InvalidArgumentException; use Throwable; @@ -62,7 +63,7 @@ public function indexAction() } try { - $conn = $this->getDb(); + $conn = Database::get(); $query = $conn->delete( 'x509_target', ['last_scan < ?' => $sinceLastScan->format('Uv')] diff --git a/application/clicommands/ImportCommand.php b/application/clicommands/ImportCommand.php index bb0c2d1e..2e7b1578 100644 --- a/application/clicommands/ImportCommand.php +++ b/application/clicommands/ImportCommand.php @@ -7,6 +7,7 @@ use Icinga\Application\Logger; use Icinga\Module\X509\CertificateUtils; use Icinga\Module\X509\Command; +use Icinga\Module\X509\Common\Database; use ipl\Sql\Connection; use ipl\Sql\Expression; @@ -32,13 +33,11 @@ public function indexAction() exit(1); } - $db = $this->getDb(); - $bundle = CertificateUtils::parseBundle($file); $count = 0; - $db->transaction(function (Connection $db) use ($bundle, &$count) { + Database::get()->transaction(function (Connection $db) use ($bundle, &$count) { foreach ($bundle as $data) { $cert = openssl_x509_read($data); diff --git a/application/clicommands/JobsCommand.php b/application/clicommands/JobsCommand.php index ab3a47bb..27f72028 100644 --- a/application/clicommands/JobsCommand.php +++ b/application/clicommands/JobsCommand.php @@ -11,6 +11,7 @@ use Icinga\Data\ResourceFactory; use Icinga\Module\X509\CertificateUtils; use Icinga\Module\X509\Command; +use Icinga\Module\X509\Common\Database; use Icinga\Module\X509\Common\JobUtils; use Icinga\Module\X509\Hook\SniHook; use Icinga\Module\X509\Job; @@ -152,7 +153,13 @@ public function runAction(): void */ protected function fetchSchedules(?string $jobName, ?string $scheduleName): array { - $jobs = X509Job::on($this->getDb()); + $conn = Database::get(); + // Even if the Job class regularly pings the same connection whenever its frequency becomes due and is run by + // the scheduler, we need to explicitly ping that same connection here, as the interval of the schedule jobs + // could be larger than the daemon configuration reload interval (5m). + $conn->ping(); + + $jobs = X509Job::on($conn); if ($jobName) { $jobs->filter(Filter::equal('name', $jobName)); } @@ -227,7 +234,7 @@ protected function attachJobsLogging(Scheduler $scheduler): void ); try { - $verified = CertificateUtils::verifyCertificates($this->getDb()); + $verified = CertificateUtils::verifyCertificates(Database::get()); Logger::info('Checked %d certificate chain(s)', $verified); } catch (Exception $err) { diff --git a/application/clicommands/MigrateCommand.php b/application/clicommands/MigrateCommand.php index 5b947360..cb4e3898 100644 --- a/application/clicommands/MigrateCommand.php +++ b/application/clicommands/MigrateCommand.php @@ -8,6 +8,7 @@ use Icinga\Application\Logger; use Icinga\Authentication\Auth; use Icinga\Module\X509\Command; +use Icinga\Module\X509\Common\Database; use Icinga\Module\X509\Job; use Icinga\Repository\IniRepository; use Icinga\User; @@ -64,7 +65,7 @@ protected function migrateJobs(): void ]; }; - $conn = $this->getDb(); + $conn = Database::get(); $conn->transaction(function (Connection $conn) use ($repo) { /** @var User $user */ $user = Auth::getInstance()->getUser(); diff --git a/application/clicommands/ScanCommand.php b/application/clicommands/ScanCommand.php index 422a59ec..3743adc2 100644 --- a/application/clicommands/ScanCommand.php +++ b/application/clicommands/ScanCommand.php @@ -8,6 +8,7 @@ use Icinga\Application\Logger; use Icinga\Module\X509\CertificateUtils; use Icinga\Module\X509\Command; +use Icinga\Module\X509\Common\Database; use Icinga\Module\X509\Common\JobUtils; use Icinga\Module\X509\Hook\SniHook; use Icinga\Module\X509\Job; @@ -101,7 +102,7 @@ public function indexAction(): void } /** @var X509Job $jobConfig */ - $jobConfig = X509Job::on($this->getDb()) + $jobConfig = X509Job::on(Database::get()) ->filter(Filter::equal('name', $name)) ->first(); if ($jobConfig === null) { @@ -142,7 +143,7 @@ public function indexAction(): void Logger::info('Scanned %d target(s) from job %s', $targets, $job->getName()); try { - $verified = CertificateUtils::verifyCertificates($this->getDb()); + $verified = CertificateUtils::verifyCertificates(Database::get()); Logger::info('Checked %d certificate chain(s)', $verified); } catch (Exception $err) { diff --git a/application/clicommands/VerifyCommand.php b/application/clicommands/VerifyCommand.php index 6bd9bd33..15976fc3 100644 --- a/application/clicommands/VerifyCommand.php +++ b/application/clicommands/VerifyCommand.php @@ -7,6 +7,7 @@ use Icinga\Application\Logger; use Icinga\Module\X509\CertificateUtils; use Icinga\Module\X509\Command; +use Icinga\Module\X509\Common\Database; class VerifyCommand extends Command { @@ -19,9 +20,7 @@ class VerifyCommand extends Command */ public function indexAction() { - $db = $this->getDb(); - - $verified = CertificateUtils::verifyCertificates($db); + $verified = CertificateUtils::verifyCertificates(Database::get()); Logger::info("Checked %d certificate chain%s.", $verified, $verified !== 1 ? 's' : ''); } diff --git a/application/controllers/CertificateController.php b/application/controllers/CertificateController.php index d51edece..016b3124 100644 --- a/application/controllers/CertificateController.php +++ b/application/controllers/CertificateController.php @@ -6,6 +6,7 @@ use Icinga\Exception\ConfigurationError; use Icinga\Module\X509\CertificateDetails; +use Icinga\Module\X509\Common\Database; use Icinga\Module\X509\Controller; use Icinga\Module\X509\Model\X509Certificate; use ipl\Stdlib\Filter; @@ -20,7 +21,7 @@ public function indexAction() $certId = $this->params->getRequired('cert'); try { - $conn = $this->getDb(); + $conn = Database::get(); } catch (ConfigurationError $_) { $this->render('missing-resource', null, true); diff --git a/application/controllers/CertificatesController.php b/application/controllers/CertificatesController.php index 979eda39..37434fac 100644 --- a/application/controllers/CertificatesController.php +++ b/application/controllers/CertificatesController.php @@ -6,6 +6,7 @@ use Icinga\Exception\ConfigurationError; use Icinga\Module\X509\CertificatesTable; +use Icinga\Module\X509\Common\Database; use Icinga\Module\X509\Controller; use Icinga\Module\X509\Model\X509Certificate; use Icinga\Module\X509\Web\Control\SearchBar\ObjectSuggestions; @@ -21,7 +22,7 @@ public function indexAction() $this->getTabs()->enableDataExports(); try { - $conn = $this->getDb(); + $conn = Database::get(); } catch (ConfigurationError $_) { $this->render('missing-resource', null, true); @@ -105,7 +106,7 @@ public function completeAction() public function searchEditorAction() { - $editor = $this->createSearchEditor(X509Certificate::on($this->getDb()), [ + $editor = $this->createSearchEditor(X509Certificate::on(Database::get()), [ LimitControl::DEFAULT_LIMIT_PARAM, SortControl::DEFAULT_SORT_PARAM ]); diff --git a/application/controllers/ChainController.php b/application/controllers/ChainController.php index 94eff8f2..5408526d 100644 --- a/application/controllers/ChainController.php +++ b/application/controllers/ChainController.php @@ -6,6 +6,7 @@ use Icinga\Exception\ConfigurationError; use Icinga\Module\X509\ChainDetails; +use Icinga\Module\X509\Common\Database; use Icinga\Module\X509\Controller; use Icinga\Module\X509\Model\X509Certificate; use Icinga\Module\X509\Model\X509CertificateChain; @@ -23,7 +24,7 @@ public function indexAction() $id = $this->params->getRequired('id'); try { - $conn = $this->getDb(); + $conn = Database::get(); } catch (ConfigurationError $_) { $this->render('missing-resource', null, true); return; diff --git a/application/controllers/DashboardController.php b/application/controllers/DashboardController.php index 13b871aa..8b43761e 100644 --- a/application/controllers/DashboardController.php +++ b/application/controllers/DashboardController.php @@ -6,6 +6,7 @@ use Icinga\Exception\ConfigurationError; use Icinga\Module\X509\CertificateUtils; +use Icinga\Module\X509\Common\Database; use Icinga\Module\X509\Controller; use Icinga\Module\X509\Donut; use Icinga\Module\X509\Model\X509Certificate; @@ -22,7 +23,7 @@ public function indexAction() $this->getTabs()->disableLegacyExtensions(); try { - $db = $this->getDb(); + $db = Database::get(); } catch (ConfigurationError $_) { $this->render('missing-resource', null, true); return; diff --git a/application/controllers/JobController.php b/application/controllers/JobController.php index c97d6c88..7655a74a 100644 --- a/application/controllers/JobController.php +++ b/application/controllers/JobController.php @@ -26,8 +26,6 @@ class JobController extends CompatController { - use Database; - /** @var X509Job */ protected $job; @@ -41,7 +39,7 @@ public function init() $jobId = $this->params->getRequired('id'); /** @var X509Job $job */ - $job = X509Job::on($this->getDb()) + $job = X509Job::on(Database::get()) ->filter(Filter::equal('id', $jobId)) ->first(); @@ -164,7 +162,7 @@ public function updateScheduleAction(): void /** @var int $id */ $id = $this->params->getRequired('scheduleId'); /** @var X509Schedule $schedule */ - $schedule = X509Schedule::on($this->getDb()) + $schedule = X509Schedule::on(Database::get()) ->filter(Filter::equal('id', $id)) ->first(); if ($schedule === null) { diff --git a/application/controllers/JobsController.php b/application/controllers/JobsController.php index 1caf4ee2..48deede7 100644 --- a/application/controllers/JobsController.php +++ b/application/controllers/JobsController.php @@ -14,8 +14,6 @@ class JobsController extends CompatController { - use Database; - /** * List all jobs */ @@ -29,7 +27,7 @@ public function indexAction() 'baseTarget' => '_main' ]); - $jobs = X509Job::on($this->getDb()); + $jobs = X509Job::on(Database::get()); if ($this->hasPermission('config/x509')) { $this->addControl( (new ButtonLink($this->translate('New Job'), Url::fromPath('x509/jobs/new'), 'plus')) diff --git a/application/controllers/UsageController.php b/application/controllers/UsageController.php index 20cbf9af..35eb1aa0 100644 --- a/application/controllers/UsageController.php +++ b/application/controllers/UsageController.php @@ -5,6 +5,7 @@ namespace Icinga\Module\X509\Controllers; use Icinga\Exception\ConfigurationError; +use Icinga\Module\X509\Common\Database; use Icinga\Module\X509\Controller; use Icinga\Module\X509\Model\X509Certificate; use Icinga\Module\X509\UsageTable; @@ -22,7 +23,7 @@ public function indexAction() $this->getTabs()->enableDataExports(); try { - $conn = $this->getDb(); + $conn = Database::get(); } catch (ConfigurationError $_) { $this->render('missing-resource', null, true); return; @@ -128,7 +129,7 @@ public function completeAction() public function searchEditorAction() { - $editor = $this->createSearchEditor(X509Certificate::on($this->getDb()), [ + $editor = $this->createSearchEditor(X509Certificate::on(Database::get()), [ LimitControl::DEFAULT_LIMIT_PARAM, SortControl::DEFAULT_SORT_PARAM ]); diff --git a/application/forms/Jobs/JobConfigForm.php b/application/forms/Jobs/JobConfigForm.php index 8b46773d..539bc581 100644 --- a/application/forms/Jobs/JobConfigForm.php +++ b/application/forms/Jobs/JobConfigForm.php @@ -23,8 +23,6 @@ */ class JobConfigForm extends CompatForm { - use Database; - /** @var ?X509Job */ protected $job; @@ -110,7 +108,7 @@ protected function assemble(): void protected function onSuccess(): void { - $conn = $this->getDb(); + $conn = Database::get(); /** @var FormSubmitElement $submitElement */ $submitElement = $this->getPressedSubmitElement(); if ($submitElement->getName() === 'btn_remove') { diff --git a/application/forms/Jobs/ScheduleForm.php b/application/forms/Jobs/ScheduleForm.php index 3903e2a3..ae47e58b 100644 --- a/application/forms/Jobs/ScheduleForm.php +++ b/application/forms/Jobs/ScheduleForm.php @@ -27,8 +27,6 @@ class ScheduleForm extends CompatForm { - use Database; - /** @var int */ protected $jobId; @@ -160,7 +158,7 @@ protected function onSuccess(): void { /** @var X509Schedule $schedule */ $schedule = $this->schedule; - $conn = $this->getDb(); + $conn = Database::get(); /** @var FormSubmitElement $submitElement */ $submitElement = $this->getPressedSubmitElement(); if ($submitElement->getName() === 'btn_remove') { diff --git a/library/X509/Command.php b/library/X509/Command.php index 91eab14b..9f187275 100644 --- a/library/X509/Command.php +++ b/library/X509/Command.php @@ -5,14 +5,9 @@ namespace Icinga\Module\X509; use Icinga\Application\Icinga; -use Icinga\Data\ResourceFactory; -use Icinga\Module\X509\Common\Database; -use ipl\Sql; class Command extends \Icinga\Cli\Command { - use Database; - // Fix Web 2 issue where $configs is not properly initialized protected $configs = []; diff --git a/library/X509/Common/Database.php b/library/X509/Common/Database.php index 57711fef..d6eb3e18 100644 --- a/library/X509/Common/Database.php +++ b/library/X509/Common/Database.php @@ -9,14 +9,35 @@ use ipl\Sql; use PDO; -trait Database +final class Database { + /** @var Sql\Connection Database connection */ + private static $instance; + + private function __construct() + { + } + + /** + * Get the database connection + * + * @return Sql\Connection + */ + public static function get(): Sql\Connection + { + if (self::$instance === null) { + self::$instance = self::getDb(); + } + + return self::$instance; + } + /** * Get the connection to the X.509 database * * @return Sql\Connection */ - protected function getDb(): Sql\Connection + private static function getDb(): Sql\Connection { $config = new Sql\Config(ResourceFactory::getResourceConfig( Config::module('x509')->get('backend', 'resource', 'x509') diff --git a/library/X509/Controller.php b/library/X509/Controller.php index e6e6d0bc..f16787d5 100644 --- a/library/X509/Controller.php +++ b/library/X509/Controller.php @@ -5,7 +5,6 @@ namespace Icinga\Module\X509; use Icinga\File\Csv; -use Icinga\Module\X509\Common\Database; use Icinga\Module\X509\Web\Control\SearchBar\ObjectSuggestions; use Icinga\Util\Json; use ipl\Html\Html; @@ -17,7 +16,6 @@ class Controller extends CompatController { - use Database; use SearchControls; /** @var Filter\Rule */ diff --git a/library/X509/Job.php b/library/X509/Job.php index ffc0306a..1e0b3f73 100644 --- a/library/X509/Job.php +++ b/library/X509/Job.php @@ -36,7 +36,6 @@ class Job implements Task { - use Database; use JobOptions; use TaskProperties; @@ -100,7 +99,7 @@ class Job implements Task public function __construct(string $name, array $cidrs, array $ports, array $snimap, Schedule $schedule = null) { $this->name = $name; - $this->db = $this->getDb(); + $this->db = Database::get(); $this->dbTool = new DbTool($this->db); $this->snimap = $snimap; $this->cidrs = $cidrs; diff --git a/library/X509/ProvidedHook/DbMigration.php b/library/X509/ProvidedHook/DbMigration.php index 03a26ba1..8314e3c8 100644 --- a/library/X509/ProvidedHook/DbMigration.php +++ b/library/X509/ProvidedHook/DbMigration.php @@ -13,10 +13,6 @@ class DbMigration extends DbMigrationHook { - use Database { - getDb as private getX509Db; - } - public function getName(): string { return $this->translate('Icinga Certificate Monitoring'); @@ -89,7 +85,7 @@ public function getVersion(): string public function getDb(): Sql\Connection { - return $this->getX509Db(); + return Database::get(); } protected function getSchemaQuery(): Query diff --git a/library/X509/ProvidedHook/HostsImportSource.php b/library/X509/ProvidedHook/HostsImportSource.php index ed39d729..70d584c7 100644 --- a/library/X509/ProvidedHook/HostsImportSource.php +++ b/library/X509/ProvidedHook/HostsImportSource.php @@ -4,6 +4,7 @@ namespace Icinga\Module\X509\ProvidedHook; +use Icinga\Module\X509\Common\Database; use Icinga\Module\X509\Job; use Icinga\Module\X509\Model\X509Target; use ipl\Sql; @@ -12,7 +13,8 @@ class HostsImportSource extends X509ImportSource { public function fetchData() { - $targets = X509Target::on($this->getDb()) + $conn = Database::get(); + $targets = X509Target::on($conn) ->utilize('chain') ->utilize('chain.certificate') ->columns([ @@ -25,7 +27,7 @@ public function fetchData() ->where(new Sql\Expression('target_chain_link.order = 0')) ->groupBy(['ip', 'hostname']); - if ($this->getDb()->getAdapter() instanceof Sql\Adapter\Pgsql) { + if ($conn->getAdapter() instanceof Sql\Adapter\Pgsql) { $targets->withColumns([ 'host_ports' => new Sql\Expression("ARRAY_TO_STRING(ARRAY_AGG(DISTINCT port), ',')") ]); diff --git a/library/X509/ProvidedHook/ServicesImportSource.php b/library/X509/ProvidedHook/ServicesImportSource.php index bc0f3935..7b87cd86 100644 --- a/library/X509/ProvidedHook/ServicesImportSource.php +++ b/library/X509/ProvidedHook/ServicesImportSource.php @@ -4,6 +4,7 @@ namespace Icinga\Module\X509\ProvidedHook; +use Icinga\Module\X509\Common\Database; use Icinga\Module\X509\Job; use Icinga\Module\X509\Model\X509CertificateSubjectAltName; use Icinga\Module\X509\Model\X509Target; @@ -13,7 +14,8 @@ class ServicesImportSource extends X509ImportSource { public function fetchData() { - $targets = X509Target::on($this->getDb()) + $conn = Database::get(); + $targets = X509Target::on($conn) ->with([ 'chain', 'chain.certificate', @@ -41,13 +43,13 @@ public function fetchData() ->where(new Sql\Expression('target_chain_link.order = 0')) ->groupBy(['ip, hostname, port']); - $certAltName = X509CertificateSubjectAltName::on($this->getDb()); + $certAltName = X509CertificateSubjectAltName::on($conn); $certAltName ->getSelectBase() ->where(new Sql\Expression('certificate_id = target_chain_certificate.id')) ->groupBy(['alt_name.certificate_id']); - if ($this->getDb()->getAdapter() instanceof Sql\Adapter\Pgsql) { + if ($conn->getAdapter() instanceof Sql\Adapter\Pgsql) { $targets ->withColumns([ 'cert_fingerprint' => new Sql\Expression("ENCODE(%s, 'hex')", [ diff --git a/library/X509/ProvidedHook/X509ImportSource.php b/library/X509/ProvidedHook/X509ImportSource.php index d21f0830..dc280c04 100644 --- a/library/X509/ProvidedHook/X509ImportSource.php +++ b/library/X509/ProvidedHook/X509ImportSource.php @@ -5,9 +5,7 @@ namespace Icinga\Module\X509\ProvidedHook; use Icinga\Module\Director\Hook\ImportSourceHook; -use Icinga\Module\X509\Common\Database; abstract class X509ImportSource extends ImportSourceHook { - use Database; } diff --git a/library/X509/Web/Control/SearchBar/ObjectSuggestions.php b/library/X509/Web/Control/SearchBar/ObjectSuggestions.php index ced7f7e1..ca9630f3 100644 --- a/library/X509/Web/Control/SearchBar/ObjectSuggestions.php +++ b/library/X509/Web/Control/SearchBar/ObjectSuggestions.php @@ -18,8 +18,6 @@ class ObjectSuggestions extends Suggestions { - use Database; - /** @var Model */ protected $model; @@ -57,7 +55,7 @@ protected function shouldShowRelationFor(string $column): bool protected function createQuickSearchFilter($searchTerm) { $model = $this->model; - $resolver = $model::on($this->getDb())->getResolver(); + $resolver = $model::on(Database::get())->getResolver(); $quickFilter = Filter::any(); foreach ($model->getSearchColumns() as $column) { @@ -72,7 +70,7 @@ protected function createQuickSearchFilter($searchTerm) protected function fetchValueSuggestions($column, $searchTerm, Filter\Chain $searchFilter) { $model = $this->model; - $query = $model::on($this->getDb()); + $query = $model::on(Database::get()); $query->limit(static::DEFAULT_LIMIT); if (strpos($column, ' ') !== false) { @@ -149,7 +147,7 @@ protected function fetchValueSuggestions($column, $searchTerm, Filter\Chain $sea protected function fetchColumnSuggestions($searchTerm) { $model = $this->model; - $query = $model::on($this->getDb()); + $query = $model::on(Database::get()); yield from self::collectFilterColumns($model, $query->getResolver()); }