Merge remote-tracking branch 'origin/develop' into feature-workflows-2

pull/8530/head
Sami Mokaddem 2022-08-04 10:08:56 +02:00
commit 49575533ad
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
26 changed files with 575 additions and 82 deletions

View File

@ -16,8 +16,9 @@ App::uses('JsonTool', 'Tools');
*/
class AdminShell extends AppShell
{
public $uses = array('Event', 'Post', 'Attribute', 'Job', 'User', 'Task', 'Allowedlist', 'Server', 'Organisation', 'AdminSetting', 'Galaxy', 'Taxonomy', 'Warninglist', 'Noticelist', 'ObjectTemplate', 'Bruteforce', 'Role', 'Feed', 'SharingGroupBlueprint');
public $uses = array('Event', 'Post', 'Attribute', 'Job', 'User', 'Task', 'Allowedlist', 'Server', 'Organisation', 'AdminSetting', 'Galaxy', 'Taxonomy', 'Warninglist', 'Noticelist', 'ObjectTemplate', 'Bruteforce', 'Role', 'Feed', 'SharingGroupBlueprint', 'Correlation');
public $tasks = array('ConfigLoad');
public function getOptionParser()
{
$parser = parent::getOptionParser();
@ -99,6 +100,7 @@ class AdminShell extends AppShell
public function jobGenerateCorrelation()
{
$this->ConfigLoad->execute();
if (empty($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Generate correlation'] . PHP_EOL);
}
@ -1186,4 +1188,37 @@ class AdminShell extends AppShell
);
$this->out($message);
}
public function truncateTable()
{
$this->ConfigLoad->execute();
if (empty($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Truncate table correlation'] . PHP_EOL);
}
$userId = $this->args[0];
if ($userId) {
$user = $this->User->getAuthUser($userId);
} else {
$user = [
'id' => 0,
'email' => 'SYSTEM',
'Organisation' => [
'name' => 'SYSTEM'
]
];
}
if (empty($this->args[1])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Truncate table correlation'] . PHP_EOL);
}
if (!empty($this->args[2])) {
$jobId = $this->args[2];
}
$table = trim($this->args[1]);
$this->Correlation->truncate($user, $table);
if ($jobId) {
$this->Job->saveField('progress', 100);
$this->Job->saveField('date_modified', date("Y-m-d H:i:s"));
$this->Job->saveField('message', __('Database truncated: ' . $table));
}
}
}

View File

@ -84,10 +84,24 @@ class AttributesController extends AppController
'fields' => ['Orgc.id', 'Orgc.name', 'Orgc.uuid'],
]);
$orgTable = Hash::combine($orgTable, '{n}.Orgc.id', '{n}.Orgc');
$sgids = $this->Attribute->SharingGroup->authorizedIds($this->Auth->user());
foreach ($attributes as &$attribute) {
if (isset($orgTable[$attribute['Event']['orgc_id']])) {
$attribute['Event']['Orgc'] = $orgTable[$attribute['Event']['orgc_id']];
}
$temp = $this->Attribute->Correlation->getRelatedAttributes(
$this->Auth->user(),
$sgids,
$attribute['Attribute'],
[],
true
);
foreach ($temp as &$t) {
$t['info'] = $t['Event']['info'];
$t['org_id'] = $t['Event']['org_id'];
$t['date'] = $t['Event']['date'];
}
$attribute['Event']['RelatedAttribute'][$attribute['Attribute']['id']] = $temp;
}
list($attributes, $sightingsData) = $this->__searchUI($attributes);
@ -1582,13 +1596,19 @@ class AttributesController extends AppController
$attribute['Event']['Org'] = $orgTable[$attribute['Event']['org_id']];
}
if (isset($filters['includeCorrelations'])) {
$attribute['Event']['RelatedAttribute'][$attribute['Attribute']['id']] = $this->Attribute->Correlation->getRelatedAttributes(
$temp = $this->Attribute->Correlation->getRelatedAttributes(
$this->Auth->user(),
$sgids,
$attribute['Attribute'],
[],
true
);
foreach ($temp as &$t) {
$t['info'] = $t['Event']['info'];
$t['org_id'] = $t['Event']['org_id'];
$t['date'] = $t['Event']['date'];
}
$attribute['Event']['RelatedAttribute'][$attribute['Attribute']['id']] = $temp;
}
}
if ($this->_isRest()) {
@ -1905,36 +1925,38 @@ class AttributesController extends AppController
public function generateCorrelation()
{
$this->request->allowMethod(['post']);
if ($this->request->is('post')) {
if (!Configure::read('MISP.background_jobs')) {
$k = $this->Attribute->generateCorrelation();
$this->Flash->success(__('All done. %s attributes processed.', $k));
$this->redirect(array('controller' => 'pages', 'action' => 'display', 'administration'));
} else {
/** @var Job $job */
$job = ClassRegistry::init('Job');
$jobId = $job->createJob(
'SYSTEM',
Job::WORKER_DEFAULT,
'generate correlation',
'All attributes',
'Job created.'
);
if (!Configure::read('MISP.background_jobs')) {
$k = $this->Attribute->generateCorrelation();
$this->Flash->success(__('All done. %s attributes processed.', $k));
$this->redirect(array('controller' => 'pages', 'action' => 'display', 'administration'));
} else {
/** @var Job $job */
$job = ClassRegistry::init('Job');
$jobId = $job->createJob(
'SYSTEM',
Job::WORKER_DEFAULT,
'generate correlation',
'All attributes',
'Job created.'
);
$this->Attribute->getBackgroundJobsTool()->enqueue(
BackgroundJobsTool::DEFAULT_QUEUE,
BackgroundJobsTool::CMD_ADMIN,
[
'jobGenerateCorrelation',
$this->Attribute->getBackgroundJobsTool()->enqueue(
BackgroundJobsTool::DEFAULT_QUEUE,
BackgroundJobsTool::CMD_ADMIN,
[
'jobGenerateCorrelation',
$jobId
],
true,
$jobId
],
true,
$jobId
);
);
$this->Flash->success(__('Job queued. You can view the progress if you navigate to the active jobs view (Administration -> Jobs).'));
$this->redirect(array('controller' => 'pages', 'action' => 'display', 'administration'));
$this->Flash->success(__('Job queued. You can view the progress if you navigate to the active jobs view (Administration -> Jobs).'));
$this->redirect(Router::url($this->referer(), true));
}
} else {
$this->render('ajax/recorrelationConfirmation');
}
}

View File

@ -111,4 +111,93 @@ class CorrelationsController extends AppController
]);
}
}
public function switchEngine(string $engine)
{
$this->loadModel('Server');
if (!isset($this->Correlation->validEngines[$engine])) {
throw new MethodNotAllowedException(__('Not a valid engine choice. Please make sure you pass one of the following: ', implode(', ', array_keys($this->Correlation->validEngines))));
}
if ($this->request->is('post')) {
$setting = $this->Server->getSettingData('MISP.correlation_engine');
$result = $this->Server->serverSettingsEditValue($this->Auth->user(), $setting, $engine);
if ($result === true) {
$message = __('Engine switched.');
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Correlations', 'switchEngine', false, $this->response->type(), $message);
} else {
$this->Flash->success($message);
$this->redirect(['controller' => 'servers', 'action' => 'serverSettings', 'correlations']);
}
} else {
$message = __('Couldn\'t switch to the requested engine.');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Correlations', 'switchEngine', false, $message, $this->response->type());
} else {
$this->Flash->error($message);
$this->redirect(['controller' => 'servers', 'action' => 'serverSettings', 'correlations']);
}
}
} else {
$this->set('engine', $engine);
$this->render('ajax/switch_engine_confirmation');
}
}
public function truncate(string $engine)
{
if (!isset($this->Correlation->validEngines[$engine])) {
throw new MethodNotAllowedException(__('Not a valid engine choice. Please make sure you pass one of the following: ', implode(', ', array_keys($this->Correlation->validEngines))));
}
if ($this->request->is('post')) {
if (!Configure::read('MISP.background_jobs')) {
$result = $this->Correlation->truncate($this->Auth->user(), $engine);
$message = $result ? __('Table truncated.') : __('Could not truncate table');
if ($this->_isRest()) {
if ($result) {
$this->RestResponse->saveSuccessResponse('Correlations', 'truncate', false, $this->response->type(), $message);
} else {
$this->RestResponse->saveFailResponse('Correlations', 'truncate', false, $message, $this->response->type());
}
} else {
$this->Flash->{$result ? 'success' : 'error'}($message);
$this->redirect(['controller' => 'servers', 'action' => 'serverSettings', 'correlations']);
}
} else {
$job = ClassRegistry::init('Job');
$jobId = $job->createJob(
'SYSTEM',
Job::WORKER_DEFAULT,
'truncate table',
$this->Correlation->validEngines[$engine],
'Job created.'
);
$this->Correlation->Attribute->getBackgroundJobsTool()->enqueue(
BackgroundJobsTool::DEFAULT_QUEUE,
BackgroundJobsTool::CMD_ADMIN,
[
'truncateTable',
$this->Auth->user('id'),
$engine,
$jobId
],
true,
$jobId
);
$message = __('Job queued. You can view the progress if you navigate to the active jobs view (Administration -> Jobs).');
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Correlations', 'truncate', false, $this->response->type(), $message);
} else {
$this->Flash->success($message);
$this->redirect(['controller' => 'servers', 'action' => 'serverSettings', 'correlations']);
}
}
} else {
$this->set('engine', $engine);
$this->set('table_name', $this->Correlation->validEngines[$engine]);
$this->render('ajax/truncate_confirmation');
}
}
}

