Skip to content

Commit 2dbad0d

Browse files
committed
Make use of ipl/orm
1 parent cfc5f78 commit 2dbad0d

29 files changed

+1274
-569
lines changed

application/clicommands/CheckCommand.php

Lines changed: 64 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66

77
use Icinga\Application\Logger;
88
use Icinga\Module\X509\Command;
9-
use Icinga\Module\X509\DbTool;
109
use Icinga\Module\X509\Job;
11-
use ipl\Sql\Select;
10+
use Icinga\Module\X509\Model\X509Certificate;
11+
use Icinga\Module\X509\Model\X509Target;
12+
use ipl\Sql\Expression;
13+
use ipl\Stdlib\Filter;
1214

1315
class CheckCommand extends Command
1416
{
@@ -66,42 +68,65 @@ public function hostAction()
6668
exit(3);
6769
}
6870

69-
$dbTool = new DbTool($this->getDb());
70-
$targets = (new Select())
71-
->from('x509_target t')
72-
->columns([
73-
't.port',
74-
'cc.valid',
75-
'cc.invalid_reason',
76-
'c.subject',
77-
'self_signed' => 'COALESCE(ci.self_signed, c.self_signed)',
78-
'valid_from' => (new Select())
79-
->from('x509_certificate_chain_link xccl')
80-
->columns('MAX(GREATEST(xc.valid_from, xci.valid_from))')
81-
->join('x509_certificate xc', 'xc.id = xccl.certificate_id')
82-
->join('x509_certificate xci', 'xci.subject_hash = xc.issuer_hash')
83-
->where('xccl.certificate_chain_id = cc.id'),
84-
'valid_to' => (new Select())
85-
->from('x509_certificate_chain_link xccl')
86-
->columns('MIN(LEAST(xc.valid_to, xci.valid_to))')
87-
->join('x509_certificate xc', 'xc.id = xccl.certificate_id')
88-
->join('x509_certificate xci', 'xci.subject_hash = xc.issuer_hash')
89-
->where('xccl.certificate_chain_id = cc.id')
71+
$conn = $this->getDb();
72+
$targets = X509Target::on($conn)->with([
73+
'chain',
74+
'chain.certificate',
75+
'chain.certificate.parent'
76+
]);
77+
78+
$targets->getWith()['x509_target.chain.certificate.parent']->setJoinType('LEFT');
79+
80+
$targets->columns([
81+
'port',
82+
'chain.valid',
83+
'chain.invalid_reason',
84+
'subject' => 'chain.certificate.subject',
85+
'self_signed' => new Expression('COALESCE(%s, %s)', [
86+
'chain.certificate.parent.self_signed',
87+
'chain.certificate.self_signed'
9088
])
91-
->join('x509_certificate_chain cc', 'cc.id = t.latest_certificate_chain_id')
92-
->join('x509_certificate_chain_link ccl', 'ccl.certificate_chain_id = cc.id')
93-
->join('x509_certificate c', 'c.id = ccl.certificate_id')
94-
->joinLeft('x509_certificate ci', 'ci.subject_hash = c.issuer_hash')
95-
->where(['ccl.order = ?' => 0]);
89+
]);
90+
91+
// Sub queries for (valid_from, valid_to) columns
92+
$validFrom = X509Certificate::on($conn)->with(['chain', 'parent']);
93+
$validFrom->getResolver()->setAliasPrefix('sub_');
94+
$validFrom->columns([
95+
new Expression("MAX(GREATEST(%s, %s))", ['valid_from', 'parent.valid_from'])
96+
]);
97+
98+
$validFrom
99+
->getSelectBase()
100+
->where(new Expression(
101+
'sub_x509_certificate_x509_certificate_chain_link.certificate_chain_id = x509_target_chain.id'
102+
));
103+
104+
$validTo = clone $validFrom;
105+
$validTo->columns([
106+
new Expression('MIN(LEAST(%s, %s))', ['valid_to', 'parent.valid_to'])
107+
]);
108+
109+
list($validFromSelect, $validFromValues) = $validFrom->dump();
110+
list($validToSelect, $validToValues) = $validTo->dump();
111+
112+
$validFromAlias = 'valid_from';
113+
$validToAlias = 'valid_to';
114+
115+
$targets->withColumns([
116+
$validFromAlias => new Expression("$validFromSelect", null, ...$validFromValues),
117+
$validToAlias => new Expression("$validToSelect", null, ...$validToValues)
118+
]);
119+
120+
$targets->getSelectBase()->where(new Expression('x509_target_chain_x509_certificate_chain_link.order = 0'));
96121

97122
if ($ip !== null) {
98-
$targets->where(['t.ip = ?' => $dbTool->marshalBinary(Job::binary($ip))]);
123+
$targets->filter(Filter::equal('ip', Job::binary($ip)));
99124
}
100125
if ($hostname !== null) {
101-
$targets->where(['t.hostname = ?' => $hostname]);
126+
$targets->filter(Filter::equal('hostname', $hostname));
102127
}
103128
if ($this->params->has('port')) {
104-
$targets->where(['t.port = ?' => $this->params->get('port')]);
129+
$targets->filter(Filter::equal('port', (int) $this->params->get('port')));
105130
}
106131

107132
$allowSelfSigned = (bool) $this->params->get('allow-self-signed', false);
@@ -112,9 +137,9 @@ public function hostAction()
112137
$perfData = [];
113138

114139
$state = 3;
115-
foreach ($this->getDb()->select($targets) as $target) {
116-
if ($target['valid'] === 'no' && ($target['self_signed'] === 'no' || ! $allowSelfSigned)) {
117-
$invalidMessage = $target['subject'] . ': ' . $target['invalid_reason'];
140+
foreach ($targets as $target) {
141+
if (! $target->chain->valid && (! $target['self_signed'] || ! $allowSelfSigned)) {
142+
$invalidMessage = $target['subject'] . ': ' . $target->chain->invalid_reason;
118143
$output[$invalidMessage] = $invalidMessage;
119144
$state = 2;
120145
}
@@ -181,7 +206,7 @@ public function hostAction()
181206
/**
182207
* Parse the given threshold definition
183208
*
184-
* @param string $threshold
209+
* @param string $threshold
185210
*
186211
* @return array
187212
*/
@@ -229,10 +254,10 @@ protected function splitThreshold($threshold)
229254
/**
230255
* Convert the given threshold information to a DateTime object
231256
*
232-
* @param \DateTime $from
233-
* @param \DateTime $to
234-
* @param int|\DateInterval $thresholdValue
235-
* @param string $thresholdUnit
257+
* @param \DateTime $from
258+
* @param \DateTime $to
259+
* @param int|\DateInterval $thresholdValue
260+
* @param string $thresholdUnit
236261
*
237262
* @return \DateTime
238263
*/

application/controllers/CertificateController.php

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
use Icinga\Exception\ConfigurationError;
88
use Icinga\Module\X509\CertificateDetails;
99
use Icinga\Module\X509\Controller;
10+
use Icinga\Module\X509\Model\X509Certificate;
1011
use ipl\Sql;
12+
use ipl\Stdlib\Filter;
1113

1214
class CertificateController extends Controller
1315
{
@@ -22,18 +24,17 @@ public function indexAction()
2224
return;
2325
}
2426

25-
$cert = $conn->select(
26-
(new Sql\Select())
27-
->from('x509_certificate')
28-
->columns('*')
29-
->where(['id = ?' => $certId])
30-
)->fetch();
27+
$certificates = X509Certificate::on($conn);
28+
$certificates->filter(Filter::equal('id', $certId));
3129

32-
if ($cert === false) {
30+
$cert = $certificates->first();
31+
32+
if (! $cert) {
3333
$this->httpNotFound($this->translate('Certificate not found.'));
3434
}
3535

36-
$this->setTitle($this->translate('X.509 Certificate'));
36+
$this->addTitleTab($this->translate('X.509 Certificate'));
37+
$this->getTabs()->disableLegacyExtensions();
3738

3839
$this->view->certificateDetails = (new CertificateDetails())
3940
->setCert($cert);

application/controllers/CertificatesController.php

Lines changed: 86 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,21 @@
44

55
namespace Icinga\Module\X509\Controllers;
66

7-
use Icinga\Data\Filter\FilterExpression;
87
use Icinga\Exception\ConfigurationError;
98
use Icinga\Module\X509\CertificatesTable;
109
use Icinga\Module\X509\Controller;
11-
use Icinga\Module\X509\FilterAdapter;
12-
use Icinga\Module\X509\SortAdapter;
13-
use Icinga\Module\X509\SqlFilter;
14-
use ipl\Web\Control\PaginationControl;
15-
use ipl\Sql;
16-
use ipl\Web\Url;
10+
use Icinga\Module\X509\Model\X509Certificate;
11+
use Icinga\Module\X509\Web\Control\SearchBar\ObjectSuggestions;
12+
use ipl\Orm\Query;
13+
use ipl\Web\Control\LimitControl;
14+
use ipl\Web\Control\SortControl;
1715

1816
class CertificatesController extends Controller
1917
{
2018
public function indexAction()
2119
{
22-
$this
23-
->initTabs()
24-
->setTitle($this->translate('Certificates'));
20+
$this->initTabs();
21+
$this->addTitleTab($this->translate('Certificates'));
2522

2623
try {
2724
$conn = $this->getDb();
@@ -30,100 +27,99 @@ public function indexAction()
3027
return;
3128
}
3229

33-
$select = (new Sql\Select())
34-
->from('x509_certificate c')
35-
->columns([
36-
'c.id', 'c.subject', 'c.issuer', 'c.version', 'c.self_signed', 'c.ca', 'c.trusted',
37-
'c.pubkey_algo', 'c.pubkey_bits', 'c.signature_algo', 'c.signature_hash_algo',
38-
'c.valid_from', 'c.valid_to',
39-
]);
40-
41-
$this->view->paginator = new PaginationControl(new Sql\Cursor($conn, $select), Url::fromRequest());
42-
$this->view->paginator->apply();
43-
44-
$sortAndFilterColumns = [
45-
'subject' => $this->translate('Certificate'),
46-
'issuer' => $this->translate('Issuer'),
47-
'version' => $this->translate('Version'),
48-
'self_signed' => $this->translate('Is Self-Signed'),
49-
'ca' => $this->translate('Is Certificate Authority'),
50-
'trusted' => $this->translate('Is Trusted'),
51-
'pubkey_algo' => $this->translate('Public Key Algorithm'),
52-
'pubkey_bits' => $this->translate('Public Key Strength'),
53-
'signature_algo' => $this->translate('Signature Algorithm'),
30+
$certificates = X509Certificate::on($conn);
31+
32+
$sortColumns = [
33+
'subject' => $this->translate('Certificate'),
34+
'issuer' => $this->translate('Issuer'),
35+
'version' => $this->translate('Version'),
36+
'self_signed' => $this->translate('Is Self-Signed'),
37+
'ca' => $this->translate('Is Certificate Authority'),
38+
'trusted' => $this->translate('Is Trusted'),
39+
'pubkey_algo' => $this->translate('Public Key Algorithm'),
40+
'pubkey_bits' => $this->translate('Public Key Strength'),
41+
'signature_algo' => $this->translate('Signature Algorithm'),
5442
'signature_hash_algo' => $this->translate('Signature Hash Algorithm'),
55-
'valid_from' => $this->translate('Valid From'),
56-
'valid_to' => $this->translate('Valid To'),
57-
'duration' => $this->translate('Duration'),
58-
'expires' => $this->translate('Expiration')
43+
'valid_from' => $this->translate('Valid From'),
44+
'valid_to' => $this->translate('Valid To'),
45+
'duration' => $this->translate('Duration'),
46+
'expires' => $this->translate('Expiration')
5947
];
6048

61-
$this->setupSortControl(
62-
$sortAndFilterColumns,
63-
new SortAdapter($select, function ($field) {
64-
if ($field === 'duration') {
65-
return '(valid_to - valid_from)';
66-
} elseif ($field === 'expires') {
67-
return 'CASE WHEN UNIX_TIMESTAMP() > valid_to'
68-
. ' THEN 0 ELSE (valid_to - UNIX_TIMESTAMP()) / 86400 END';
69-
}
70-
})
71-
);
72-
73-
$this->setupLimitControl();
74-
75-
$filterAdapter = new FilterAdapter();
76-
$this->setupFilterControl(
77-
$filterAdapter,
78-
$sortAndFilterColumns,
79-
['subject', 'issuer'],
80-
['format']
81-
);
82-
83-
(new SqlFilter($conn))->apply($select, $filterAdapter->getFilter(), function (FilterExpression $filter) {
84-
switch ($filter->getColumn()) {
85-
case 'issuer_hash':
86-
$value = $filter->getExpression();
87-
88-
if (is_array($value)) {
89-
$value = array_map('hex2bin', $value);
90-
} else {
91-
$value = hex2bin($value);
92-
}
93-
94-
return $filter->setExpression($value);
95-
case 'duration':
96-
return $filter->setColumn('(valid_to - valid_from)');
97-
case 'expires':
98-
return $filter->setColumn(
99-
'CASE WHEN UNIX_TIMESTAMP() > valid_to THEN 0 ELSE (valid_to - UNIX_TIMESTAMP()) / 86400 END'
100-
);
101-
case 'valid_from':
102-
case 'valid_to':
103-
$expr = $filter->getExpression();
104-
if (! is_numeric($expr)) {
105-
return $filter->setExpression(strtotime($expr));
106-
}
107-
108-
// expression doesn't need changing
109-
default:
110-
return false;
49+
$limitControl = $this->createLimitControl();
50+
$paginator = $this->createPaginationControl($certificates);
51+
$sortControl = $this->createSortControl($certificates, $sortColumns);
52+
53+
$searchBar = $this->createSearchBar($certificates, [
54+
$limitControl->getLimitParam(),
55+
$sortControl->getSortParam()
56+
]);
57+
58+
if ($searchBar->hasBeenSent() && ! $searchBar->isValid()) {
59+
if ($searchBar->hasBeenSubmitted()) {
60+
$filter = $this->getFilter();
61+
} else {
62+
$this->addControl($searchBar);
63+
$this->sendMultipartUpdate();
64+
return;
11165
}
112-
});
66+
} else {
67+
$filter = $searchBar->getFilter();
68+
}
69+
70+
$certificates->peekAhead($this->view->compact);
71+
72+
$certificates->filter($filter);
11373

114-
$this->handleFormatRequest($conn, $select, function (\PDOStatement $stmt) {
115-
foreach ($stmt as $cert) {
74+
$this->addControl($paginator);
75+
$this->addControl($sortControl);
76+
$this->addControl($limitControl);
77+
$this->addControl($searchBar);
78+
79+
// List of allowed columns to be exported
80+
$exportable = array_flip([
81+
'id', 'subject', 'issuer', 'version', 'self_signed', 'ca', 'trusted',
82+
'pubkey_algo', 'pubkey_bits', 'signature_algo', 'signature_hash_algo',
83+
'valid_from', 'valid_to'
84+
]);
85+
86+
$this->handleFormatRequest($certificates, function (Query $certificates) use ($exportable) {
87+
/** @var X509Certificate $cert */
88+
foreach ($certificates as $cert) {
11689
$cert['valid_from'] = (new \DateTime())
11790
->setTimestamp($cert['valid_from'])
11891
->format('l F jS, Y H:i:s e');
11992
$cert['valid_to'] = (new \DateTime())
12093
->setTimestamp($cert['valid_to'])
12194
->format('l F jS, Y H:i:s e');
12295

123-
yield $cert;
96+
yield array_intersect_key(iterator_to_array($cert->getIterator()), $exportable);
12497
}
12598
});
12699

127-
$this->view->certificatesTable = (new CertificatesTable())->setData($conn->select($select));
100+
$this->addContent((new CertificatesTable())->setData($certificates));
101+
102+
if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) {
103+
$this->sendMultipartUpdate(); // Updates the browser search bar
104+
}
105+
}
106+
107+
public function completeAction()
108+
{
109+
$suggestions = new ObjectSuggestions();
110+
$suggestions->setModel(X509Certificate::class);
111+
$suggestions->forRequest($this->getServerRequest());
112+
$this->getDocument()->add($suggestions);
113+
}
114+
115+
public function searchEditorAction()
116+
{
117+
$editor = $this->createSearchEditor(X509Certificate::on($this->getDb()), [
118+
LimitControl::DEFAULT_LIMIT_PARAM,
119+
SortControl::DEFAULT_SORT_PARAM
120+
]);
121+
122+
$this->getDocument()->add($editor);
123+
$this->setTitle(t('Adjust Filter'));
128124
}
129125
}

0 commit comments

Comments
 (0)