MISP/app/Controller/LogsController.php

538 lines
22 KiB
PHP

<?php
App::uses('AppController', 'Controller');
/**
* @property Log $Log
*/
class LogsController extends AppController
{
public $components = array(
'RequestHandler',
'AdminCrud' => array(
'crud' => array('index')
)
);
public $paginate = array(
'limit' => 60,
'order' => array(
'Log.id' => 'DESC'
)
);
public function beforeFilter()
{
parent::beforeFilter();
// No need for CSRF tokens for a search
if ('admin_search' === $this->request->params['action']) {
$this->Security->csrfCheck = false;
}
}
public function index()
{
$paramArray = array('id', 'title', 'created', 'model', 'model_id', 'action', 'user_id', 'change', 'email', 'org', 'description', 'ip');
$filterData = array(
'request' => $this->request,
'named_params' => $this->request->params['named'],
'paramArray' => $paramArray,
'ordered_url_params' => func_get_args()
);
$exception = false;
$filters = $this->_harvestParameters($filterData, $exception);
unset($filterData);
if ($this->_isRest()) {
if ($filters === false) {
return $exception;
}
$conditions = array();
foreach ($filters as $filter => $data) {
if ($filter === 'created') {
$tempData = $data;
if (!is_array($data)) {
$tempData = array($data);
}
foreach ($tempData as $k => $v) {
$tempData[$k] = $this->Log->resolveTimeDelta($v);
}
if (count($tempData) == 1) {
$conditions['AND']['created >='] = date("Y-m-d H:i:s", $tempData[0]);
} else {
if ($tempData[0] < $tempData[1]) {
$temp = $tempData[1];
$tempData[1] = $tempData[0];
$tempData[0] = $temp;
}
$conditions['AND'][] = array('created <= ' => date("Y-m-d H:i:s", $tempData[0]));
$conditions['AND'][] = array('created >= ' => date("Y-m-d H:i:s", $tempData[1]));
}
} else if ($filter !== 'limit' && $filter !== 'page') {
$data = array('OR' => $data);
$conditions = $this->Log->generic_add_filter($conditions, $data, 'Log.' . $filter);
}
}
if (!$this->_isSiteAdmin()) {
if ($this->_isAdmin()) {
// ORG admins can see their own org info
$orgRestriction = $this->Auth->user('Organisation')['name'];
$conditions['Log.org'] = $orgRestriction;
} else {
// users can see their own info
$conditions['Log.user_id'] = $this->Auth->user('id');
}
}
$params = array(
'conditions' => $conditions,
'recursive' => -1
);
if (isset($filters['limit'])) {
$params['limit'] = $filters['limit'];
}
if (isset($filters['page'])) {
$params['page'] = $filters['page'];
}
$log_entries = $this->Log->find('all', $params);
return $this->RestResponse->viewData($log_entries, 'json');
}
$this->set('isSearch', 0);
$this->recursive = 0;
$validFilters = $this->Log->logMeta;
if ($this->_isSiteAdmin()) {
$validFilters = array_merge_recursive($validFilters, $this->Log->logMetaAdmin);
}
else if (!$this->_isSiteAdmin() && $this->_isAdmin()) {
// ORG admins can see their own org info
$orgRestriction = $this->Auth->user('Organisation')['name'];
$conditions['Log.org'] = $orgRestriction;
$this->paginate['conditions'] = $conditions;
} else {
// users can see their own info
$conditions['Log.email'] = $this->Auth->user('email');
$this->paginate['conditions'] = $conditions;
}
if (isset($this->params['named']['filter']) && in_array($this->params['named']['filter'], array_keys($validFilters))) {
$this->paginate['conditions']['Log.action'] = $validFilters[$this->params['named']['filter']]['values'];
}
foreach ($filters as $key => $value) {
if ($key == 'page' || $key == 'limit') { // These should not be part of the condition parameter
continue;
}
if ($key === 'created') {
$key = 'created >=';
}
if ($key == 'page' || $key == 'limit') {
continue;
}
$this->paginate['conditions']["Log.$key"] = $value;
}
$this->set('validFilters', $validFilters);
$this->set('filter', isset($this->params['named']['filter']) ? $this->params['named']['filter'] : false);
$this->set('list', $this->paginate());
}
public function admin_index()
{
$this->view = 'index';
return $this->index();
}
// Shows a minimalistic history for the currently selected event
public function event_index($id, $org = null)
{
$this->loadModel('Event');
$event = $this->Event->fetchEvent($this->Auth->user(), array(
'eventid' => $id,
'sgReferenceOnly' => 1,
'deleted' => [0, 1],
'deleted_proposals' => 1,
'noSightings' => true,
'noEventReports' => true,
'includeEventCorrelations' => false,
'excludeGalaxy' => true,
));
if (empty($event)) {
throw new NotFoundException('Invalid event.');
}
$event = $event[0];
$attribute_ids = array();
$object_ids = array();
$proposal_ids = array_column($event['ShadowAttribute'], 'id');
if (!empty($event['Attribute'])) {
foreach ($event['Attribute'] as $aa) {
$attribute_ids[] = $aa['id'];
if (!empty($aa['ShadowAttribute'])) {
foreach ($aa['ShadowAttribute'] as $sa) {
$proposal_ids[] = $sa['id'];
}
}
}
unset($event['Attribute']);
}
if (!empty($event['Object'])) {
foreach ($event['Object'] as $ob) {
foreach ($ob['Attribute'] as $aa) {
$attribute_ids[] = $aa['id'];
if (!empty($aa['ShadowAttribute'])) {
foreach ($aa['ShadowAttribute'] as $sa) {
$proposal_ids[] = $sa['id'];
}
}
}
$object_ids[] = $ob['id'];
}
unset($event['Object']);
}
$conditions = array();
$conditions['OR'][] = array(
'AND' => array(
'model' => 'Event',
'model_id' => $event['Event']['id']
)
);
if (!empty($attribute_ids)) {
$conditions['OR'][] = array(
'AND' => array(
'model' => 'Attribute',
'model_id' => $attribute_ids
)
);
}
if (!empty($proposal_ids)) {
$conditions['OR'][] = array(
'AND' => array(
'model' => 'ShadowAttribute',
'model_id' => $proposal_ids
)
);
}
if (!empty($object_ids)) {
$conditions['OR'][] = array(
'AND' => array(
'model' => 'MispObject',
'model_id' => $object_ids
)
);
}
if ($org) {
$conditions['org'] = $org;
}
$this->paginate['fields'] = array('title', 'created', 'model', 'model_id', 'action', 'change', 'org', 'email');
$this->paginate['conditions'] = $conditions;
$list = $this->paginate();
if (!$this->_isSiteAdmin()) {
$this->loadModel('User');
$orgEmails = $this->User->find('column', array(
'conditions' => array('User.org_id' => $this->Auth->user('org_id')),
'fields' => array('User.email')
));
foreach ($list as $k => $item) {
if (!in_array($item['Log']['email'], $orgEmails, true)) {
$list[$k]['Log']['email'] = '';
}
}
}
if ($this->_isRest()) {
$list = array('Log' => array_column($list, 'Log'));
return $this->RestResponse->viewData($list, $this->response->type());
}
// send unauthorised people away. Only site admins and users of the same org may see events that are "your org only". Everyone else can proceed for all other levels of distribution
$mineOrAdmin = true;
if (!$this->_isSiteAdmin() && $event['Event']['org_id'] != $this->Auth->user('org_id')) {
$mineOrAdmin = false;
}
$mayModify = false;
if ($mineOrAdmin && $this->userRole['perm_modify']) {
$mayModify = true;
}
$this->set('published', $event['Event']['published']);
$this->set('event', $event);
$this->set('list', $list);
$this->set('eventId', $id);
$this->set('mayModify', $mayModify);
}
public function admin_search($new = false)
{
$orgRestriction = null;
if ($this->_isSiteAdmin()) {
$orgRestriction = false;
} else {
$orgRestriction = $this->Auth->user('Organisation')['name'];
}
$this->set('orgRestriction', $orgRestriction);
$validFilters = $this->Log->logMeta;
if ($this->_isSiteAdmin()) {
$validFilters = array_merge_recursive($validFilters, $this->Log->logMetaAdmin);
}
$this->set('validFilters', $validFilters);
$this->set('filters', false);
if ($new !== false) {
$this->set('actionDefinitions', $this->{$this->defaultModel}->actionDefinitions);
// reset the paginate_conditions
//$this->Session->write('paginate_conditions_log', array());
if ($this->request->is('post')) {
$filters['email'] = $this->request->data['Log']['email'];
if (!$orgRestriction) {
$filters['org'] = $this->request->data['Log']['org'];
} else {
$filters['org'] = $this->Auth->user('Organisation')['name'];
}
$filters['action'] = $this->request->data['Log']['action'];
$filters['model'] = $this->request->data['Log']['model'];
$filters['model_id'] = $this->request->data['Log']['model_id'];
$filters['title'] = $this->request->data['Log']['title'];
if (!empty ($this->request->data['Log']['from'])) {
$filters['from'] = $this->request->data['Log']['from'];
}
if (!empty ($this->request->data['Log']['to'])) {
$filters['to'] = $this->request->data['Log']['to'];
}
$filters['change'] = $this->request->data['Log']['change'];
if (Configure::read('MISP.log_client_ip')) {
$filters['ip'] = $this->request->data['Log']['ip'];
}
// for info on what was searched for
$this->set('emailSearch', $filters['email']);
$this->set('orgSearch', $filters['org']);
$this->set('actionSearch', $filters['action']);
$this->set('modelSearch', $filters['model']);
$this->set('model_idSearch', $filters['model_id']);
$this->set('titleSearch', $filters['title']);
$this->set('fromSearch', $filters['from'] ?? null);
$this->set('toSearch', $filters['to'] ?? null);
$this->set('changeSearch', $filters['change']);
if (Configure::read('MISP.log_client_ip')) {
$this->set('ipSearch', $filters['ip']);
}
$this->set('isSearch', 1);
// search the db
$conditions = $this->__buildSearchConditions($filters);
$this->{$this->defaultModel}->recursive = 0;
$this->paginate = array(
'limit' => 60,
'conditions' => $conditions,
'order' => array('Log.id' => 'DESC')
);
$list = $this->paginate();
if (empty($this->Auth->user('Role')['perm_site_admin'])) {
$list = $this->Log->filterSiteAdminSensitiveLogs($list);
}
$this->set('list', $list);
if ($this->_isRest()) {
return $this->RestResponse->viewData($list, $this->response->type());
} else {
// and store into session
$this->Session->write('paginate_conditions_log', $this->paginate);
$this->Session->write('paginate_conditions_log_email', $filters['email']);
$this->Session->write('paginate_conditions_log_org', $filters['org']);
$this->Session->write('paginate_conditions_log_action', $filters['action']);
$this->Session->write('paginate_conditions_log_model', $filters['model']);
$this->Session->write('paginate_conditions_log_model_id', $filters['model_id']);
$this->Session->write('paginate_conditions_log_title', $filters['title']);
$this->Session->write('paginate_conditions_log_change', $filters['change']);
$this->Session->write('paginate_conditions_log_from', $filters['from'] ?? null);
$this->Session->write('paginate_conditions_log_to', $filters['to'] ?? null);
if (Configure::read('MISP.log_client_ip')) {
$this->Session->write('paginate_conditions_log_ip', $filters['ip']);
}
// set the same view as the index page
$this->render('index');
}
} else {
// get from Session
$filters['email'] = $this->Session->read('paginate_conditions_log_email');
$filters['org'] = $this->Session->read('paginate_conditions_log_org');
$filters['action'] = $this->Session->read('paginate_conditions_log_action');
$filters['model'] = $this->Session->read('paginate_conditions_log_model');
$filters['model_id'] = $this->Session->read('paginate_conditions_log_model_id');
$filters['title'] = $this->Session->read('paginate_conditions_log_title');
$filters['change'] = $this->Session->read('paginate_conditions_log_change');
$filters['from'] = $this->Session->read('paginate_conditions_log_from') ?? null;
$filters['to'] = $this->Session->read('paginate_conditions_log_to') ?? null;
if (Configure::read('MISP.log_client_ip')) {
$filters['ip'] = $this->Session->read('paginate_conditions_log_ip');
}
// for info on what was searched for
$this->set('emailSearch', $filters['email']);
$this->set('orgSearch', $filters['org']);
$this->set('actionSearch', $filters['action']);
$this->set('modelSearch', $filters['model']);
$this->set('model_idSearch', $filters['model_id']);
$this->set('titleSearch', $filters['title']);
$this->set('changeSearch', $filters['change']);
$this->set('changeSearch', $filters['from'] ?? null);
$this->set('changeSearch', $filters['to'] ?? null);
if (Configure::read('MISP.log_client_ip')) {
$this->set('ipSearch', $filters['ip']);
}
$this->set('isSearch', 1);
// re-get pagination
$this->{$this->defaultModel}->recursive = 0;
$this->paginate = array_replace_recursive($this->paginate, $this->Session->read('paginate_conditions_log'));
if (!isset($this->paginate['order'])) {
$this->paginate['order'] = array('Log.id' => 'DESC');
}
$conditions = $this->__buildSearchConditions($filters);
$this->paginate['conditions'] = $conditions;
$list = $this->paginate();
if (empty($this->Auth->user('Role')['perm_site_admin'])) {
$list = $this->Log->filterSiteAdminSensitiveLogs($list);
}
$this->set('list', $list);
// set the same view as the index page
$this->render('index');
}
} else {
// no search keyword is given, show the search form
// combobox for actions
$actions = array('' => array('ALL' => 'ALL'), 'actions' => array());
$actions['actions'] = array_merge($actions['actions'], $this->_arrayToValuesIndexArray($this->{$this->defaultModel}->validate['action']['rule'][1]));
$this->set('actions', $actions);
// combobox for models
$models = [
'Attribute',
'Allowedlist',
'AuthKey',
'Event',
'EventBlocklist',
'EventTag',
'Feed',
'DecayingModel',
'EventGraph',
'EventReport',
'MispObject',
'Organisation',
'Post',
'Regexp',
'Role',
'Server',
'ShadowAttribute',
'SharingGroup',
'Tag',
'Task',
'Taxonomy',
'Template',
'Thread',
'User',
'Galaxy',
'GalaxyCluster',
'GalaxyClusterRelation',
'Workflow',
];
sort($models);
$models = array('' => 'ALL') + $this->_arrayToValuesIndexArray($models);
$this->set('models', $models);
$this->set('actionDefinitions', $this->{$this->defaultModel}->actionDefinitions);
}
}
private function __buildSearchConditions($filters)
{
$conditions = array();
if (isset($filters['email']) && !empty($filters['email'])) {
$conditions['LOWER(Log.email) LIKE'] = '%' . strtolower($filters['email']) . '%';
}
if (isset($filters['org']) && !empty($filters['org'])) {
$conditions['LOWER(Log.org) LIKE'] = '%' . strtolower($filters['org']) . '%';
}
if ($filters['action'] != 'ALL') {
$conditions['Log.action'] = $filters['action'];
}
if ($filters['model'] != '') {
$conditions['Log.model'] = $filters['model'];
}
if ($filters['model_id'] != '') {
$conditions['Log.model_id'] = $filters['model_id'];
}
if (isset($filters['title']) && !empty($filters['title'])) {
$conditions['LOWER(Log.title) LIKE'] = '%' . strtolower($filters['title']) . '%';
}
if (isset($filters['change']) && !empty($filters['change'])) {
$conditions['LOWER(Log.change) LIKE'] = '%' . strtolower($filters['change']) . '%';
}
if (isset($filters['from']) && !empty($filters['from'])) {
$conditions['Log.created >='] = $filters['from'];
}
if (isset($filters['to']) && !empty($filters['to'])) {
$conditions['Log.created <='] = $filters['to'];
}
if (Configure::read('MISP.log_client_ip') && isset($filters['ip']) && !empty($filters['ip'])) {
$conditions['Log.ip LIKE'] = '%' . $filters['ip'] . '%';
}
return $conditions;
}
public function returnDates($org = 'all')
{
if (!$this->Auth->user('Role')['perm_sharing_group'] && !empty(Configure::read('Security.hide_organisation_index_from_users'))) {
if ($org !== 'all' && $org !== $this->Auth->user('Organisation')['name']) {
throw new MethodNotAllowedException('Invalid organisation.');
}
}
$data = $this->Log->returnDates($org);
$this->set('data', $data);
$this->set('_serialize', 'data');
}
public function pruneUpdateLogs()
{
if (!$this->request->is('post')) {
//throw new MethodNotAllowedException('This functionality is only accessible via POST requests');
}
$this->Log->pruneUpdateLogsRouter($this->Auth->user());
if (Configure::read('MISP.background_jobs')) {
$this->Flash->success('The pruning job is queued.');
} else {
$this->Flash->success('The pruning is complete.');
}
$this->redirect($this->referer());
}
public function testForStolenAttributes()
{
$logs = $this->Log->find('list', array(
'recursive' => -1,
'conditions' => array(
'Log.model' => 'Attribute',
'Log.action' => 'edit'
),
'fields' => array('Log.title')
));
$ids = array();
foreach ($logs as $log) {
preg_match('/Attribute \(([0-9]+?)\)/', $log, $attribute_id);
preg_match('/Event \(([0-9]+?)\)/', $log, $event_id);
if (!isset($attribute_id[1])) {
continue;
}
if (empty($ids[$attribute_id[1]]) || !in_array($event_id[1], $ids[$attribute_id[1]])) {
$ids[$attribute_id[1]][] = $event_id[1];
}
}
$issues = array();
foreach ($ids as $aid => $eids) {
if (count($eids) > 1) {
$issues[$aid] = $eids;
}
}
$this->set('issues', $issues);
}
}