View File

@ -1072,6 +1072,11 @@ class ServersController extends AppController
$diagnostic_errors = 0;
App::uses('File', 'Utility');
App::uses('Folder', 'Utility');
if ($tab === 'correlations') {
$this->loadModel('Correlation');
$correlation_metrics = $this->Correlation->collectMetrics();
$this->set('correlation_metrics', $correlation_metrics);
}
if ($tab === 'files') {
$files = $this->Server->grabFiles();
$this->set('files', $files);

View File

@ -82,7 +82,7 @@ class AppModel extends Model
69 => false, 70 => false, 71 => true, 72 => true, 73 => false, 74 => false,
75 => false, 76 => true, 77 => false, 78 => false, 79 => false, 80 => false,
81 => false, 82 => false, 83 => false, 84 => false, 85 => false, 86 => false,
87 => false, 88 => false, 89 => false,
87 => false, 88 => false, 89 => false, 90 => false,
);
const ADVANCED_UPDATES_DESCRIPTION = array(
@ -228,6 +228,9 @@ class AppModel extends Model
$dbUpdateSuccess = $this->__generateCorrelations();
break;
case 89:
$this->__retireOldCorrelationEngine();
break;
case 90:
$this->Workflow = Classregistry::init('Workflow');
$this->Workflow->enableDefaultModules();
break;
@ -1770,7 +1773,7 @@ class AppModel extends Model
$sqlArray[] = 'ALTER TABLE `users` ADD `external_auth_required` tinyint(1) NOT NULL DEFAULT 0;';
$sqlArray[] = 'ALTER TABLE `users` ADD `external_auth_key` text COLLATE utf8_bin;';
break;
case 89:
case 90:
$sqlArray[] = "CREATE TABLE IF NOT EXISTS `workflows` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uuid` varchar(40) COLLATE utf8_bin NOT NULL ,
@ -2227,7 +2230,7 @@ class AppModel extends Model
'fields' => ['id', 'value'],
]);
if (count($db_version) > 1) {
// we rgan into a bug where we have more than one db_version entry. This bug happened in some rare circumstances around 2.4.50-2.4.57
// we ran into a bug where we have more than one db_version entry. This bug happened in some rare circumstances around 2.4.50-2.4.57
foreach ($db_version as $k => $v) {
if ($k > 0) {
$this->AdminSetting->delete($v['AdminSetting']['id']);
@ -3578,4 +3581,61 @@ class AppModel extends Model
}
return $this->_eventManager;
}
private function __retireOldCorrelationEngine($user = null) {
if ($user === null) {
$user = [
'id' => 0,
'email' => 'SYSTEM',
'Organisation' => [
'name' => 'SYSTEM'
]
];
}
$this->Correlation = ClassRegistry::init('Correlation');
$this->Attribute = ClassRegistry::init('Attribute');
if (!Configure::read('MISP.background_jobs')) {
$this->Correlation->truncate($user, 'Legacy');
$this->Attribute->generateCorrelation();
} else {
$job = ClassRegistry::init('Job');
$jobId = $job->createJob(
'SYSTEM',
Job::WORKER_DEFAULT,
'truncate table',
$this->Correlation->validEngines['Legacy'],
'Job created.'
);
$this->Correlation->Attribute->getBackgroundJobsTool()->enqueue(
BackgroundJobsTool::DEFAULT_QUEUE,
BackgroundJobsTool::CMD_ADMIN,
[
'truncateTable',
0,
'Legacy',
$jobId
],
true,
$jobId
);
$jobId = $job->createJob(
'SYSTEM',
Job::WORKER_DEFAULT,
'generate correlation',
'All attributes',
'Job created.'
);
$this->Attribute->getBackgroundJobsTool()->enqueue(
BackgroundJobsTool::DEFAULT_QUEUE,
BackgroundJobsTool::CMD_ADMIN,
[
'jobGenerateCorrelation',
$jobId
],
true,
$jobId
);
}
}
}

