Event ID translation feature (#6212)

* new: [sync] Event ID translation between sync servers
pull/6262/head
Loïc Fortemps 2020-08-26 10:01:14 +02:00 committed by GitHub
parent c5e200634c
commit 8844fd7ada
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 186 additions and 5 deletions

View File

@ -423,6 +423,7 @@ class ACLComponent extends Component
'getSubmoduleQuickUpdateForm' => array(),
'getWorkers' => array(),
'getVersion' => array('*'),
'idTranslator' => array('*'),
'import' => array(),
'index' => array(),
'ondemandAction' => array(),

View File

@ -1266,6 +1266,90 @@ class ServersController extends AppController
}
}
public function idTranslator() {
// The id translation feature is limited to people from the host org
if (!$this->_isSiteAdmin() && $this->Auth->user('org_id') != Configure::read('MISP.host_org_id')) {
throw new MethodNotAllowedException(__('You don\'t have the required privileges to do that.'));
}
//We retrieve the list of remote servers that we can query
$options = array();
$options['conditions'] = array("pull" => true);
$servers = $this->Server->find('all', $options);
// We generate the list of servers for the dropdown
$displayServers = array();
foreach($servers as $s) {
$displayServers[] = array('name' => $s['Server']['name'],
'value' => $s['Server']['id']);
}
$this->set('servers', $displayServers);
if ($this->request->is('post')) {
$remote_events = array();
if(!empty($this->request->data['Event']['uuid']) && $this->request->data['Event']['local'] == "local") {
$local_event = $this->Event->fetchSimpleEvent($this->Auth->user(), $this->request->data['Event']['uuid']);
} else if (!empty($this->request->data['Event']['uuid']) && $this->request->data['Event']['local'] == "remote" && !empty($this->request->data['Server']['id'])) {
//We check on the remote server for any event with this id and try to find a match locally
$conditions = array('AND' => array('Server.id' => $this->request->data['Server']['id'], 'Server.pull' => true));
$remote_server = $this->Server->find('first', array('conditions' => $conditions));
if(!empty($remote_server)) {
try {
$remote_event = $this->Event->downloadEventFromServer($this->request->data['Event']['uuid'], $remote_server, null, true);
} catch (Exception $e) {
$error_msg = __("Issue while contacting the remote server to retrieve event information");
$this->logException($error_msg, $e);
$this->Flash->error($error_msg);
return;
}
$local_event = $this->Event->fetchSimpleEvent($this->Auth->user(), $remote_event[0]['uuid']);
//we record it to avoid re-querying the same server in the 2nd phase
if(!empty($local_event)) {
$remote_events[] = array(
"server_id" => $remote_server['Server']['id'],
"server_name" => $remote_server['Server']['name'],
"url" => $remote_server['Server']['url']."/events/view/".$remote_event[0]['id'],
"remote_id" => $remote_event[0]['id']
);
}
}
}
if(empty($local_event)) {
$this->Flash->error( __("This event could not be found or you don't have permissions to see it."));
return;
} else {
$this->Flash->success(__('The event has been found.'));
}
// In the second phase, we query all configured sync servers to get their info on the event
foreach($servers as $s) {
// We check if the server was not already contacted in phase 1
if(count($remote_events) > 0 && $remote_events[0]['server_id'] == $s['Server']['id']) {
continue;
}
try {
$remote_event = $this->Event->downloadEventFromServer($local_event['Event']['uuid'], $s, null, true);
$remote_event_id = $remote_event[0]['id'];
} catch (Exception $e) {
$this->logException("Couldn't download event from server", $e);
$remote_event_id = null;
}
$remote_events[] = array(
"server_id" => $s['Server']['id'],
"server_name" => $s['Server']['name'],
"url" => isset($remote_event_id) ? $s['Server']['url']."/events/view/".$remote_event_id : $s['Server']['url'],
"remote_id" => isset($remote_event_id) ? $remote_event_id : false
);
}
$this->set('local_event', $local_event);
$this->set('remote_events', $remote_events);
}
}
public function getSubmodulesStatus() {
if (!$this->_isSiteAdmin()) {
throw new MethodNotAllowedException();

View File

@ -1382,19 +1382,28 @@ class Event extends AppModel
* @param int $eventId
* @param array $server
* @param null|HttpSocket $HttpSocket
* @param boolean $metadataOnly, if True, we only retrieve the metadata
* without attributes and attachments which is much faster
* @return array
* @throws Exception
*/
public function downloadEventFromServer($eventId, $server, $HttpSocket=null)
public function downloadEventFromServer($eventId, $server, $HttpSocket=null, $metadataOnly=false)
{
$url = $server['Server']['url'];
$HttpSocket = $this->setupHttpSocket($server, $HttpSocket);
$request = $this->setupSyncRequest($server);
$uri = $url . '/events/view/' . $eventId . '/deleted[]:0/deleted[]:1/excludeGalaxy:1';
if (!empty($server['Server']['internal'])) {
$uri = $uri . '/excludeLocalTags:1';
if ($metadataOnly) {
$uri = $url . '/events/index';
$data = ['eventid' => $eventId];
$data = json_encode($data);
$response = $HttpSocket->post($uri, $data, $request);
} else {
$uri = $url . '/events/view/' . $eventId . '/deleted[]:0/deleted[]:1/excludeGalaxy:1';
if (!empty($server['Server']['internal'])) {
$uri = $uri . '/excludeLocalTags:1';
}
$response = $HttpSocket->get($uri, $data = '', $request);
}
$response = $HttpSocket->get($uri, $data = '', $request);
if ($response === false) {
throw new Exception("Could not reach '$uri'.");

View File

@ -701,6 +701,13 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
));
}
}
if ($menuItem === 'id_translator') {
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'text' => __('Event ID translator'),
'url' => '/servers/id_translator',
'element_id' => 'id_translator'
));
}
break;
case 'admin':

