array( 'rule' => array('inList', array( 'accept', 'accept_delegation', 'add', 'admin_email', 'auth', 'auth_fail', 'blacklisted', 'change_pw', 'delete', 'disable', 'discard', 'edit', 'email', 'enable', 'error', 'export', 'file_upload', 'galaxy', 'login', 'login_fail', 'logout', 'merge', 'pruneUpdateLogs', 'publish', 'publish alert', 'pull', 'push', 'remove_dead_workers', 'request', 'request_delegation', 'reset_auth_key', 'security', 'serverSettingsEdit', 'tag', 'undelete', 'update', 'update_database', 'upgrade_24', 'upload_sample', 'version_warning', 'warning' )), 'message' => 'Options : ...' ) ); 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") ); public $logMeta = array( 'email' => array('values' => array('email'), 'name' => 'Emails'), 'auth_issues' => array('values' => array('login_fail', 'auth_fail'), 'name' => 'Authentication issues') ); public $logMetaAdmin = array( 'update' => array('values' => array('update_database'), 'name' => 'MISP Update results'), 'settings' => array('values' => array('serverSettingsEdit', 'remove_dead_workers'), 'name' => 'Setting changes'), 'errors' => array('values' => array('warning', 'errors', 'version_warning'), 'name' => 'Warnings and errors'), 'email' => array('values' => array('admin_email')) ); public function beforeValidete() { parent::beforeValidate(); if (!isset($this->data['Log']['org']) || empty($this->data['Log']['org'])) { $this->data['Log']['org'] = 'SYSTEM'; } // truncate the description if it would exceed the allowed size in mysql if (!empty($this->data['Log']['description'] && strlen($this->data['Log']['description']) > 65536)) { $this->data['Log']['description'] = substr($this->data['Log']['description'], 0, 65535); } } public function beforeSave($options = array()) { if (!empty(Configure::read('MISP.log_skip_db_logs_completely'))) { return false; } if (Configure::read('MISP.log_client_ip') && isset($_SERVER['REMOTE_ADDR'])) { $this->data['Log']['ip'] = $_SERVER['REMOTE_ADDR']; } $setEmpty = array('title' => '', 'model' => '', 'model_id' => 0, 'action' => '', 'user_id' => 0, 'change' => '', 'email' => '', 'org' => '', 'description' => ''); foreach ($setEmpty as $field => $empty) { if (!isset($this->data['Log'][$field]) || empty($this->data['Log'][$field])) { $this->data['Log'][$field] = $empty; } } if (!isset($this->data['Log']['created'])) { $this->data['Log']['created'] = date('Y-m-d H:i:s'); } if (!isset($this->data['Log']['org'])) { $this->data['Log']['org'] = 'SYSTEM'; } $truncate_fields = array('title', 'change', 'description'); foreach ($truncate_fields as $tf) { if (isset($this->data['Log'][$tf]) && strlen($this->data['Log'][$tf]) >= 65535) { $this->data['Log'][$tf] = substr($this->data['Log'][$tf], 0, 65532) . '...'; } } $this->logData($this->data); if ($this->data['Log']['action'] === 'request' && !empty(Configure::read('MISP.log_paranoid_skip_db'))) { return false; } return true; } public function returnDates($org = 'all') { $dataSourceConfig = ConnectionManager::getDataSource('default')->config; $dataSource = $dataSourceConfig['datasource']; $conditions = array(); $this->Organisation = ClassRegistry::init('Organisation'); if ($org !== 'all') { $org = $this->Organisation->find('first', array('fields' => array('name'), 'recursive' => -1, 'conditions' => array('UPPER(Organisation.name) LIKE' => strtoupper($org)))); if (empty($org)) { return MethodNotAllowedException('Invalid organisation.'); } $conditions['org'] = $org['Organisation']['name']; } $conditions['AND']['NOT'] = array('action' => array('login', 'logout', 'changepw')); if ($dataSource == 'Database/Mysql') { $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') )); } elseif ($dataSource == 'Database/Postgres') { // 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'])) { $condOrg = ' AND org = "' . $conditions['org'] . '"'; } else { $condOrg = ''; } $sql = 'SELECT DISTINCT EXTRACT(EPOCH FROM CAST(created AS DATE)) AS "Date", COUNT(id) AS count FROM logs WHERE action NOT IN (' . $condnotinaction . ') ' . $condOrg . ' GROUP BY "Date" ORDER BY "Date"'; $validDates = $this->query($sql); } $data = array(); foreach ($validDates as $k => $date) { $data[$date[0]['Date']] = intval($date[0]['count']); } return $data; } public function createLogEntry($user = array('Organisation' => array('name' => 'SYSTEM'), 'email' => 'SYSTEM', 'id' => 0), $action, $model, $model_id = 0, $title = '', $change = '') { $this->create(); $this->save(array( 'org' => $user['Organisation']['name'], 'email' =>$user['email'], 'user_id' => $user['id'], 'action' => $action, 'title' => $title, 'change' => $change, 'model' => $model, 'model_id' => $model_id, )); } // 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 )); } public function pruneUpdateLogsRouter($user) { if (Configure::read('MISP.background_jobs')) { $job = ClassRegistry::init('Job'); $job->create(); $data = array( 'worker' => 'default', 'job_type' => 'prune_update_logs', 'job_input' => 'All update entries', 'status' => 0, 'retries' => 0, 'org_id' => $user['org_id'], 'org' => $user['Organisation']['name'], 'message' => 'Purging the heretic.', ); $job->save($data); $jobId = $job->id; $process_id = CakeResque::enqueue( 'default', 'AdminShell', array('prune_update_logs', $jobId, $user['id']), true ); $job->saveField('process_id', $process_id); return $process_id; } else { $result = $this->pruneUpdateLogs(false, $user); return $result; } } public function logData($data) { if (Configure::read('Plugin.ZeroMQ_enable') && Configure::read('Plugin.ZeroMQ_audit_notifications_enable')) { $pubSubTool = $this->getPubSubTool(); $pubSubTool->publish($data, 'audit', 'log'); } $this->publishKafkaNotification('audit', $data, 'log'); 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); } if (Configure::read('Security.syslog')) { // write to syslogd as well $syslog = new SysLog(); $action = 'info'; if (isset($data['Log']['action'])) { if (in_array($data['Log']['action'], $this->errorActions)) { $action = 'err'; } if (in_array($data['Log']['action'], $this->warningActions)) { $action = 'warning'; } } $entry = $data['Log']['action']; if (!empty($data['Log']['description'])) { $entry .= sprintf(' -- %s', $data['Log']['description']); } $syslog->write($action, $entry); } return true; } 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; } }