View File

@ -1601,6 +1601,9 @@ class Attribute extends AppModel
$attributes = $this->find('all', $query);
foreach ($attributes as $attribute) {
$attribute['Attribute']['event_id'] = $eventId;
if ($full) {
$this->Correlation->beforeSaveCorrelation($attribute['Attribute']);
}
$this->Correlation->afterSaveCorrelation($attribute['Attribute'], $full);
}
$fetchedAttributes = count($attributes);

View File

@ -136,7 +136,7 @@ class NoAclCorrelationBehavior extends ModelBehavior
$max_correlations = Configure::read('MISP.max_correlations_per_event') ?: 5000;
$source = $primary ? '' : '1_';
$prefix = $primary ? '1_' : '';
$correlations = $this->Correlation->find('all', array(
$correlations = $this->Correlation->find('all', [
'fields' => [
$source . 'attribute_id',
$prefix . 'attribute_id',
@ -166,7 +166,7 @@ class NoAclCorrelationBehavior extends ModelBehavior
],
'order' => false,
'limit' => $max_correlations
));
]);
return $correlations;
}

View File

@ -31,6 +31,12 @@ class Correlation extends AppModel
]
);
public $validEngines = [
'Default' => 'default_correlations',
'NoAcl' => 'no_acl_correlations',
'Legacy' => 'correlations'
];
public $actsAs = array(
'Containable'
);
@ -255,7 +261,7 @@ class Correlation extends AppModel
$this->runBeforeSaveCorrelation($attribute);
}
private function __cachedGetContainData($scope, $id): array
private function __cachedGetContainData($scope, $id)
{
if (!empty($this->getContainRules($scope))) {
if (empty($this->__tempContainCache[$scope][$id])) {
@ -345,6 +351,8 @@ class Correlation extends AppModel
'Event.disable_correlation' => 0,
'Attribute.deleted' => 0,
];
$correlationLimit = $this->OverCorrelatingValue->getLimit();
$correlatingAttributes = $this->Attribute->find('all', [
'conditions' => $conditions,
'recursive' => -1,
@ -352,9 +360,11 @@ class Correlation extends AppModel
'contain' => $this->getContainRules(),
'order' => [],
'callbacks' => 'before', // memory leak fix
// let's fetch the limit +1 - still allows us to detect overcorrelations, but we'll also never need more
'limit' => empty($correlationLimit) ? null : ($correlationLimit+1)
]);
// Let's check if we don't have a case of an over-correlating attribute
$correlationLimit = $this->OverCorrelatingValue->getLimit();
$count = count($correlatingAttributes);
if ($count > $correlationLimit) {
// If we have more correlations for the value than the limit, set the block entry and stop the correlation process
@ -372,9 +382,7 @@ class Correlation extends AppModel
$value = $cV;
}
if ($a['Attribute']['id'] > $b['Attribute']['id']) {
if (!$full) {
$correlations[] = $this->__createCorrelationEntry($value, $a, $b);
}
$correlations[] = $this->__createCorrelationEntry($value, $a, $b);
} else {
$correlations[] = $this->__createCorrelationEntry($value, $b, $a);
}
@ -878,4 +886,81 @@ class Correlation extends AppModel
}
return $attribute;
}
public function collectMetrics()
{
$results['engine'] = $this->getCorrelationModelName();
$results['db'] = [
'Default' => [
'name' => __('Default correlation engine'),
'tables' => [
'default_correlations' => [
'id_limit' => 4294967295
],
'correlation_values' => [
'id_limit' => 4294967295
]
]
],
'NoAcl' => [
'name' => __('No ACL correlation engine'),
'tables' => [
'no_acl_correlations' => [
'id_limit' => 4294967295
],
'correlation_values' => [
'id_limit' => 4294967295
]
]
],
'Legacy' => [
'name' => __('Legacy correlation engine (< 2.4.160)'),
'tables' => [
'correlations' => [
'id_limit' => 2147483647
]
]
]
];
$results['over_correlations'] = $this->OverCorrelatingValue->find('count');
$this->CorrelationExclusion = ClassRegistry::init('CorrelationExclusion');
$results['excluded_correlations'] = $this->CorrelationExclusion->find('count');
foreach ($results['db'] as &$result) {
foreach ($result['tables'] as $table_name => &$table_data) {
$size_metrics = $this->query(sprintf('show table status like \'%s\';', $table_name));
if (!empty($size_metrics)) {
$table_data['size_on_disk'] = $this->query(
//'select FILE_SIZE from information_schema.innodb_sys_tablespaces where FILENAME like \'%/' . $table_name . '.ibd\';'
sprintf(
'select TABLE_NAME, ROUND((DATA_LENGTH + INDEX_LENGTH)) AS size FROM information_schema.TABLES where TABLE_SCHEMA="%s" AND TABLE_NAME="%s"',
$this->getDataSource()->config['database'],
$table_name
)
)[0][0]['size'];
$last_id = $this->query(sprintf('select max(id) as max_id from %s;', $table_name));
$table_data['row_count'] = $size_metrics[0]['TABLES']['Rows'];
$table_data['last_id'] = $last_id[0][0]['max_id'];
$table_data['id_saturation'] = round(100 * $table_data['last_id'] / $table_data['id_limit'], 2);
}
}
}
return $results;
}
public function truncate(array $user, string $engine)
{
$table = $this->validEngines[$engine];
$result = $this->query('truncate table ' . $table);
if ($result !== true) {
$this->loadLog()->createLogEntry(
$user,
'truncate',
'Correlation',
0,
'Could not truncate table ' . $table,
'Errors: ' . json_encode($result)
);
}
return $result === true;
}
}

