mirror of https://github.com/MISP/MISP
new: [log] Audit log
parent
25ceea7f4e
commit
ad1b373766
|
@ -32,6 +32,7 @@
|
|||
Router::connect('/users/admin_index/*', array('controller' => 'users', 'action' => 'index', 'admin' => true));
|
||||
Router::connect('/roles/admin_index/*', array('controller' => 'roles', 'action' => 'index', 'admin' => true));
|
||||
Router::connect('/logs/admin_search/*', array('controller' => 'logs', 'action' => 'search', 'admin' => true));
|
||||
Router::connect('/audit_logs/admin_index/*', array('controller' => 'audit_logs', 'action' => 'index', 'admin' => true));
|
||||
Router::connect('/logs/admin_index/*', array('controller' => 'logs', 'action' => 'index', 'admin' => true));
|
||||
Router::connect('/regexp/admin_index/*', array('controller' => 'regexp', 'action' => 'index', 'admin' => true));
|
||||
|
||||
|
|
|
@ -445,6 +445,7 @@ class EventShell extends AppShell
|
|||
$inputData = $tempFile->read();
|
||||
$inputData = json_decode($inputData, true);
|
||||
$tempFile->delete();
|
||||
Configure::write('CurrentUserId', $inputData['user']['id']);
|
||||
$this->Event->processFreeTextData(
|
||||
$inputData['user'],
|
||||
$inputData['attributes'],
|
||||
|
@ -465,6 +466,7 @@ class EventShell extends AppShell
|
|||
$tempFile = new File(APP . 'tmp/cache/ingest' . DS . $inputFile);
|
||||
$inputData = json_decode($tempFile->read(), true);
|
||||
$tempFile->delete();
|
||||
Configure::write('CurrentUserId', $inputData['user']['id']);
|
||||
$this->Event->processModuleResultsData(
|
||||
$inputData['user'],
|
||||
$inputData['misp_format'],
|
||||
|
@ -530,6 +532,7 @@ class EventShell extends AppShell
|
|||
if (empty($user)) {
|
||||
$this->error("User with ID $userId does not exists.");
|
||||
}
|
||||
Configure::write('CurrentUserId', $user['id']); // for audit logging purposes
|
||||
return $user;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,571 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
App::uses('AuditLog', 'Model');
|
||||
|
||||
/**
|
||||
* @property AuditLog $AuditLog
|
||||
*/
|
||||
class AuditLogsController extends AppController
|
||||
{
|
||||
public $components = [
|
||||
'Security',
|
||||
'RequestHandler',
|
||||
];
|
||||
|
||||
/** @var array */
|
||||
private $actions;
|
||||
|
||||
/** @var string[] */
|
||||
private $models = [
|
||||
'AdminSetting',
|
||||
'Attribute',
|
||||
'Allowedlist',
|
||||
'AuthKey',
|
||||
'Cerebrate',
|
||||
'CorrelationExclusion',
|
||||
'Event',
|
||||
'EventBlocklist',
|
||||
'Feed',
|
||||
'DecayingModel',
|
||||
'Object',
|
||||
'ObjectTemplate',
|
||||
'ObjectTemplateElement',
|
||||
'Organisation',
|
||||
'OrgBlocklist',
|
||||
'Post',
|
||||
'Regexp',
|
||||
'Role',
|
||||
'Server',
|
||||
'ShadowAttribute',
|
||||
'SharingGroup',
|
||||
'Tag',
|
||||
'TagCollection',
|
||||
'TagCollectionTag',
|
||||
'Task',
|
||||
'Taxonomy',
|
||||
'Template',
|
||||
'Thread',
|
||||
'User',
|
||||
'UserSetting',
|
||||
'Galaxy',
|
||||
'GalaxyCluster',
|
||||
'GalaxyClusterBlocklist',
|
||||
'GalaxyClusterRelation',
|
||||
'News',
|
||||
'Warninglist',
|
||||
];
|
||||
|
||||
public $paginate = [
|
||||
'recursive' => -1,
|
||||
'limit' => 60,
|
||||
'fields' => ['id', 'created', 'user_id', 'org_id', 'action', 'model', 'model_id', 'model_title', 'event_id', 'change'],
|
||||
'contain' => [
|
||||
'User' => ['fields' => ['id', 'email', 'org_id']],
|
||||
'Organisation' => ['fields' => ['id', 'name', 'uuid']],
|
||||
],
|
||||
'order' => [
|
||||
'AuditLog.id' => 'DESC'
|
||||
],
|
||||
];
|
||||
|
||||
public function __construct($id = false, $table = null, $ds = null)
|
||||
{
|
||||
parent::__construct($id, $table, $ds);
|
||||
$this->actions = [
|
||||
AuditLog::ACTION_ADD => __('Add'),
|
||||
AuditLog::ACTION_EDIT => __('Edit'),
|
||||
AuditLog::ACTION_SOFT_DELETE => __('Soft delete'),
|
||||
AuditLog::ACTION_DELETE => __('Delete'),
|
||||
AuditLog::ACTION_UNDELETE => __('Undelete'),
|
||||
AuditLog::ACTION_TAG => __('Tag'),
|
||||
AuditLog::ACTION_TAG_LOCAL => __('Tag'),
|
||||
AuditLog::ACTION_REMOVE_TAG => __('Remove tag'),
|
||||
AuditLog::ACTION_REMOVE_TAG_LOCAL => __('Remove tag'),
|
||||
AuditLog::ACTION_GALAXY => __('Galaxy cluster'),
|
||||
AuditLog::ACTION_GALAXY_LOCAL => __('Galaxy cluster'),
|
||||
AuditLog::ACTION_REMOVE_GALAXY => __('Remove galaxy cluster'),
|
||||
AuditLog::ACTION_REMOVE_GALAXY_LOCAL => __('Remove galaxy cluster'),
|
||||
AuditLog::ACTION_PUBLISH => __('Publish'),
|
||||
AuditLog::ACTION_PUBLISH_SIGHTINGS => __('Publish sightings'),
|
||||
];
|
||||
}
|
||||
|
||||
public function admin_index()
|
||||
{
|
||||
$this->paginate['fields'][] = 'ip';
|
||||
$this->paginate['fields'][] = 'request_type';
|
||||
$this->paginate['fields'][] = 'authkey_id';
|
||||
|
||||
if ($this->_isRest()) {
|
||||
$this->paginate['fields'][] = 'request_id';
|
||||
}
|
||||
|
||||
$this->paginate['conditions'] = $this->__searchConditions();
|
||||
$list = $this->paginate();
|
||||
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($list, 'json');
|
||||
}
|
||||
|
||||
$list = $this->__appendModelLinks($list);
|
||||
foreach ($list as $k => $item) {
|
||||
$list[$k]['AuditLog']['action_human'] = $this->actions[$item['AuditLog']['action']];
|
||||
}
|
||||
|
||||
$this->set('list', $list);
|
||||
$this->set('actions', [
|
||||
AuditLog::ACTION_ADD => __('Add'),
|
||||
AuditLog::ACTION_EDIT => __('Edit'),
|
||||
AuditLog::ACTION_SOFT_DELETE => __('Soft delete'),
|
||||
AuditLog::ACTION_DELETE => __('Delete'),
|
||||
AuditLog::ACTION_UNDELETE => __('Undelete'),
|
||||
AuditLog::ACTION_TAG . '||' . AuditLog::ACTION_TAG_LOCAL => __('Tag'),
|
||||
AuditLog::ACTION_REMOVE_TAG . '||' . AuditLog::ACTION_REMOVE_TAG_LOCAL => __('Remove tag'),
|
||||
AuditLog::ACTION_GALAXY . '||' . AuditLog::ACTION_GALAXY_LOCAL => __('Galaxy cluster'),
|
||||
AuditLog::ACTION_REMOVE_GALAXY . '||' . AuditLog::ACTION_REMOVE_GALAXY_LOCAL => __('Remove galaxy cluster'),
|
||||
AuditLog::ACTION_PUBLISH => __('Publish'),
|
||||
AuditLog::ACTION_PUBLISH_SIGHTINGS => $this->actions[AuditLog::ACTION_PUBLISH_SIGHTINGS],
|
||||
]);
|
||||
$models = $this->models;
|
||||
sort($models);
|
||||
$this->set('models', $models);
|
||||
$this->set('title_for_layout', __('Audit logs'));
|
||||
}
|
||||
|
||||
public function eventIndex($eventId, $org = null)
|
||||
{
|
||||
$this->loadModel('Event');
|
||||
$event = $this->Event->fetchSimpleEvent($this->Auth->user(), $eventId);
|
||||
if (empty($event)) {
|
||||
throw new NotFoundException('Invalid event.');
|
||||
}
|
||||
|
||||
$this->paginate['conditions'] = $this->__createEventIndexConditions($event);
|
||||
|
||||
if ($org) {
|
||||
$org = $this->AuditLog->Organisation->fetchOrg($org);
|
||||
if ($org) {
|
||||
$this->paginate['conditions']['AND']['org_id'] = $org['id'];
|
||||
} else {
|
||||
$this->paginate['conditions']['AND']['org_id'] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
$list = $this->paginate();
|
||||
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
// Remove all user info about users from different org
|
||||
$this->loadModel('User');
|
||||
$orgUserIds = $this->User->find('column', array(
|
||||
'conditions' => ['User.org_id' => $this->Auth->user('org_id')],
|
||||
'fields' => ['User.id'],
|
||||
));
|
||||
foreach ($list as $k => $item) {
|
||||
if ($item['AuditLog']['user_id'] == 0) {
|
||||
continue;
|
||||
}
|
||||
if (!in_array($item['User']['id'], $orgUserIds)) {
|
||||
unset($list[$k]['User']);
|
||||
unset($list[$k]['AuditLog']['user_id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($list, 'json');
|
||||
}
|
||||
|
||||
foreach ($list as $k => $item) {
|
||||
$list[$k]['AuditLog']['action_human'] = $this->actions[$item['AuditLog']['action']];
|
||||
}
|
||||
|
||||
$this->set('list', $list);
|
||||
$this->set('event', $event);
|
||||
$this->set('mayModify', $this->__canModifyEvent($event));
|
||||
$this->set('title_for_layout', __('Audit logs for event #%s', $event['Event']['id']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function __searchConditions()
|
||||
{
|
||||
$params = $this->IndexFilter->harvestParameters([
|
||||
'ip',
|
||||
'user',
|
||||
'request_id',
|
||||
'authkey_id',
|
||||
'model',
|
||||
'model_id',
|
||||
'event_id',
|
||||
'model_title',
|
||||
'action',
|
||||
'org',
|
||||
'created',
|
||||
'request_type',
|
||||
]);
|
||||
|
||||
$qbRules = [];
|
||||
foreach ($params as $key => $value) {
|
||||
if ($key === 'model' && strpos($value, ':') !== false) {
|
||||
$parts = explode(':', $value);
|
||||
$qbRules[] = [
|
||||
'id' => 'model',
|
||||
'value' => $parts[0],
|
||||
];
|
||||
$qbRules[] = [
|
||||
'id' => 'model_id',
|
||||
'value' => $parts[1],
|
||||
];
|
||||
} elseif ($key === 'created') {
|
||||
$qbRules[] = [
|
||||
'id' => $key,
|
||||
'operator' => is_array($value) ? 'between' : 'greater_or_equal',
|
||||
'value' => $value,
|
||||
];
|
||||
} else {
|
||||
if (is_array($value)) {
|
||||
$value = implode('||', $value);
|
||||
}
|
||||
$qbRules[] = [
|
||||
'id' => $key,
|
||||
'value' => $value,
|
||||
];
|
||||
}
|
||||
}
|
||||
$this->set('qbRules', $qbRules);
|
||||
|
||||
$conditions = [];
|
||||
if (isset($params['user'])) {
|
||||
if (strtoupper($params['user']) === 'SYSTEM') {
|
||||
$conditions['AuditLog.user_id'] = 0;
|
||||
} else if (is_numeric($params['user'])) {
|
||||
$conditions['AuditLog.user_id'] = $params['user'];
|
||||
} else {
|
||||
$user = $this->User->find('first', [
|
||||
'conditions' => ['User.email' => $params['user']],
|
||||
'fields' => ['id'],
|
||||
]);
|
||||
if (!empty($user)) {
|
||||
$conditions['AuditLog.user_id'] = $user['User']['id'];
|
||||
} else {
|
||||
$conditions['AuditLog.user_id'] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($params['ip'])) {
|
||||
$conditions['AuditLog.ip'] = inet_pton($params['ip']);
|
||||
}
|
||||
if (isset($params['authkey_id'])) {
|
||||
$conditions['AuditLog.authkey_id'] = $params['authkey_id'];
|
||||
}
|
||||
if (isset($params['request_id'])) {
|
||||
$conditions['AuditLog.request_id'] = $params['request_id'];
|
||||
}
|
||||
if (isset($params['request_type'])) {
|
||||
$conditions['AuditLog.request_type'] = $params['request_type'];
|
||||
}
|
||||
if (isset($params['model'])) {
|
||||
$conditions['AuditLog.model'] = $params['model'];
|
||||
}
|
||||
if (isset($params['model_id'])) {
|
||||
$conditions['AuditLog.model_id'] = $params['model_id'];
|
||||
}
|
||||
if (isset($params['event_id'])) {
|
||||
$conditions['AuditLog.event_id'] = $params['event_id'];
|
||||
}
|
||||
if (isset($params['model_title'])) {
|
||||
$conditions['AuditLog.model_title LIKE'] = '%' . $params['model_title'] . '%';
|
||||
}
|
||||
if (isset($params['action'])) {
|
||||
$conditions['AuditLog.action'] = $params['action'];
|
||||
}
|
||||
if (isset($params['org'])) {
|
||||
if (is_numeric($params['org'])) {
|
||||
$conditions['AuditLog.org_id'] = $params['org'];
|
||||
} else {
|
||||
$org = $this->AuditLog->Organisation->fetchOrg($params['org']);
|
||||
if ($org) {
|
||||
$conditions['AuditLog.org_id'] = $org['id'];
|
||||
} else {
|
||||
$conditions['AuditLog.org_id'] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($params['created'])) {
|
||||
$tempData = is_array($params['created']) ? $params['created'] : [$params['created']];
|
||||
foreach ($tempData as $k => $v) {
|
||||
$tempData[$k] = $this->AuditLog->resolveTimeDelta($v);
|
||||
}
|
||||
if (count($tempData) === 1) {
|
||||
$conditions['AuditLog.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'][] = ['AuditLog.created <=' => date("Y-m-d H:i:s", $tempData[0])];
|
||||
$conditions['AND'][] = ['AuditLog.created >=' => date("Y-m-d H:i:s", $tempData[1])];
|
||||
}
|
||||
}
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create conditions that will include just events parts that user can see.
|
||||
* @param array $event
|
||||
* @return array
|
||||
*/
|
||||
private function __createEventIndexConditions(array $event)
|
||||
{
|
||||
if ($this->_isSiteAdmin() || $event['Event']['orgc_id'] == $this->Auth->user('org_id')) {
|
||||
// Site admins and event owners can see all changes
|
||||
return ['event_id' => $event['Event']['id']];
|
||||
}
|
||||
|
||||
$event = $this->Event->fetchEvent($this->Auth->user(), [
|
||||
'eventid' => $event['Event']['id'],
|
||||
'sgReferenceOnly' => 1,
|
||||
'deleted' => [0, 1],
|
||||
'deleted_proposals' => 1,
|
||||
'noSightings' => true,
|
||||
'includeEventCorrelations' => false,
|
||||
'excludeGalaxy' => true,
|
||||
])[0];
|
||||
|
||||
$attributeIds = [];
|
||||
$objectIds = [];
|
||||
$proposalIds = array_column($event['ShadowAttribute'], 'id');
|
||||
$objectReferenceId = [];
|
||||
foreach ($event['Attribute'] as $aa) {
|
||||
$attributeIds[] = $aa['id'];
|
||||
if (!empty($aa['ShadowAttribute'])) {
|
||||
foreach ($aa['ShadowAttribute'] as $sa) {
|
||||
$proposalIds[] = $sa['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($event['Attribute']);
|
||||
foreach ($event['Object'] as $ob) {
|
||||
foreach ($ob['Attribute'] as $aa) {
|
||||
$attributeIds[] = $aa['id'];
|
||||
if (!empty($aa['ShadowAttribute'])) {
|
||||
foreach ($aa['ShadowAttribute'] as $sa) {
|
||||
$proposalIds[] = $sa['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($ob['ObjectReference'] as $or) {
|
||||
$objectReferenceId[] = $or['id'];
|
||||
}
|
||||
$objectIds[] = $ob['id'];
|
||||
}
|
||||
unset($event['Object']);
|
||||
|
||||
$conditions = [];
|
||||
$conditions['AND']['event_id'] = $event['Event']['id'];
|
||||
$conditions['AND']['OR'][] = ['model' => 'Event'];
|
||||
|
||||
$parts = [
|
||||
'Attribute' => $attributeIds,
|
||||
'ShadowAttribute' => $proposalIds,
|
||||
'Object' => $objectIds,
|
||||
'ObjectReference' => $objectReferenceId,
|
||||
'EventReport' => array_column($event['EventReport'], 'id'),
|
||||
];
|
||||
|
||||
foreach ($parts as $model => $modelIds) {
|
||||
if (!empty($modelIds)) {
|
||||
$conditions['AND']['OR'][] = [
|
||||
'AND' => [
|
||||
'model' => $model,
|
||||
'model_id' => $modelIds,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate link to model view if exists and use has permission to access it.
|
||||
* @param array $auditLogs
|
||||
* @return array
|
||||
*/
|
||||
private function __appendModelLinks(array $auditLogs)
|
||||
{
|
||||
$models = [];
|
||||
foreach ($auditLogs as $auditLog) {
|
||||
if (isset($models[$auditLog['AuditLog']['model']])) {
|
||||
$models[$auditLog['AuditLog']['model']][] = $auditLog['AuditLog']['model_id'];
|
||||
} else {
|
||||
$models[$auditLog['AuditLog']['model']] = [$auditLog['AuditLog']['model_id']];
|
||||
}
|
||||
}
|
||||
|
||||
$eventIds = isset($models['Event']) ? $models['Event'] : [];
|
||||
|
||||
if (isset($models['ObjectReference'])) {
|
||||
$this->loadModel('ObjectReference');
|
||||
$objectReferences = $this->ObjectReference->find('list', [
|
||||
'conditions' => ['ObjectReference.id' => array_unique($models['ObjectReference'])],
|
||||
'fields' => ['ObjectReference.id', 'ObjectReference.object_id'],
|
||||
]);
|
||||
}
|
||||
|
||||
if (isset($models['Object']) || isset($objectReferences)) {
|
||||
$objectIds = array_unique(array_merge(
|
||||
isset($models['Object']) ? $models['Object'] : [],
|
||||
isset($objectReferences) ? array_values($objectReferences) : []
|
||||
));
|
||||
$this->loadModel('MispObject');
|
||||
$conditions = $this->MispObject->buildConditions($this->Auth->user());
|
||||
$conditions['Object.id'] = $objectIds;
|
||||
$objects = $this->MispObject->find('all', [
|
||||
'conditions' => $conditions,
|
||||
'contain' => ['Event'],
|
||||
'fields' => ['Object.id', 'Object.event_id', 'Object.uuid', 'Object.deleted'],
|
||||
]);
|
||||
$objects = array_column(array_column($objects, 'Object'), null, 'id');
|
||||
$eventIds = array_merge($eventIds, array_column($objects, 'event_id'));
|
||||
}
|
||||
|
||||
if (isset($models['Attribute'])) {
|
||||
$this->loadModel('Attribute');
|
||||
$attributes = $this->Attribute->fetchAttributesSimple($this->Auth->user(), [
|
||||
'conditions' => ['Attribute.id' => array_unique($models['Attribute'])],
|
||||
'fields' => ['Attribute.id', 'Attribute.event_id', 'Attribute.uuid', 'Attribute.deleted'],
|
||||
]);
|
||||
$attributes = array_column(array_column($attributes, 'Attribute'), null, 'id');
|
||||
$eventIds = array_merge($eventIds, array_column($attributes, 'event_id'));
|
||||
}
|
||||
|
||||
if (isset($models['ShadowAttribute'])) {
|
||||
$this->loadModel('ShadowAttribute');
|
||||
$conditions = $this->ShadowAttribute->buildConditions($this->Auth->user());
|
||||
$conditions['AND'][] = ['ShadowAttribute.id' => array_unique($models['ShadowAttribute'])];
|
||||
$shadowAttributes = $this->ShadowAttribute->find('all', [
|
||||
'conditions' => $conditions,
|
||||
'fields' => ['ShadowAttribute.id', 'ShadowAttribute.event_id', 'ShadowAttribute.uuid', 'ShadowAttribute.deleted'],
|
||||
'contain' => ['Event', 'Attribute'],
|
||||
]);
|
||||
$shadowAttributes = array_column(array_column($shadowAttributes, 'ShadowAttribute'), null, 'id');
|
||||
$eventIds = array_merge($eventIds, array_column($shadowAttributes, 'event_id'));
|
||||
}
|
||||
|
||||
if (!empty($eventIds)) {
|
||||
$this->loadModel('Event');
|
||||
$events = $this->Event->fetchSimpleEvents($this->Auth->user(), [
|
||||
'conditions' => ['Event.id' => array_unique($eventIds)],
|
||||
]);
|
||||
$events = array_column(array_column($events, 'Event'), null, 'id');
|
||||
}
|
||||
|
||||
$existingObjects = [];
|
||||
foreach (['User', 'Organisation', 'Galaxy', 'GalaxyCluster', 'Warninglist'] as $modelName) {
|
||||
if (isset($models[$modelName])) {
|
||||
$this->loadModel($modelName);
|
||||
$data = $this->{$modelName}->find('column', [
|
||||
'conditions' => ['id' => array_unique($models[$modelName])],
|
||||
'fields' => ['id'],
|
||||
]);
|
||||
$existingObjects[$modelName] = array_flip($data);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($auditLogs as $k => $auditLog) {
|
||||
$auditLog = $auditLog['AuditLog'];
|
||||
$modelId = $auditLog['model_id'];
|
||||
$url = null;
|
||||
$eventInfo = null;
|
||||
switch ($auditLog['model']) {
|
||||
case 'Event':
|
||||
if (isset($events[$modelId])) {
|
||||
$url = '/events/view/' . $modelId;
|
||||
$eventInfo = $events[$modelId]['info'];
|
||||
}
|
||||
break;
|
||||
case 'ObjectReference':
|
||||
if (isset($objectReferences[$modelId]) && isset($objects[$objectReferences[$modelId]])) {
|
||||
$url = '/events/view/' . $objects[$objectReferences[$modelId]]['event_id'] . '/focus:' . $objects[$objectReferences[$modelId]]['uuid'];
|
||||
if ($objects[$objectReferences[$modelId]]['deleted']) {
|
||||
$url .= '/deleted:2';
|
||||
}
|
||||
if (isset($events[$objects[$objectReferences[$modelId]]['event_id']])) {
|
||||
$eventInfo = $events[$objects[$objectReferences[$modelId]]['event_id']]['info'];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'Object':
|
||||
if (isset($objects[$modelId])) {
|
||||
$url = '/events/view/' . $objects[$modelId]['event_id'] . '/focus:' . $objects[$modelId]['uuid'];
|
||||
if ($objects[$modelId]['deleted']) {
|
||||
$url .= '/deleted:2';
|
||||
}
|
||||
if (isset($events[$objects[$modelId]['event_id']])) {
|
||||
$eventInfo = $events[$objects[$modelId]['event_id']]['info'];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'Attribute':
|
||||
if (isset($attributes[$modelId])) {
|
||||
$url = '/events/view/' . $attributes[$modelId]['event_id'] . '/focus:' . $attributes[$modelId]['uuid'];
|
||||
if ($attributes[$modelId]['deleted']) {
|
||||
$url .= '/deleted:2';
|
||||
}
|
||||
if (isset($events[$attributes[$modelId]['event_id']])) {
|
||||
$eventInfo = $events[$attributes[$modelId]['event_id']]['info'];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'ShadowAttribute':
|
||||
if (isset($shadowAttributes[$modelId])) {
|
||||
$url = '/events/view/' . $shadowAttributes[$modelId]['event_id'] . '/focus:' . $shadowAttributes[$modelId]['uuid'];
|
||||
if (isset($events[$shadowAttributes[$modelId]['event_id']])) {
|
||||
$eventInfo = $events[$shadowAttributes[$modelId]['event_id']]['info'];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'User':
|
||||
if (isset($existingObjects['User'][$modelId])) {
|
||||
$url = '/admin/users/view/' . $modelId;
|
||||
}
|
||||
break;
|
||||
case 'Warninglist':
|
||||
if (isset($existingObjects['Warninglist'][$modelId])) {
|
||||
$url = '/warninglists/view/' . $modelId;
|
||||
}
|
||||
break;
|
||||
case 'Organisation':
|
||||
if (isset($existingObjects['Organisation'][$modelId])) {
|
||||
$url = '/organisation/view/' . $modelId;
|
||||
}
|
||||
break;
|
||||
case 'Galaxy':
|
||||
if (isset($existingObjects['Galaxy'][$modelId])) {
|
||||
$url = '/galaxies/view/' . $modelId;
|
||||
}
|
||||
break;
|
||||
case 'GalaxyCluster':
|
||||
if (isset($existingObjects['GalaxyCluster'][$modelId])) {
|
||||
$url = '/galaxy_clusters/view/' . $modelId;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
continue 2;
|
||||
}
|
||||
if ($url) {
|
||||
$auditLogs[$k]['AuditLog']['model_link'] = $this->baseurl . $url;
|
||||
}
|
||||
if ($eventInfo) {
|
||||
$auditLogs[$k]['AuditLog']['event_info'] = $eventInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return $auditLogs;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -377,6 +377,10 @@ class ACLComponent extends Component
|
|||
'testForStolenAttributes' => array(),
|
||||
'pruneUpdateLogs' => array()
|
||||
),
|
||||
'auditLogs' => [
|
||||
'admin_index' => ['perm_audit'],
|
||||
'eventIndex' => ['*'],
|
||||
],
|
||||
'modules' => array(
|
||||
'index' => array('perm_auth'),
|
||||
'queryEnrichment' => array('perm_auth'),
|
||||
|
|
|
@ -118,6 +118,8 @@ class LogsController extends AppController
|
|||
'deleted_proposals' => 1,
|
||||
'noSightings' => true,
|
||||
'noEventReports' => true,
|
||||
'includeEventCorrelations' => false,
|
||||
'excludeGalaxy' => true,
|
||||
));
|
||||
if (empty($event)) {
|
||||
throw new NotFoundException('Invalid event.');
|
||||
|
@ -125,7 +127,7 @@ class LogsController extends AppController
|
|||
$event = $event[0];
|
||||
$attribute_ids = array();
|
||||
$object_ids = array();
|
||||
$proposal_ids = array();
|
||||
$proposal_ids = array_column($event['ShadowAttribute'], 'id');;
|
||||
if (!empty($event['Attribute'])) {
|
||||
foreach ($event['Attribute'] as $aa) {
|
||||
$attribute_ids[] = $aa['id'];
|
||||
|
|
|
@ -6,6 +6,7 @@ class AdminSetting extends AppModel
|
|||
public $useTable = 'admin_settings';
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array(
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
|
|
|
@ -9,6 +9,7 @@ class Allowedlist extends AppModel
|
|||
public $displayField = 'name';
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Trim',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'roleModel' => 'Role',
|
||||
|
|
|
@ -89,7 +89,8 @@ class AppModel extends Model
|
|||
45 => false, 46 => false, 47 => false, 48 => false, 49 => false, 50 => false,
|
||||
51 => false, 52 => false, 53 => false, 54 => false, 55 => false, 56 => false,
|
||||
57 => false, 58 => false, 59 => false, 60 => false, 61 => false, 62 => false,
|
||||
63 => true, 64 => false, 65 => false, 66 => false, 67 => false, 68 => false
|
||||
63 => true, 64 => false, 65 => false, 66 => false, 67 => false, 68 => false,
|
||||
69 => false,
|
||||
);
|
||||
|
||||
public $advanced_updates_description = array(
|
||||
|
@ -1578,6 +1579,27 @@ class AppModel extends Model
|
|||
case 68:
|
||||
$sqlArray[] = "ALTER TABLE `correlation_exclusions` ADD `comment` text DEFAULT NULL;";
|
||||
break;
|
||||
case 69:
|
||||
$sqlArray[] = "CREATE TABLE IF NOT EXISTS `audit_logs` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`created` datetime NOT NULL,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`org_id` int(11) NOT NULL,
|
||||
`authkey_id` int(11) DEFAULT NULL,
|
||||
`ip` varbinary(16) DEFAULT NULL,
|
||||
`request_type` tinyint NOT NULL,
|
||||
`request_id` varchar(255) DEFAULT NULL,
|
||||
`action` varchar(20) NOT NULL,
|
||||
`model` varchar(80) NOT NULL,
|
||||
`model_id` int(11) NOT NULL,
|
||||
`model_title` text DEFAULT NULL,
|
||||
`event_id` int(11) NULL,
|
||||
`change` blob,
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `event_id` (`event_id`),
|
||||
INDEX `model_id` (`model_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
|
||||
break;
|
||||
case 'fixNonEmptySharingGroupID':
|
||||
$sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
|
||||
$sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
|
||||
|
|
|
@ -23,6 +23,7 @@ class Attribute extends AppModel
|
|||
public $name = 'Attribute'; // TODO general
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
|
|
|
@ -7,7 +7,7 @@ App::uses('AppModel', 'Model');
|
|||
*/
|
||||
class AttributeTag extends AppModel
|
||||
{
|
||||
public $actsAs = array('Containable');
|
||||
public $actsAs = array('AuditLog', 'Containable');
|
||||
|
||||
public $validate = array(
|
||||
'attribute_id' => array(
|
||||
|
|
|
@ -0,0 +1,323 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
|
||||
/**
|
||||
* @property Event $Event
|
||||
* @property User $User
|
||||
* @property Organisation $Organisation
|
||||
*/
|
||||
class AuditLog extends AppModel
|
||||
{
|
||||
const BROTLI_HEADER = "\xce\xb2\xcf\x81";
|
||||
const BROTLI_MIN_LENGTH = 200;
|
||||
|
||||
const ACTION_ADD = 'add',
|
||||
ACTION_EDIT = 'edit',
|
||||
ACTION_SOFT_DELETE = 'soft_delete',
|
||||
ACTION_DELETE = 'delete',
|
||||
ACTION_UNDELETE = 'undelete',
|
||||
ACTION_TAG = 'tag',
|
||||
ACTION_TAG_LOCAL = 'tag_local',
|
||||
ACTION_REMOVE_TAG = 'remove_tag',
|
||||
ACTION_REMOVE_TAG_LOCAL = 'remove_local_tag',
|
||||
ACTION_GALAXY = 'galaxy',
|
||||
ACTION_GALAXY_LOCAL = 'galaxy_local',
|
||||
ACTION_REMOVE_GALAXY = 'remove_galaxy',
|
||||
ACTION_REMOVE_GALAXY_LOCAL = 'remove_local_galaxy',
|
||||
ACTION_PUBLISH = 'publish',
|
||||
ACTION_PUBLISH_SIGHTINGS = 'publish_sightings';
|
||||
|
||||
const REQUEST_TYPE_DEFAULT = 0,
|
||||
REQUEST_TYPE_API = 1,
|
||||
REQUEST_TYPE_CLI = 2;
|
||||
|
||||
public $actsAs = [
|
||||
'Containable',
|
||||
];
|
||||
|
||||
/** @var array|null */
|
||||
private $user = null;
|
||||
|
||||
/** @var bool */
|
||||
private $compressionEnabled;
|
||||
|
||||
/**
|
||||
* Null when not defined, false when not enabled
|
||||
* @var Syslog|null|false
|
||||
*/
|
||||
private $syslog;
|
||||
|
||||
public $compressionStats = [
|
||||
'compressed' => 0,
|
||||
'bytes_compressed' => 0,
|
||||
'bytes_uncompressed' => 0,
|
||||
];
|
||||
|
||||
public $belongsTo = [
|
||||
'User' => [
|
||||
'className' => 'User',
|
||||
'foreignKey' => 'user_id',
|
||||
],
|
||||
'Event' => [
|
||||
'className' => 'Event',
|
||||
'foreignKey' => 'event_id',
|
||||
],
|
||||
'Organisation' => [
|
||||
'className' => 'Organisation',
|
||||
'foreignKey' => 'org_id',
|
||||
],
|
||||
];
|
||||
|
||||
public function __construct($id = false, $table = null, $ds = null)
|
||||
{
|
||||
parent::__construct($id, $table, $ds);
|
||||
$this->compressionEnabled = Configure::read('MISP.log_new_audit_compress') && function_exists('brotli_compress');
|
||||
}
|
||||
|
||||
public function afterFind($results, $primary = false)
|
||||
{
|
||||
foreach ($results as $key => $result) {
|
||||
if (isset($result['AuditLog']['ip'])) {
|
||||
$results[$key]['AuditLog']['ip'] = inet_ntop($result['AuditLog']['ip']);
|
||||
}
|
||||
if (isset($result['AuditLog']['change']) && $result['AuditLog']['change']) {
|
||||
$results[$key]['AuditLog']['change'] = $this->decodeChange($result['AuditLog']['change']);
|
||||
}
|
||||
if (isset($result['AuditLog']['action']) && isset($result['AuditLog']['model']) && isset($result['AuditLog']['model_id'])) {
|
||||
$results[$key]['AuditLog']['title'] = $this->generateUserFriendlyTitle($result['AuditLog']);
|
||||
}
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $auditLog
|
||||
* @return string
|
||||
*/
|
||||
private function generateUserFriendlyTitle(array $auditLog)
|
||||
{
|
||||
if (in_array($auditLog['action'], [self::ACTION_TAG, self::ACTION_TAG_LOCAL, self::ACTION_REMOVE_TAG, self::ACTION_REMOVE_TAG_LOCAL], true)) {
|
||||
$attached = ($auditLog['action'] === self::ACTION_TAG || $auditLog['action'] === self::ACTION_TAG_LOCAL);
|
||||
$local = ($auditLog['action'] === self::ACTION_TAG_LOCAL || $auditLog['action'] === self::ACTION_REMOVE_TAG_LOCAL) ? 'local' : 'global';
|
||||
if ($attached) {
|
||||
return __('Attached %s tag "%s" to %s #%s', $local, $auditLog['model_title'], strtolower($auditLog['model']), $auditLog['model_id']);
|
||||
} else {
|
||||
return __('Detached %s tag "%s" from %s #%s', $local, $auditLog['model_title'], strtolower($auditLog['model']), $auditLog['model_id']);
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($auditLog['action'], [self::ACTION_GALAXY, self::ACTION_GALAXY_LOCAL, self::ACTION_REMOVE_GALAXY, self::ACTION_REMOVE_GALAXY_LOCAL], true)) {
|
||||
$attached = ($auditLog['action'] === self::ACTION_GALAXY || $auditLog['action'] === self::ACTION_GALAXY_LOCAL);
|
||||
$local = ($auditLog['action'] === self::ACTION_GALAXY_LOCAL || $auditLog['action'] === self::ACTION_REMOVE_GALAXY_LOCAL) ? 'local' : 'global';
|
||||
if ($attached) {
|
||||
return __('Attached %s galaxy cluster "%s" to %s #%s', $local, $auditLog['model_title'], strtolower($auditLog['model']), $auditLog['model_id']);
|
||||
} else {
|
||||
return __('Detached %s galaxy cluster "%s" from %s #%s', $local, $auditLog['model_title'], strtolower($auditLog['model']), $auditLog['model_id']);
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($auditLog['model'], ['Attribute', 'Object', 'ShadowAttribute'], true)) {
|
||||
$modelName = $auditLog['model'] === 'ShadowAttribute' ? 'Proposal' : $auditLog['model'];
|
||||
$title = __('%s from Event #%s', $modelName, $auditLog['event_id']);
|
||||
} else {
|
||||
$title = "{$auditLog['model']} #{$auditLog['model_id']}";
|
||||
}
|
||||
if (isset($auditLog['model_title']) && $auditLog['model_title']) {
|
||||
$title .= ": {$auditLog['model_title']}";
|
||||
}
|
||||
return $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $change
|
||||
* @return array|string
|
||||
* @throws JsonException
|
||||
*/
|
||||
private function decodeChange($change)
|
||||
{
|
||||
if (substr($change, 0, 4) === self::BROTLI_HEADER) {
|
||||
$this->compressionStats['compressed']++;
|
||||
if (function_exists('brotli_uncompress')) {
|
||||
$this->compressionStats['bytes_compressed'] += strlen($change);
|
||||
$change = brotli_uncompress(substr($change, 4));
|
||||
$this->compressionStats['bytes_uncompressed'] += strlen($change);
|
||||
if ($change === false) {
|
||||
return 'Compressed';
|
||||
}
|
||||
} else {
|
||||
return 'Compressed';
|
||||
}
|
||||
}
|
||||
return $this->jsonDecode($change);
|
||||
}
|
||||
|
||||
public function beforeValidate($options = array())
|
||||
{
|
||||
if (isset($this->data['AuditLog']['change']) && !is_array($this->data['AuditLog']['change'])) {
|
||||
$this->invalidate('change', 'Change field must be array');
|
||||
}
|
||||
}
|
||||
|
||||
public function beforeSave($options = array())
|
||||
{
|
||||
if (!isset($this->data['AuditLog']['ip']) && Configure::read('MISP.log_client_ip')) {
|
||||
$ipHeader = Configure::read('MISP.log_client_ip_header') ?: 'REMOTE_ADDR';
|
||||
if (isset($_SERVER[$ipHeader])) {
|
||||
$this->data['AuditLog']['ip'] = inet_pton($_SERVER[$ipHeader]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($this->data['AuditLog']['user_id'])) {
|
||||
$this->data['AuditLog']['user_id'] = $this->userInfo()['id'];
|
||||
}
|
||||
|
||||
if (!isset($this->data['AuditLog']['org_id'])) {
|
||||
$this->data['AuditLog']['org_id'] = $this->userInfo()['org_id'];
|
||||
}
|
||||
|
||||
if (!isset($this->data['AuditLog']['request_type'])) {
|
||||
$this->data['AuditLog']['request_type'] = $this->userInfo()['request_type'];
|
||||
}
|
||||
|
||||
if (!isset($this->data['AuditLog']['authkey_id'])) {
|
||||
$this->data['AuditLog']['authkey_id'] = $this->userInfo()['authkey_id'];
|
||||
}
|
||||
|
||||
if (!isset($this->data['AuditLog']['request_id'] ) && isset($_SERVER['HTTP_X_REQUEST_ID'])) {
|
||||
$this->data['AuditLog']['request_id'] = $_SERVER['HTTP_X_REQUEST_ID'];
|
||||
}
|
||||
|
||||
// Truncate request_id
|
||||
if (isset($this->data['AuditLog']['request_id']) && strlen($this->data['AuditLog']['request_id']) > 255) {
|
||||
$this->data['AuditLog']['request_id'] = substr($this->data['AuditLog']['request_id'], 0, 255);
|
||||
}
|
||||
|
||||
// Truncate model title
|
||||
if (isset($this->data['AuditLog']['model_title']) && mb_strlen($this->data['AuditLog']['model_title']) > 255) {
|
||||
$this->data['AuditLog']['model_title'] = mb_substr($this->data['AuditLog']['model_title'], 0, 252) . '...';
|
||||
}
|
||||
|
||||
$this->logData($this->data);
|
||||
|
||||
if (isset($this->data['AuditLog']['change'])) {
|
||||
$change = json_encode($this->data['AuditLog']['change'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
if ($this->compressionEnabled && strlen($change) >= self::BROTLI_MIN_LENGTH) {
|
||||
$change = self::BROTLI_HEADER . brotli_compress($change, 4, BROTLI_TEXT);
|
||||
}
|
||||
$this->data['AuditLog']['change'] = $change;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
private function logData(array $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);
|
||||
}
|
||||
|
||||
// write to syslogd as well if enabled
|
||||
if ($this->syslog === null) {
|
||||
if (Configure::read('Security.syslog')) {
|
||||
$options = [];
|
||||
$syslogToStdErr = Configure::read('Security.syslog_to_stderr');
|
||||
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;
|
||||
}
|
||||
}
|
||||
if ($this->syslog) {
|
||||
$entry = $data['AuditLog']['action'];
|
||||
$title = $this->generateUserFriendlyTitle($data['AuditLog']);
|
||||
if ($title) {
|
||||
$entry .= " -- $title";
|
||||
}
|
||||
$this->syslog->write('info', $entry);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function userInfo()
|
||||
{
|
||||
if ($this->user !== null) {
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
$this->user = ['id' => 0, 'org_id' => 0, 'authkey_id' => 0, 'request_type' => self::REQUEST_TYPE_DEFAULT];
|
||||
|
||||
$isShell = defined('CAKEPHP_SHELL') && CAKEPHP_SHELL;
|
||||
if ($isShell) {
|
||||
// do not start session for shell commands and fetch user info from configuration
|
||||
$this->user['request_type'] = self::REQUEST_TYPE_CLI;
|
||||
$currentUserId = Configure::read('CurrentUserId');
|
||||
if (!empty($currentUserId)) {
|
||||
$this->user['id'] = $currentUserId;
|
||||
$userFromDb = $this->User->find('first', [
|
||||
'conditions' => ['User.id' => $currentUserId],
|
||||
'fields' => ['User.org_id'],
|
||||
]);
|
||||
$this->user['org_id'] = $userFromDb['User']['org_id'];
|
||||
}
|
||||
} else {
|
||||
App::uses('AuthComponent', 'Controller/Component');
|
||||
$authUser = AuthComponent::user();
|
||||
if (!empty($authUser)) {
|
||||
$this->user['id'] = $authUser['id'];
|
||||
$this->user['org_id'] = $authUser['org_id'];
|
||||
if (isset($authUser['logged_by_authkey']) && $authUser['logged_by_authkey']) {
|
||||
$this->user['request_type'] = self::REQUEST_TYPE_API;
|
||||
}
|
||||
if (isset($authUser['authkey_id'])) {
|
||||
$this->user['authkey_id'] = $authUser['authkey_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function insert(array $data)
|
||||
{
|
||||
try {
|
||||
$this->create();
|
||||
} catch (Exception $e) {
|
||||
return; // Table is missing when updating, so this is intentional
|
||||
}
|
||||
if ($this->save($data) === false) {
|
||||
throw new Exception($this->validationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
public function recompress()
|
||||
{
|
||||
$changes = $this->find('all', [
|
||||
'fields' => ['AuditLog.id', 'AuditLog.change'],
|
||||
'recursive' => -1,
|
||||
'conditions' => ['length(AuditLog.change) >=' => self::BROTLI_MIN_LENGTH],
|
||||
]);
|
||||
foreach ($changes as $change) {
|
||||
$this->save($change, true, ['id', 'change']);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ class AuthKey extends AppModel
|
|||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array(
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
|
|
|
@ -0,0 +1,351 @@
|
|||
<?php
|
||||
App::uses('AuditLog', 'Model');
|
||||
|
||||
class AuditLogBehavior extends ModelBehavior
|
||||
{
|
||||
/** @var array */
|
||||
private $config;
|
||||
|
||||
/** @var array|null */
|
||||
private $old;
|
||||
|
||||
/** @var AuditLog|null */
|
||||
private $AuditLog;
|
||||
|
||||
/** @var bool */
|
||||
private $enabled;
|
||||
|
||||
private $skipFields = [
|
||||
'id' => true,
|
||||
'lastpushedid' => true,
|
||||
'timestamp' => true,
|
||||
'revision' => true,
|
||||
'modified' => true,
|
||||
'date_modified' => true, // User
|
||||
'current_login' => true, // User
|
||||
'last_login' => true, // User
|
||||
'newsread' => true, // User
|
||||
'proposal_email_lock' => true, // Event
|
||||
];
|
||||
|
||||
private $modelInfo = [
|
||||
'Event' => 'info',
|
||||
'User' => 'email',
|
||||
'Object' => 'name',
|
||||
'EventReport' => 'name',
|
||||
'Server' => 'name',
|
||||
'Feed' => 'name',
|
||||
'Role' => 'name',
|
||||
'SharingGroup' => 'name',
|
||||
'Tag' => 'name',
|
||||
'TagCollection' => 'name',
|
||||
'Taxonomy' => 'namespace',
|
||||
'Organisation' => 'name',
|
||||
'AdminSetting' => 'setting',
|
||||
'UserSetting' => 'setting',
|
||||
'Galaxy' => 'name',
|
||||
'GalaxyCluster' => 'value',
|
||||
'Warninglist' => 'name',
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->enabled = Configure::read('MISP.log_new_audit');
|
||||
}
|
||||
|
||||
public function setup(Model $model, $config = [])
|
||||
{
|
||||
$this->config = $config;
|
||||
// Generate model info for attribute and proposals
|
||||
$attributeInfo = function (array $new, array $old) {
|
||||
$category = isset($new['category']) ? $new['category'] : $old['category'];
|
||||
$type = isset($new['type']) ? $new['type'] : $old['type'];
|
||||
$value1 = trim(isset($new['value1']) ? $new['value1'] : $old['value1']);
|
||||
$value2 = trim(isset($new['value2']) ? $new['value2'] : $old['value2']);
|
||||
$value = $value1 . (empty($value2) ? '' : '|' . $value2);
|
||||
return "$category/$type $value";
|
||||
};
|
||||
$this->modelInfo['Attribute'] = $attributeInfo;
|
||||
$this->modelInfo['ShadowAttribute'] = $attributeInfo;
|
||||
}
|
||||
|
||||
public function beforeSave(Model $model, $options = [])
|
||||
{
|
||||
if (!$this->enabled) {
|
||||
return true;
|
||||
}
|
||||
if ($model->id) {
|
||||
$this->old = $model->find('first', [
|
||||
'conditions' => [$model->alias . '.' . $model->primaryKey => $model->id],
|
||||
'recursive' => -1,
|
||||
'callbacks' => false,
|
||||
]);
|
||||
} else {
|
||||
$this->old = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function afterSave(Model $model, $created, $options = [])
|
||||
{
|
||||
if (!$this->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($model->id) {
|
||||
$id = $model->id;
|
||||
} else if ($model->insertId) {
|
||||
$id = $model->insertId;
|
||||
} else {
|
||||
$id = null;
|
||||
}
|
||||
|
||||
if ($created) {
|
||||
$action = AuditLog::ACTION_ADD;
|
||||
} else {
|
||||
$action = AuditLog::ACTION_EDIT;
|
||||
if (isset($model->data[$model->alias]['deleted'])) {
|
||||
if ($model->data[$model->alias]['deleted']) {
|
||||
$action = AuditLog::ACTION_SOFT_DELETE;
|
||||
} else if (!$model->data[$model->alias]['deleted'] && $this->old[$model->alias]['deleted']) {
|
||||
$action = AuditLog::ACTION_UNDELETE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$changedFields = $this->changedFields($model, isset($options['fieldList']) ? $options['fieldList'] : null);
|
||||
if (empty($changedFields)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($model->name === 'Event') {
|
||||
$eventId = $id;
|
||||
} else if (isset($model->data[$model->alias]['event_id'])) {
|
||||
$eventId = $model->data[$model->alias]['event_id'];
|
||||
} else if (isset($this->old[$model->alias]['event_id'])) {
|
||||
$eventId = $this->old[$model->alias]['event_id'];
|
||||
} else {
|
||||
$eventId = null;
|
||||
}
|
||||
|
||||
$modelTitle = null;
|
||||
if (isset($this->modelInfo[$model->name])) {
|
||||
$modelTitleField = $this->modelInfo[$model->name];
|
||||
if (is_callable($modelTitleField)) {
|
||||
$modelTitle = $modelTitleField($model->data[$model->alias], isset($this->old[$model->alias]) ? $this->old[$model->alias] : []);
|
||||
} else if (isset($model->data[$model->alias][$modelTitleField])) {
|
||||
$modelTitle = $model->data[$model->alias][$modelTitleField];
|
||||
} else if ($this->old[$model->alias][$modelTitleField]) {
|
||||
$modelTitle = $this->old[$model->alias][$modelTitleField];
|
||||
}
|
||||
}
|
||||
|
||||
$modelName = $model->name === 'MispObject' ? 'Object' : $model->name;
|
||||
|
||||
if ($modelName === 'AttributeTag' || $modelName === 'EventTag') {
|
||||
$action = $model->data[$model->alias]['local'] ? AuditLog::ACTION_TAG_LOCAL : AuditLog::ACTION_TAG;
|
||||
$tagInfo = $this->getTagInfo($model, $model->data[$model->alias]['tag_id']);
|
||||
if ($tagInfo) {
|
||||
$modelTitle = $tagInfo['tag_name'];
|
||||
if ($tagInfo['is_galaxy']) {
|
||||
$action = $model->data[$model->alias]['local'] ? AuditLog::ACTION_GALAXY_LOCAL : AuditLog::ACTION_GALAXY;
|
||||
if ($tagInfo['galaxy_cluster_name']) {
|
||||
$modelTitle = $tagInfo['galaxy_cluster_name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$id = $modelName === 'AttributeTag' ? $model->data[$model->alias]['attribute_id'] : $model->data[$model->alias]['event_id'];
|
||||
$modelName = $modelName === 'AttributeTag' ? 'Attribute' : 'Event';
|
||||
}
|
||||
|
||||
if ($modelName === 'Event') {
|
||||
if (isset($changedFields['published'][1]) && $changedFields['published'][1]) {
|
||||
$action = AuditLog::ACTION_PUBLISH;
|
||||
} else if (isset($changedFields['sighting_timestamp'][1]) && $changedFields['sighting_timestamp'][1]) {
|
||||
$action = AuditLog::ACTION_PUBLISH_SIGHTINGS;
|
||||
}
|
||||
}
|
||||
|
||||
$this->auditLog()->insert([
|
||||
'action' => $action,
|
||||
'model' => $modelName,
|
||||
'model_id' => $id,
|
||||
'model_title' => $modelTitle,
|
||||
'event_id' => $eventId,
|
||||
'change' => $changedFields,
|
||||
]);
|
||||
}
|
||||
|
||||
public function beforeDelete(Model $model, $cascade = true)
|
||||
{
|
||||
if (!$this->enabled) {
|
||||
return true;
|
||||
}
|
||||
$model->recursive = -1;
|
||||
$model->read();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function afterDelete(Model $model)
|
||||
{
|
||||
if (!$this->enabled) {
|
||||
return;
|
||||
}
|
||||
if ($model->name === 'Event') {
|
||||
$eventId = $model->id;
|
||||
} else {
|
||||
$eventId = isset($model->data[$model->alias]['event_id']) ? $model->data[$model->alias]['event_id'] : null;
|
||||
}
|
||||
|
||||
$modelTitle = null;
|
||||
if (isset($this->modelInfo[$model->name])) {
|
||||
$modelTitleField = $this->modelInfo[$model->name];
|
||||
if (is_callable($modelTitleField)) {
|
||||
$modelTitle = $modelTitleField($model->data[$model->alias], []);
|
||||
} else if (isset($model->data[$model->alias][$modelTitleField])) {
|
||||
$modelTitle = $model->data[$model->alias][$modelTitleField];
|
||||
}
|
||||
}
|
||||
|
||||
$modelName = $model->name === 'MispObject' ? 'Object' : $model->name;
|
||||
$action = AuditLog::ACTION_DELETE;
|
||||
$id = $model->id;
|
||||
|
||||
if ($modelName === 'AttributeTag' || $modelName === 'EventTag') {
|
||||
$action = $model->data[$model->alias]['local'] ? AuditLog::ACTION_REMOVE_TAG_LOCAL : AuditLog::ACTION_REMOVE_TAG;
|
||||
$tagInfo = $this->getTagInfo($model, $model->data[$model->alias]['tag_id']);
|
||||
if ($tagInfo) {
|
||||
$modelTitle = $tagInfo['tag_name'];
|
||||
if ($tagInfo['is_galaxy']) {
|
||||
$action = $model->data[$model->alias]['local'] ? AuditLog::ACTION_REMOVE_GALAXY_LOCAL : AuditLog::ACTION_REMOVE_GALAXY;
|
||||
if ($tagInfo['galaxy_cluster_name']) {
|
||||
$modelTitle = $tagInfo['galaxy_cluster_name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$id = $modelName === 'AttributeTag' ? $model->data[$model->alias]['attribute_id'] : $model->data[$model->alias]['event_id'];
|
||||
$modelName = $modelName === 'AttributeTag' ? 'Attribute' : 'Event';
|
||||
}
|
||||
|
||||
$this->auditLog()->insert([
|
||||
'action' => $action,
|
||||
'model' => $modelName,
|
||||
'model_id' => $id,
|
||||
'model_title' => $modelTitle,
|
||||
'event_id' => $eventId,
|
||||
'change' => $this->changedFields($model),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model $model
|
||||
* @param int $tagId
|
||||
* @return array|null
|
||||
*/
|
||||
private function getTagInfo(Model $model, $tagId)
|
||||
{
|
||||
$tag = $model->Tag->find('first', [
|
||||
'conditions' => ['Tag.id' => $tagId],
|
||||
'recursive' => -1,
|
||||
'fields' => ['Tag.name', 'Tag.is_galaxy'],
|
||||
]);
|
||||
if (empty($tag)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$galaxyClusterName = null;
|
||||
if ($tag['Tag']['is_galaxy']) {
|
||||
if (!isset($this->GalaxyCluster)) {
|
||||
$this->GalaxyCluster = ClassRegistry::init('GalaxyCluster');
|
||||
}
|
||||
$galaxyCluster = $this->GalaxyCluster->find('first', [
|
||||
'conditions' => ['GalaxyCluster.tag_name' => $tag['Tag']['name']],
|
||||
'recursive' => -1,
|
||||
'fields' => ['GalaxyCluster.value'],
|
||||
]);
|
||||
if (!empty($galaxyCluster)) {
|
||||
$galaxyClusterName = $galaxyCluster['GalaxyCluster']['value'];
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'tag_name' => $tag['Tag']['name'],
|
||||
'is_galaxy' => $tag['Tag']['is_galaxy'],
|
||||
'galaxy_cluster_name' => $galaxyClusterName,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Model $model
|
||||
* @param array|null $fieldsToSave
|
||||
* @return array
|
||||
*/
|
||||
private function changedFields(Model $model, $fieldsToSave = null)
|
||||
{
|
||||
$dbFields = $model->schema();
|
||||
$changedFields = [];
|
||||
foreach ($model->data[$model->alias] as $key => $value) {
|
||||
if (isset($this->skipFields[$key])) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($dbFields[$key])) {
|
||||
continue;
|
||||
}
|
||||
if ($fieldsToSave && !in_array($key, $fieldsToSave, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($model->data[$model->alias][$model->primaryKey]) && isset($this->old[$model->alias][$key])) {
|
||||
$old = $this->old[$model->alias][$key];
|
||||
} else {
|
||||
$old = null;
|
||||
}
|
||||
|
||||
// Normalize
|
||||
if (is_bool($old)) {
|
||||
$old = $old ? 1 : 0;
|
||||
}
|
||||
if (is_bool($value)) {
|
||||
$value = $value ? 1 : 0;
|
||||
}
|
||||
$dbType = $dbFields[$key]['type'];
|
||||
if ($dbType === 'integer' || $dbType === 'tinyinteger' || $dbType === 'biginteger' || $dbType === 'boolean') {
|
||||
$value = (int)$value;
|
||||
if ($old !== null) {
|
||||
$old = (int)$old;
|
||||
}
|
||||
}
|
||||
|
||||
if ($value == $old) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($key === 'password' || $key === 'authkey') {
|
||||
$value = '*****';
|
||||
if ($old !== null) {
|
||||
$old = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if ($old === null) {
|
||||
$changedFields[$key] = $value;
|
||||
} else {
|
||||
$changedFields[$key] = [$old, $value];
|
||||
}
|
||||
}
|
||||
|
||||
return $changedFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AuditLog
|
||||
*/
|
||||
private function auditLog()
|
||||
{
|
||||
if ($this->AuditLog === null) {
|
||||
$this->AuditLog = ClassRegistry::init('AuditLog');
|
||||
}
|
||||
return $this->AuditLog;
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ App::uses('AppModel', 'Model');
|
|||
class Cerebrate extends AppModel
|
||||
{
|
||||
public $actsAs = [
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => [
|
||||
'roleModel' => 'Role',
|
||||
'roleKey' => 'role_id',
|
||||
|
|
|
@ -9,6 +9,7 @@ class CorrelationExclusion extends AppModel
|
|||
public $key = 'misp:correlation_exclusions';
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array(
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
|
|
|
@ -17,6 +17,7 @@ App::uses('SendEmailTemplate', 'Tools');
|
|||
class Event extends AppModel
|
||||
{
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array(
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
|
|
|
@ -8,6 +8,7 @@ class EventBlocklist extends AppModel
|
|||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
|
|
|
@ -3,7 +3,7 @@ App::uses('AppModel', 'Model');
|
|||
|
||||
class EventDelegation extends AppModel
|
||||
{
|
||||
public $actsAs = array('Containable');
|
||||
public $actsAs = array('AuditLog', 'Containable');
|
||||
|
||||
public $validate = array(
|
||||
'event_id' => array(
|
||||
|
|
|
@ -7,6 +7,7 @@ App::uses('AppModel', 'Model');
|
|||
class EventReport extends AppModel
|
||||
{
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Containable',
|
||||
'SysLogLogable.SysLogLogable' => array(
|
||||
'userModel' => 'User',
|
||||
|
|
|
@ -6,7 +6,7 @@ App::uses('AppModel', 'Model');
|
|||
*/
|
||||
class EventTag extends AppModel
|
||||
{
|
||||
public $actsAs = array('Containable');
|
||||
public $actsAs = array('AuditLog', 'Containable');
|
||||
|
||||
public $validate = array(
|
||||
'event_id' => array(
|
||||
|
|
|
@ -5,7 +5,9 @@ App::uses('TmpFileTool', 'Tools');
|
|||
|
||||
class Feed extends AppModel
|
||||
{
|
||||
public $actsAs = array('SysLogLogable.SysLogLogable' => array(
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array(
|
||||
'change' => 'full'
|
||||
),
|
||||
'Trim',
|
||||
|
|
|
@ -7,6 +7,7 @@ class Galaxy extends AppModel
|
|||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
|
|
|
@ -13,6 +13,7 @@ class GalaxyCluster extends AppModel
|
|||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
|
|
|
@ -7,6 +7,7 @@ class GalaxyClusterBlocklist extends AppModel
|
|||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
|
|
|
@ -8,6 +8,7 @@ class GalaxyClusterRelation extends AppModel
|
|||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
|
|
|
@ -4,7 +4,7 @@ App::uses('AppModel', 'Model');
|
|||
class GalaxyClusterRelationTag extends AppModel
|
||||
{
|
||||
public $useTable = 'galaxy_cluster_relation_tags';
|
||||
public $actsAs = array('Containable');
|
||||
public $actsAs = array('AuditLog', 'Containable');
|
||||
|
||||
public $validate = array(
|
||||
'galaxy_cluster_relation_id' => array(
|
||||
|
|
|
@ -7,6 +7,7 @@ class GalaxyElement extends AppModel
|
|||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Containable',
|
||||
);
|
||||
|
||||
|
|
|
@ -206,6 +206,9 @@ class Log extends AppModel
|
|||
*/
|
||||
public function createLogEntry($user, $action, $model, $modelId = 0, $title = '', $change = '')
|
||||
{
|
||||
if (in_array($action, ['tag', 'galaxy', 'publish', 'publish_sightings'], true) && Configure::read('MISP.log_new_audit')) {
|
||||
return; // Do not store tag changes when new audit is enabled
|
||||
}
|
||||
if ($user === 'SYSTEM') {
|
||||
$user = array('Organisation' => array('name' => 'SYSTEM'), 'email' => 'SYSTEM', 'id' => 0);
|
||||
} else if (!is_array($user)) {
|
||||
|
|
|
@ -15,6 +15,7 @@ class MispObject extends AppModel
|
|||
public $useTable = 'objects';
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Containable',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'userModel' => 'User',
|
||||
|
|
|
@ -3,7 +3,7 @@ App::uses('AppModel', 'Model');
|
|||
|
||||
class News extends AppModel
|
||||
{
|
||||
public $actsAs = array('Containable');
|
||||
public $actsAs = array('AuditLog', 'Containable');
|
||||
|
||||
public $validate = array(
|
||||
'message' => array(
|
||||
|
|
|
@ -5,6 +5,7 @@ App::uses('AppModel', 'Model');
|
|||
class ObjectReference extends AppModel
|
||||
{
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Containable',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'userModel' => 'User',
|
||||
|
|
|
@ -5,6 +5,7 @@ App::uses('AppModel', 'Model');
|
|||
class ObjectRelationship extends AppModel
|
||||
{
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Containable',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'userModel' => 'User',
|
||||
|
|
|
@ -5,6 +5,7 @@ App::uses('AppModel', 'Model');
|
|||
class ObjectTemplate extends AppModel
|
||||
{
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Containable',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'userModel' => 'User',
|
||||
|
|
|
@ -5,6 +5,7 @@ App::uses('AppModel', 'Model');
|
|||
class ObjectTemplateElement extends AppModel
|
||||
{
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Containable'
|
||||
);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ class OrgBlocklist extends AppModel
|
|||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
|
|
|
@ -12,6 +12,7 @@ class Organisation extends AppModel
|
|||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Containable',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'roleModel' => 'Organisation',
|
||||
|
|
|
@ -8,6 +8,7 @@ App::uses('AppModel', 'Model');
|
|||
class Post extends AppModel
|
||||
{
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Containable',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'roleModel' => 'Post',
|
||||
|
|
|
@ -9,6 +9,7 @@ App::uses('AppModel', 'Model');
|
|||
class Regexp extends AppModel
|
||||
{
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'roleModel' => 'Role',
|
||||
'roleKey' => 'role_id',
|
||||
|
|
|
@ -39,6 +39,7 @@ class Role extends AppModel
|
|||
);
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Trim',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'roleModel' => 'Role',
|
||||
|
|
|
@ -14,7 +14,9 @@ class Server extends AppModel
|
|||
|
||||
public $name = 'Server';
|
||||
|
||||
public $actsAs = array('SysLogLogable.SysLogLogable' => array(
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array(
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
'change' => 'full'
|
||||
|
@ -469,6 +471,7 @@ class Server extends AppModel
|
|||
public function pull($user, $id = null, $technique=false, $server, $jobId = false, $force = false)
|
||||
{
|
||||
if ($jobId) {
|
||||
Configure::write('CurrentUserId', $user['id']);
|
||||
$job = ClassRegistry::init('Job');
|
||||
$job->read(null, $jobId);
|
||||
$email = "Scheduled job";
|
||||
|
|
|
@ -20,6 +20,7 @@ class ShadowAttribute extends AppModel
|
|||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
|
|
|
@ -9,6 +9,7 @@ App::uses('AppModel', 'Model');
|
|||
class SharingGroup extends AppModel
|
||||
{
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Containable',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'roleModel' => 'SharingGroup',
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
App::uses('AppModel', 'Model');
|
||||
class SharingGroupOrg extends AppModel
|
||||
{
|
||||
public $actsAs = array('Containable');
|
||||
public $actsAs = array('AuditLog', 'Containable');
|
||||
|
||||
public $belongsTo = array(
|
||||
'SharingGroup' => array(
|
||||
|
|
|
@ -3,7 +3,7 @@ App::uses('AppModel', 'Model');
|
|||
|
||||
class SharingGroupServer extends AppModel
|
||||
{
|
||||
public $actsAs = array('Containable');
|
||||
public $actsAs = array('AuditLog', 'Containable');
|
||||
|
||||
public $belongsTo = array(
|
||||
'SharingGroup' => array(
|
||||
|
|
|
@ -15,6 +15,7 @@ class Sightingdb extends AppModel
|
|||
);
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array(
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
|
|
|
@ -17,6 +17,7 @@ class SightingdbOrg extends AppModel
|
|||
);
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array(
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
|
|
|
@ -12,6 +12,7 @@ class Tag extends AppModel
|
|||
public $displayField = 'name';
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'roleModel' => 'Tag',
|
||||
'roleKey' => 'tag_id',
|
||||
|
|
|
@ -9,6 +9,7 @@ class TagCollection extends AppModel
|
|||
public $displayField = 'name';
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Trim',
|
||||
'SysLogLogable.SysLogLogable' => array(
|
||||
'roleModel' => 'Role',
|
||||
|
|
|
@ -7,6 +7,7 @@ class TagCollectionTag extends AppModel
|
|||
public $useTable = 'tag_collection_tags';
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Trim',
|
||||
'SysLogLogable.SysLogLogable' => array(
|
||||
'roleModel' => 'Role',
|
||||
|
|
|
@ -11,6 +11,7 @@ class Taxonomy extends AppModel
|
|||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Containable',
|
||||
);
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ App::uses('AppModel', 'Model');
|
|||
class Thread extends AppModel
|
||||
{
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Containable',
|
||||
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
|
||||
'roleModel' => 'Thread',
|
||||
|
|
|
@ -216,6 +216,7 @@ class User extends AppModel
|
|||
);
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array(
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
|
|
|
@ -7,6 +7,7 @@ class UserSetting extends AppModel
|
|||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'SysLogLogable.SysLogLogable' => array(
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
|
|
|
@ -13,6 +13,7 @@ class Warninglist extends AppModel
|
|||
public $recursive = -1;
|
||||
|
||||
public $actsAs = array(
|
||||
'AuditLog',
|
||||
'Containable',
|
||||
);
|
||||
|
||||
|
|
|
@ -2,9 +2,15 @@
|
|||
|
||||
App::import('Lib', 'SysLog.SysLog'); // Audit, syslogd, extra
|
||||
|
||||
class SysLogLogableBehavior extends LogableBehavior {
|
||||
class SysLogLogableBehavior extends LogableBehavior
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->defaults['enabled'] = !Configure::read('MISP.log_new_audit');
|
||||
}
|
||||
|
||||
function afterSave(Model $Model, $created, $options = array()) {
|
||||
function afterSave(Model $Model, $created, $options = array()) {
|
||||
if (!$this->settings[$Model->alias]['enabled']) {
|
||||
return true;
|
||||
}
|
||||
|
@ -264,6 +270,10 @@ class SysLogLogableBehavior extends LogableBehavior {
|
|||
$this->settings[$Model->alias] = array_merge($this->defaults, $config);
|
||||
$this->settings[$Model->alias]['ignore'][] = $Model->primaryKey;
|
||||
|
||||
if (!$this->settings[$Model->alias]['enabled']) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
if ($this->settings[$Model->alias]['userModel'] != $Model->alias) {
|
||||
$this->UserModel = ClassRegistry::init($this->settings[$Model->alias]['userModel']);
|
||||
|
|
|
@ -0,0 +1,390 @@
|
|||
<?php
|
||||
$formatValue = function($field, $value) {
|
||||
if (strpos($field, 'timestamp') !== false && is_numeric($value)) {
|
||||
$date = date('Y-m-d H:i:s', $value);
|
||||
if ($date !== false) {
|
||||
return '<span title="Original value: ' . h($value) . '">' . h($date) . '</span>';
|
||||
}
|
||||
} else if ($field === 'last_seen' || $field === 'first_seen') {
|
||||
$ls_sec = intval($value / 1000000); // $ls is in micro (10^6)
|
||||
$ls_micro = $value % 1000000;
|
||||
$ls_micro = str_pad($ls_micro, 6, "0", STR_PAD_LEFT);
|
||||
$ls = $ls_sec . '.' . $ls_micro;
|
||||
$date = DateTime::createFromFormat('U.u', $ls)->format('Y-m-d\TH:i:s.u');
|
||||
return '<span title="Original value: ' . h($value) . '">' . h($date) . '</span>';
|
||||
}
|
||||
|
||||
if (mb_strlen($value) > 64) {
|
||||
$value = mb_substr($value, 0, 64) . '...';
|
||||
}
|
||||
return h(json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
};
|
||||
|
||||
$removeActions = [
|
||||
AuditLog::ACTION_DELETE => true,
|
||||
AuditLog::ACTION_REMOVE_GALAXY_LOCAL => true,
|
||||
AuditLog::ACTION_REMOVE_GALAXY => true,
|
||||
AuditLog::ACTION_REMOVE_TAG => true,
|
||||
AuditLog::ACTION_REMOVE_TAG_LOCAL => true,
|
||||
];
|
||||
|
||||
?><div class="logs index">
|
||||
<h2><?= __('Audit logs') ?></h2>
|
||||
<div>
|
||||
<div id="builder"></div>
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 5px;">
|
||||
<button id="qbSubmit" type="button" class="btn btn-success" style="margin-right: 5px;"> <i class="fa fa-filter"></i> <?= __('Filter'); ?></button>
|
||||
<button id="qbClear" type="button" class="btn btn-xs btn-danger" title="<?= __('Clear filtering rules'); ?>"> <i class="fa fa-times"></i> <?= __('Clear'); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
echo $this->Html->script('moment-with-locales');
|
||||
echo $this->Html->script('doT');
|
||||
echo $this->Html->script('extendext');
|
||||
echo $this->Html->css('query-builder.default');
|
||||
echo $this->Html->script('query-builder');
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
var qbOptions = {
|
||||
plugins: {
|
||||
'unique-filter': null,
|
||||
'filter-description' : {
|
||||
mode: 'inline'
|
||||
},
|
||||
},
|
||||
conditions: ['AND'],
|
||||
allow_empty: true,
|
||||
filters: [
|
||||
{
|
||||
id: 'created',
|
||||
label: 'Created',
|
||||
type: 'date',
|
||||
operators: ['greater_or_equal', 'between'],
|
||||
validation: {
|
||||
format: 'YYYY-MM-DD'
|
||||
},
|
||||
plugin: 'datepicker',
|
||||
plugin_config: {
|
||||
format: 'yyyy-mm-dd',
|
||||
todayBtn: 'linked',
|
||||
todayHighlight: true,
|
||||
autoclose: true
|
||||
}
|
||||
},
|
||||
{
|
||||
input: "select",
|
||||
type: "string",
|
||||
operators: [
|
||||
"equal",
|
||||
],
|
||||
unique: true,
|
||||
id: "action",
|
||||
label: "Action",
|
||||
values: <?= json_encode($actions) ?>
|
||||
},
|
||||
{
|
||||
input: "select",
|
||||
type: "string",
|
||||
operators: [
|
||||
"equal",
|
||||
],
|
||||
unique: true,
|
||||
id: "model",
|
||||
label: "Model type",
|
||||
values: <?= json_encode($models) ?>
|
||||
},
|
||||
{
|
||||
input: "text",
|
||||
type: "integer",
|
||||
operators: [
|
||||
"equal",
|
||||
],
|
||||
unique: true,
|
||||
id: "model_id",
|
||||
label: "Model ID",
|
||||
},
|
||||
{
|
||||
input: "text",
|
||||
type: "integer",
|
||||
operators: [
|
||||
"equal",
|
||||
],
|
||||
unique: true,
|
||||
id: "event_id",
|
||||
label: "Belongs to event with ID",
|
||||
},
|
||||
{
|
||||
input: "text",
|
||||
type: "string",
|
||||
operators: [
|
||||
"contains",
|
||||
],
|
||||
unique: true,
|
||||
id: "model_title",
|
||||
label: "Model title",
|
||||
},
|
||||
{
|
||||
input: "text",
|
||||
type: "string",
|
||||
operators: [
|
||||
"equal",
|
||||
],
|
||||
unique: true,
|
||||
id: "ip",
|
||||
label: "IP",
|
||||
},
|
||||
{
|
||||
input: "text",
|
||||
type: "string",
|
||||
operators: [
|
||||
"equal",
|
||||
],
|
||||
unique: true,
|
||||
id: "user",
|
||||
label: "User",
|
||||
description: "User ID or mail",
|
||||
},
|
||||
{
|
||||
input: "text",
|
||||
type: "integer",
|
||||
operators: [
|
||||
"equal",
|
||||
],
|
||||
unique: true,
|
||||
id: "authkey_id",
|
||||
label: "Authentication key ID",
|
||||
},
|
||||
{
|
||||
input: "select",
|
||||
type: "integer",
|
||||
operators: [
|
||||
"equal",
|
||||
],
|
||||
unique: true,
|
||||
id: "request_type",
|
||||
label: "Request type",
|
||||
values: {0: "Browser", 1: "API", 2: "CLI or background job"}
|
||||
},
|
||||
{
|
||||
input: "text",
|
||||
type: "string",
|
||||
operators: [
|
||||
"equal",
|
||||
],
|
||||
unique: true,
|
||||
id: "request_id",
|
||||
label: "Request ID",
|
||||
description: "Request ID from X-Request-ID HTTP header",
|
||||
},
|
||||
{
|
||||
input: "text",
|
||||
type: "string",
|
||||
operators: [
|
||||
"equal",
|
||||
],
|
||||
unique: true,
|
||||
id: "org",
|
||||
label: "Organisation",
|
||||
description: "Organisation ID, UUID or name",
|
||||
}
|
||||
],
|
||||
rules: {
|
||||
condition: 'AND',
|
||||
not: false,
|
||||
rules: <?= json_encode($qbRules) ?>,
|
||||
flags: {
|
||||
no_add_group: true,
|
||||
condition_readonly: true,
|
||||
}
|
||||
},
|
||||
icons: {
|
||||
add_group: 'fa fa-plus-square',
|
||||
add_rule: 'fa fa-plus-circle',
|
||||
remove_group: 'fa fa-minus-square',
|
||||
remove_rule: 'fa fa-minus-circle',
|
||||
error: 'fa fa-exclamation-triangle'
|
||||
}
|
||||
};
|
||||
|
||||
$(function() {
|
||||
var $builder = $('#builder');
|
||||
|
||||
// Fix for Bootstrap Datepicker
|
||||
$builder.on('afterUpdateRuleValue.queryBuilder', function (e, rule) {
|
||||
if (rule.filter.plugin === 'datepicker') {
|
||||
rule.$el.find('.rule-value-container input').datepicker('update');
|
||||
}
|
||||
});
|
||||
|
||||
var queryBuilder = $builder.queryBuilder(qbOptions);
|
||||
queryBuilder = queryBuilder[0].queryBuilder;
|
||||
|
||||
$('#qbClear').off('click').on('click', function () {
|
||||
queryBuilder.reset();
|
||||
});
|
||||
|
||||
// Submit on enter
|
||||
$builder.on('keyup', 'input[type=text], select', function (event) {
|
||||
if (event.keyCode === 13) {
|
||||
$('#qbSubmit').click();
|
||||
}
|
||||
});
|
||||
|
||||
$('#qbSubmit').off('click').on('click', function () {
|
||||
var rules = queryBuilder.getRules({skip_empty: true});
|
||||
passedArgs = [];
|
||||
for (var key in rules.rules) {
|
||||
var rule = rules.rules[key];
|
||||
var k = rule.id;
|
||||
var v = rule.value;
|
||||
if (Array.isArray(v)) {
|
||||
v = v.join('||');
|
||||
}
|
||||
passedArgs[k] = v;
|
||||
}
|
||||
|
||||
var url = here;
|
||||
for (var key in passedArgs) {
|
||||
if (typeof key === 'number') {
|
||||
url += "/" + passedArgs[key];
|
||||
} else if (key !== 'page') {
|
||||
url += "/" + key + ":" + encodeURIComponent(passedArgs[key]);
|
||||
}
|
||||
}
|
||||
window.location.href = url;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<div class="pagination">
|
||||
<ul>
|
||||
<?php
|
||||
$paginator = $this->Paginator->prev('« ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
|
||||
$paginator .= $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
|
||||
$paginator .= $this->Paginator->next(__('next') . ' »', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
|
||||
echo $paginator;
|
||||
?>
|
||||
</ul>
|
||||
</div>
|
||||
<table class="table table-striped table-hover table-condensed">
|
||||
<tr>
|
||||
<th><?= $this->Paginator->sort('created') ?></th>
|
||||
<th><?= $this->Paginator->sort('user_id', __('User')) ?></th>
|
||||
<th><?= $this->Paginator->sort('ip', __('IP')) ?></th>
|
||||
<th><?= $this->Paginator->sort('org_id', __('Org')) ?></th>
|
||||
<th><?= $this->Paginator->sort('action') ?></th>
|
||||
<th>Model</th>
|
||||
<th>Title</th>
|
||||
<th>Change</th>
|
||||
</tr>
|
||||
<?php foreach ($list as $item): ?>
|
||||
<tr>
|
||||
<td class="short"><?= h($item['AuditLog']['created']); ?></td>
|
||||
<td class="short" data-search="user" data-search-value="<?= h($item['AuditLog']['user_id']) ?>"><?php
|
||||
if (isset($item['AuditLog']['user_id']) && $item['AuditLog']['user_id'] == 0) {
|
||||
echo __('SYSTEM') . ' <i class="fas fa-terminal" title="' . __('Action done by CLI') .'"></i>';
|
||||
} else if (isset($item['User']['email'])) {
|
||||
echo '<a href="' . $baseurl . '/admin/users/view/' . h($item['User']['id']) . '">' . h($item['User']['email']) . '</a>';
|
||||
|
||||
if ($item['AuditLog']['request_type'] == AuditLog::REQUEST_TYPE_CLI) {
|
||||
echo ' <i class="fas fa-terminal" title="' . __('Action done by CLI or background job') .'"></i>';
|
||||
} else if ($item['AuditLog']['request_type'] == AuditLog::REQUEST_TYPE_API) {
|
||||
$key = $item['AuditLog']['authkey_id'] ? ' ' . __('by auth key %s', h($item['AuditLog']['authkey_id'])) : '';
|
||||
echo ' <i class="fas fa-cogs" title="' . __('Action done trough API') . $key . '"></i>';
|
||||
}
|
||||
} else {
|
||||
echo __('<i>Deleted user #%s</i>', h($item['AuditLog']['user_id']));
|
||||
} ?></td>
|
||||
<td class="short" data-search="ip" data-search-value="<?= h($item['AuditLog']['ip']) ?>"><?= h($item['AuditLog']['ip']) ?></td>
|
||||
<td class="short" data-search="org" data-search-value="<?= h($item['AuditLog']['org_id']) ?>">
|
||||
<?php if (isset($item['Organisation']) && $item['Organisation']['id']) {
|
||||
echo $this->OrgImg->getOrgLogo($item, 24);
|
||||
} else if ($item['AuditLog']['org_id'] != 0) {
|
||||
echo __('<i>Deleted org #%s</i>', h($item['AuditLog']['org_id']));
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td class="short"><?= h($item['AuditLog']['action_human']) ?></td>
|
||||
<td class="short" data-search="model" data-search-value="<?= h($item['AuditLog']['model']) . ':' . h($item['AuditLog']['model_id']) ?>">
|
||||
<?php $title = isset($item['AuditLog']['event_info']) ? ' title="' . __('Event #%s: %s', $item['AuditLog']['event_id'], h($item['AuditLog']['event_info'])) . '"' : '' ?>
|
||||
<?= isset($item['AuditLog']['model_link']) ? '<a href="' . h($item['AuditLog']['model_link']) . '"' . $title . '>' : '' ?>
|
||||
<?= h($item['AuditLog']['model']) . ' #' . h($item['AuditLog']['model_id']) ?>
|
||||
<?= isset($item['AuditLog']['model_link']) ? '</a>' : '' ?>
|
||||
</td>
|
||||
<td class="limitedWidth"><?= h($item['AuditLog']['title']) ?></td>
|
||||
<td><?php
|
||||
if (is_array($item['AuditLog']['change'])) {
|
||||
foreach ($item['AuditLog']['change'] as $field => $values) {
|
||||
echo '<span class="json_key">' . h($field) . ':</span> ';
|
||||
if (isset($removeActions[$item['AuditLog']['action']])) {
|
||||
echo '<span class="json_string">' . $formatValue($field, $values) . '</span> <i class="fas fa-arrow-right json_null"></i> <i class="fas fa-times json_string"></i><br>';
|
||||
} else {
|
||||
if (is_array($values)) {
|
||||
echo '<span class="json_string">' . $formatValue($field, $values[0]) . '</span> ';
|
||||
$value = $values[1];
|
||||
} else {
|
||||
$value = $values;
|
||||
}
|
||||
echo '<i class="fas fa-arrow-right json_null"></i> <span class="json_string">' . $formatValue($field, $value) . '</span><br>';
|
||||
}
|
||||
}
|
||||
}
|
||||
?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
<p>
|
||||
<?= $this->Paginator->counter(array(
|
||||
'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}')
|
||||
));
|
||||
?>
|
||||
</p>
|
||||
<div class="pagination">
|
||||
<ul>
|
||||
<?= $paginator ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var passedArgs = <?= $passedArgs ?>;
|
||||
$('td[data-search]').mouseenter(function() {
|
||||
var $td = $(this);
|
||||
if ($td.data('search-value').length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$td.find('#quickEditButton').remove(); // clean all similar if exist
|
||||
var $div = $('<div id="quickEditButton"></div>');
|
||||
$div.addClass('quick-edit-row-div');
|
||||
var $span = $('<span></span>');
|
||||
$span.addClass('fa-as-icon fa fa-search-plus');
|
||||
$span.css('font-size', '12px');
|
||||
$div.append($span);
|
||||
$td.append($div);
|
||||
|
||||
$span.click(function() {
|
||||
if ($td.data('search') === 'model') {
|
||||
var val = $td.data('search-value').split(":");
|
||||
passedArgs['model'] = encodeURIComponent(val[0]);
|
||||
passedArgs['model_id'] = encodeURIComponent(val[1]);
|
||||
} else {
|
||||
passedArgs[$td.data('search')] = encodeURIComponent($td.data('search-value'));
|
||||
}
|
||||
|
||||
var url = here;
|
||||
for (var key in passedArgs) {
|
||||
if (typeof key === 'number') {
|
||||
url += "/" + passedArgs[key];
|
||||
} else if (key !== 'page') {
|
||||
url += "/" + key + ":" + passedArgs[key];
|
||||
}
|
||||
}
|
||||
window.location.href = url;
|
||||
});
|
||||
|
||||
$td.off('mouseleave').on('mouseleave', function() {
|
||||
$div.remove();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?= $this->element('/genericElements/SideMenu/side_menu', ['menuList' => 'logs', 'menuItem' => 'listAuditLogs']);
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
$formatValue = function($value) {
|
||||
if (mb_strlen($value) > 64) {
|
||||
$value = mb_substr($value, 0, 64) . '...';
|
||||
}
|
||||
return h(json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
};
|
||||
|
||||
$removeActions = [
|
||||
AuditLog::ACTION_DELETE => true,
|
||||
AuditLog::ACTION_REMOVE_GALAXY_LOCAL => true,
|
||||
AuditLog::ACTION_REMOVE_GALAXY => true,
|
||||
AuditLog::ACTION_REMOVE_TAG => true,
|
||||
AuditLog::ACTION_REMOVE_TAG_LOCAL => true,
|
||||
];
|
||||
|
||||
?><div class="logs index">
|
||||
<h2><?= __('Audit logs for event #%s', $event['Event']['id']) ?></h2>
|
||||
<div class="pagination">
|
||||
<ul>
|
||||
<?php
|
||||
$paginator = $this->Paginator->prev('« ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
|
||||
$paginator .= $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
|
||||
$paginator .= $this->Paginator->next(__('next') . ' »', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
|
||||
echo $paginator;
|
||||
?>
|
||||
<li><a href="<?= $baseurl . '/logs/event_index/' . h($event['Event']['id']) ?>"><?= __('Older logs') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<table class="table table-striped table-hover table-condensed">
|
||||
<tr>
|
||||
<th><?= $this->Paginator->sort('created');?></th>
|
||||
<th><?= $this->Paginator->sort('user_id', __('User'));?></th>
|
||||
<th><?= $this->Paginator->sort('org_id', __('Org'));?></th>
|
||||
<th><?= $this->Paginator->sort('action');?></th>
|
||||
<th>Model</th>
|
||||
<th>Title</th>
|
||||
<th>Change</th>
|
||||
</tr>
|
||||
<?php foreach ($list as $item): ?>
|
||||
<tr>
|
||||
<td class="short"><?= h($item['AuditLog']['created']); ?></td>
|
||||
<td class="short"><?php
|
||||
if (isset($item['AuditLog']['user_id']) && $item['AuditLog']['user_id'] == 0) {
|
||||
echo __('SYSTEM');
|
||||
} else if (isset($item['User']['email'])) {
|
||||
echo h($item['User']['email']);
|
||||
} ?></td>
|
||||
<td class="short"><?= isset($item['Organisation']) ? $this->OrgImg->getOrgLogo($item, 24) : '' ?></td>
|
||||
<td class="short"><?= h($item['AuditLog']['action_human']) ?></td>
|
||||
<td class="short"><?= h($item['AuditLog']['model']) . ' #' . h($item['AuditLog']['model_id']) ?></td>
|
||||
<td class="limitedWidth"><?= h($item['AuditLog']['title']) ?></td>
|
||||
<td><?php
|
||||
if (is_array($item['AuditLog']['change'])) {
|
||||
foreach ($item['AuditLog']['change'] as $field => $values) {
|
||||
echo '<span class="json_key">' . h($field) . ':</span> ';
|
||||
if (isset($removeActions[$item['AuditLog']['action']])) {
|
||||
echo '<span class="json_string">' . $formatValue($values) . '</span> <i class="fas fa-arrow-right json_null"></i> <i class="fas fa-times json_string"></i><br>';
|
||||
} else {
|
||||
if (is_array($values)) {
|
||||
echo '<span class="json_string">' . $formatValue($values[0]) . '</span> ';
|
||||
$value = $values[1];
|
||||
} else {
|
||||
$value = $values;
|
||||
}
|
||||
echo '<i class="fas fa-arrow-right json_null"></i> <span class="json_string">' . $formatValue($value) . '</span><br>';
|
||||
}
|
||||
}
|
||||
}
|
||||
?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
<p>
|
||||
<?= $this->Paginator->counter(array(
|
||||
'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}')
|
||||
));
|
||||
?>
|
||||
</p>
|
||||
<div class="pagination">
|
||||
<ul>
|
||||
<?= $paginator ?>
|
||||
<li><a href="<?= $baseurl . '/logs/event_index/' . h($event['Event']['id']) ?>"><?= __('Older logs') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<?= $this->element('/genericElements/SideMenu/side_menu', ['menuList' => 'event', 'menuItem' => 'eventLog', 'event' => $event, 'mayModify' => $mayModify]);
|
||||
|
|
@ -94,7 +94,7 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
|
|||
));
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'eventLog',
|
||||
'url' => $baseurl . '/logs/event_index/' . $eventId,
|
||||
'url' => $baseurl . (Configure::read('MISP.log_new_audit') ? '/audit_logs/eventIndex/' : '/logs/event_index/') . $eventId,
|
||||
'text' => __('View Event History')
|
||||
));
|
||||
echo $divider;
|
||||
|
@ -1024,6 +1024,12 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
|
|||
'url' => $baseurl . '/admin/logs/index',
|
||||
'text' => __('List Logs')
|
||||
));
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'listAuditLogs',
|
||||
'url' => $baseurl . '/admin/audit_logs/index',
|
||||
'text' => __('List Audit Logs'),
|
||||
'requirement' => Configure::read('MISP.log_new_audit'),
|
||||
));
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'url' => $baseurl . '/admin/logs/search',
|
||||
'text' => __('Search Logs')
|
||||
|
|
|
@ -459,13 +459,18 @@
|
|||
),
|
||||
array(
|
||||
'type' => 'root',
|
||||
'text' => __('Audit'),
|
||||
'text' => __('Logs'),
|
||||
'requirement' => $isAclAudit,
|
||||
'children' => array(
|
||||
array(
|
||||
'text' => __('List Logs'),
|
||||
'url' => $baseurl . '/admin/logs/index'
|
||||
),
|
||||
array(
|
||||
'text' => __('List Audit Logs'),
|
||||
'url' => $baseurl . '/admin/audit_logs/index',
|
||||
'requirement' => Configure::read('MISP.log_new_audit'),
|
||||
),
|
||||
array(
|
||||
'text' => __('Search Logs'),
|
||||
'url' => $baseurl . '/admin/logs/search'
|
||||
|
|
|
@ -82,8 +82,12 @@
|
|||
$contributorsContent = [];
|
||||
foreach ($contributors as $organisationId => $name) {
|
||||
$org = ['Organisation' => ['id' => $organisationId, 'name' => $name]];
|
||||
$link = $baseurl . "/logs/event_index/" . $event['Event']['id'] . '/' . h($name);
|
||||
$contributorsContent[] = $this->OrgImg->getNameWithImg($org, $link);
|
||||
if (Configure::read('MISP.log_new_audit')) {
|
||||
$link = $baseurl . "/audit_logs/eventIndex/" . h($event['Event']['id']) . '/' . h($organisationId);
|
||||
} else {
|
||||
$link = $baseurl . "/logs/event_index/" . h($event['Event']['id']) . '/' . h($name);
|
||||
}
|
||||
$contributorsContent[] = $this->OrgImg->getNameWithImg($org, $link);
|
||||
}
|
||||
$table_data[] = array(
|
||||
'key' => __('Contributors'),
|
||||
|
|
|
@ -2732,3 +2732,8 @@ Query builder
|
|||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Fix text input for query builder */
|
||||
.query-builder .rule-value-container input[type="text"] {
|
||||
padding: 4px !important;
|
||||
height: 30px;
|
||||
}
|
||||
|
|
163
db_schema.json
163
db_schema.json
|
@ -384,6 +384,162 @@
|
|||
"extra": ""
|
||||
}
|
||||
],
|
||||
"audit_logs": [
|
||||
{
|
||||
"column_name": "id",
|
||||
"is_nullable": "NO",
|
||||
"data_type": "int",
|
||||
"character_maximum_length": null,
|
||||
"numeric_precision": "10",
|
||||
"collation_name": null,
|
||||
"column_type": "int(11)",
|
||||
"column_default": null,
|
||||
"extra": "auto_increment"
|
||||
},
|
||||
{
|
||||
"column_name": "created",
|
||||
"is_nullable": "NO",
|
||||
"data_type": "datetime",
|
||||
"character_maximum_length": null,
|
||||
"numeric_precision": null,
|
||||
"collation_name": null,
|
||||
"column_type": "datetime",
|
||||
"column_default": null,
|
||||
"extra": ""
|
||||
},
|
||||
{
|
||||
"column_name": "user_id",
|
||||
"is_nullable": "NO",
|
||||
"data_type": "int",
|
||||
"character_maximum_length": null,
|
||||
"numeric_precision": "10",
|
||||
"collation_name": null,
|
||||
"column_type": "int(11)",
|
||||
"column_default": null,
|
||||
"extra": ""
|
||||
},
|
||||
{
|
||||
"column_name": "org_id",
|
||||
"is_nullable": "NO",
|
||||
"data_type": "int",
|
||||
"character_maximum_length": null,
|
||||
"numeric_precision": "10",
|
||||
"collation_name": null,
|
||||
"column_type": "int(11)",
|
||||
"column_default": null,
|
||||
"extra": ""
|
||||
},
|
||||
{
|
||||
"column_name": "authkey_id",
|
||||
"is_nullable": "NO",
|
||||
"data_type": "int",
|
||||
"character_maximum_length": null,
|
||||
"numeric_precision": "10",
|
||||
"collation_name": null,
|
||||
"column_type": "int(11)",
|
||||
"column_default": null,
|
||||
"extra": ""
|
||||
},
|
||||
{
|
||||
"column_name": "ip",
|
||||
"is_nullable": "YES",
|
||||
"data_type": "varbinary",
|
||||
"character_maximum_length": "16",
|
||||
"numeric_precision": null,
|
||||
"collation_name": null,
|
||||
"column_type": "varbinary(16)",
|
||||
"column_default": "NULL",
|
||||
"extra": ""
|
||||
},
|
||||
{
|
||||
"column_name": "request_type",
|
||||
"is_nullable": "NO",
|
||||
"data_type": "tinyint",
|
||||
"character_maximum_length": null,
|
||||
"numeric_precision": "3",
|
||||
"collation_name": null,
|
||||
"column_type": "tinyint(4)",
|
||||
"column_default": null,
|
||||
"extra": ""
|
||||
},
|
||||
{
|
||||
"column_name": "request_id",
|
||||
"is_nullable": "YES",
|
||||
"data_type": "varchar",
|
||||
"character_maximum_length": "255",
|
||||
"numeric_precision": null,
|
||||
"collation_name": "utf8mb4_unicode_ci",
|
||||
"column_type": "varchar(255)",
|
||||
"column_default": "NULL",
|
||||
"extra": ""
|
||||
},
|
||||
{
|
||||
"column_name": "action",
|
||||
"is_nullable": "NO",
|
||||
"data_type": "varchar",
|
||||
"character_maximum_length": "20",
|
||||
"numeric_precision": null,
|
||||
"collation_name": "utf8mb4_unicode_ci",
|
||||
"column_type": "varchar(20)",
|
||||
"column_default": null,
|
||||
"extra": ""
|
||||
},
|
||||
{
|
||||
"column_name": "model",
|
||||
"is_nullable": "NO",
|
||||
"data_type": "varchar",
|
||||
"character_maximum_length": "80",
|
||||
"numeric_precision": null,
|
||||
"collation_name": "utf8mb4_unicode_ci",
|
||||
"column_type": "varchar(80)",
|
||||
"column_default": null,
|
||||
"extra": ""
|
||||
},
|
||||
{
|
||||
"column_name": "model_id",
|
||||
"is_nullable": "NO",
|
||||
"data_type": "int",
|
||||
"character_maximum_length": null,
|
||||
"numeric_precision": "10",
|
||||
"collation_name": null,
|
||||
"column_type": "int(11)",
|
||||
"column_default": null,
|
||||
"extra": ""
|
||||
},
|
||||
{
|
||||
"column_name": "model_title",
|
||||
"is_nullable": "YES",
|
||||
"data_type": "text",
|
||||
"character_maximum_length": "65535",
|
||||
"numeric_precision": null,
|
||||
"collation_name": "utf8mb4_unicode_ci",
|
||||
"column_type": "text",
|
||||
"column_default": "NULL",
|
||||
"extra": ""
|
||||
},
|
||||
{
|
||||
"column_name": "event_id",
|
||||
"is_nullable": "YES",
|
||||
"data_type": "int",
|
||||
"character_maximum_length": null,
|
||||
"numeric_precision": "10",
|
||||
"collation_name": null,
|
||||
"column_type": "int(11)",
|
||||
"column_default": "NULL",
|
||||
"extra": ""
|
||||
},
|
||||
{
|
||||
"column_name": "change",
|
||||
"is_nullable": "YES",
|
||||
"data_type": "blob",
|
||||
"character_maximum_length": "65535",
|
||||
"numeric_precision": null,
|
||||
"collation_name": null,
|
||||
"column_type": "blob",
|
||||
"column_default": "NULL",
|
||||
"extra": ""
|
||||
}
|
||||
],
|
||||
"auth_keys": [
|
||||
{
|
||||
"column_name": "id",
|
||||
|
@ -7591,6 +7747,11 @@
|
|||
"event_id": false,
|
||||
"tag_id": false
|
||||
},
|
||||
"audit_logs": {
|
||||
"id": true,
|
||||
"event_id": false,
|
||||
"model_id": false
|
||||
},
|
||||
"auth_keys": {
|
||||
"id": true,
|
||||
"authkey_start": false,
|
||||
|
@ -7997,5 +8158,5 @@
|
|||
"id": true
|
||||
}
|
||||
},
|
||||
"db_version": "68"
|
||||
"db_version": "69"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue