Add support for sync server SSL client certificates

pull/1486/head
Richard van den Berg 2016-08-01 16:30:22 +02:00
parent 1b73e38621
commit 81a5838131
9 changed files with 86 additions and 21 deletions

View File

@ -363,6 +363,7 @@ CREATE TABLE IF NOT EXISTS `servers` (
`pull_rules` text COLLATE utf8_bin NOT NULL,
`push_rules` text COLLATE utf8_bin NOT NULL,
`cert_file` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`client_cert_file` varchar(255) COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `org_id` (`org_id`),
INDEX `remote_org_id` (`remote_org_id`)

View File

@ -44,6 +44,7 @@ call AddColumnUnlessExists(Database(), 'servers', 'push_rules', 'TEXT( 11 ) COLL
call AddColumnUnlessExists(Database(), 'servers', 'org_id', 'INT( 11 ) NOT NULL DEFAULT 0');
call AddColumnUnlessExists(Database(), 'servers', 'remote_org_id', 'INT( 11 ) NOT NULL DEFAULT 0');
call AddColumnUnlessExists(Database(), 'servers', 'name', 'varchar(255) COLLATE utf8_bin NOT NULL');
call AddColumnUnlessExists(Database(), 'servers', 'client_cert_file', 'varchar(255) COLLATE utf8_bin DEFAULT NULL');
call AddColumnUnlessExists(Database(), 'shadow_attributes', 'org_id', 'INT( 11 ) NOT NULL DEFAULT 0');
call AddColumnUnlessExists(Database(), 'shadow_attributes', 'event_org_id', 'INT( 11 ) NOT NULL DEFAULT 0');

View File

@ -168,6 +168,7 @@ class AppSchema extends CakeSchema {
'lastpushedid' => array('type' => 'integer', 'null' => false, 'default' => null),
'self_signed' => array('type' => 'boolean', 'null' => false, 'default' => null),
'cert_file' => array('type' => 'string', 'null' => false, 'default' => null, 'collate' => 'utf8_bin', 'charset' => 'utf8'),
'client_cert_file' => array('type' => 'string', 'null' => false, 'default' => null, 'collate' => 'utf8_bin', 'charset' => 'utf8'),
'indexes' => array(
'PRIMARY' => array('column' => 'id', 'unique' => 1)
),

View File

@ -224,7 +224,10 @@ class ServersController extends AppController {
$this->request->data['Server']['org_id'] = $this->Auth->user('org_id');
if ($this->Server->save($this->request->data)) {
if (isset($this->request->data['Server']['submitted_cert']) && $this->request->data['Server']['submitted_cert']['size'] != 0) {
$this->__saveCert($this->request->data, $this->Server->id);
$this->__saveCert($this->request->data, $this->Server->id, false);
}
if (isset($this->request->data['Server']['submitted_client_cert']) && $this->request->data['Server']['submitted_client_cert']['size'] != 0) {
$this->__saveCert($this->request->data, $this->Server->id, true);
}
$this->Session->setFlash(__('The server has been saved'));
$this->redirect(array('action' => 'index'));
@ -300,7 +303,7 @@ class ServersController extends AppController {
}
if (!$fail) {
// say what fields are to be updated
$fieldList = array('id', 'url', 'push', 'pull', 'remote_org_id', 'name' ,'self_signed', 'cert_file', 'push_rules', 'pull_rules');
$fieldList = array('id', 'url', 'push', 'pull', 'remote_org_id', 'name' ,'self_signed', 'cert_file', 'client_cert_file', 'push_rules', 'pull_rules');
$this->request->data['Server']['id'] = $id;
if ("" != $this->request->data['Server']['authkey']) $fieldList[] = 'authkey';
if ($this->request->data['Server']['organisation_type'] < 2) $this->request->data['Server']['remote_org_id'] = $json['id'];
@ -340,9 +343,14 @@ class ServersController extends AppController {
// Save the data
if ($this->Server->save($this->request->data, true, $fieldList)) {
if (isset($this->request->data['Server']['submitted_cert']) && $this->request->data['Server']['submitted_cert']['size'] != 0 && !$this->request->data['Server']['delete_cert']) {
$this->__saveCert($this->request->data, $this->Server->id);
$this->__saveCert($this->request->data, $this->Server->id, false);
} else {
if ($this->request->data['Server']['delete_cert']) $this->__saveCert($this->request->data, $this->Server->id, true);
if ($this->request->data['Server']['delete_cert']) $this->__saveCert($this->request->data, $this->Server->id, false, true);
}
if (isset($this->request->data['Server']['submitted_client_cert']) && $this->request->data['Server']['submitted_client_cert']['size'] != 0 && !$this->request->data['Server']['delete_client_cert']) {
$this->__saveCert($this->request->data, $this->Server->id, true);
} else {
if ($this->request->data['Server']['delete_client_cert']) $this->__saveCert($this->request->data, $this->Server->id, true, true);
}
$this->Session->setFlash(__('The server has been saved'));
$this->redirect(array('action' => 'index'));
@ -546,33 +554,42 @@ class ServersController extends AppController {
}
}
private function __saveCert($server, $id, $delete = false) {
private function __saveCert($server, $id, $client = false, $delete = false) {
if ($client) {
$subm = 'submitted_client_cert';
$attr = 'client_cert_file';
$ins = '_client';
} else {
$subm = 'submitted_cert';
$attr = 'cert_file';
$ins = '';
}
if (!$delete) {
$ext = '';
App::uses('File', 'Utility');
App::uses('Folder', 'Utility');
App::uses('FileAccess', 'Tools');
$file = new File($server['Server']['submitted_cert']['name']);
$file = new File($server['Server'][$subm]['name']);
$ext = $file->ext();
if (($ext != 'pem') || !$server['Server']['submitted_cert']['size'] > 0) {
if (($ext != 'pem') || !$server['Server'][$subm]['size'] > 0) {
$this->Session->setFlash('Incorrect extension or empty file.');
$this->redirect(array('action' => 'index'));
}
// read pem file data
$pemData = FileAccess::readFromFile($server['Server']['submitted_cert']['tmp_name'], $server['Server']['submitted_cert']['size']);
$pemData = FileAccess::readFromFile($server['Server'][$subm]['tmp_name'], $server['Server'][$subm]['size']);
$destpath = APP . "files" . DS . "certs" . DS;
$dir = new Folder(APP . "files" . DS . "certs", true);
if (!preg_match('@^[\w-,\s,\.]+\.[A-Za-z0-9_]{2,4}$@', $server['Server']['submitted_cert']['name'])) throw new Exception ('Filename not allowed');
$pemfile = new File($destpath . $id . '.' . $ext);
if (!preg_match('@^[\w-,\s,\.]+\.[A-Za-z0-9_]{2,4}$@', $server['Server'][$subm]['name'])) throw new Exception ('Filename not allowed');
$pemfile = new File($destpath . $id . $ins . '.' . $ext);
$result = $pemfile->write($pemData);
$s = $this->Server->read(null, $id);
$s['Server']['cert_file'] = $s['Server']['id'] . '.' . $ext;
$s['Server'][$attr] = $s['Server']['id'] . $ins . '.' . $ext;
if ($result) $this->Server->save($s);
} else {
$s = $this->Server->read(null, $id);
$s['Server']['cert_file'] = '';
$s['Server'][$attr] = '';
$this->Server->save($s);
}
}

View File

@ -7,6 +7,7 @@ class SyncTool {
App::uses('HttpSocket', 'Network/Http');
if (!empty($server)) {
if ($server['Server']['cert_file']) $params['ssl_cafile'] = APP . "files" . DS . "certs" . DS . $server['Server']['id'] . '.pem';
if ($server['Server']['client_cert_file']) $params['ssl_local_cert'] = APP . "files" . DS . "certs" . DS . $server['Server']['id'] . '_client.pem';
if ($server['Server']['self_signed']) $params['ssl_allow_self_signed'] = $server['Server']['self_signed'];
}
$HttpSocket = new HttpSocket($params);

View File

@ -49,7 +49,7 @@ class AppModel extends Model {
// major -> minor -> hotfix -> requires_logout
public $db_changes = array(
2 => array(
4 => array(18 => false, 19 => false, 20 => false, 25 => false, 27 => false, 32 => false, 33 => true, 38 => true, 39 => true, 40 => false, 42 => false, 44 => false, 45 => false, 49 => true)
4 => array(18 => false, 19 => false, 20 => false, 25 => false, 27 => false, 32 => false, 33 => true, 38 => true, 39 => true, 40 => false, 42 => false, 44 => false, 45 => false, 49 => true, 50 => false)
)
);
@ -435,6 +435,9 @@ class AppModel extends Model {
$sqlArray[] = "ALTER TABLE `tags` ADD `org_id` int(11) NOT NULL DEFAULT 0;";
$sqlArray[] = 'ALTER TABLE `tags` ADD INDEX `org_id` (`org_id`);';
break;
case '2.4.50':
$sqlArray[] = "ALTER TABLE `servers` ADD `client_cert_file` varchar(255) COLLATE utf8_bin DEFAULT NULL;";
break;
case 'fixNonEmptySharingGroupID':
$sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
$sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';

View File

@ -64,7 +64,13 @@
));
echo $this->Form->input('Server.submitted_cert', array(
'label' => '<b>Certificate file</b>',
'label' => '<b>Server certificate file</b>',
'type' => 'file',
'div' => 'clear'
));
echo $this->Form->input('Server.submitted_client_cert', array(
'label' => '<b>Client certificate file</b>',
'type' => 'file',
'div' => 'clear'
));

View File

@ -70,7 +70,7 @@
?>
<div class="clear">
<p>
<span class="bold">Ceritificate file: </span>
<span class="bold">Server ceritificate file: </span>
<span id="serverEditCertValue">
<?php
if (isset($server['Server']['cert_file']) && !empty($server['Server']['cert_file'])) echo h($server['Server']['cert_file']);
@ -84,7 +84,29 @@
<div style="width: 0px;height: 0px;overflow: hidden;">
<?php
echo $this->Form->input('Server.submitted_cert', array(
'label' => false,
'label' => 'submitted_cert',
'type' => 'file',
'div' => false
));
?>
</div>
<div class="clear">
<p>
<span class="bold">Client ceritificate file: </span>
<span id="serverEditClientCertValue">
<?php
if (isset($server['Server']['client_cert_file']) && !empty($server['Server']['client_cert_file'])) echo h($server['Server']['client_cert_file']);
else echo '<span class="green bold">Not set.</span>';
?>
</span>
<br />
<span id="add_client_cert_file" class="btn btn-inverse" style="line-height:10px; padding: 4px 4px;">Add certificate file</span>
<span id="remove_client_cert_file" class="btn btn-inverse" style="line-height:10px; padding: 4px 4px;">Remove certificate file</span>
</p>
<div style="width: 0px;height: 0px;overflow: hidden;">
<?php
echo $this->Form->input('Server.submitted_client_cert', array(
'label' => 'submitted_client_cert',
'type' => 'file',
'div' => false
));
@ -134,6 +156,7 @@ var formInfoValues = {
'ServerPush' : "Allow the upload of events and their attributes.",
'ServerPull' : "Allow the download of events and their attributes from the server.",
'ServerSubmittedCert' : "You can also upload a certificate file if the instance you are trying to connect to has its own signing authority.",
'ServerSubmittedClientCert' : "You can also upload a client certificate file if the instance you are trying to connect requires this.",
'ServerSelfSigned' : "Click this, if you would like to allow a connection despite the other instance using a self-signed certificate (not recommended)."
};
@ -151,11 +174,11 @@ $(document).ready(function() {
serverOrgTypeChange();
});
$("#ServerUrl, #ServerOrganization, #ServerName, #ServerAuthkey, #ServerPush, #ServerPull, #ServerSubmittedCert, #ServerSelfSigned").on('mouseleave', function(e) {
$("#ServerUrl, #ServerOrganization, #ServerName, #ServerAuthkey, #ServerPush, #ServerPull, #ServerSubmittedCert, #ServerSubmittedClientCert, #ServerSelfSigned").on('mouseleave', function(e) {
$('#'+e.currentTarget.id).popover('destroy');
});
$("#ServerUrl, #ServerOrganization, #ServerName, #ServerAuthkey, #ServerPush, #ServerPull, #ServerSubmittedCert, #ServerSelfSigned").on('mouseover', function(e) {
$("#ServerUrl, #ServerOrganization, #ServerName, #ServerAuthkey, #ServerPush, #ServerPull, #ServerSubmittedCert, #ServerSubmittedClientCert, #ServerSelfSigned").on('mouseover', function(e) {
var $e = $(e.target);
$('#'+e.currentTarget.id).popover('destroy');
$('#'+e.currentTarget.id).popover({
@ -176,14 +199,24 @@ $(document).ready(function() {
$('#add_cert_file').click(function() {
$('#ServerSubmittedCert').trigger('click');
});
$('input[type=file]').change(function() {
$('#serverEditCertValue').text($('input[type=file]').val());
$('#add_client_cert_file').click(function() {
$('#ServerSubmittedClientCert').trigger('click');
});
$('input[label=submitted_cert]').change(function() {
$('#serverEditCertValue').text($('input[label=submitted_cert]').val());
$('#ServerDeleteCert').prop('checked', false);
});
$('input[label=submitted_client_cert]').change(function() {
$('#serverEditClientCertValue').text($('input[label=submitted_client_cert]').val());
$('#ServerDeleteClientCert').prop('checked', false);
});
$('#remove_cert_file').click(function() {
$('#serverEditCertValue').html('<span class="green bold">Not set.</span>');
$('#ServerDeleteCert').prop('checked', true);
});
$('#remove_client_cert_file').click(function() {
$('#serverEditClientCertValue').html('<span class="green bold">Not set.</span>');
$('#ServerDeleteClientCert').prop('checked', true);
});
});
</script>

View File

@ -25,6 +25,7 @@
<th><?php echo $this->Paginator->sort('url');?></th>
<th>Remote Organisation</th>
<th><?php echo $this->Paginator->sort('cert_file');?></th>
<th><?php echo $this->Paginator->sort('client_cert_file');?></th>
<th><?php echo $this->Paginator->sort('self_signed');?></th>
<th><?php echo $this->Paginator->sort('org');?></th>
<th>Last Pulled ID</th>
@ -70,6 +71,7 @@ foreach ($servers as $server):
<td><?php echo h($server['Server']['url']); ?>&nbsp;</td>
<td><a href="/organisations/view/<?php echo h($server['RemoteOrg']['id']); ?>"><?php echo h($server['RemoteOrg']['name']); ?></a></td>
<td class="short"><?php echo h($server['Server']['cert_file']); ?>&nbsp;</td>
<td class="short"><?php echo h($server['Server']['client_cert_file']); ?>&nbsp;</td>
<td class="short"><span class="<?php echo ($server['Server']['self_signed'] ? 'icon-ok' : 'icon-remove'); ?>"></span></td>
<td class="short"><a href="/organisations/view/<?php echo h($server['Organisation']['id']); ?>"><?php echo h($server['Organisation']['name']); ?></a></td>
<td class="short"><?php echo $server['Server']['lastpulledid']; ?></td>