View File

@ -1938,7 +1938,7 @@ class Event extends AppModel
if (!empty($options['includeRelatedTags'])) {
$event = $this->includeRelatedTags($event, $options);
}
$event['RelatedShadowAttribute'] = $this->getRelatedAttributes($user, $event['Event']['id'], true);
//$event['RelatedShadowAttribute'] = $this->getRelatedAttributes($user, $event['Event']['id'], true);
}
$shadowAttributeByOldId = [];
if (!empty($event['ShadowAttribute'])) {

View File

@ -304,6 +304,20 @@ class MispObject extends AppModel
$object = $this->data['Object'];
$this->Attribute->Correlation->updateContainedCorrelations($object, 'object');
}
if ($this->data['Object']['deleted']) {
$attributes_to_delete = $this->Attribute->find('all', [
'recursive' => -1,
'conditions' => [
'Attribute.object_id' => $this->id,
'Attribute.deleted' => 0
]
]);
foreach ($attributes_to_delete as &$attribute_to_delete) {
$attribute_to_delete['Attribute']['deleted'] = 1;
unset($attribute_to_delete['Attribute']['timestamp']);
}
$this->Attribute->saveMany($attributes_to_delete);
}
$workflowErrors = [];
$logging = [
'model' => 'Object',
@ -1015,6 +1029,9 @@ class MispObject extends AppModel
$objectId = $this->id;
if (!empty($object['Object']['Attribute'])) {
foreach ($object['Object']['Attribute'] as $attribute) {
if (!empty($object['Object']['deleted'])) {
$attribute['deleted'] = 1;
}
$this->Attribute->captureAttribute($attribute, $eventId, $user, $objectId, false, $parentEvent);
}
}
@ -1099,6 +1116,9 @@ class MispObject extends AppModel
}
if (!empty($object['Attribute'])) {
foreach ($object['Attribute'] as $attribute) {
if (!empty($object['deleted'])) {
$attribute['deleted'] = 1;
}
$result = $this->Attribute->editAttribute($attribute, $event, $user, $object['id'], false, $force);
}
}

