2012-06-28 17:24:12 +02:00
|
|
|
<?php
|
|
|
|
App::uses('AppModel', 'Model');
|
2013-01-04 16:48:46 +01:00
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
class Log extends AppModel
|
|
|
|
{
|
2021-08-11 12:37:31 +02:00
|
|
|
const WARNING_ACTIONS = array(
|
2018-07-19 11:48:22 +02:00
|
|
|
'warning',
|
|
|
|
'change_pw',
|
|
|
|
'login_fail',
|
|
|
|
'version_warning',
|
|
|
|
'auth_fail'
|
|
|
|
);
|
2021-08-11 12:37:31 +02:00
|
|
|
const ERROR_ACTIONS = array(
|
2018-07-19 11:48:22 +02:00
|
|
|
'error'
|
|
|
|
);
|
|
|
|
public $validate = array(
|
2021-09-27 12:34:51 +02:00
|
|
|
'action' => array(
|
2019-11-18 11:35:10 +01:00
|
|
|
'rule' => array(
|
|
|
|
'inList',
|
|
|
|
array( // ensure that the length of the rules is < 20 in length
|
|
|
|
'accept',
|
|
|
|
'accept_delegation',
|
2020-04-07 13:21:01 +02:00
|
|
|
'acceptRegistrations',
|
2019-11-18 11:35:10 +01:00
|
|
|
'add',
|
|
|
|
'admin_email',
|
2020-05-22 09:04:01 +02:00
|
|
|
'attachTags',
|
2019-11-18 11:35:10 +01:00
|
|
|
'auth',
|
|
|
|
'auth_fail',
|
2020-09-01 12:56:35 +02:00
|
|
|
'blocklisted',
|
2020-05-20 10:33:33 +02:00
|
|
|
'captureRelations',
|
2019-11-18 11:35:10 +01:00
|
|
|
'change_pw',
|
|
|
|
'delete',
|
|
|
|
'disable',
|
|
|
|
'discard',
|
2020-04-07 13:21:01 +02:00
|
|
|
'discardRegistrations',
|
2019-11-18 11:35:10 +01:00
|
|
|
'edit',
|
|
|
|
'email',
|
|
|
|
'enable',
|
2020-11-12 11:18:36 +01:00
|
|
|
'enrichment',
|
2019-11-18 11:35:10 +01:00
|
|
|
'error',
|
2022-03-02 02:09:20 +01:00
|
|
|
'execute_blueprint',
|
2022-06-13 14:36:44 +02:00
|
|
|
'execute_workflow',
|
2022-05-31 11:58:18 +02:00
|
|
|
'exec_module',
|
2019-11-18 11:35:10 +01:00
|
|
|
'export',
|
2020-07-21 10:33:16 +02:00
|
|
|
'fetchEvent',
|
2019-11-18 11:35:10 +01:00
|
|
|
'file_upload',
|
|
|
|
'galaxy',
|
|
|
|
'include_formula',
|
2022-05-31 11:58:18 +02:00
|
|
|
'load_module',
|
2019-11-18 11:35:10 +01:00
|
|
|
'login',
|
|
|
|
'login_fail',
|
|
|
|
'logout',
|
|
|
|
'merge',
|
|
|
|
'pruneUpdateLogs',
|
|
|
|
'publish',
|
2019-11-29 11:54:15 +01:00
|
|
|
'publish_sightings',
|
2019-11-18 11:35:10 +01:00
|
|
|
'publish alert',
|
|
|
|
'pull',
|
|
|
|
'purge_events',
|
|
|
|
'push',
|
2020-05-07 15:56:08 +02:00
|
|
|
'registration',
|
|
|
|
'registration_error',
|
2019-11-18 11:35:10 +01:00
|
|
|
'remove_dead_workers',
|
2022-11-28 13:27:40 +01:00
|
|
|
'request',
|
2019-11-18 11:35:10 +01:00
|
|
|
'request_delegation',
|
|
|
|
'reset_auth_key',
|
2020-05-07 17:11:42 +02:00
|
|
|
'send_mail',
|
2019-11-18 11:35:10 +01:00
|
|
|
'security',
|
|
|
|
'serverSettingsEdit',
|
|
|
|
'tag',
|
|
|
|
'undelete',
|
|
|
|
'update',
|
|
|
|
'update_database',
|
|
|
|
'update_db_worker',
|
2022-03-14 00:34:19 +01:00
|
|
|
'updateCryptoKeys',
|
2019-11-18 11:35:10 +01:00
|
|
|
'upgrade_24',
|
|
|
|
'upload_sample',
|
2022-03-14 00:34:19 +01:00
|
|
|
'validateSig',
|
2019-11-18 11:35:10 +01:00
|
|
|
'version_warning',
|
2021-04-21 12:53:05 +02:00
|
|
|
'warning',
|
|
|
|
'wipe_default'
|
2019-11-18 11:35:10 +01:00
|
|
|
)
|
|
|
|
),
|
2018-07-19 11:48:22 +02:00
|
|
|
'message' => 'Options : ...'
|
|
|
|
)
|
|
|
|
);
|
2012-09-24 16:02:01 +02:00
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
public $actionDefinitions = array(
|
|
|
|
'login' => array('desc' => 'Login action', 'formdesc' => "Login action"),
|
|
|
|
'logout' => array('desc' => 'Logout action', 'formdesc' => "Logout action"),
|
|
|
|
'add' => array('desc' => 'Add action', 'formdesc' => "Add action"),
|
|
|
|
'edit' => array('desc' => 'Edit action', 'formdesc' => "Edit action"),
|
|
|
|
'change_pw' => array('desc' => 'Change_pw action', 'formdesc' => "Change_pw action"),
|
|
|
|
'delete' => array('desc' => 'Delete action', 'formdesc' => "Delete action"),
|
|
|
|
'publish' => array('desc' => "Publish action", 'formdesc' => "Publish action")
|
|
|
|
);
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
public $logMeta = array(
|
|
|
|
'email' => array('values' => array('email'), 'name' => 'Emails'),
|
|
|
|
'auth_issues' => array('values' => array('login_fail', 'auth_fail'), 'name' => 'Authentication issues')
|
|
|
|
);
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
public $logMetaAdmin = array(
|
|
|
|
'update' => array('values' => array('update_database'), 'name' => 'MISP Update results'),
|
|
|
|
'settings' => array('values' => array('serverSettingsEdit', 'remove_dead_workers'), 'name' => 'Setting changes'),
|
2020-01-29 22:33:27 +01:00
|
|
|
'errors' => array('values' => array('warning', 'error', 'version_warning'), 'name' => 'Warnings and errors'),
|
2018-07-19 11:48:22 +02:00
|
|
|
'email' => array('values' => array('admin_email'))
|
|
|
|
);
|
2020-11-10 15:23:05 +01:00
|
|
|
|
2022-07-20 15:56:30 +02:00
|
|
|
public $actsAs = ['LightPaginator'];
|
|
|
|
|
2022-11-12 20:42:27 +01:00
|
|
|
private $elasticSearchClient;
|
|
|
|
|
2020-11-10 15:23:05 +01:00
|
|
|
/**
|
|
|
|
* Null when not defined, false when not enabled
|
|
|
|
* @var Syslog|null|false
|
|
|
|
*/
|
2020-11-10 14:20:19 +01:00
|
|
|
private $syslog;
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
public function beforeSave($options = array())
|
|
|
|
{
|
2019-07-31 09:52:05 +02:00
|
|
|
if (!empty(Configure::read('MISP.log_skip_db_logs_completely'))) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-08-26 01:29:34 +02:00
|
|
|
if (Configure::read('MISP.log_client_ip')) {
|
2022-11-28 14:08:11 +01:00
|
|
|
$this->data['Log']['ip'] = $this->_remoteIp();
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
2020-09-08 13:10:10 +02:00
|
|
|
$setEmpty = array('title' => '', 'model' => '', 'model_id' => 0, 'action' => '', 'user_id' => 0, 'change' => '', 'email' => '', 'org' => '', 'description' => '', 'ip' => '');
|
2018-07-19 11:48:22 +02:00
|
|
|
foreach ($setEmpty as $field => $empty) {
|
2021-09-27 12:34:51 +02:00
|
|
|
if (empty($this->data['Log'][$field])) {
|
2018-07-19 11:48:22 +02:00
|
|
|
$this->data['Log'][$field] = $empty;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!isset($this->data['Log']['created'])) {
|
|
|
|
$this->data['Log']['created'] = date('Y-m-d H:i:s');
|
|
|
|
}
|
2021-09-27 12:34:51 +02:00
|
|
|
if (empty($this->data['Log']['org'])) {
|
2018-07-19 11:48:22 +02:00
|
|
|
$this->data['Log']['org'] = 'SYSTEM';
|
|
|
|
}
|
|
|
|
$truncate_fields = array('title', 'change', 'description');
|
|
|
|
foreach ($truncate_fields as $tf) {
|
2021-09-27 12:34:51 +02:00
|
|
|
if (strlen($this->data['Log'][$tf]) >= 65535) {
|
2018-07-19 11:48:22 +02:00
|
|
|
$this->data['Log'][$tf] = substr($this->data['Log'][$tf], 0, 65532) . '...';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$this->logData($this->data);
|
2022-11-28 13:48:22 +01:00
|
|
|
if ($this->data['Log']['action'] === 'request' && !empty(Configure::read('MISP.log_paranoid_skip_db'))) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-07-19 11:48:22 +02:00
|
|
|
return true;
|
|
|
|
}
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2023-03-31 02:36:10 +02:00
|
|
|
public function afterSave($created, $options = array())
|
|
|
|
{
|
|
|
|
// run workflow if needed, but skip workflow for certain types, to prevent loops
|
|
|
|
if (!in_array($this->data['Log']['model'], ['Log', 'Workflow'])) {
|
|
|
|
$trigger_id = 'log-after-save';
|
|
|
|
$workflowErrors = [];
|
|
|
|
$logging = [
|
|
|
|
'model' => 'Log',
|
|
|
|
'action' => 'execute_workflow',
|
|
|
|
'id' => $this->data['Log']['user_id']
|
|
|
|
];
|
|
|
|
$this->executeTrigger($trigger_id, $this->data, $workflowErrors);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
public function returnDates($org = 'all')
|
|
|
|
{
|
|
|
|
$conditions = array();
|
|
|
|
$this->Organisation = ClassRegistry::init('Organisation');
|
|
|
|
if ($org !== 'all') {
|
2021-01-30 10:04:44 +01:00
|
|
|
$org = $this->Organisation->fetchOrg($org);
|
2018-07-19 11:48:22 +02:00
|
|
|
if (empty($org)) {
|
2021-01-30 10:04:44 +01:00
|
|
|
throw new MethodNotAllowedException('Invalid organisation.');
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
2021-01-30 10:04:44 +01:00
|
|
|
$conditions['org'] = $org['name'];
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
$conditions['AND']['NOT'] = array('action' => array('login', 'logout', 'changepw'));
|
2022-05-14 10:17:06 +02:00
|
|
|
if ($this->isMysql()) {
|
2018-07-19 11:48:22 +02:00
|
|
|
$validDates = $this->find('all', array(
|
|
|
|
'fields' => array('DISTINCT UNIX_TIMESTAMP(DATE(created)) AS Date', 'count(id) AS count'),
|
|
|
|
'conditions' => $conditions,
|
|
|
|
'group' => array('Date'),
|
|
|
|
'order' => array('Date')
|
|
|
|
));
|
2022-05-14 10:17:06 +02:00
|
|
|
} else {
|
2018-07-19 11:48:22 +02:00
|
|
|
// manually generate the query for Postgres
|
|
|
|
// cakephp ORM would escape "DATE" datatype in CAST expression
|
|
|
|
$condnotinaction = "'" . implode("', '", $conditions['AND']['NOT']['action']) . "'";
|
|
|
|
if (!empty($conditions['org'])) {
|
2021-08-19 14:53:10 +02:00
|
|
|
$condOrg = sprintf('AND org = %s', $this->getDataSource()->value($conditions['org']));
|
2018-07-19 11:48:22 +02:00
|
|
|
} else {
|
|
|
|
$condOrg = '';
|
|
|
|
}
|
|
|
|
$sql = 'SELECT DISTINCT EXTRACT(EPOCH FROM CAST(created AS DATE)) AS "Date",
|
2019-02-10 13:08:12 +01:00
|
|
|
COUNT(id) AS count
|
|
|
|
FROM logs
|
|
|
|
WHERE action NOT IN (' . $condnotinaction . ')
|
|
|
|
' . $condOrg . '
|
|
|
|
GROUP BY "Date" ORDER BY "Date"';
|
2018-07-19 11:48:22 +02:00
|
|
|
$validDates = $this->query($sql);
|
|
|
|
}
|
|
|
|
$data = array();
|
|
|
|
foreach ($validDates as $k => $date) {
|
|
|
|
$data[$date[0]['Date']] = intval($date[0]['count']);
|
|
|
|
}
|
|
|
|
return $data;
|
|
|
|
}
|
2016-06-04 01:08:16 +02:00
|
|
|
|
2019-09-23 07:49:42 +02:00
|
|
|
/**
|
|
|
|
* @param string|array $user
|
|
|
|
* @param string $action
|
|
|
|
* @param string $model
|
|
|
|
* @param int $modelId
|
|
|
|
* @param string $title
|
|
|
|
* @param string|array $change
|
2021-02-27 11:13:47 +01:00
|
|
|
* @return array|null
|
2019-09-23 07:49:42 +02:00
|
|
|
* @throws Exception
|
2020-08-11 10:55:01 +02:00
|
|
|
* @throws InvalidArgumentException
|
2019-09-23 07:49:42 +02:00
|
|
|
*/
|
|
|
|
public function createLogEntry($user, $action, $model, $modelId = 0, $title = '', $change = '')
|
2018-07-19 11:48:22 +02:00
|
|
|
{
|
2022-06-07 13:19:08 +02:00
|
|
|
if (in_array($action, ['tag', 'galaxy', 'publish', 'publish_sightings', 'enable', 'edit'], true) && Configure::read('MISP.log_new_audit')) {
|
2021-01-22 13:01:23 +01:00
|
|
|
return; // Do not store tag changes when new audit is enabled
|
|
|
|
}
|
2019-09-23 07:49:42 +02:00
|
|
|
if ($user === 'SYSTEM') {
|
2022-02-22 17:27:13 +01:00
|
|
|
$user = ['Organisation' => ['name' => 'SYSTEM'], 'email' => 'SYSTEM', 'id' => 0];
|
2019-09-23 07:49:42 +02:00
|
|
|
} else if (!is_array($user)) {
|
|
|
|
throw new InvalidArgumentException("User must be array or 'SYSTEM' string.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_array($change)) {
|
2022-02-22 17:27:13 +01:00
|
|
|
$output = [];
|
2019-09-23 07:49:42 +02:00
|
|
|
foreach ($change as $field => $values) {
|
2020-05-04 15:13:17 +02:00
|
|
|
$isSecret = strpos($field, 'password') !== false || ($field === 'authkey' && Configure::read('Security.do_not_log_authkeys'));
|
|
|
|
if ($isSecret) {
|
2019-09-23 07:49:42 +02:00
|
|
|
$oldValue = $newValue = "*****";
|
|
|
|
} else {
|
|
|
|
list($oldValue, $newValue) = $values;
|
|
|
|
}
|
|
|
|
$output[] = "$field ($oldValue) => ($newValue)";
|
|
|
|
}
|
|
|
|
$change = implode(", ", $output);
|
|
|
|
}
|
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
$this->create();
|
2022-02-22 17:27:13 +01:00
|
|
|
$result = $this->save(['Log' => [
|
2019-09-23 07:49:42 +02:00
|
|
|
'org' => $user['Organisation']['name'],
|
|
|
|
'email' => $user['email'],
|
|
|
|
'user_id' => $user['id'],
|
|
|
|
'action' => $action,
|
|
|
|
'title' => $title,
|
|
|
|
'change' => $change,
|
|
|
|
'model' => $model,
|
|
|
|
'model_id' => $modelId,
|
2022-02-22 17:27:13 +01:00
|
|
|
]]);
|
2019-09-23 07:49:42 +02:00
|
|
|
|
|
|
|
if (!$result) {
|
2022-11-28 13:48:22 +01:00
|
|
|
if ($action === 'request' && !empty(Configure::read('MISP.log_paranoid_skip_db'))) {
|
|
|
|
return null;
|
|
|
|
}
|
2021-10-23 19:19:52 +02:00
|
|
|
if (!empty(Configure::read('MISP.log_skip_db_logs_completely'))) {
|
|
|
|
return null;
|
|
|
|
}
|
2021-02-27 11:13:47 +01:00
|
|
|
|
2019-09-23 07:49:42 +02:00
|
|
|
throw new Exception("Cannot save log because of validation errors: " . json_encode($this->validationErrors));
|
|
|
|
}
|
2019-09-23 19:07:24 +02:00
|
|
|
|
|
|
|
return $result;
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
2017-01-30 09:16:43 +01:00
|
|
|
|
2021-10-24 20:42:55 +02:00
|
|
|
/**
|
|
|
|
* @param array|string $user
|
|
|
|
* @param string $action
|
|
|
|
* @param string $model
|
|
|
|
* @param string $title
|
|
|
|
* @param array $validationErrors
|
|
|
|
* @param array $fullObject
|
|
|
|
* @throws Exception
|
|
|
|
*/
|
|
|
|
public function validationError($user, $action, $model, $title, array $validationErrors, array $fullObject)
|
|
|
|
{
|
|
|
|
$this->log($title, LOG_WARNING);
|
|
|
|
$change = 'Validation errors: ' . json_encode($validationErrors) . ' Full ' . $model . ': ' . json_encode($fullObject);
|
|
|
|
$this->createLogEntry($user, $action, $model, 0, $title, $change);
|
|
|
|
}
|
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
// to combat a certain bug that causes the upgrade scripts to loop without being able to set the correct version
|
|
|
|
// this function remedies a fixed upgrade bug instance by eliminating the massive number of erroneous upgrade log entries
|
|
|
|
public function pruneUpdateLogs($jobId = false, $user)
|
|
|
|
{
|
|
|
|
$max = $this->find('first', array('fields' => array('MAX(id) AS lastid')));
|
|
|
|
if (!empty($max)) {
|
|
|
|
$max = $max[0]['lastid'];
|
|
|
|
}
|
|
|
|
if ($jobId) {
|
|
|
|
$this->Job = ClassRegistry::init('Job');
|
|
|
|
$this->Job->id = $jobId;
|
|
|
|
if (!$this->Job->exists()) {
|
|
|
|
$jobId = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$iterations = ($max / 1000);
|
|
|
|
for ($i = 0; $i < $iterations; $i++) {
|
|
|
|
$this->deleteAll(array(
|
|
|
|
'OR' => array(
|
|
|
|
'action' => 'update_database',
|
|
|
|
'AND' => array(
|
|
|
|
'action' => 'edit',
|
|
|
|
'model' => 'AdminSetting'
|
|
|
|
)
|
|
|
|
),
|
|
|
|
'id >' => $i * 1000,
|
|
|
|
'id <' => ($i+1) * 1000));
|
|
|
|
if ($jobId) {
|
|
|
|
$this->Job->saveField('progress', $i * 100 / $iterations);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$this->create();
|
|
|
|
$this->save(array(
|
|
|
|
'org' => $user['Organisation']['name'],
|
|
|
|
'email' =>$user['email'],
|
|
|
|
'user_id' => $user['id'],
|
|
|
|
'action' => 'pruneUpdateLogs',
|
|
|
|
'title' => 'Pruning updates',
|
|
|
|
'change' => 'Pruning completed in ' . $i . ' iteration(s).',
|
|
|
|
'model' => 'Log',
|
|
|
|
'model_id' => 0
|
|
|
|
));
|
|
|
|
}
|
2017-01-30 09:16:43 +01:00
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
public function pruneUpdateLogsRouter($user)
|
|
|
|
{
|
|
|
|
if (Configure::read('MISP.background_jobs')) {
|
2021-11-03 10:57:41 +01:00
|
|
|
|
|
|
|
/** @var Job $job */
|
2018-07-19 11:48:22 +02:00
|
|
|
$job = ClassRegistry::init('Job');
|
2021-11-03 10:57:41 +01:00
|
|
|
$jobId = $job->createJob(
|
|
|
|
$user,
|
|
|
|
Job::WORKER_DEFAULT,
|
|
|
|
'prune_update_logs',
|
|
|
|
'All update entries',
|
|
|
|
'Purging the heretic.'
|
2018-07-19 11:48:22 +02:00
|
|
|
);
|
2021-11-03 10:57:41 +01:00
|
|
|
|
|
|
|
return $this->getBackgroundJobsTool()->enqueue(
|
|
|
|
BackgroundJobsTool::DEFAULT_QUEUE,
|
|
|
|
BackgroundJobsTool::CMD_ADMIN,
|
|
|
|
[
|
|
|
|
'prune_update_logs',
|
|
|
|
$jobId,
|
|
|
|
$user['id']
|
|
|
|
],
|
|
|
|
true,
|
|
|
|
$jobId
|
2018-07-19 11:48:22 +02:00
|
|
|
);
|
|
|
|
} else {
|
|
|
|
$result = $this->pruneUpdateLogs(false, $user);
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
}
|
2017-11-24 12:00:50 +01:00
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
public function logData($data)
|
|
|
|
{
|
2022-11-11 17:09:09 +01:00
|
|
|
if ($this->pubToZmq('audit')) {
|
|
|
|
$this->getPubSubTool()->publish($data, 'audit', 'log');
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
2018-07-10 18:01:57 +02:00
|
|
|
|
2019-03-05 12:24:56 +01:00
|
|
|
$this->publishKafkaNotification('audit', $data, 'log');
|
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
if (Configure::read('Plugin.ElasticSearch_logging_enable')) {
|
|
|
|
// send off our logs to distributed /dev/null
|
|
|
|
$logIndex = Configure::read("Plugin.ElasticSearch_log_index");
|
|
|
|
$elasticSearchClient = $this->getElasticSearchTool();
|
|
|
|
$elasticSearchClient->pushDocument($logIndex, "log", $data);
|
|
|
|
}
|
2020-11-10 15:23:05 +01:00
|
|
|
|
2022-11-28 13:48:22 +01:00
|
|
|
// Do not save request action logs to syslog, because they contain no information
|
|
|
|
if ($data['Log']['action'] === 'request') {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-11-10 15:23:05 +01:00
|
|
|
// write to syslogd as well if enabled
|
|
|
|
if ($this->syslog === null) {
|
|
|
|
if (Configure::read('Security.syslog')) {
|
|
|
|
$options = [];
|
2020-11-10 14:20:19 +01:00
|
|
|
$syslogToStdErr = Configure::read('Security.syslog_to_stderr');
|
2020-11-10 15:23:05 +01:00
|
|
|
if ($syslogToStdErr !== null) {
|
|
|
|
$options['to_stderr'] = $syslogToStdErr;
|
|
|
|
}
|
|
|
|
$syslogIdent = Configure::read('Security.syslog_ident');
|
|
|
|
if ($syslogIdent) {
|
|
|
|
$options['ident'] = $syslogIdent;
|
|
|
|
}
|
|
|
|
$this->syslog = new SysLog($options);
|
|
|
|
} else {
|
|
|
|
$this->syslog = false;
|
2020-11-10 14:20:19 +01:00
|
|
|
}
|
2020-11-10 15:23:05 +01:00
|
|
|
}
|
|
|
|
if ($this->syslog) {
|
2022-03-27 13:05:33 +02:00
|
|
|
$action = LOG_INFO;
|
2018-07-19 11:48:22 +02:00
|
|
|
if (isset($data['Log']['action'])) {
|
2021-08-11 12:37:31 +02:00
|
|
|
if (in_array($data['Log']['action'], self::ERROR_ACTIONS, true)) {
|
2022-03-27 13:05:33 +02:00
|
|
|
$action = LOG_ERR;
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
2021-08-11 12:37:31 +02:00
|
|
|
if (in_array($data['Log']['action'], self::WARNING_ACTIONS, true)) {
|
2022-03-27 13:05:33 +02:00
|
|
|
$action = LOG_WARNING;
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
}
|
2017-11-24 12:00:50 +01:00
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
$entry = $data['Log']['action'];
|
2020-04-01 23:53:08 +02:00
|
|
|
if (!empty($data['Log']['title'])) {
|
2020-11-10 14:20:19 +01:00
|
|
|
$entry .= " -- {$data['Log']['title']}";
|
2020-04-01 23:53:08 +02:00
|
|
|
}
|
2018-07-19 11:48:22 +02:00
|
|
|
if (!empty($data['Log']['description'])) {
|
2020-11-10 14:20:19 +01:00
|
|
|
$entry .= " -- {$data['Log']['description']}";
|
2022-08-24 09:06:11 +02:00
|
|
|
} else if (!empty($data['Log']['change'])) {
|
|
|
|
$entry .= " -- " . json_encode($data['Log']['change']);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
2020-11-10 14:20:19 +01:00
|
|
|
$this->syslog->write($action, $entry);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2019-06-11 11:05:34 +02:00
|
|
|
|
|
|
|
public function filterSiteAdminSensitiveLogs($list)
|
|
|
|
{
|
|
|
|
$this->User = ClassRegistry::init('User');
|
|
|
|
$site_admin_roles = $this->User->Role->find('list', array(
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => array('Role.perm_site_admin' => 1),
|
|
|
|
'fields' => array('Role.id', 'Role.id')
|
|
|
|
));
|
|
|
|
$site_admins = $this->User->find('list', array(
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => array(
|
|
|
|
'User.role_id' => array_values($site_admin_roles)
|
|
|
|
),
|
|
|
|
'fields' => array('User.id', 'User.id')
|
|
|
|
));
|
|
|
|
foreach ($list as $k => $v) {
|
|
|
|
if (
|
|
|
|
$v['Log']['model'] === 'User' &&
|
|
|
|
in_array($v['Log']['model_id'], array_values($site_admins)) &&
|
|
|
|
in_array($v['Log']['action'], array('add', 'edit', 'reset_auth_key'))
|
|
|
|
) {
|
|
|
|
$list[$k]['Log']['change'] = __('Redacted');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $list;
|
|
|
|
}
|
2020-09-15 23:54:59 +02:00
|
|
|
|
|
|
|
public function changeParser($change)
|
|
|
|
{
|
|
|
|
$change = explode(',', $change);
|
|
|
|
$data = [];
|
|
|
|
foreach ($change as $entry) {
|
|
|
|
$entry = trim($entry);
|
|
|
|
$fieldName = explode(' ', $entry)[0];
|
|
|
|
$entry = substr($entry, strlen($fieldName));
|
|
|
|
$entry = trim($entry);
|
|
|
|
if (strpos($entry, ') => (')) {
|
|
|
|
list($before, $after) = explode(') => (', $entry);
|
|
|
|
$before = substr($before, 1);
|
|
|
|
$after = substr($after, 0, -1);
|
|
|
|
$data[$fieldName] = $after;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $data;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function findDeletedEvents($conditions)
|
|
|
|
{
|
|
|
|
$conditions['model'] = 'Event';
|
|
|
|
$conditions['action'] = 'delete';
|
|
|
|
$this->Event = ClassRegistry::init('Event');
|
|
|
|
$deletions = $this->find('all', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => $conditions,
|
|
|
|
'order' => ['Log.id']
|
|
|
|
]);
|
|
|
|
$deleted_events = [];
|
|
|
|
$users = [];
|
|
|
|
$orgs = [];
|
|
|
|
$deleted_event_ids = [];
|
|
|
|
foreach ($deletions as $deletion_entry) {
|
|
|
|
if (!empty($deleted_event_ids[$deletion_entry['Log']['model_id']])) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
$deleted_event_ids[$deletion_entry['Log']['model_id']] = true;
|
|
|
|
}
|
|
|
|
$event = $this->Event->find('first', [
|
|
|
|
'conditions' => ['Event.id' => $deletion_entry['Log']['model_id']],
|
|
|
|
'recursive' => -1,
|
|
|
|
'fields' => ['Event.id']
|
|
|
|
]);
|
|
|
|
if (!empty($event)) {
|
|
|
|
// event is already restored / not deleted
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$temp = [
|
|
|
|
'event_id' => $deletion_entry['Log']['model_id'],
|
|
|
|
'user_id' => $deletion_entry['Log']['user_id'],
|
|
|
|
'created' => $deletion_entry['Log']['created']
|
|
|
|
];
|
|
|
|
$event_creation_entry = $this->find('first', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => [
|
|
|
|
'model_id' => $temp['event_id'],
|
|
|
|
'model' => 'Event',
|
|
|
|
'action' => 'add'
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
$event = $this->changeParser($event_creation_entry['Log']['change']);
|
|
|
|
$temp['event_info'] = $event['info'];
|
|
|
|
$temp['event_org_id'] = $event['org_id'];
|
|
|
|
$temp['event_orgc_id'] = $event['orgc_id'];
|
|
|
|
$temp['event_user_id'] = $event['user_id'];
|
|
|
|
$temp['event_info'] = $event['info'];
|
|
|
|
$temp['event_created'] = $event_creation_entry['Log']['created'];
|
|
|
|
foreach (['org', 'orgc'] as $scope) {
|
|
|
|
if (empty($orgs[$temp['event_' . $scope . '_id']])) {
|
|
|
|
$orgs[$temp['event_' . $scope . '_id']] = array_values($this->Event->Orgc->find('list', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => array('id' => $temp['event_' . $scope . '_id']),
|
|
|
|
'fields' => array('id', 'name')
|
|
|
|
]))[0];
|
|
|
|
}
|
|
|
|
$temp['event_' . $scope . '_name'] = $orgs[$temp['event_' . $scope . '_id']];
|
|
|
|
}
|
|
|
|
$users[$temp['user_id']] = array_values($this->Event->User->find('list', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => array('id' => $temp['user_id']),
|
|
|
|
'fields' => array('id', 'email')
|
|
|
|
]))[0];
|
|
|
|
$temp['user_name'] = $users[$temp['user_id']];
|
|
|
|
$users[$temp['event_user_id']] = array_values($this->Event->User->find('list', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => array('id' => $temp['event_user_id']),
|
|
|
|
'fields' => array('id', 'email')
|
|
|
|
]))[0];
|
|
|
|
$temp['event_user_name'] = $users[$temp['event_user_id']];
|
|
|
|
$deleted_events[] = $temp;
|
|
|
|
}
|
|
|
|
return $deleted_events;
|
|
|
|
}
|
|
|
|
|
2020-09-18 13:42:52 +02:00
|
|
|
public function recoverDeletedEvent($id, $mock = false)
|
2020-09-15 23:54:59 +02:00
|
|
|
{
|
2020-09-18 13:42:52 +02:00
|
|
|
if ($mock) {
|
|
|
|
$this->mockRecovery = true;
|
|
|
|
$this->mockLog = [];
|
|
|
|
}
|
2020-09-15 23:54:59 +02:00
|
|
|
$objectMap = [];
|
|
|
|
$logEntries = [];
|
|
|
|
$this->__recoverDeletedEventContainer($id, $objectMap, $logEntries);
|
|
|
|
$this->__recoverDeletedObjects($id, $objectMap, $logEntries);
|
|
|
|
$this->__recoverDeletedAttributes($id, $objectMap, $logEntries);
|
|
|
|
$this->__recoverDeletedObjectReferences($id, $objectMap, $logEntries);
|
|
|
|
$this->__recoverDeletedTagConnectors($id, $objectMap, $logEntries, 'Event');
|
|
|
|
$this->__recoverDeletedTagConnectors($id, $objectMap, $logEntries, 'Attribute');
|
2020-09-18 13:42:52 +02:00
|
|
|
$this->__recoverDeletedProposals($id, $objectMap, $logEntries);
|
2020-09-15 23:54:59 +02:00
|
|
|
ksort($logEntries);
|
|
|
|
foreach ($logEntries as $logEntry) {
|
|
|
|
$this->{'__executeRecovery' . $logEntry['model']}($logEntry, $id);
|
|
|
|
}
|
|
|
|
return count($logEntries);
|
|
|
|
// order: event -> object -> attribute -> object reference -> tag -> galaxy -> shadow_attribute -> sighting
|
|
|
|
}
|
|
|
|
|
|
|
|
private function __recoverDeletedEventContainer($id, &$objectMap, &$logEntries)
|
|
|
|
{
|
|
|
|
$logs = $this->find('all', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => [
|
|
|
|
'model' => 'Event',
|
|
|
|
'model_id' => $id,
|
|
|
|
'action' => ['add', 'edit', 'publish', 'alert']
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
if (empty($logs)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
foreach ($logs as $log) {
|
|
|
|
$logEntries[$log['Log']['id']] = [
|
|
|
|
'model_id' => $log['Log']['model_id'],
|
|
|
|
'model' => $log['Log']['model'],
|
|
|
|
'action' => $log['Log']['action'],
|
|
|
|
'data' => array_merge(
|
|
|
|
$this->changeParser($log['Log']['change']),
|
|
|
|
[
|
|
|
|
'timestamp' => strtotime($log['Log']['created']),
|
|
|
|
'id' => $log['Log']['model_id']
|
|
|
|
]
|
|
|
|
)
|
|
|
|
];
|
|
|
|
$objectMap['Event'][$log['Log']['model_id']] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function __recoverDeletedObjects($id, &$objectMap, &$logEntries)
|
|
|
|
{
|
|
|
|
$logs = $this->find('all', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => [
|
|
|
|
'model' => 'MispObject',
|
|
|
|
'change LIKE ' => '%event_id () => (' . $id . ')%',
|
|
|
|
'action' => ['add']
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
if (empty($logs)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
foreach ($logs as $log) {
|
|
|
|
$objectMap['MispObject'][$log['Log']['model_id']] = true;
|
|
|
|
}
|
|
|
|
$logs = $this->find('all', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => [
|
|
|
|
'model' => 'MispObject',
|
|
|
|
'model_id' => array_keys($objectMap['MispObject']),
|
|
|
|
'action' => ['add', 'edit', 'delete']
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
foreach ($logs as $log) {
|
|
|
|
$logEntries[$log['Log']['id']] = [
|
|
|
|
'model_id' => $log['Log']['model_id'],
|
|
|
|
'model' => $log['Log']['model'],
|
|
|
|
'action' => $log['Log']['action'],
|
|
|
|
'data' => array_merge(
|
|
|
|
$this->changeParser($log['Log']['change']),
|
|
|
|
[
|
|
|
|
'timestamp' => strtotime($log['Log']['created']),
|
|
|
|
'id' => $log['Log']['model_id']
|
|
|
|
]
|
|
|
|
)
|
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function __recoverDeletedObjectReferences($id, &$objectMap, &$logEntries)
|
|
|
|
{
|
|
|
|
$logs = $this->find('all', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => [
|
|
|
|
'model' => 'ObjectReference',
|
|
|
|
'change LIKE ' => '%event_id () => (' . $id . ')%',
|
|
|
|
'action' => ['add']
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
if (empty($logs)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
foreach ($logs as $log) {
|
|
|
|
$objectMap['ObjectReference'][$log['Log']['model_id']] = true;
|
|
|
|
}
|
|
|
|
$logs = $this->find('all', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => [
|
|
|
|
'model' => 'ObjectReference',
|
|
|
|
'model_id' => array_keys($objectMap['ObjectReference']),
|
|
|
|
'action' => ['add', 'edit']
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
foreach ($logs as $log) {
|
|
|
|
$logEntries[$log['Log']['id']] = [
|
|
|
|
'model_id' => $log['Log']['model_id'],
|
|
|
|
'model' => $log['Log']['model'],
|
|
|
|
'action' => $log['Log']['action'],
|
|
|
|
'data' => array_merge(
|
|
|
|
$this->changeParser($log['Log']['change']),
|
|
|
|
[
|
|
|
|
'timestamp' => strtotime($log['Log']['created']),
|
|
|
|
'id' => $log['Log']['model_id']
|
|
|
|
]
|
|
|
|
)
|
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function __recoverDeletedTagConnectors($id, &$objectMap, &$logEntries, $scope)
|
|
|
|
{
|
|
|
|
if (empty($objectMap[$scope])) {
|
|
|
|
// example: we have no attributes, so we return
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
$logs = $this->find('all', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => [
|
|
|
|
'model' => $scope,
|
|
|
|
'action' => ['tag', 'galaxy'],
|
|
|
|
'model_id' => array_keys($objectMap[$scope])
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
if (empty($logs)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
foreach ($logs as $log) {
|
|
|
|
if ($log['Log']['action'] === 'tag') {
|
|
|
|
$temp = explode(' ', $log['Log']['title']);
|
|
|
|
$local = ($temp[1] === 'local' ? true : false);
|
|
|
|
$tag_id = ($local ? $temp[3] : $temp[2]);
|
|
|
|
$tag_id = substr($tag_id, 1, -1);
|
|
|
|
} else {
|
|
|
|
$matches = [];
|
|
|
|
preg_match('/\(([0-9]*)\)\s(from|to)/', $log['Log']['title'], $matches);
|
|
|
|
if (!isset($matches[1])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$local = false;
|
|
|
|
$tag_id = $matches[1];
|
|
|
|
}
|
|
|
|
$logEntries[$log['Log']['id']] = [
|
|
|
|
'model_id' => $log['Log']['model_id'],
|
|
|
|
'model' => $log['Log']['model'],
|
|
|
|
'action' => (strpos($log['Log']['title'], 'Attached')) === false ? 'remove_tag' : 'add_tag',
|
|
|
|
'data' => [
|
|
|
|
'tag_id' => $tag_id,
|
|
|
|
'id' => $log['Log']['model_id'],
|
|
|
|
'target_type' => $log['Log']['model'],
|
|
|
|
'tag_type' => $log['Log']['action'],
|
|
|
|
'local' => $local
|
|
|
|
]
|
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function __recoverDeletedAttributes($id, &$objectMap, &$logEntries)
|
|
|
|
{
|
|
|
|
$logs = $this->find('all', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => [
|
|
|
|
'model' => 'Attribute',
|
|
|
|
'title LIKE ' => '%from Event (' . $id . ')%',
|
|
|
|
'action' => ['add']
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
if (empty($logs)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
foreach ($logs as $log) {
|
|
|
|
$objectMap['Attribute'][$log['Log']['model_id']] = true;
|
|
|
|
}
|
|
|
|
$logs = $this->find('all', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => [
|
|
|
|
'model' => 'Attribute',
|
|
|
|
'model_id' => array_keys($objectMap['Attribute']),
|
|
|
|
'action' => ['add', 'edit', 'delete']
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
foreach ($logs as $log) {
|
|
|
|
$logEntries[$log['Log']['id']] = [
|
|
|
|
'model_id' => $log['Log']['model_id'],
|
|
|
|
'model' => $log['Log']['model'],
|
|
|
|
'action' => $log['Log']['action'],
|
|
|
|
'data' => array_merge(
|
|
|
|
$this->changeParser($log['Log']['change']),
|
|
|
|
[
|
|
|
|
'timestamp' => strtotime($log['Log']['created']),
|
|
|
|
'id' => $log['Log']['model_id']
|
|
|
|
]
|
|
|
|
)
|
|
|
|
];
|
|
|
|
$objectMap['Attribute'][$log['Log']['model_id']] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-18 13:42:52 +02:00
|
|
|
private function __recoverDeletedProposals($id, &$objectMap, &$logEntries)
|
|
|
|
{
|
|
|
|
$logs = $this->find('all', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => [
|
|
|
|
'model' => 'ShadowAttribute',
|
|
|
|
'title LIKE ' => '%: to Event (' . $id . '): %',
|
|
|
|
'action' => ['add']
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
if (empty($logs)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
foreach ($logs as $log) {
|
|
|
|
$objectMap['ShadowAttribute'][$log['Log']['model_id']] = true;
|
|
|
|
}
|
|
|
|
$logs = $this->find('all', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => [
|
|
|
|
'model' => 'ShadowAttribute',
|
|
|
|
'model_id' => array_keys($objectMap['ShadowAttribute']),
|
|
|
|
'action' => ['add', 'accept', 'delete']
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
foreach ($logs as $log) {
|
|
|
|
$logEntries[$log['Log']['id']] = [
|
|
|
|
'model_id' => $log['Log']['model_id'],
|
|
|
|
'model' => $log['Log']['model'],
|
|
|
|
'action' => $log['Log']['action'],
|
|
|
|
'data' => array_merge(
|
|
|
|
$this->changeParser($log['Log']['change']),
|
|
|
|
[
|
|
|
|
'timestamp' => strtotime($log['Log']['created']),
|
|
|
|
'id' => $log['Log']['model_id']
|
|
|
|
]
|
|
|
|
)
|
|
|
|
];
|
|
|
|
$objectMap['ShadowAttribute'][$log['Log']['model_id']] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-15 23:54:59 +02:00
|
|
|
|
|
|
|
private function __executeRecoveryEvent($logEntry, $id)
|
|
|
|
{
|
|
|
|
if (empty($this->Event)) {
|
|
|
|
$this->Event = ClassRegistry::init('Event');
|
|
|
|
}
|
|
|
|
if (empty($this->GalaxyCluster)) {
|
|
|
|
$this->GalaxyCluster = ClassRegistry::init('GalaxyCluster');
|
|
|
|
}
|
2020-09-18 15:50:26 +02:00
|
|
|
if (empty($this->EventBlocklist)) {
|
|
|
|
$this->EventBlocklist = ClassRegistry::init('EventBlocklist');
|
|
|
|
}
|
2020-09-15 23:54:59 +02:00
|
|
|
switch($logEntry['action']) {
|
|
|
|
case 'add':
|
2020-09-18 13:42:52 +02:00
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'Event', 'action' => 'add', 'data' => $logEntry['data']];
|
|
|
|
} else {
|
|
|
|
$this->Event->create();
|
|
|
|
$this->Event->save($logEntry['data']);
|
2020-09-18 15:50:26 +02:00
|
|
|
$blockListEntry = $this->EventBlocklist->find('first', array(
|
|
|
|
'conditions' => array('event_uuid' => $logEntry['data']['uuid']),
|
|
|
|
'fields' => 'id'
|
|
|
|
));
|
|
|
|
if (!empty($blockListEntry)) {
|
|
|
|
$this->EventBlocklist->delete($blockListEntry['EventBlocklist']['id']);
|
|
|
|
}
|
2020-09-18 13:42:52 +02:00
|
|
|
}
|
2020-09-15 23:54:59 +02:00
|
|
|
break;
|
|
|
|
case 'edit':
|
|
|
|
case 'publish':
|
|
|
|
$event = $this->Event->find('first', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => ['Event.id' => $logEntry['model_id']]
|
|
|
|
]);
|
|
|
|
if (!empty($event)) {
|
|
|
|
if ($logEntry['action'] === 'publish' || $logEntry['action'] === 'alert') {
|
|
|
|
$event['Event']['published'] = 1;
|
|
|
|
$event['Event']['publish_timestamp'] = strtotime($logEntry['data']['timestamp']);
|
|
|
|
} else {
|
|
|
|
foreach ($logEntry['data'] as $field => $value) {
|
|
|
|
$event['Event'][$field] = $value;
|
|
|
|
}
|
|
|
|
}
|
2020-09-18 13:42:52 +02:00
|
|
|
$this->Event->create();
|
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'Event', 'action' => 'edit', 'data' => $event];
|
|
|
|
} else {
|
|
|
|
$this->Event->save($event);
|
|
|
|
}
|
2020-09-15 23:54:59 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'add_tag':
|
|
|
|
$tag_id = $logEntry['data']['tag_type'] === 'galaxy' ? $this->GalaxyCluster->getTagIdByClusterId($logEntry['data']['tag_id']) : $logEntry['data']['tag_id'];
|
|
|
|
$this->Event->EventTag->create();
|
2020-09-18 13:42:52 +02:00
|
|
|
$this->Event->create();
|
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'EventTag', 'action' => 'add', 'data' => [
|
|
|
|
'tag_id' => $tag_id,
|
|
|
|
'event_id' => $logEntry['data']['id'],
|
|
|
|
'local' => !empty($logEntry['data']['local'])
|
|
|
|
]];
|
|
|
|
} else {
|
|
|
|
$this->Event->EventTag->save([
|
|
|
|
'tag_id' => $tag_id,
|
|
|
|
'event_id' => $logEntry['data']['id'],
|
|
|
|
'local' => !empty($logEntry['data']['local'])
|
|
|
|
]);
|
|
|
|
}
|
2020-09-15 23:54:59 +02:00
|
|
|
break;
|
|
|
|
case 'remove_tag':
|
|
|
|
$tag_id = $logEntry['data']['tag_type'] === 'galaxy' ? $this->GalaxyCluster->getTagIdByClusterId($logEntry['data']['tag_id']) : $logEntry['data']['tag_id'];
|
|
|
|
$et = $this->Event->EventTag->find('first', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => [
|
|
|
|
'tag_id' => $tag_id,
|
|
|
|
'event_id' => $logEntry['data']['id']
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
if (!empty($et)) {
|
2020-09-18 13:42:52 +02:00
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'EventTag', 'action' => 'delete', 'data' => $et['EventTag']['id']];
|
|
|
|
} else {
|
|
|
|
$this->Event->EventTag->delete($et['EventTag']['id']);
|
|
|
|
}
|
2020-09-15 23:54:59 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function __executeRecoveryAttribute($logEntry, $id)
|
|
|
|
{
|
|
|
|
if (empty($this->Attribute)) {
|
|
|
|
$this->Attribute = ClassRegistry::init('Attribute');
|
|
|
|
}
|
|
|
|
if (empty($this->GalaxyCluster)) {
|
|
|
|
$this->GalaxyCluster = ClassRegistry::init('GalaxyCluster');
|
|
|
|
}
|
|
|
|
if (!empty($logEntry['data']['value1'])) {
|
|
|
|
$logEntry['data']['value'] = $logEntry['data']['value1'];
|
|
|
|
if (!empty($logEntry['data']['value2'])) {
|
|
|
|
$logEntry .= '|' . $logEntry['data']['value2'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch($logEntry['action']) {
|
|
|
|
case 'add':
|
2020-09-18 15:32:50 +02:00
|
|
|
$logEntry['data'] = $this->Attribute->UTCToISODatetime(['Attribute' => $logEntry['data']], 'Attribute');
|
2020-09-19 18:35:15 +02:00
|
|
|
$logEntry['data'] = $logEntry['data']['Attribute'];
|
2020-09-18 13:42:52 +02:00
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'Attribute', 'action' => 'add', 'data' => $logEntry['data']];
|
|
|
|
} else {
|
|
|
|
$this->Attribute->create();
|
2020-09-19 18:35:15 +02:00
|
|
|
if (!isset($logEntry['data']['to_ids'])) {
|
|
|
|
$logEntry['data']['to_ids'] = 0;
|
|
|
|
}
|
2020-09-18 13:42:52 +02:00
|
|
|
$this->Attribute->save($logEntry['data']);
|
|
|
|
}
|
2020-09-15 23:54:59 +02:00
|
|
|
break;
|
|
|
|
case 'edit':
|
|
|
|
$attribute = $this->Attribute->find('first', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => ['Attribute.id' => $logEntry['model_id']]
|
|
|
|
]);
|
|
|
|
if (!empty($attribute)) {
|
2020-09-18 15:32:50 +02:00
|
|
|
$logEntry['data'] = $this->Attribute->UTCToISODatetime(['Attribute' => $logEntry['data']], 'Attribute');
|
2020-09-19 18:35:15 +02:00
|
|
|
$logEntry['data'] = $logEntry['data']['Attribute'];
|
2020-09-15 23:54:59 +02:00
|
|
|
foreach ($logEntry['data'] as $field => $value) {
|
|
|
|
$attribute['Attribute'][$field] = $value;
|
|
|
|
}
|
2020-09-18 13:42:52 +02:00
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'Attribute', 'action' => 'edit', 'data' => $attribute];
|
|
|
|
} else {
|
|
|
|
$this->Attribute->save($attribute);
|
|
|
|
}
|
2020-09-15 23:54:59 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'delete':
|
|
|
|
$attribute = $this->Attribute->find('first', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => ['Attribute.id' => $logEntry['model_id']]
|
|
|
|
]);
|
|
|
|
if (!empty($attribute)) {
|
|
|
|
$attribute['Attribute']['deleted'] = 1;
|
|
|
|
$attribute['Attribute']['timestamp'] = $logEntry['data']['timestamp'];
|
2020-09-18 13:42:52 +02:00
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'Attribute', 'action' => 'delete', 'data' => $attribute];
|
|
|
|
} else {
|
|
|
|
$this->Attribute->save($attribute);
|
|
|
|
}
|
2020-09-15 23:54:59 +02:00
|
|
|
}
|
2020-09-18 13:42:52 +02:00
|
|
|
break;
|
2020-09-15 23:54:59 +02:00
|
|
|
case 'add_tag':
|
|
|
|
$tag_id = $logEntry['data']['tag_type'] === 'galaxy' ? $this->GalaxyCluster->getTagIdByClusterId($logEntry['data']['tag_id']) : $logEntry['data']['tag_id'];
|
2020-09-18 13:42:52 +02:00
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'AttributeTag', 'action' => 'add', 'data' => [
|
|
|
|
'tag_id' => $tag_id,
|
|
|
|
'attribute_id' => $logEntry['data']['id'],
|
|
|
|
'event_id' => $id,
|
|
|
|
'local' => !empty($logEntry['data']['local'])
|
|
|
|
]];
|
|
|
|
} else {
|
|
|
|
$this->Attribute->AttributeTag->create();
|
|
|
|
$this->Attribute->AttributeTag->save([
|
|
|
|
'tag_id' => $tag_id,
|
|
|
|
'attribute_id' => $logEntry['data']['id'],
|
|
|
|
'event_id' => $id,
|
|
|
|
'local' => !empty($logEntry['data']['local'])
|
|
|
|
]);
|
|
|
|
}
|
2020-09-15 23:54:59 +02:00
|
|
|
break;
|
|
|
|
case 'remove_tag':
|
|
|
|
$tag_id = $logEntry['data']['tag_type'] === 'galaxy' ? $this->GalaxyCluster->getTagIdByClusterId($logEntry['data']['tag_id']) : $logEntry['data']['tag_id'];
|
|
|
|
$at = $this->Attribute->AttributeTag->find('first', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => [
|
|
|
|
'tag_id' => $tag_id,
|
|
|
|
'attribute_id' => $logEntry['data']['id'],
|
|
|
|
'event_id' => $id
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
if (!empty($at)) {
|
2020-09-18 13:42:52 +02:00
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'AttributeTag', 'action' => 'delete', 'data' => $at['AttributeTag']['id']];
|
|
|
|
} else {
|
|
|
|
$this->Attribute->AttributeTag->delete($at['AttributeTag']['id']);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function __executeRecoveryShadowAttribute($logEntry, $id)
|
|
|
|
{
|
|
|
|
if (empty($this->Attribute)) {
|
|
|
|
$this->Attribute = ClassRegistry::init('Attribute');
|
|
|
|
}
|
|
|
|
if (empty($this->ShadowAttribute)) {
|
|
|
|
$this->ShadowAttribute = ClassRegistry::init('ShadowAttribute');
|
|
|
|
}
|
|
|
|
if (!empty($logEntry['data']['value1'])) {
|
|
|
|
$logEntry['data']['value'] = $logEntry['data']['value1'];
|
|
|
|
if (!empty($logEntry['data']['value2'])) {
|
|
|
|
$logEntry .= '|' . $logEntry['data']['value2'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch($logEntry['action']) {
|
|
|
|
case 'add':
|
|
|
|
$logEntry['data']['value'] = $logEntry['data']['value1'];
|
|
|
|
if (!empty($logEntry['data']['value2'])) {
|
|
|
|
$logEntry['data']['value'] .= '|' . $logEntry['data']['value2'];
|
|
|
|
}
|
2020-09-18 16:51:53 +02:00
|
|
|
$logEntry['data'] = $this->Attribute->UTCToISODatetime(['ShadowAttribute' => $logEntry['data']], 'ShadowAttribute');
|
2020-09-18 13:42:52 +02:00
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'ShadowAttribute', 'action' => 'add', 'data' => $logEntry['data']];
|
|
|
|
} else {
|
|
|
|
$this->ShadowAttribute->create();
|
|
|
|
$this->ShadowAttribute->save($logEntry['data']);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'delete':
|
|
|
|
$shadow_attribute = $this->ShadowAttribute->find('first', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => ['ShadowAttribute.id' => $logEntry['model_id']]
|
|
|
|
]);
|
|
|
|
if (!empty($shadow_attribute)) {
|
|
|
|
$shadow_attribute['ShadowAttribute']['deleted'] = 1;
|
|
|
|
$shadow_attribute['ShadowAttribute']['timestamp'] = $logEntry['data']['timestamp'];
|
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'ShadowAttribute', 'action' => 'delete', 'data' => $attribute];
|
|
|
|
} else {
|
|
|
|
$this->ShadowAttribute->save($attribute);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'accept':
|
|
|
|
$shadow_attribute = $this->ShadowAttribute->find('first', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => ['ShadowAttribute.id' => $logEntry['model_id']]
|
|
|
|
]);
|
|
|
|
if (!empty($shadow_attribute['ShadowAttribute']['old_id'])) {
|
|
|
|
$attribute = $this->Attribute->find('first', [
|
|
|
|
'conditions' => ['Attribute.id' => $shadow_attribute['ShadowAttribute']['old_id']],
|
|
|
|
'recursive' => -1
|
|
|
|
]);
|
|
|
|
if (!empty($shadow_attribute['ShadowAttribute']['proposal_to_delete'])) {
|
|
|
|
$attribute['Attribute']['deleted'] = 1;
|
|
|
|
} else {
|
|
|
|
foreach(['category', 'type', 'value', 'to_ids', 'comment', 'first_seen', 'last_seen'] as $field) {
|
|
|
|
if (isset($shadow_attribute['ShadowAttribute'][$field])) {
|
|
|
|
$attribute['Attribute'][$field] = $shadow_attribute['ShadowAttribute'][$field];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$attribute['Attribute']['timestamp'] = $logEntry['data']['timestamp'];
|
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'Attribute', 'action' => 'edit', 'data' => $attribute];
|
|
|
|
} else {
|
|
|
|
$this->Attribute->save($attribute);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$this->Attribute->create();
|
|
|
|
$attribute = $shadow_attribute['ShadowAttribute'];
|
|
|
|
if (isset($attribute['id'])) {
|
|
|
|
unset($attribute['id']);
|
|
|
|
}
|
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'Attribute', 'action' => 'add', 'data' => $attribute];
|
|
|
|
} else {
|
|
|
|
$this->Attribute->save($attribute);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$shadow_attribute['ShadowAttribute']['deleted'] = 1;
|
|
|
|
$shadow_attribute['ShadowAttribute']['timestamp'] = $logEntry['data']['timestamp'];
|
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'ShadowAttribute', 'action' => 'delete', 'data' => $shadow_attribute];
|
|
|
|
} else {
|
|
|
|
$this->ShadowAttribute->save($shadow_attribute);
|
2020-09-15 23:54:59 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function __executeRecoveryObjectReference($logEntry, $id)
|
|
|
|
{
|
|
|
|
if (empty($this->ObjectReference)) {
|
|
|
|
$this->ObjectReference = ClassRegistry::init('ObjectReference');
|
|
|
|
}
|
|
|
|
switch($logEntry['action']) {
|
|
|
|
case 'add':
|
2020-09-18 13:42:52 +02:00
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'ObjectReference', 'action' => 'add', 'data' => $logEntry['data']];
|
|
|
|
} else {
|
|
|
|
$this->ObjectReference->create();
|
|
|
|
$this->ObjectReference->save($logEntry['data']);
|
|
|
|
}
|
2020-09-15 23:54:59 +02:00
|
|
|
break;
|
|
|
|
case 'edit':
|
|
|
|
$objectRef = $this->ObjectReference->find('first', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => ['ObjectReference.id' => $logEntry['model_id']]
|
|
|
|
]);
|
|
|
|
if (!empty($object)) {
|
|
|
|
foreach ($logEntry['data'] as $field => $value) {
|
|
|
|
$object['ObjectReference'][$field] = $value;
|
|
|
|
}
|
2020-09-18 13:42:52 +02:00
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'ObjectReference', 'action' => 'edit', 'data' => $object];
|
|
|
|
} else {
|
|
|
|
$this->ObjectReference->save($object);
|
|
|
|
}
|
2020-09-15 23:54:59 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function __executeRecoveryMispObject($logEntry)
|
|
|
|
{
|
2020-09-18 16:51:53 +02:00
|
|
|
if (empty($this->Attribute)) {
|
|
|
|
$this->Attribute = ClassRegistry::init('Attribute');
|
|
|
|
}
|
2020-09-15 23:54:59 +02:00
|
|
|
if (empty($this->MispObject)) {
|
|
|
|
$this->MispObject = ClassRegistry::init('MispObject');
|
|
|
|
}
|
|
|
|
switch($logEntry['action']) {
|
|
|
|
case 'add':
|
2020-09-19 18:35:15 +02:00
|
|
|
$logEntry['data'] = $this->MispObject->Attribute->UTCToISODatetime(['Object' => $logEntry['data']], 'Object');
|
|
|
|
$logEntry['data'] = $logEntry['data']['Object'];
|
2020-09-18 13:42:52 +02:00
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'MispObject', 'action' => 'add', 'data' => $logEntry['data']];
|
|
|
|
} else {
|
|
|
|
$this->MispObject->create();
|
|
|
|
$this->MispObject->save($logEntry['data']);
|
|
|
|
}
|
2020-09-15 23:54:59 +02:00
|
|
|
break;
|
|
|
|
case 'edit':
|
2020-09-19 18:35:15 +02:00
|
|
|
$logEntry['data'] = $this->MispObject->Attribute->UTCToISODatetime(['Object' => $logEntry['data']], 'Object');
|
|
|
|
$logEntry['data'] = $logEntry['data']['Object'];
|
2020-09-15 23:54:59 +02:00
|
|
|
$object = $this->MispObject->find('first', [
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => ['Object.id' => $logEntry['model_id']]
|
|
|
|
]);
|
|
|
|
if (!empty($object)) {
|
|
|
|
foreach ($logEntry['data'] as $field => $value) {
|
|
|
|
$object['Object'][$field] = $value;
|
|
|
|
}
|
2020-09-18 13:42:52 +02:00
|
|
|
if (!empty($this->mockRecovery)) {
|
|
|
|
$this->mockLog[] = ['model' => 'MispObject', 'action' => 'add', 'data' => $object];
|
|
|
|
} else {
|
|
|
|
$this->MispObject->save($object);
|
|
|
|
}
|
2020-09-15 23:54:59 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2022-11-12 20:42:27 +01:00
|
|
|
|
|
|
|
private function getElasticSearchTool()
|
|
|
|
{
|
|
|
|
if (!$this->elasticSearchClient) {
|
|
|
|
App::uses('ElasticSearchClient', 'Tools');
|
|
|
|
$client = new ElasticSearchClient();
|
|
|
|
$client->initTool();
|
|
|
|
$this->elasticSearchClient = $client;
|
|
|
|
}
|
|
|
|
return $this->elasticSearchClient;
|
|
|
|
}
|
2016-06-06 10:09:55 +02:00
|
|
|
}
|