View File

@ -298,6 +298,11 @@
'text' => __('List Communities'),
'url' => $baseurl . '/communities/index',
'requirement' => ($isSiteAdmin)
),
array(
'text' => __('Event ID translator'),
'url' => '/servers/idTranslator',
'requirement' => ($isSiteAdmin || $hostOrgUser)
)
)
),

View File

@ -0,0 +1,75 @@
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'sync', 'menuItem' => 'id_translator'));
echo $this->element('/genericElements/Form/genericForm', array(
'form' => $this->Form,
'data' => array(
'title' => __('Event ID translator'),
'description' => __('Allows to translate a local ID into the corresponding event ID on sync servers configured.'),
'model' => 'Event',
'fields' => array(
array(
"field" => "uuid",
"label" => __("Event ID or UUID"),
"type" => "text",
"placeholder" => __("1234"),
"stayInLine" => true,
),
array(
"field" => "local",
"label" => __("referencing an event which is"),
"default" => "local",
"options" => array("local" => __("local"), "remote" => __("remote")),
"type" => "select",
"stayInLine" => true,
),
array(
"field" => "Server.id",
"div" => "input select optional-server-select hide",
"options" => $servers,
"label" => __("ID referenced on server:"),
"type" => "select",
)
),
"submit" => array (
"action" => "idTranslator",
),
)
));
echo '<div class="view">';
echo $this->Flash->render();
if (isset($remote_events) && isset($local_event)) {
$table_data = array();
$table_data[] = array('key' => __('UUID'), 'value' => $local_event['Event']['uuid']);
$link = '<a href="'.$base_url.'/events/view/'.$local_event['Event']['id'].'" target="_blank">'.$local_event['Event']['id'].'</a>';
$table_data[] = array('key' => __('Local ID'), 'html' => $link);
foreach($remote_events as $remote_event) {
if($remote_event['remote_id']) {
$value = __('Remote ID: ').'<a href="'.h($remote_event['url']).'" target="_blank">'.$remote_event['remote_id'].'</a>';
$table_data[] = array('key' => h($remote_event['server_name']), 'html' => $value);
} else {
$table_data[] = array('key' => h($remote_event['server_name']), 'value' => __('Not found or server unreachable'));
}
}
echo $this->element('genericElements/viewMetaTable', array('table_data' => $table_data));
}
echo "</div>";
?>
<script type="text/javascript">
function IDTranslatorUISetup() {
if($('#EventLocal').val() == "remote") {
$(".optional-server-select").show();
} else {
$(".optional-server-select").hide();
}
}
$(function() {
IDTranslatorUISetup();
});
$("#EventLocal").change(function(){
IDTranslatorUISetup();
});
</script>