Add CRUD for client identities

refs #3016
This commit is contained in:
Alexander A. Klimov 2017-11-22 12:25:14 +01:00
parent 4bd1a35dd2
commit 64b51ade16
5 changed files with 400 additions and 0 deletions

View File

@ -0,0 +1,134 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
namespace Icinga\Controllers;
use Exception;
use Icinga\File\Storage\LocalFileStorage;
use Icinga\Forms\Config\Tls\ClientIdentity\CreateForm;
use Icinga\Forms\Config\Tls\ClientIdentity\EditForm;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Web\Controller;
use Icinga\Web\Notification;
/**
* Manage TLS client identities
*/
class TlsclientidentityController extends Controller
{
public function init()
{
$this->assertPermission('config/application/tlscert');
parent::init();
}
public function createAction()
{
$this->view->form = $form = new CreateForm();
$form->setRedirectUrl('tlsclientidentity/edit')
->handleRequest();
$this->addTitleTab(
$this->translate('New Client Identity'),
$this->translate('Create A New TLS Client Identity')
);
$this->render('form');
}
public function editAction()
{
$this->view->editForm = $editForm = new EditForm();
$name = $this->params->getRequired('name');
$editForm->setOldName($name)
->setRedirectUrl('tlsclientidentity/edit')
->handleRequest();
$rawIdentity = LocalFileStorage::common('tls/clientidentities')->read(bin2hex($name) . '.pem');
preg_match(
'/-+BEGIN CERTIFICATE-+.+?-+END CERTIFICATE-+/s',
$rawIdentity,
$cert
);
$this->view->cert = array(
'info' => openssl_x509_parse($cert[0]),
'sha1' => openssl_x509_fingerprint($cert[0], 'sha1'),
'sha256' => openssl_x509_fingerprint($cert[0], 'sha256'),
);
preg_match(
'/-+BEGIN PRIVATE KEY-+.+?-+END PRIVATE KEY-+/s',
$rawIdentity,
$key
);
$keyDetails = openssl_pkey_get_details(openssl_pkey_get_private($key[0]));
$pubKey = base64_decode(preg_replace('/-+(?:BEGIN|END) PUBLIC KEY-+/', '', $keyDetails['key']));
$this->view->pubKey = array(
'sha1' => hash('sha1', $pubKey),
'sha256' => hash('sha256', $pubKey)
);
$this->addTitleTab(
$this->translate('Edit Client Identity'),
sprintf($this->translate('Edit TLS Client Identity "%s"'), $name)
);
}
public function removeAction()
{
$clientIdentities = LocalFileStorage::common('tls/clientidentities');
$name = $this->params->getRequired('name');
$fileName = bin2hex($name) . '.pem';
$clientIdentities->resolvePath($fileName, true);
$this->view->form = $form = new ConfirmRemovalForm();
$form->setOnSuccess(function (ConfirmRemovalForm $form) use ($name, $fileName, $clientIdentities) {
try {
$clientIdentities->delete($fileName);
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
Notification::success(
sprintf(t('TLS client identity "%s" successfully removed'), $name)
);
return true;
})
->setRedirectUrl('config/tls')
->handleRequest();
$this->addTitleTab(
$this->translate('Remove Client Identity'),
sprintf($this->translate('Remove TLS Client Identity "%s"'), $name)
);
$this->render('form');
}
/**
* Add primary tab with the given label and title
*
* @param string $label
* @param string $title
*/
protected function addTitleTab($label, $title)
{
$url = clone $this->getRequest()->getUrl();
$this->getTabs()->add(
preg_replace('~\A.*/~', '', $url->getPath()),
array(
'active' => true,
'label' => $label,
'title' => $title,
'url' => $url
)
);
}
}

View File

@ -0,0 +1,97 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\Tls\ClientIdentity;
use Exception;
use Icinga\File\Storage\LocalFileStorage;
use Icinga\Web\Form;
use Icinga\Web\Form\Validator\TlsCertFileValidator;
use Icinga\Web\Form\Validator\TlsKeyFileValidator;
/**
* Configuration form for creating TLS client identities
*/
class CreateForm extends Form
{
public function init()
{
$this->setName('form_config_tlsclientidentity');
$this->setSubmitLabel($this->translate('Create'));
}
public function createElements(array $formData)
{
$this->addElement(
'text',
'name',
array(
'label' => $this->translate('Name'),
'description' => $this->translate('The new TLS client identity\'s name'),
'required' => true
)
);
$this->addElement(
'file',
'cert',
array(
'label' => $this->translate('Certificate (PEM)'),
'description' => $this->translate('The new TLS client certificate'),
'required' => true,
'validators' => array(new TlsCertFileValidator())
)
);
$this->addElement(
'file',
'key',
array(
'label' => $this->translate('Private Key (PEM)'),
'description' => $this->translate('The new TLS client private key'),
'required' => true,
'validators' => array(new TlsKeyFileValidator())
)
);
}
public function onSuccess()
{
$name = $this->getElement('name')->getValue();
try {
/** @var \Zend_Form_Element_File $cert */
$cert = $this->getElement('cert');
if ($cert->isUploaded()) {
$cert->getValue();
}
openssl_x509_export('file://' . $cert->getFileName(), $newCert);
/** @var \Zend_Form_Element_File $key */
$key = $this->getElement('key');
if ($key->isUploaded()) {
$key->getValue();
}
openssl_pkey_export('file://' . $key->getFileName(), $newKey);
$newCertDetails = openssl_pkey_get_details(openssl_pkey_get_public($newCert));
$newKeyDetails = openssl_pkey_get_details(openssl_pkey_get_private($newKey));
if ($newCertDetails['key'] !== $newKeyDetails['key']) {
$this->error($this->translate('The public keys of the certificate and the private key don\'t match'));
return false;
}
LocalFileStorage::common('tls/clientidentities')->create(bin2hex($name) . '.pem', $newCert . $newKey);
} catch (Exception $e) {
$this->error($e->getMessage());
return false;
}
$this->getRedirectUrl()->setParam('name', $name);
}
}

View File

@ -0,0 +1,85 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
namespace Icinga\Forms\Config\Tls\ClientIdentity;
use Exception;
use Icinga\File\Storage\LocalFileStorage;
use Icinga\Web\Form;
/**
* Configuration form for editing TLS client identities
*/
class EditForm extends Form
{
/**
* The TLS client identity's old name
*
* @var string
*/
protected $oldName;
public function init()
{
$this->setName('form_config_tlsclientidentity');
$this->setSubmitLabel($this->translate('Save Changes'));
}
public function createElements(array $formData)
{
$this->addElement(
'text',
'name',
array(
'label' => $this->translate('Name'),
'description' => $this->translate('The TLS client identity\'s name'),
'required' => true,
'value' => $this->oldName
)
);
$this->addElement(
'hidden',
'old_name',
array(
'required' => true,
'disabled' => true,
'value' => $this->oldName
)
);
}
public function onSuccess()
{
$name = $this->getElement('name')->getValue();
if ($name !== $this->oldName) {
try {
$clientIdentities = LocalFileStorage::common('tls/clientidentities');
$oldFileName = bin2hex($this->oldName) . '.pem';
$clientIdentities->create(bin2hex($name) . '.pem', $clientIdentities->read($oldFileName));
$clientIdentities->delete($oldFileName);
} catch (Exception $e) {
$this->error($e->getMessage());
return false;
}
}
$this->getRedirectUrl()->setParam('name', $name);
}
/**
* Set the TLS client identity's old name
*
* @param string $oldName
*
* @return $this
*/
public function setOldName($oldName)
{
$this->oldName = $oldName;
return $this;
}
}

View File

@ -0,0 +1,78 @@
<?php
use Icinga\Util\TimezoneDetect;
/** @var \Icinga\Web\View $this */
/** @var array $cert */
/** @var array $pubKey */
$timezoneDetect = new TimezoneDetect();
$timeZone = new DateTimeZone(
$timezoneDetect->success() ? $timezoneDetect->getTimezoneName() : date_default_timezone_get()
);
$subject = array();
foreach ($cert['info']['subject'] as $key => $value) {
$subject[] = $this->escape("$key = " . var_export($value, true));
}
$issuer = array();
foreach ($cert['info']['issuer'] as $key => $value) {
$issuer[] = $this->escape("$key = " . var_export($value, true));
}
?>
<div class="controls">
<?= /** @var \Icinga\Web\Widget\Tabs $tabs */ $tabs ?>
</div>
<div class="content">
<?= /** @var \Icinga\Forms\Config\Tls\RootCaCollection\EditForm $editForm */ $editForm ?>
<h1><?= /** @var \Icinga\Web\View $this */ $this->translate('TLS Client Certificate') ?></h1>
<table class="name-value-list">
<tr>
<td><?= $this->escape($this->translate('Subject', 'x509.certificate')) ?></td>
<td><?= implode('<br>', $subject) ?></td>
</tr>
<tr>
<td><?= $this->escape($this->translate('Issuer', 'x509.certificate')) ?></td>
<td><?= implode('<br>', $issuer) ?></td>
</tr>
<tr>
<td><?= $this->escape($this->translate('Valid from', 'x509.certificate')) ?></td>
<td><?= $this->escape(
DateTime::createFromFormat('U', $cert['info']['validFrom_time_t'])
->setTimezone($timeZone)
->format(DateTime::ISO8601)
) ?></td>
</tr>
<tr>
<td><?= $this->escape($this->translate('Valid until', 'x509.certificate')) ?></td>
<td><?= $this->escape(
DateTime::createFromFormat('U', $cert['info']['validTo_time_t'])
->setTimezone($timeZone)
->format(DateTime::ISO8601)
) ?></td>
</tr>
<tr>
<td><?= $this->escape($this->translate('SHA256 fingerprint', 'x509.certificate')) ?></td>
<td><?= $this->escape(implode(' ', str_split(strtoupper($cert['sha256']), 2))) ?></td>
</tr>
<tr>
<td><?= $this->escape($this->translate('SHA1 fingerprint', 'x509.certificate')) ?></td>
<td><?= $this->escape(implode(' ', str_split(strtoupper($cert['sha1']), 2))) ?></td>
</tr>
</table>
<h1><?= /** @var \Icinga\Web\View $this */ $this->translate('TLS Client Public Key') ?></h1>
<table class="name-value-list">
<tr>
<td><?= $this->escape($this->translate('SHA256 fingerprint', 'x509.certificate')) ?></td>
<td><?= $this->escape(implode(' ', str_split(strtoupper($pubKey['sha256']), 2))) ?></td>
</tr>
<tr>
<td><?= $this->escape($this->translate('SHA1 fingerprint', 'x509.certificate')) ?></td>
<td><?= $this->escape(implode(' ', str_split(strtoupper($pubKey['sha1']), 2))) ?></td>
</tr>
</table>
</div>

View File

@ -0,0 +1,6 @@
<div class="controls">
<?= /** @var \Icinga\Web\Widget\Tabs $tabs */ $tabs ?>
</div>
<div class="content">
<?= /** @var Icinga\Web\Form $form */ $form ?>
</div>