Fix to several issues with the sync and and an issue preventing the editing of events, fixes #788, fixes #784

pull/796/head
iglocska 2015-12-24 15:22:05 +01:00
parent 9f6d78af8d
commit 627f9abbd6
7 changed files with 250 additions and 317 deletions

View File

@ -501,7 +501,7 @@ class AppController extends Controller {
$this->loadModel('Server');
if (!Configure::read('MISP.background_jobs')) {
$this->Server->upgrade2324($this->Auth->user('id'));
$this->Session->setFlash('Done. For more details check the audit logs.');
$this->Session->setFlash('Done. For more details check the audit logs.');
$this->redirect(array('controller' => 'pages', 'action' => 'display', 'administration'));
} else {
$job = ClassRegistry::init('Job');

View File

@ -1039,197 +1039,17 @@ class EventsController extends AppController {
}
if ($this->request->is('post') || $this->request->is('put')) {
if ($this->_isRest()) {
$saveEvent = false;
if ($this->_isRest()) {
if (isset($this->request->data['response'])) $this->request->data = $this->Event->updateXMLArray($this->request->data, true);
else $this->request->data = $this->Event->updateXMLArray($this->request->data, false);
}
// Workaround for different structure in XML/array than what CakePHP expects
if (isset($this->request->data['response'])) $this->request->data = $this->request->data['response'];
$this->request->data = $this->Event->cleanupEventArrayFromXML($this->request->data);
// the event_id field is not set (normal) so make sure no validation errors are thrown
// LATER do this with $this->validator()->remove('event_id');
unset($this->Event->Attribute->validate['event_id']);
unset($this->Event->Attribute->validate['value']['unique']); // otherwise gives bugs because event_id is not set
// http://book.cakephp.org/2.0/en/models/saving-your-data.html
// Creating or updating is controlled by the models id field.
// If $Model->id is set, the record with this primary key is updated.
// Otherwise a new record is created
// reposition to get the event.id with given uuid
$existingEvent = $this->Event->findByUuid($this->request->data['Event']['uuid']);
// If the event exists...
$dateObj = new DateTime();
$date = $dateObj->getTimestamp();
if (count($existingEvent)) {
$this->request->data['Event']['id'] = $existingEvent['Event']['id'];
// Conditions affecting all:
// user.org == event.org
// edit timestamp newer than existing event timestamp
if (!isset($this->request->data['Event']['timestamp'])) $this->request->data['Event']['timestamp'] = $date;
if ($this->request->data['Event']['timestamp'] > $existingEvent['Event']['timestamp']) {
if ($this->request->data['Event']['distribution'] == 4) {
$this->request->data['Event']['sharing_group_id'] = $this->Event->SharingGroup->captureSG($this->request->data['Event']['SharingGroup'], $this->Auth->user());
unset ($this->request->data['Event']['SharingGroup']);
}
// If the above is true, we have two more options:
// For users that are of the creating org of the event, always allow the edit
// For users that are sync users, only allow the edit if the event is locked
if ($existingEvent['Event']['orgc_id'] === $this->_checkOrg()
|| ($this->userRole['perm_sync'] && $existingEvent['Event']['locked']) || $this->_isSiteAdmin()) {
if ($this->userRole['perm_sync']) {
if ($this->Event->data['Event']['distribution'] == 4 && !$this->Event->SharingGroup->checkIfAuthorisedExtend($this->Auth->user(), $this->Event->data['Event']['sharing_group_id'])) {
throw new MethodNotAllowedException('Event could not be saved: The sync user has to either be an extender of the sharing group or be the sync user that has first synchronised the sharing group to this instance.');
}
}
// Only allow an edit if this is true!
$saveEvent = true;
} else throw new MethodNotAllowedException('Event could not be saved: The user used to edit the event is not authorised to do so. This can be caused by the user not being of the same organisation as the original creator of the event whilst also not being a site administrator.');
} else throw new MethodNotAllowedException('Event could not be saved: Event in the request not newer than the local copy.');
// If a field is not set in the request, just reuse the old value
$recoverFields = array('analysis', 'threat_level_id', 'info', 'distribution', 'date');
foreach ($recoverFields as $rF) if (!isset($this->request->data['Event'][$rF])) $this->request->data['Event'][$rF] = $existingEvent['Event'][$rF];
} else throw new MethodNotAllowedException('Event could not be saved: Could not find the local event.');
$fieldList = array(
'Event' => array('date', 'threat_level_id', 'analysis', 'info', 'published', 'uuid', 'distribution', 'timestamp'),
'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'revision', 'distribution', 'timestamp', 'comment', 'sharing_group_id'),
'ShadowAttribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'org_id', 'event_org_id', 'comment', 'event_uuid', 'deleted', 'to_ids', 'uuid')
);
$saveResult = $this->Event->save(array('Event' => $this->request->data['Event']), array('fieldList' => $fieldList['Event']));
$this->loadModel('Log');
if ($saveResult) {
$validationErrors = array();
if (isset($this->request->data['Event']['Attribute'])) {
foreach ($this->request->data['Event']['Attribute'] as $k => $attribute) {
if (isset($attribute['uuid'])) {
$existingAttribute = $this->Event->Attribute->findByUuid($attribute['uuid']);
if (count($existingAttribute)) {
if ($existingAttribute['Attribute']['event_id'] != $id) {
$result = $this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Event',
'model_id' => $id,
'email' => $this->Auth->user('email'),
'action' => 'edit',
'user_id' => $this->Auth->user('id'),
'title' => 'Duplicate UUID found in attribute',
'change' => 'An attribute was blocked from being saved due to a duplicate UUID. The uuid in question is: ' . $attribute['uuid'],
));
unset($this->request->data['Event']['Attribute'][$k]);
} else {
// If a field is not set in the request, just reuse the old value
$recoverFields = array('value', 'to_ids', 'distribution', 'category', 'type', 'comment', 'sharing_group_id');
foreach ($recoverFields as $rF) if (!isset($attribute[$rF])) $this->request->data['Event']['Attribute'][$c][$rF] = $existingAttribute['Attribute'][$rF];
$this->request->data['Event']['Attribute'][$k]['id'] = $existingAttribute['Attribute']['id'];
// Check if the attribute's timestamp is bigger than the one that already exists.
// If yes, it means that it's newer, so insert it. If no, it means that it's the same attribute or older - don't insert it, insert the old attribute.
// Alternatively, we could unset this attribute from the request, but that could lead with issues if we decide that we want to start deleting attributes that don't exist in a pushed event.
if (isset($this->request->data['Event']['Attribute'][$k]['timestamp'])) {
if ($this->request->data['Event']['Attribute'][$k]['timestamp'] <= $existingAttribute['Attribute']['timestamp']) {
unset($this->request->data['Event']['Attribute'][$k]);
continue;
}
} else $this->request->data['Event']['timestamp'] = $date;
}
} else {
$this->Event->Attribute->create();
}
} else {
$this->Event->Attribute->create();
}
$this->request->data['Event']['Attribute'][$k]['event_id'] = $this->Event->id;
if ($this->request->data['Event']['Attribute'][$k]['distribution'] == 4) {
$sid = $this->Event->SharingGroup->captureSG($this->request->data['Event']['Attribute'][$k]['SharingGroup'], $this->Auth->user());
$this->request->data['Event']['Attribute'][$k]['sharing_group_id'] = $this->Event->SharingGroup->captureSG($this->request->data['Event']['Attribute'][$k]['SharingGroup'], $this->Auth->user());
}
if (!$this->Event->Attribute->save($this->request->data['Event']['Attribute'][$k], array('fieldList' => $fieldList))) {
$validationErrors['Attribute'][$k] = $this->Event->Attribute->validationErrors;
$attribute_short = (isset($this->request->data['Event']['Attribute'][$k]['category']) ? $this->request->data['Event']['Attribute'][$k]['category'] : 'N/A') . '/' . (isset($this->request->data['Event']['Attribute'][$k]['type']) ? $this->request->data['Event']['Attribute'][$k]['type'] : 'N/A') . ' ' . (isset($this->request->data['Event']['Attribute'][$k]['value']) ? $this->request->data['Event']['Attribute'][$k]['value'] : 'N/A');
$this->Log->create();
$this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Attribute',
'model_id' => 0,
'email' => $this->Auth->user('email'),
'action' => 'edit',
'user_id' => $this->Auth->user('id'),
'title' => 'Attribute dropped due to validation for Event ' . $this->id . ' failed: ' . $attribute_short,
'change' => json_encode($this->Event->Attribute->validationErrors),
));
}
}
}
if (isset($this->request->data['Event']['Tag']) && $this->userRole['perm_tagger']) {
foreach ($this->request->data['Event']['Tag'] as $tag) {
$tag_id = $this->Event->EventTag->Tag->captureTag($tag, $this->Auth->user());
if ($tag_id) {
$this->Event->EventTag->attachTagToEvent($this->Event->id, $tag_id);
} else {
$this->Log->create();
$this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Event',
'model_id' => $this->Event->id,
'email' => $this->Auth->user('email'),
'action' => 'edit',
'user_id' => $this->Auth->user('id'),
'title' => 'Failed create or attach Tag ' . $tag['name'] . ' to the event.',
'change' => ''
));
}
}
}
// check if the exact proposal exists, if yes check if the incoming one is deleted or not. If it is deleted, remove the old proposal and replace it with the one marked for being deleted
// otherwise throw the new one away.
if (isset($this->request->data['Event']['ShadowAttribute'])) {
foreach ($this->request->data['Event']['ShadowAttribute'] as $k => &$proposal) {
$existingProposal = $this->Event->ShadowAttribute->find('first', array(
'recursive' => -1,
'conditions' => array(
'value' => $proposal['value'],
'category' => $proposal['category'],
'to_ids' => $proposal['to_ids'],
'type' => $proposal['type'],
'event_uuid' => $proposal['event_uuid'],
'uuid' => $proposal['uuid']
)
));
if (!empty($existingProposal)) {
if ($existingProposal['ShadowAttribute']['deleted'] == 1) {
$this->Event->ShadowAttribute->delete($existingProposal['ShadowAttribute']['id'], false);
} else {
unset($this->request->data['ShadowAttribute'][$k]);
continue;
}
$this->Event->ShadowAttribute->create();
}
$this->Event->ShadowAttribute->save($this->request->data['Event']['ShadowAttribute'][$k], array('fieldList' => $fieldList));
}
}
// this saveAssociated() function will save not only the event, but also the attributes
// from the attributes attachments are also saved to the disk thanks to the afterSave() fonction of Attribute
if ($saveEvent) {
$saveResult = $this->Event->saveAssociated($this->request->data, array('validate' => true, 'fieldList' => $fieldList));
} else {
throw new MethodNotAllowedException();
}
$message = 'Saved';
$this->set('event', $this->Event->data);
//if published -> do the actual publishing
if ((!empty($this->request->data['Event']['published']) && 1 == $this->request->data['Event']['published'])) {
// do the necessary actions to publish the event (email, upload,...)
if ('true' != Configure::read('MISP.disablerestalert')) {
$this->Event->sendAlertEmailRouter($id, $this->Auth->user());
}
$this->Event->publish($existingEvent['Event']['id']);
}
$result = $this->Event->_edit($this->request->data, $this->Auth->user(), $id);
if ($result === true) {
// REST users want to see the newly created event
$results = $this->Event->fetchEvent($this->Auth->user(), array('eventid' => $id));
$event = &$results[0];
if (!empty($validationErrors)) {
$event['errors'] = $validationErrors;
}
$this->set('event', $event);
$this->render('view');
return true;
@ -1238,8 +1058,9 @@ class EventsController extends AppController {
if ($this->_isRest()) {
App::uses('JSONConverterTool', 'Tools');
$converter = new JSONConverterTool();
$errors = $converter->arrayPrinter($this->Event->validationErrors);
$this->set('name', 'Add event failed.');
if (isset($result['error'])) $errors = $result['error'];
else $errors = $converter->arrayPrinter($result);
$this->set('name', 'Edit event failed.');
$this->set('message', $message);
$this->set('errors', $errors);
$this->set('url', '/events/edit/' . $id);
@ -1285,7 +1106,6 @@ class EventsController extends AppController {
// even if the SG is not local, we still want the option to select the currently assigned SG
$sgs = $this->Event->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'name', 1);
if (!isset($sgs[$this->Event->data['SharingGroup']['id']])) $sgs[$this->Event->data['SharingGroup']['id']] = $this->Event->data['SharingGroup']['name'];
$this->set('sharingGroups', $sgs);
$distributionLevels = $this->Event->distributionLevels;

View File

@ -1008,9 +1008,19 @@ class ShadowAttributesController extends AppController {
}
$this->ShadowAttribute->recursive = -1;
$temp = $this->ShadowAttribute->findAllByEventUuid($uuid);
if ($temp == null) throw new NotFoundException(__('Invalid event'));
$this->set('proposal', $temp);
$this->render('get_proposals_by_uuid');
if ($temp == null) {
$this->response->statusCode(404);
$this->set('name', 'Invalid Event.');
$this->set('message', 'Invalid Event');
$this->set('errors', 'Invalid Event');
$this->set('url', '/shadow_attributes/getProposalsByUuid/edit/' . $uuid);
$this->set('_serialize', array('name', 'message', 'url', 'errors'));
$this->response->send();
return false;
} else {
$this->set('proposal', $temp);
$this->render('get_proposals_by_uuid');
}
}
public function fetchEditForm($id, $field = null) {

View File

@ -1,19 +1,19 @@
<?php
class JSONConverterTool {
public function event2JSON($event, $isSiteAdmin=false) {
$event['Event']['Org'][0] = $event['Org'];
$event['Event']['Orgc'][0] = $event['Orgc'];
$event['Event']['Org'] = $event['Org'];
$event['Event']['Orgc'] = $event['Orgc'];
if (isset($event['SharingGroup']['SharingGroupOrg'])) {
foreach ($event['SharingGroup']['SharingGroupOrg'] as $key => $sgo) {
$event['SharingGroup']['SharingGroupOrg'][$key]['Organisation'] = array(0 => $event['SharingGroup']['SharingGroupOrg'][$key]['Organisation']);
$event['SharingGroup']['SharingGroupOrg'][$key]['Organisation'] = $event['SharingGroup']['SharingGroupOrg'][$key]['Organisation'];
}
}
if (isset($event['SharingGroup']['SharingGroupServer'])) {
foreach ($event['SharingGroup']['SharingGroupServer'] as $key => $sgs) {
$event['SharingGroup']['SharingGroupServer'][$key]['Server'] = array(0 => $event['SharingGroup']['SharingGroupServer'][$key]['Server']);
$event['SharingGroup']['SharingGroupServer'][$key]['Server'] = $event['SharingGroup']['SharingGroupServer'][$key]['Server'];
}
}
if (isset($event['SharingGroup'])) $event['Event']['SharingGroup'][0] = $event['SharingGroup'];
if (isset($event['SharingGroup'])) $event['Event']['SharingGroup'] = $event['SharingGroup'];
$event['Event']['Attribute'] = $event['Attribute'];
$event['Event']['ShadowAttribute'] = $event['ShadowAttribute'];
$event['Event']['RelatedEvent'] = $event['RelatedEvent'];

View File

@ -594,16 +594,20 @@ class Event extends AppModel {
*/
public function cleanupEventArrayFromXML(&$data) {
$objects = array('Attribute', 'ShadowAttribute');
foreach ($objects as $object) {
// Workaround for different structure in XML/array than what CakePHP expects
if (isset($data['Event'][$object]) && is_array($data['Event'][$object]) && count($data['Event'][$object])) {
if (!is_numeric(implode(array_keys($data['Event'][$object]), ''))) {
// single attribute
$data['Event'][$object][0] = $data['Event'][$object];
//$data['Event'][$object] = array(0 => $data['Event'][$object]);
}
}
}
$objects = array('Org', 'Orgc', 'SharingGroup');
foreach ($objects as $object) {
if (isset($data['Event'][$object][0])) $data['Event'][$object] = $data['Event'][$object][0];
}
return $data;
}
@ -954,8 +958,8 @@ class Event extends AppModel {
$request = array(
'header' => array(
'Authorization' => $authkey,
'Accept' => 'application/xml',
'Content-Type' => 'application/xml',
'Accept' => 'application/json',
'Content-Type' => 'application/json',
//'Connection' => 'keep-alive' // LATER followup cakephp ticket 2854 about this problem http://cakephp.lighthouseapp.com/projects/42648-cakephp/tickets/2854
)
);
@ -966,9 +970,7 @@ class Event extends AppModel {
}
$response = $HttpSocket->get($uri, $data = '', $request);
if ($response->isOk()) {
$xmlArray = Xml::toArray(Xml::build($response->body));
$xmlArray = $this->updateXMLArray($xmlArray);
return $xmlArray['response'];
return(json_decode($response->body, true));
} else {
// TODO parse the XML response and keep the reason why it failed
return null;
@ -1132,6 +1134,7 @@ class Event extends AppModel {
array('fields' => array('SharingGroup.id','SharingGroup.name', 'SharingGroup.releasability', 'SharingGroup.description')),
array(
'fields' => array('SharingGroup.*'),
'Organisation' => array('fields' => $fieldsOrg),
'SharingGroupOrg' => array(
'Organisation' => array('fields' => $fieldsOrg),
),
@ -1203,6 +1206,13 @@ class Event extends AppModel {
$attribute['data'] = $encodedFile;
}
}
if (isset($attribute['SharingGroup']['SharingGroupServer'])) {
foreach ($attribute['SharingGroup']['SharingGroupServer'] as &$sgs) {
if ($sgs['server_id'] == 0) {
$sgs['Server'] = array('id' => '0', 'url' => Configure::read('MISP.baseurl'), 'name' => Configure::read('MISP.baseurl'));
}
}
}
$attribute['ShadowAttribute'] = array();
// If a shadowattribute can be linked to an attribute, link it to it then remove it from the event
// This is to differentiate between proposals that were made to an attribute for modification and between proposals for new attributes
@ -1607,6 +1617,7 @@ class Event extends AppModel {
if ($data['Event']['distribution'] == 4) $sgs[$data['Event']['SharingGroup']['uuid']] = $data['Event']['SharingGroup'];
if (isset($data['Event']['Attribute'])) {
foreach ($data['Event']['Attribute'] as &$attribute) {
if (isset($attribute['SharingGroup']) && !empty($attribute['SharingGroup']) && isset($attribute['SharingGroup'][0])) $attribute['SharingGroup'] = $attribute['SharingGroup'][0];
if ($attribute['distribution'] == 4 && !isset($sgs[$attribute['SharingGroup']['uuid']])) $sgs[$attribute['SharingGroup']['uuid']] = $attribute['SharingGroup'];
}
}
@ -1772,66 +1783,171 @@ class Event extends AppModel {
}
public function _edit(&$data, $user, $id, $jobId = null) {
if ($jobId) {
App::import('Component','Auth');
}
$this->Log = ClassRegistry::init('Log');
$localEvent = $this->find('first', array('conditions' => array('Event.id' => $id), 'recursive' => -1, 'contain' => array('Attribute', 'ThreatLevel', 'ShadowAttribute')));
if (!isset($data['Event']['orgc_id']) && !isset($data['Event']['orgc'])) $data['Event']['orgc_id'] = $data['Event']['org_id'];
if ($localEvent['Event']['timestamp'] < $data['Event']['timestamp']) {
} else {
return 'Event exists and is the same or newer.';
}
if (!$localEvent['Event']['locked']) {
return 'Event originated on this instance, any changes to it have to be done locally.';
}
$fieldList = array(
'Event' => array('date', 'threat_level_id', 'analysis', 'info', 'published', 'uuid', 'from', 'distribution', 'timestamp'),
'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'distribution', 'timestamp', 'comment')
);
$data['Event']['id'] = $localEvent['Event']['id'];
$data = $this->cleanupEventArrayFromXML($data);
$saveResult = $this->save($data, array('fieldList' => $fieldList['Event']));
unset($this->Attribute->validate['event_id']);
unset($this->Attribute->validate['value']['unique']); // otherwise gives bugs because event_id is not set
// reposition to get the event.id with given uuid
$existingEvent = $this->findByUuid($data['Event']['uuid']);
// If the event exists...
$dateObj = new DateTime();
$date = $dateObj->getTimestamp();
if (count($existingEvent)) {
$data['Event']['id'] = $existingEvent['Event']['id'];
// Conditions affecting all:
// user.org == event.org
// edit timestamp newer than existing event timestamp
if (!isset($data['Event']['timestamp'])) $data['Event']['timestamp'] = $date;
if ($data['Event']['timestamp'] > $existingEvent['Event']['timestamp']) {
if ($data['Event']['distribution'] == 4) {
$data['Event']['sharing_group_id'] = $this->SharingGroup->captureSG($data['Event']['SharingGroup'], $user);
if ($data['Event']['sharing_group_id'] === false) return (array('error' => 'Event could not be saved: User not authorised to create the associated sharing group.'));
unset ($data['Event']['SharingGroup']);
}
// If the above is true, we have two more options:
// For users that are of the creating org of the event, always allow the edit
// For users that are sync users, only allow the edit if the event is locked
if ($existingEvent['Event']['orgc_id'] === $user['org_id']
|| ($user['Role']['perm_sync'] && $existingEvent['Event']['locked']) || $user['Role']['perm_site_admin']) {
if ($user['Role']['perm_sync']) {
if ($data['Event']['distribution'] == 4 && !$this->SharingGroup->checkIfAuthorisedExtend($user, $data['Event']['sharing_group_id'])) {
return (array('error' => 'Event could not be saved: The sync user has to either be an extender of the sharing group or be the sync user that has first synchronised the sharing group to this instance.'));
}
}
// Only allow an edit if this is true!
$saveEvent = true;
} else return (array('error' => 'Event could not be saved: The user used to edit the event is not authorised to do so. This can be caused by the user not being of the same organisation as the original creator of the event whilst also not being a site administrator.'));
} else return (array('error' => 'Event could not be saved: Event in the request not newer than the local copy.'));
// If a field is not set in the request, just reuse the old value
$recoverFields = array('analysis', 'threat_level_id', 'info', 'distribution', 'date');
foreach ($recoverFields as $rF) if (!isset($data['Event'][$rF])) $data['Event'][$rF] = $existingEvent['Event'][$rF];
} else return (array('error' => 'Event could not be saved: Could not find the local event.'));
$fieldList = array(
'Event' => array('date', 'threat_level_id', 'analysis', 'info', 'published', 'uuid', 'distribution', 'timestamp', 'sharing_group_id'),
'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'revision', 'distribution', 'timestamp', 'comment', 'sharing_group_id'),
'ShadowAttribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'org_id', 'event_org_id', 'comment', 'event_uuid', 'deleted', 'to_ids', 'uuid')
);
$saveResult = $this->save(array('Event' => $data['Event']), array('fieldList' => $fieldList['Event']));
$this->Log = ClassRegistry::init('Log');
if ($saveResult) {
$validationErrors = array();
if (isset($data['Event']['Attribute'])) {
foreach ($data['Event']['Attribute'] as $k => &$attribute) {
$mode = 'add';
$data['Event']['Attribute'][$k]['event_id'] = $this->id;
$existingAttribute = $this->__searchUuidInAttributeArray($attribute['uuid'], $localEvent);
if (count($existingAttribute)) {
// Check if the attribute's timestamp is bigger than the one that already exists.
// If yes, it means that it's newer, so insert it. If no, it means that it's the same attribute or older - don't insert it, insert the old attribute.
// Alternatively, we could unset this attribute from the request, but that could lead with issues if we decide that we want to start deleting attributes that don't exist in a pushed event.
if ($data['Event']['Attribute'][$k]['timestamp'] > $existingAttribute['Attribute']['timestamp']) {
$data['Event']['Attribute'][$k]['id'] = $existingAttribute['Attribute']['id'];
foreach ($data['Event']['Attribute'] as $k => $attribute) {
if (isset($attribute['uuid'])) {
$existingAttribute = $this->Attribute->findByUuid($attribute['uuid']);
if (count($existingAttribute)) {
if ($existingAttribute['Attribute']['event_id'] != $id) {
$result = $this->Log->save(array(
'org' => $user['Organisation']['name'],
'model' => 'Event',
'model_id' => $id,
'email' => $user['email'],
'action' => 'edit',
'user_id' => $user['id'],
'title' => 'Duplicate UUID found in attribute',
'change' => 'An attribute was blocked from being saved due to a duplicate UUID. The uuid in question is: ' . $attribute['uuid'],
));
unset($data['Event']['Attribute'][$k]);
} else {
// If a field is not set in the request, just reuse the old value
$recoverFields = array('value', 'to_ids', 'distribution', 'category', 'type', 'comment', 'sharing_group_id');
foreach ($recoverFields as $rF) if (!isset($attribute[$rF])) $data['Event']['Attribute'][$c][$rF] = $existingAttribute['Attribute'][$rF];
$data['Event']['Attribute'][$k]['id'] = $existingAttribute['Attribute']['id'];
// Check if the attribute's timestamp is bigger than the one that already exists.
// If yes, it means that it's newer, so insert it. If no, it means that it's the same attribute or older - don't insert it, insert the old attribute.
// Alternatively, we could unset this attribute from the request, but that could lead with issues if we decide that we want to start deleting attributes that don't exist in a pushed event.
if (isset($data['Event']['Attribute'][$k]['timestamp'])) {
if ($data['Event']['Attribute'][$k]['timestamp'] <= $existingAttribute['Attribute']['timestamp']) {
unset($data['Event']['Attribute'][$k]);
continue;
}
} else $data['Event']['timestamp'] = $date;
}
} else {
unset($data['Event']['Attribute'][$k]);
continue;
$this->Attribute->create();
}
} else {
unset($data['Event']['Attribute'][$k]['id']);
$this->Attribute->create();
}
if (!$this->Attribute->save($data['Event']['Attribute'][$k], array('fieldList' => $fieldList['Attribute']))) {
$validationErrors[] = $this->Attribute->validationErrors;
$data['Event']['Attribute'][$k]['event_id'] = $this->id;
if ($data['Event']['Attribute'][$k]['distribution'] == 4) {
$sid = $this->SharingGroup->captureSG($data['Event']['Attribute'][$k]['SharingGroup'], $user);
$data['Event']['Attribute'][$k]['sharing_group_id'] = $this->SharingGroup->captureSG($data['Event']['Attribute'][$k]['SharingGroup'], $user);
}
if (!$this->Attribute->save($data['Event']['Attribute'][$k], array('fieldList' => $fieldList))) {
$validationErrors['Attribute'][$k] = $this->Attribute->validationErrors;
$attribute_short = (isset($data['Event']['Attribute'][$k]['category']) ? $data['Event']['Attribute'][$k]['category'] : 'N/A') . '/' . (isset($data['Event']['Attribute'][$k]['type']) ? $data['Event']['Attribute'][$k]['type'] : 'N/A') . ' ' . (isset($data['Event']['Attribute'][$k]['value']) ? $data['Event']['Attribute'][$k]['value'] : 'N/A');
$this->Log->create();
$this->Log->save(array(
'org' => $user['Organisation']['name'],
'model' => 'Attribute',
'model_id' => 0,
'email' => $user['email'],
'action' => 'validation_error',
'user_id' => $user['id'],
'title' => 'Attribute validation for Event ' . $this->id . ' failed: ' . $attribute_short,
'change' => json_encode($this->Attribute->validationErrors),
'org' => $user['Organisation']['name'],
'model' => 'Attribute',
'model_id' => 0,
'email' => $user['email'],
'action' => 'edit',
'user_id' => $user['id'],
'title' => 'Attribute dropped due to validation for Event ' . $this->id . ' failed: ' . $attribute_short,
'change' => json_encode($this->Attribute->validationErrors),
));
}
}
}
return 'success';
} else return 'Saving the event has failed.';
if (isset($data['Event']['Tag']) && $user['Role']['perm_tagger']) {
foreach ($data['Event']['Tag'] as $tag) {
$tag_id = $this->EventTag->Tag->captureTag($tag, $user);
if ($tag_id) {
$this->EventTag->attachTagToEvent($this->id, $tag_id);
} else {
$this->Log->create();
$this->Log->save(array(
'org' => $user['Organisation']['name'],
'model' => 'Event',
'model_id' => $this->id,
'email' => $user['email'],
'action' => 'edit',
'user_id' => $user['id'],
'title' => 'Failed create or attach Tag ' . $tag['name'] . ' to the event.',
'change' => ''
));
}
}
}
// check if the exact proposal exists, if yes check if the incoming one is deleted or not. If it is deleted, remove the old proposal and replace it with the one marked for being deleted
// otherwise throw the new one away.
if (isset($data['Event']['ShadowAttribute'])) {
foreach ($data['Event']['ShadowAttribute'] as $k => &$proposal) {
$existingProposal = $this->ShadowAttribute->find('first', array(
'recursive' => -1,
'conditions' => array(
'value' => $proposal['value'],
'category' => $proposal['category'],
'to_ids' => $proposal['to_ids'],
'type' => $proposal['type'],
'event_uuid' => $proposal['event_uuid'],
'uuid' => $proposal['uuid']
)
));
if (!empty($existingProposal)) {
if ($existingProposal['ShadowAttribute']['deleted'] == 1) {
$this->ShadowAttribute->delete($existingProposal['ShadowAttribute']['id'], false);
} else {
unset($data['ShadowAttribute'][$k]);
continue;
}
$this->ShadowAttribute->create();
}
$this->ShadowAttribute->save($data['Event']['ShadowAttribute'][$k], array('fieldList' => $fieldList));
}
}
//if published -> do the actual publishing
if ((!empty($data['Event']['published']) && 1 == $data['Event']['published'])) {
// do the necessary actions to publish the event (email, upload,...)
if (true != Configure::read('MISP.disablerestalert')) {
$this->sendAlertEmailRouter($id, $user);
}
$this->publish($existingEvent['Event']['id']);
}
return true;
} return $this->validationErrors;
}
private function __searchUuidInAttributeArray($uuid, &$attr_array) {
@ -2210,6 +2326,17 @@ class Event extends AppModel {
if (empty($localEvent) || $incomingEvent['timestamp'] > $localEvent['Event']['timestamp']) return true;
return false;
}
public function removeOlder(&$eventArray) {
$uuidsToCheck = array();
foreach ($eventArray as $k => &$event) {
$uuidsToCheck[$event['uuid']] = $k;
}
$localEvents = $this->find('list', array('recursive' => -1, 'fields' => array('Event.uuid', 'Event.timestamp')));
foreach ($uuidsToCheck as $uuid => $eventArrayId) {
if (isset($localEvents[$uuid]) && $localEvents[$uuid] >= $eventArray[$eventArrayId]['timestamp']) unset($eventArray[$eventArrayId]);
}
}
public function stix($id, $tags, $attachments, $user, $returnType, $from = false, $to = false, $last = false) {
$eventIDs = $this->Attribute->dissectArgs($id);

View File

@ -908,49 +908,12 @@ class Server extends AppModel {
$event['Event']['distribution'] = '0';
break;
}
// correct $event if just one Attribute
if (is_array($event['Event']['Attribute']) && isset($event['Event']['Attribute']['id'])) {
$tmp = $event['Event']['Attribute'];
unset($event['Event']['Attribute']);
$event['Event']['Attribute'][0] = $tmp;
}
if (is_array($event['Event']['Attribute'])) {
$size = count($event['Event']['Attribute']);
if ($size == 0) {
$fails[$eventId] = 'Empty event received.';
continue;
}
for ($i = 0; $i < $size; $i++) {
if (!isset($event['Event']['Attribute'][$i]['distribution'])) { // version 1
$event['Event']['Attribute'][$i]['distribution'] = 1;
}
switch($event['Event']['Attribute'][$i]['distribution']) {
case 1:
case 'This community only': // backwards compatibility
// if community falseonly, downgrade to org only after pull
$event['Event']['Attribute'][$i]['distribution'] = '0';
break;
case 2:
case 'Connected communities': // backwards compatibility
// if connected communities downgrade to community only
$event['Event']['Attribute'][$i]['distribution'] = '1';
break;
case 'All communities': // backwards compatibility
$event['Event']['Attribute'][$i]['distribution'] = '3';
break;
case 'Your organisation only': // backwards compatibility
$event['Event']['Attribute'][$i]['distribution'] = '0';
break;
}
}
$event['Event']['Attribute'] = array_values($event['Event']['Attribute']);
} else {
if (!is_array($event['Event']['Attribute']) || empty($event['Event']['Attribute'])) {
$fails[$eventId] = 'Empty event received.';
continue;
}
$event['Event']['Attribute'] = array_values($event['Event']['Attribute']);
} else {
$fails[$eventId] = 'Empty event received.';
$fails[$eventId] = 'Event blocked by blacklist.';
continue;
}
// Distribution, set reporter of the event, being the admin that initiated the pull
@ -968,9 +931,12 @@ class Server extends AppModel {
}
} else {
$result = $eventModel->_edit($event, $user, $existingEvent['Event']['id'], $jobId);
if ($result === 'success') $successes[] = $eventId;
else $fails[$eventId] = $result;
$tempUser = $user;
$tempUser['Role']['perm_site_admin'] = false;
$result = $eventModel->_edit($event, $tempUser, $existingEvent['Event']['id'], $jobId);
if ($result === true) $successes[] = $eventId;
else if (isset($result['error'])) $fails[$eventId] = $result['error'];
else $fails[$eventId] = json_encode($result);
}
} else {
// error
@ -1078,6 +1044,7 @@ class Server extends AppModel {
* @return array of event_ids
*/
public function getEventIdsFromServer($server, $all = false, $HttpSocket=null, $force_uuid=false, $ignoreFilterRules = false) {
$start = microtime(true);
$url = $server['Server']['url'];
$authkey = $server['Server']['authkey'];
if ($ignoreFilterRules) $filter_rules = array();
@ -1118,13 +1085,14 @@ class Server extends AppModel {
} else {
// multiple events, iterate over the array
$this->Event = ClassRegistry::init('Event');
foreach ($eventArray as &$event) {
foreach ($eventArray as $k => &$event) {
if (1 != $event['published']) {
continue; // do not keep non-published events
unset($eventArray[$k]); // do not keep non-published events
}
// get rid of events that are the same timestamp as ours or older, we don't want to transfer the attributes for those
// The event's timestamp also matches the newest attribute timestamp by default
if ($this->Event->checkIfNewer($event)) {
}
$this->Event->removeOlder($eventArray);
if (!empty($eventArray)) {
foreach ($eventArray as $event) {
if ($force_uuid) $eventIds[] = $event['uuid'];
else $eventIds[] = $event['id'];
}
@ -1849,6 +1817,7 @@ class Server extends AppModel {
}
public function captureServer($server, $user) {
if (isset($server[0])) $server = $server[0];
if ($server['url'] == Configure::read('MISP.baseurl')) return 0;
$existingServer = $this->find('first', array(
'recursive' => -1,

View File

@ -198,29 +198,27 @@ class SharingGroup extends AppModel {
if ($this->checkIfOwner($user, $id)) return true;
$this->id = $id;
if (!$this->exists()) return false;
$sgo = $this->SharingGroupOrg->find('first', array(
'conditions' => array(
'sharing_group_id' => $id,
'org_id' => $user['org_id'],
'extend' => 1,
),
'recursive' => -1,
'fields' => array('id', 'org_id', 'extend')
));
if (empty($sgo)) {
if ($user['Role']['perm_sync']) {
$sg = $this->find('first', array(
'conditions' => array(
'id' => $id,
'sync_user_id' => $user['id'],
),
'recursive' => -1,
));
if (empty($sg)) return false;
else return true;
}
return false;
if ($user['Role']['perm_sync']) {
$sg = $this->find('first', array(
'conditions' => array(
'id' => $id,
'sync_user_id' => $user['id'],
),
'recursive' => -1,
));
if (empty($sg)) return false;
else return true;
}
$sgo = $this->SharingGroupOrg->find('first', array(
'conditions' => array(
'sharing_group_id' => $id,
'org_id' => $user['org_id'],
'extend' => 1,
),
'recursive' => -1,
'fields' => array('id', 'org_id', 'extend')
));
if (empty($sgo)) return false;
else return true;
}
@ -327,13 +325,21 @@ class SharingGroup extends AppModel {
));
$force = false;
if (empty($existingSG)) {
if (!$user['Role']['perm_sharing_group']) throw new Exception('User not authorised to create sharing groups.');
$this->create();
$newSG = array();
$attributes = array('name', 'releasability', 'description', 'uuid', 'organisation_uuid', 'created', 'modified');
foreach ($attributes as $a) $newSG[$a] = $sg[$a];
$newSG['local'] = 0;
$newSG['sync_user_id'] = $user['id'];
$newSG['org_id'] = $this->Organisation->captureOrg($sg['Organisation'], $user);
if (!isset($sg['Organisation'])) {
foreach ($sg['SharingGroupOrg'] as $k => $org) {
if (isset($org['Organisation'][0])) $org['Organisation'] = $org['Organisation'][0];
if ($org['Organisation']['uuid'] == $sg['uuid']) $newSG['org_id'] = $this->Organisation->captureOrg($org['Organisation'], $user);
}
} else {
$newSG['org_id'] = $this->Organisation->captureOrg($sg['Organisation'], $user);
}
if (!$this->save($newSG)) return false;
$sgids = $this->id;
} else {
@ -353,7 +359,7 @@ class SharingGroup extends AppModel {
if ($force) {
$sgids = $existingSG['SharingGroup']['id'];
$editedSG = $existingSG['SharingGroup'];
$attributes = array('name', 'releasability', 'description', 'uuid', 'organisation_uuid', 'created', 'modified');
$attributes = array('name', 'releasability', 'description', 'created', 'modified');
foreach ($attributes as &$a) $editedSG[$a] = $sg[$a];
$this->save($editedSG);
} else {
@ -363,7 +369,6 @@ class SharingGroup extends AppModel {
return $existingSG['SharingGroup']['id'];
}
}
$sg['org_id'] = $this->Organisation->captureOrg($sg['Organisation'], $user, $force);
unset ($sg['Organisation']);
if (isset($sg['SharingGroupOrg']['id'])) {
@ -372,6 +377,7 @@ class SharingGroup extends AppModel {
$sg['SharingGroupOrg'][0] = $temp;
}
foreach ($sg['SharingGroupOrg'] as $k => $org) {
if (isset($org['Organisation'][0])) $org['Organisation'] = $org['Organisation'][0];
$sg['SharingGroupOrg'][$k]['org_id'] = $this->Organisation->captureOrg($org['Organisation'], $user, $force);
unset ($sg['SharingGroupOrg'][$k]['Organisation']);
if ($force) {
@ -399,6 +405,7 @@ class SharingGroup extends AppModel {
$sg['SharingGroupServer'][0] = $temp;
}
foreach ($sg['SharingGroupServer'] as $k => $server) {
if (isset($server[0])) $server = $server[0];
$sg['SharingGroupServer'][$k]['server_id'] = $this->SharingGroupServer->Server->captureServer($server['Server'], $user, $force);
if ($sg['SharingGroupServer'][$k]['server_id'] === false) unset ($sg['SharingGroupServer'][$k]);
else {