View File

@ -3160,13 +3160,6 @@ class Server extends AppModel
$tableIndexDiff = array_diff(array_keys($indexes), array_keys($actualIndex[$tableName])); // check for missing indexes
foreach ($tableIndexDiff as $columnDiff) {
$shouldBeUnique = $indexes[$columnDiff];
if ($shouldBeUnique && !$this->checkIfColumnContainsJustUniqueValues($tableName, $columnDiff)) {
$indexDiff[$tableName][$columnDiff] = array(
'message' => __('Column `%s` should be unique indexed, but contains duplicate values', $columnDiff),
'sql' => '',
);
continue;
}
$message = __('Column `%s` should be indexed', $columnDiff);
$indexDiff[$tableName][$columnDiff] = array(
@ -3194,15 +3187,6 @@ class Server extends AppModel
'sql' => $sql,
);
} else {
if (!$this->checkIfColumnContainsJustUniqueValues($tableName, $column)) {
$message = __('Column `%s` should be unique index, but contains duplicate values', $column);
$indexDiff[$tableName][$column] = array(
'message' => $message,
'sql' => '',
);
continue;
}
$sql = $this->generateSqlDropIndexQuery($tableName, $column);
$sql .= '<br>' . $this->generateSqlIndexQuery($dbExpectedSchema, $tableName, $column, true);
@ -7479,6 +7463,7 @@ class Server extends AppModel
'Get IPs for user ID' => 'MISP/app/Console/cake Admin UserIP [user_id]',
'Get user ID for user IP' => 'MISP/app/Console/cake Admin IPUser [ip]',
'Generate correlation' => 'MISP/app/Console/cake Admin jobGenerateCorrelation [job_id]',
'Truncate correlation table' => 'MISP/app/Console/cake Admin truncateTable [user_id] [correlation_engine_name] [job_id]',
'Purge correlation' => 'MISP/app/Console/cake Admin jobPurgeCorrelation [job_id]',
'Generate shadow attribute correlation' => 'MISP/app/Console/cake Admin jobGenerateShadowAttributeCorrelation [job_id]',
'Update MISP' => 'MISP/app/Console/cake Admin updateMISP',

View File

@ -0,0 +1,26 @@
<div class="confirmation">
<?php
echo $this->Form->create('Attribute', ['style' => 'margin:0px;', 'id' => 'PromptForm', 'url' => $baseurl . '/attributes/generateCorrelation']);
$message = __('Recorrelate instance', );
$buttonTitle = __('Recorrelate');
?>
<legend><?= $message ?></legend>
<div style="padding-left:5px;padding-right:5px;padding-bottom:5px;">
<?php
echo '<p>' . __('Are you sure you wish to start a recorrelation for the currently active correlation engine?') . '</p>';
echo '<p>' . __('Depending on the system and the amount of attributes, this might take a long time.') . '</p>';
?>
<table>
<tr>
<td style="vertical-align:top">
<button role="button" tabindex="0" aria-label="<?= $buttonTitle ?>" title="<?= $buttonTitle ?>" id="PromptYesButton" class="btn btn-primary"><?= __('Yes') ?></button>
</td>
<td style="width:100%;"></td>
<td style="vertical-align:top;">
<span role="button" tabindex="0" aria-label="<?= __('Cancel');?>" title="<?= __('Cancel');?>" class="btn btn-inverse" id="PromptNoButton" onclick="cancelPrompt()"><?= __('No');?></span>
</td>
</tr>
</table>
</div>
<?= $this->Form->end(); ?>
</div>

View File

@ -2,7 +2,6 @@
$modules = isset($modules) ? $modules : null;
$cortex_modules = isset($cortex_modules) ? $cortex_modules : null;
echo '<div class="index">';
echo $this->element('/genericElements/IndexTable/index_table', [
'data' => [

View File

@ -0,0 +1,25 @@
<div class="confirmation">
<?php
echo $this->Form->create('Correlation', ['style' => 'margin:0px;', 'id' => 'PromptForm', 'url' => $baseurl . '/correlations/switchEngine/' . urlencode($engine)]);
$message = __('Switch Engine');
$buttonTitle = __('Switch');
?>
<legend><?= $message ?></legend>
<div style="padding-left:5px;padding-right:5px;padding-bottom:5px;">
<?php
echo '<p>' . __('Are you sure you want to switch to the given correlation engine (' . h($engine) . ')? If so, it is highly recommended that you recorrelate afterwards.') . '</p>';
?>
<table>
<tr>
<td style="vertical-align:top">
<button role="button" tabindex="0" aria-label="<?= $buttonTitle ?>" title="<?= $buttonTitle ?>" id="PromptYesButton" class="btn btn-primary"><?= __('Yes') ?></button>
</td>
<td style="width:100%;"></td>
<td style="vertical-align:top;">
<span role="button" tabindex="0" aria-label="<?= __('Cancel');?>" title="<?= __('Cancel');?>" class="btn btn-inverse" id="PromptNoButton" onclick="cancelPrompt()"><?= __('No');?></span>
</td>
</tr>
</table>
</div>
<?= $this->Form->end(); ?>
</div>

View File

@ -0,0 +1,25 @@
<div class="confirmation">
<?php
echo $this->Form->create('Correlation', ['style' => 'margin:0px;', 'id' => 'PromptForm', 'url' => $baseurl . '/correlations/truncate/' . urlencode($engine)]);
$message = __('Truncate database of the associated correlation engine');
$buttonTitle = __('Truncate');
?>
<legend><?= $message ?></legend>
<div style="padding-left:5px;padding-right:5px;padding-bottom:5px;">
<?php
echo '<p>' . __('Are you sure you want to truncate the correlation table (' . h($table_name) . ') used by the disabled correlation engine ' . h($engine) . '?') . '</p>';
?>
<table>
<tr>
<td style="vertical-align:top">
<button role="button" tabindex="0" aria-label="<?= $buttonTitle ?>" title="<?= $buttonTitle ?>" id="PromptYesButton" class="btn btn-primary"><?= __('Yes') ?></button>
</td>
<td style="width:100%;"></td>
<td style="vertical-align:top;">
<span role="button" tabindex="0" aria-label="<?= __('Cancel');?>" title="<?= __('Cancel');?>" class="btn btn-inverse" id="PromptNoButton" onclick="cancelPrompt()"><?= __('No');?></span>
</td>
</tr>
</table>
</div>
<?= $this->Form->end(); ?>
</div>

View File

@ -57,11 +57,18 @@
);
}
}
if (!empty($object['over_correlation']) || !empty($object['correlation_exclusion'])) {
if (!empty($object['correlation_exclusion'])) {
echo sprintf(
'<span class="bold red">%s</span> ',
'<span class="bold red" title="%s">%s</span> ',
__('The attribute value matches a correlation exclusion rule defined by a site-administrator for this instance. Click the magnifying glass to search for all occurrences of the value.'),
__('Excluded.')
);
} else if (!empty($object['over_correlation'])) {
echo sprintf(
'<span class="bold red" title="%s">%s</span> ',
__('The instance threshold for the maximum number of correlations for the given attribute value has been exceeded. Click the magnifying glass to search for all occurrences of the value.'),
__('Too many correlations.')
);
}
echo $this->Html->link(
'',

View File

@ -1,18 +1,20 @@
<?php
$object = Hash::extract($row, $field['data']['object']['value_path']);
$event = Hash::extract($row, 'Event');
if (!empty($object['RelatedAttribute'])) {
$event['RelatedAttribute'] = array($object['id'] => $object['RelatedAttribute']);
}
if (!empty($event['RelatedAttribute'][$object['id']])) {
echo '<ul class="inline" style="margin:0">';
echo $this->element('Events/View/attribute_correlations', array(
'scope' => $field['data']['scope'],
'object' => $object,
'event' => $event,
));
echo '</ul>';
}
$object = Hash::extract($row, $field['data']['object']['value_path']);
$event = Hash::extract($row, 'Event');
if (!empty($object['RelatedAttribute'])) {
$event['RelatedAttribute'] = array($object['id'] => $object['RelatedAttribute']);
}
foreach ($event['RelatedAttribute'] as $k => &$ra) {
if (!empty($ra['Event'])) {
$ra['info'] = $ra['Event']['info'];
$ra['org_id'] = $ra['Event']['org_id'];
}
}
echo sprintf(
'<ul class="inline" style="margin:0">%s</ul>',
$this->element('Events/View/attribute_correlations', [
'scope' => $field['data']['scope'],
'object' => $object,
'event' => $event,
])
);

View File

@ -0,0 +1,45 @@
<?php
echo '<div style="border:1px solid #dddddd; margin-top:1px; width:100%; padding:10px">';
echo sprintf(
'<p>%s</p><p>%s</p>',
__('This is the correlation management interface. Its goal is to provide youwith information about the currently used correlation engine as well as the data stores of currently dormant engines.'),
__('You will also find management tools for the various engines below, make sure that you keep an eye on the disk requirements as well as the exhaustion of IDs and recorrelate the instance when needed.')
);
echo sprintf(
'<div style="width:300px;">%s</div>',
$this->element(
'/healthElements/correlations_generic_data',
[
'correlation_metrics' => $correlation_metrics
]
)
);
$currentEngineData = $correlation_metrics['db'][$correlation_metrics['engine']];
unset($correlation_metrics['db'][$correlation_metrics['engine']]);
echo sprintf(
'<hr /><h2 class="overflow label label-success">%s</h2><div style="width:800px;">%s<div>%s</div></div>',
__('Active engine: %s', $currentEngineData['name']),
$this->element('/healthElements/correlations_table', ['currentEngineData' => $currentEngineData]),
sprintf(
'<div class="btn btn-primary" onClick="simplePopup(\'%s\');">Recorrelate</div>',
$baseurl . '/attributes/generateCorrelation'
)
);
foreach ($correlation_metrics['db'] as $engine => $engineData) {
echo sprintf(
'<hr /><h2 class="overflow label">%s</h2><div style="width:800px;">%s<div>%s %s</div></div>',
__('Dormant engine: %s', $engineData['name']),
$this->element('/healthElements/correlations_table', ['currentEngineData' => $engineData]),
$engine === 'Legacy' ? '' : sprintf(
'<div class="btn btn-primary" onClick="simplePopup(\'%s\');">Activate engine</div>',
$baseurl . '/correlations/switchEngine/' . h($engine)
),
sprintf(
'<div class="btn btn-danger" onClick="simplePopup(\'%s\');">Truncate</div>',
$baseurl . '/correlations/truncate/' . h($engine)
)
);
}
echo '</div>';
?>

View File

@ -0,0 +1,19 @@
<?php
$rows = '';
$rows .= sprintf(
'<tr><td><a href="%s">%s</a></td><td style="text-align:right;">%s</td></tr>',
$baseurl . '/correlations/overCorrelations',
__('Over correlations'),
h($correlation_metrics['over_correlations'])
);
$rows .= sprintf(
'<tr><td><a href="%s">%s</a></td><td style="text-align:right;">%s</td></tr>',
$baseurl . '/correlation_exclusions/index',
__('Excluded correlations'),
h($correlation_metrics['excluded_correlations'])
);
echo sprintf(
'<table class="meta_table table table-striped table-condensed"><tr><th>Field</th><th style="text-align:right;">Value</th></tr>%s</table>',
$rows
);

View File

@ -0,0 +1,32 @@
<?php
$engine_contents = sprintf(
'<tr><th title="%s">%s</th><th title="%s">%s</th><th title="%s">%s</th><th title="%s">%s</th></tr>',
__('Table name'),
__('Table'),
__('Number of entries in the table'),
__('# of rows'),
__('The table\'s size in MB on disk'),
__('Size on disk'),
__('The saturation of the ID space of the given table. Be careful, reaching the limit will block further correlations from being created - make sure you recorrelate in time or extend the ID space by changing the column type.'),
__('ID space saturation'),
);
foreach ($currentEngineData['tables'] as $table_name => $table_data) {
$engine_contents .= sprintf(
'<tr><td>%s</td><td>%s</td><td title="%s">%s</td><td title="%s">%s</td></tr>',
h($table_name),
h($table_data['row_count']),
h($table_data['size_on_disk']) . ' B',
h(round($table_data['size_on_disk']/1024/1024), 2) . ' MB',
sprintf(
"Last inserted correlation ID: %s\nHighest possible ID: %s",
h($table_data['last_id']),
h($table_data['id_limit']),
),
h($table_data['id_saturation']) . '%'
);
}
echo sprintf(
'<table class="meta_table table table-striped table-condensed">%s</table>',
$engine_contents
);

View File

@ -15,7 +15,7 @@
foreach ($tabs as $k => $tab) {
$data['children'][0]['children'][] = array(
'html' => sprintf(
__('%s settings%s'),
__('%s %s'),
ucfirst(h($k)),
($tab['errors'] == 0) ? '' : sprintf(
' (<span>%s%s</span>)',
@ -27,6 +27,13 @@
'active' => $k == $active_tab
);
}
$data['children'][0]['children'][] = array(
'url' => $baseurl . '/servers/serverSettings/correlations',
'html' => __('Correlations'),
'active' => $active_tab === 'correlations'
);
$data['children'][0]['children'][] = array(
'url' => $baseurl . '/servers/serverSettings/diagnostics',
'html' => sprintf(

View File

@ -13,6 +13,8 @@
echo $this->element('healthElements/workers');
} else if ($tab === 'files') {
echo $this->element('healthElements/files');
} else if ($tab === 'correlations') {
echo $this->element('healthElements/correlations');
} else {
echo $this->element('healthElements/overview');
}

@ -1 +1 @@
Subproject commit 2330c1760250dd4b92623fab1b4f3ffa1523ec49
Subproject commit bfda561f5f29a8cca573e22789fb58252ad36c93

@ -1 +1 @@
Subproject commit 50f61a03beb9aac4256a82348b5cb2f4ed6d1932
Subproject commit 9b9c8389616b516a2d5cbe41a4407b2e9d7f24d3

@ -1 +1 @@
Subproject commit f4fb812c37a201b93f8b07ddddc293ee1d5110bd
Subproject commit fc12a106f5481e466275799de71e29dd7cf764f7

@ -1 +1 @@
Subproject commit f270e406bcad4744b81cb02fc7b39cba8c0d5e99
Subproject commit fc5599114f92926838b519f322b81a061039fe0e