MISP/app/Controller/ServersController.php

2501 lines
111 KiB
PHP

<?php
App::uses('AppController', 'Controller');
App::uses('Xml', 'Utility');
App::uses('AttachmentTool', 'Tools');
App::uses('JsonTool', 'Tools');
App::uses('SecurityAudit', 'Tools');
/**
* @property Server $Server
*/
class ServersController extends AppController
{
public $components = array('RequestHandler'); // XXX ACL component
public $paginate = array(
'limit' => 60,
'recursive' => -1,
'contain' => array(
'User' => array(
'fields' => array('User.id', 'User.org_id', 'User.email'),
),
'Organisation' => array(
'fields' => array('Organisation.name', 'Organisation.id'),
),
'RemoteOrg' => array(
'fields' => array('RemoteOrg.name', 'RemoteOrg.id'),
),
),
'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 events
'order' => array(
'Server.priority' => 'ASC'
),
);
public $uses = array('Server', 'Event');
public function beforeFilter()
{
$this->Auth->allow(['cspReport']); // cspReport must work without authentication
parent::beforeFilter();
$this->Security->unlockedActions[] = 'cspReport';
// permit reuse of CSRF tokens on some pages.
switch ($this->request->params['action']) {
case 'push':
case 'pull':
case 'getVersion':
case 'testConnection':
$this->Security->csrfUseOnce = false;
}
}
public function index()
{
// Do not fetch server authkey from DB
$fields = $this->Server->schema();
unset($fields['authkey']);
$fields = array_keys($fields);
if ($this->_isRest()) {
$params = array(
'fields' => $fields,
'recursive' => -1,
'contain' => array(
'User' => array(
'fields' => array('User.id', 'User.org_id', 'User.email'),
),
'Organisation' => array(
'fields' => array('Organisation.id', 'Organisation.name', 'Organisation.uuid', 'Organisation.nationality', 'Organisation.sector', 'Organisation.type'),
),
'RemoteOrg' => array(
'fields' => array('RemoteOrg.id', 'RemoteOrg.name', 'RemoteOrg.uuid', 'RemoteOrg.nationality', 'RemoteOrg.sector', 'RemoteOrg.type'),
),
),
);
$servers = $this->Server->find('all', $params);
$servers = $this->Server->attachServerCacheTimestamps($servers);
return $this->RestResponse->viewData($servers, $this->response->type());
} else {
$this->paginate['fields'] = $fields;
$servers = $this->paginate();
$servers = $this->Server->attachServerCacheTimestamps($servers);
$this->set('servers', $servers);
$collection = array();
$collection['orgs'] = $this->Server->Organisation->find('list', array(
'fields' => array('id', 'name'),
));
$this->loadModel('Tag');
$collection['tags'] = $this->Tag->find('list', array(
'fields' => array('id', 'name'),
));
$this->set('collection', $collection);
}
}
public function previewIndex($id)
{
$urlparams = '';
$passedArgs = array();
$server = $this->Server->find('first', array('conditions' => array('Server.id' => $id), 'recursive' => -1));
if (empty($server)) {
throw new NotFoundException('Invalid server ID.');
}
$validFilters = $this->Server->validEventIndexFilters;
foreach ($validFilters as $k => $filter) {
if (isset($this->passedArgs[$filter])) {
$passedArgs[$filter] = $this->passedArgs[$filter];
if ($k != 0) {
$urlparams .= '/';
}
$urlparams .= $filter . ':' . $this->passedArgs[$filter];
}
}
$combinedArgs = array_merge($this->passedArgs, $passedArgs);
if (!isset($combinedArgs['sort'])) {
$combinedArgs['sort'] = 'timestamp';
$combinedArgs['direction'] = 'desc';
}
if (empty($combinedArgs['page'])) {
$combinedArgs['page'] = 1;
}
if (empty($combinedArgs['limit'])) {
$combinedArgs['limit'] = 60;
}
try {
list($events, $total_count) = $this->Server->previewIndex($server, $this->Auth->user(), $combinedArgs);
} catch (Exception $e) {
if ($this->_isRest()) {
return $this->RestResponse->throwException(500, $e->getMessage());
} else {
$this->Flash->error(__('Download failed.') . ' ' . $e->getMessage());
$this->redirect(array('action' => 'index'));
}
}
if ($this->_isRest()) {
return $this->RestResponse->viewData($events, $this->response->type());
}
$this->loadModel('Event');
$this->set('threatLevels', $this->Event->ThreatLevel->listThreatLevels());
App::uses('CustomPaginationTool', 'Tools');
$customPagination = new CustomPaginationTool();
$params = $customPagination->createPaginationRules($events, $this->passedArgs, $this->alias);
if (!empty($total_count)) {
$params['pageCount'] = ceil($total_count / $params['limit']);
}
$this->params->params['paging'] = array($this->modelClass => $params);
if (count($events) > 60) {
$customPagination->truncateByPagination($events, $params);
}
$this->set('events', $events);
$this->set('eventDescriptions', $this->Event->fieldDescriptions);
$this->set('analysisLevels', $this->Event->analysisLevels);
$this->set('distributionLevels', $this->Event->distributionLevels);
$shortDist = array(0 => 'Organisation', 1 => 'Community', 2 => 'Connected', 3 => 'All', 4 => ' sharing Group');
$this->set('shortDist', $shortDist);
$this->set('id', $id);
$this->set('urlparams', $urlparams);
$this->set('passedArgs', json_encode($passedArgs));
$this->set('passedArgsArray', $passedArgs);
$this->set('server', $server);
}
public function previewEvent($serverId, $eventId, $all = false)
{
$server = $this->Server->find('first', array(
'conditions' => array('Server.id' => $serverId),
'recursive' => -1,
));
if (empty($server)) {
throw new NotFoundException('Invalid server ID.');
}
try {
$event = $this->Server->previewEvent($server, $eventId);
} catch (NotFoundException $e) {
throw new NotFoundException(__("Event '%s' not found.", $eventId));
} catch (Exception $e) {
$this->Flash->error(__('Download failed. %s', $e->getMessage()));
$this->redirect(array('action' => 'previewIndex', $serverId));
}
if ($this->_isRest()) {
return $this->RestResponse->viewData($event, $this->response->type());
}
$this->loadModel('Warninglist');
if (isset($event['Event']['Attribute'])) {
$this->Warninglist->attachWarninglistToAttributes($event['Event']['Attribute']);
}
if (isset($event['Event']['ShadowAttribute'])) {
$this->Warninglist->attachWarninglistToAttributes($event['Event']['ShadowAttribute']);
}
$this->loadModel('Event');
$params = $this->Event->rearrangeEventForView($event, $this->passedArgs, $all);
$this->__removeGalaxyClusterTags($event);
$this->params->params['paging'] = array('Server' => $params);
$this->set('event', $event);
$this->set('server', $server);
$dataForView = array(
'Attribute' => array('attrDescriptions' => 'fieldDescriptions', 'distributionDescriptions' => 'distributionDescriptions', 'distributionLevels' => 'distributionLevels'),
'Event' => array('eventDescriptions' => 'fieldDescriptions', 'analysisLevels' => 'analysisLevels'),
'Object' => array()
);
foreach ($dataForView as $m => $variables) {
if ($m === 'Event') {
$currentModel = $this->Event;
} elseif ($m === 'Attribute') {
$currentModel = $this->Event->Attribute;
} elseif ($m === 'Object') {
$currentModel = $this->Event->Object;
}
foreach ($variables as $alias => $variable) {
$this->set($alias, $currentModel->{$variable});
}
}
$this->set('threatLevels', $this->Event->ThreatLevel->listThreatLevels());
$this->set('title_for_layout', __('Remote event preview'));
}
private function __removeGalaxyClusterTags(array &$event)
{
$galaxyTagIds = [];
foreach ($event['Galaxy'] as $galaxy) {
foreach ($galaxy['GalaxyCluster'] as $galaxyCluster) {
$galaxyTagIds[$galaxyCluster['tag_id']] = true;
}
}
if (empty($galaxyTagIds)) {
return;
}
foreach ($event['Tag'] as $k => $eventTag) {
if (isset($galaxyTagIds[$eventTag['id']])) {
unset($event['Tag'][$k]);
}
}
}
public function compareServers()
{
list($servers, $overlap) = $this->Server->serverEventsOverlap();
$this->set('servers', $servers);
$this->set('overlap', $overlap);
$this->set('title_for_layout', __('Server overlap analysis matrix'));
}
public function filterEventIndex($id)
{
if (!$this->_isSiteAdmin()) {
throw new MethodNotAllowedException('You are not authorised to do that.');
}
$validFilters = $this->Server->validEventIndexFilters;
$validatedFilterString = '';
foreach ($this->passedArgs as $k => $v) {
if (in_array('' . $k, $validFilters)) {
if ($validatedFilterString != '') {
$validatedFilterString .= '/';
}
$validatedFilterString .= $k . ':' . $v;
}
}
$this->set('id', $id);
$this->set('validFilters', $validFilters);
$this->set('filter', $validatedFilterString);
}
public function add()
{
if ($this->request->is('post')) {
if ($this->_isRest()) {
if (!isset($this->request->data['Server'])) {
$this->request->data = array('Server' => $this->request->data);
}
}
if (!empty($this->request->data['Server']['json'])) {
$json = json_decode($this->request->data['Server']['json'], true);
} elseif ($this->_isRest()) {
if (empty($this->request->data['Server']['remote_org_id'])) {
throw new MethodNotAllowedException('No remote org ID set. Please pass it as remote_org_id');
}
}
$fail = false;
if (empty(Configure::read('MISP.host_org_id'))) {
$this->request->data['Server']['internal'] = 0;
}
// test the filter fields
if (!empty($this->request->data['Server']['pull_rules']) && !JsonTool::isValid($this->request->data['Server']['pull_rules'])) {
$fail = true;
$error_msg = __('The pull filter rules must be in valid JSON format.');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Servers', 'add', false, array('pull_rules' => $error_msg), $this->response->type());
} else {
$this->Flash->error($error_msg);
}
}
if (!$fail && !empty($this->request->data['Server']['push_rules']) && !JsonTool::isValid($this->request->data['Server']['push_rules'])) {
$fail = true;
$error_msg = __('The push filter rules must be in valid JSON format.');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Servers', 'add', false, array('push_rules' => $error_msg), $this->response->type());
} else {
$this->Flash->error($error_msg);
}
}
if (!$fail) {
if ($this->_isRest()) {
$defaultPushRules = json_encode(["tags" => ["OR" => [], "NOT" => []], "orgs" => ["OR" => [], "NOT" => []]]);
$defaultPullRules = json_encode(["tags" => ["OR" => [], "NOT" => []], "orgs" => ["OR" => [], "NOT" => []], "type_attributes" => ["NOT" => []], "type_objects" => ["NOT" => []], "url_params" => ""]);
$defaults = array(
'push' => 0,
'pull' => 0,
'push_sightings' => 0,
'push_galaxy_clusters' => 0,
'pull_galaxy_clusters' => 0,
'caching_enabled' => 0,
'json' => '[]',
'push_rules' => $defaultPushRules,
'pull_rules' => $defaultPullRules,
'self_signed' => 0,
'remove_missing_tags' => 0
);
foreach ($defaults as $default => $dvalue) {
if (!isset($this->request->data['Server'][$default])) {
$this->request->data['Server'][$default] = $dvalue;
}
}
}
// force check userid and orgname to be from yourself
$this->request->data['Server']['org_id'] = $this->Auth->user('org_id');
if ($this->_isRest()) {
if (empty($this->request->data['Server']['remote_org_id'])) {
return $this->RestResponse->saveFailResponse('Servers', 'add', false, array('Organisation' => 'Remote Organisation\'s id/uuid not given (remote_org_id)'), $this->response->type());
}
if (Validation::uuid($this->request->data['Server']['remote_org_id'])) {
$orgCondition = array('uuid' => $this->request->data['Server']['remote_org_id']);
} else {
$orgCondition = array('id' => $this->request->data['Server']['remote_org_id']);
}
$existingOrgs = $this->Server->Organisation->find('first', array(
'conditions' => $orgCondition,
'recursive' => -1,
'fields' => array('id', 'uuid')
));
if (empty($existingOrgs)) {
return $this->RestResponse->saveFailResponse('Servers', 'add', false, array('Organisation' => 'Invalid Remote Organisation'), $this->response->type());
}
} else {
if ($this->request->data['Server']['organisation_type'] < 2) {
$this->request->data['Server']['remote_org_id'] = $json['id'];
} else {
$existingOrgs = $this->Server->Organisation->find('first', array(
'conditions' => array('uuid' => $json['uuid']),
'recursive' => -1,
'fields' => array('id', 'uuid')
));
if (!empty($existingOrgs)) {
$fail = true;
$this->Flash->error(__('That organisation could not be created as the uuid is in use already.'));
}
if (!$fail) {
$this->Server->Organisation->create();
$orgSave = $this->Server->Organisation->save(array(
'name' => $json['name'],
'uuid' => $json['uuid'],
'local' => 0,
'created_by' => $this->Auth->user('id')
));
if (!$orgSave) {
$this->Flash->error(__('Couldn\'t save the new organisation, are you sure that the uuid is in the correct format? Also, make sure the organisation\'s name doesn\'t clash with an existing one.'));
$fail = true;
$this->request->data['Server']['external_name'] = $json['name'];
$this->request->data['Server']['external_uuid'] = $json['uuid'];
} else {
$this->request->data['Server']['remote_org_id'] = $this->Server->Organisation->id;
$this->request->data['Server']['organisation_type'] = 1;
}
}
}
}
if (!$fail) {
if (Configure::read('MISP.host_org_id') == 0 || $this->request->data['Server']['remote_org_id'] != Configure::read('MISP.host_org_id')) {
$this->request->data['Server']['internal'] = 0;
}
$this->request->data['Server']['org_id'] = $this->Auth->user('org_id');
if (empty($this->request->data['Server']['push_rules'])) {
$this->request->data['Server']['push_rules'] = $defaultPushRules;
}
if (empty($this->request->data['Server']['pull_rules'])) {
$this->request->data['Server']['pull_rules'] = $defaultPullRules;
}
if ($this->Server->save($this->request->data)) {
if (isset($this->request->data['Server']['submitted_cert'])) {
$this->__saveCert($this->request->data, $this->Server->id, false);
}
if (isset($this->request->data['Server']['submitted_client_cert'])) {
$this->__saveCert($this->request->data, $this->Server->id, true);
}
if ($this->_isRest()) {
$server = $this->Server->find('first', array(
'conditions' => array('Server.id' => $this->Server->id),
'recursive' => -1
));
return $this->RestResponse->viewData($server, $this->response->type());
} else {
$this->Flash->success(__('The server has been saved'));
$this->redirect(array('action' => 'index'));
}
} else {
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Servers', 'add', false, $this->Server->validationErrors, $this->response->type());
} else {
$this->Flash->error(__('The server could not be saved. Please, try again.'));
}
}
}
}
}
if ($this->_isRest()) {
return $this->RestResponse->describe('Servers', 'add', false, $this->response->type());
} else {
$organisationOptions = array(0 => 'Local organisation', 1 => 'External organisation', 2 => 'New external organisation');
$temp = $this->Server->Organisation->find('all', array(
'fields' => array('id', 'name', 'local'),
'order' => array('lower(Organisation.name) ASC')
));
$allOrgs = [];
$localOrganisations = array();
$externalOrganisations = array();
foreach ($temp as $o) {
if ($o['Organisation']['local']) {
$localOrganisations[$o['Organisation']['id']] = $o['Organisation']['name'];
} else {
$externalOrganisations[$o['Organisation']['id']] = $o['Organisation']['name'];
}
$allOrgs[] = array('id' => $o['Organisation']['id'], 'name' => $o['Organisation']['name']);
}
$allTypes = $this->Server->getAllTypes();
$this->set('host_org_id', Configure::read('MISP.host_org_id'));
$this->set('organisationOptions', $organisationOptions);
$this->set('localOrganisations', $localOrganisations);
$this->set('externalOrganisations', $externalOrganisations);
$this->set('allOrganisations', $allOrgs);
$this->set('allAttributeTypes', $allTypes['attribute']);
$this->set('allObjectTypes', $allTypes['object']);
$this->set('allTags', $this->__getTags());
$this->set('host_org_id', Configure::read('MISP.host_org_id'));
$this->set('pull_scope', 'server');
$this->render('edit');
}
}
public function edit($id = null)
{
$this->Server->id = $id;
if (!$this->Server->exists()) {
throw new NotFoundException(__('Invalid server'));
}
$s = $this->Server->read(null, $id);
if ($this->request->is('post') || $this->request->is('put')) {
if ($this->_isRest()) {
if (!isset($this->request->data['Server'])) {
$this->request->data = array('Server' => $this->request->data);
}
}
if (empty(Configure::read('MISP.host_org_id'))) {
$this->request->data['Server']['internal'] = 0;
}
if (isset($this->request->data['Server']['json'])) {
$json = json_decode($this->request->data['Server']['json'], true);
} else {
$json = null;
}
$fail = false;
// test the filter fields
if (!empty($this->request->data['Server']['pull_rules']) && !JsonTool::isValid($this->request->data['Server']['pull_rules'])) {
$fail = true;
$error_msg = __('The pull filter rules must be in valid JSON format.');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Servers', 'edit', false, array('pull_rules' => $error_msg), $this->response->type());
} else {
$this->Flash->error($error_msg);
}
}
if (!$fail && !empty($this->request->data['Server']['push_rules']) && !JsonTool::isValid($this->request->data['Server']['push_rules'])) {
$fail = true;
$error_msg = __('The push filter rules must be in valid JSON format.');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Servers', 'edit', false, array('push_rules' => $error_msg), $this->response->type());
} else {
$this->Flash->error($error_msg);
}
}
$pushRules = $this->_jsonDecode($this->request->data['Server']['push_rules']);
$this->loadModel('Tag');
foreach ($pushRules['tags'] as $operator => $list) {
foreach ($list as $i => $tagName) {
if (!is_numeric($tagName)) { // tag added from freetext
$tag_id = $this->Tag->captureTag(['name' => $tagName], $this->Auth->user());
$list[$i] = $tag_id;
}
}
}
if (!$fail) {
// say what fields are to be updated
$fieldList = array('id', 'url', 'push', 'pull', 'push_sightings', 'push_galaxy_clusters', 'pull_galaxy_clusters', 'caching_enabled', 'unpublish_event', 'publish_without_email', 'remote_org_id', 'name' ,'self_signed', 'remove_missing_tags', 'cert_file', 'client_cert_file', 'push_rules', 'pull_rules', 'internal', 'skip_proxy');
$this->request->data['Server']['id'] = $id;
if (isset($this->request->data['Server']['authkey']) && "" != $this->request->data['Server']['authkey']) {
$fieldList[] = 'authkey';
}
if (isset($this->request->data['Server']['organisation_type']) && isset($json)) {
// adds 'remote_org_id' in the fields to update
$fieldList[] = 'remote_org_id';
if ($this->request->data['Server']['organisation_type'] < 2) {
$this->request->data['Server']['remote_org_id'] = $json['id'];
} else {
$existingOrgs = $this->Server->Organisation->find('first', array(
'conditions' => array('uuid' => $json['uuid']),
'recursive' => -1,
'fields' => array('id', 'uuid')
));
if (!empty($existingOrgs)) {
$fail = true;
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Servers', 'edit', false, array('Organisation' => 'Remote Organisation\'s uuid already used'), $this->response->type());
} else {
$this->Flash->error(__('That organisation could not be created as the uuid is in use already.'));
}
}
if (!$fail) {
$this->Server->Organisation->create();
$orgSave = $this->Server->Organisation->save(array(
'name' => $json['name'],
'uuid' => $json['uuid'],
'local' => 0,
'created_by' => $this->Auth->user('id')
));
if (!$orgSave) {
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Servers', 'edit', false, $this->Server->Organisation->validationError, $this->response->type());
} else {
$this->Flash->error(__('Couldn\'t save the new organisation, are you sure that the uuid is in the correct format?.'));
}
$fail = true;
$this->request->data['Server']['external_name'] = $json['name'];
$this->request->data['Server']['external_uuid'] = $json['uuid'];
} else {
$this->request->data['Server']['remote_org_id'] = $this->Server->Organisation->id;
}
}
}
if (empty(Configure::read('MISP.host_org_id')) || $this->request->data['Server']['remote_org_id'] != Configure::read('MISP.host_org_id')) {
$this->request->data['Server']['internal'] = 0;
}
}
}
if (!$fail) {
// Save the data
if ($this->Server->save($this->request->data, true, $fieldList)) {
if (isset($this->request->data['Server']['submitted_cert']) && (!isset($this->request->data['Server']['delete_cert']) || !$this->request->data['Server']['delete_cert'])) {
$this->__saveCert($this->request->data, $this->Server->id, false);
} else {
if (isset($this->request->data['Server']['delete_cert']) && $this->request->data['Server']['delete_cert']) {
$this->__saveCert($this->request->data, $this->Server->id, false, true);
}
}
if (isset($this->request->data['Server']['submitted_client_cert']) && (!isset($this->request->data['Server']['delete_client_cert']) || !$this->request->data['Server']['delete_client_cert'])) {
$this->__saveCert($this->request->data, $this->Server->id, true);
} else {
if (isset($this->request->data['Server']['delete_client_cert']) && $this->request->data['Server']['delete_client_cert']) {
$this->__saveCert($this->request->data, $this->Server->id, true, true);
}
}
if ($this->_isRest()) {
$server = $this->Server->find('first', array(
'conditions' => array('Server.id' => $this->Server->id),
'recursive' => -1
));
return $this->RestResponse->viewData($server, $this->response->type());
} else {
$this->Flash->success(__('The server has been saved'));
$this->redirect(array('action' => 'index'));
}
} else {
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Servers', 'edit', false, $this->Server->validationError, $this->response->type());
} else {
$this->Flash->error(__('The server could not be saved. Please, try again.'));
}
}
}
} else {
$this->Server->read(null, $id);
$this->Server->set('authkey', '');
$this->request->data = $this->Server->data;
}
if ($this->_isRest()) {
return $this->RestResponse->describe('Servers', 'edit', false, $this->response->type());
} else {
$organisationOptions = array(0 => 'Local organisation', 1 => 'External organisation', 2 => 'New external organisation');
$temp = $this->Server->Organisation->find('all', array(
'fields' => array('id', 'name', 'local'),
'order' => array('lower(Organisation.name) ASC')
));
$allOrgs = [];
$localOrganisations = array();
$externalOrganisations = array();
foreach ($temp as $o) {
if ($o['Organisation']['local']) {
$localOrganisations[$o['Organisation']['id']] = $o['Organisation']['name'];
} else {
$externalOrganisations[$o['Organisation']['id']] = $o['Organisation']['name'];
}
$allOrgs[] = array('id' => $o['Organisation']['id'], 'name' => $o['Organisation']['name']);
}
$allTypes = $this->Server->getAllTypes();
$oldRemoteSetting = 0;
if (!$this->Server->data['RemoteOrg']['local']) {
$oldRemoteSetting = 1;
}
$this->set('host_org_id', Configure::read('MISP.host_org_id'));
$this->set('oldRemoteSetting', $oldRemoteSetting);
$this->set('oldRemoteOrg', $this->Server->data['RemoteOrg']['id']);
$this->set('organisationOptions', $organisationOptions);
$this->set('localOrganisations', $localOrganisations);
$this->set('externalOrganisations', $externalOrganisations);
$this->set('allOrganisations', $allOrgs);
$this->set('allTags', $this->__getTags());
$this->set('allAttributeTypes', $allTypes['attribute']);
$this->set('allObjectTypes', $allTypes['object']);
$this->set('server', $s);
$this->set('id', $id);
$this->set('host_org_id', Configure::read('MISP.host_org_id'));
$this->set('pull_scope', 'server');
}
}
public function delete($id = null)
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException(__('This endpoint expects POST requests.'));
}
$this->Server->id = $id;
if (!$this->Server->exists()) {
throw new NotFoundException(__('Invalid server'));
}
$s = $this->Server->read(null, $id);
if ($this->Server->delete()) {
$message = __('Server deleted');
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Servers', 'delete', $message, $this->response->type());
} else {
$this->Flash->success($message);
$this->redirect(array('controller' => 'servers', 'action' => 'index'));
}
}
$message = __('Server was not deleted');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Servers', 'delete', $id, $message, $this->response->type());
} else {
$this->Flash->error($message);
$this->redirect(array('action' => 'index'));
}
}
public function eventBlockRule()
{
$this->AdminSetting = ClassRegistry::init('AdminSetting');
$setting = $this->AdminSetting->find('first', [
'conditions' => ['setting' => 'eventBlockRule'],
'recursive' => -1
]);
if (empty($setting)) {
$setting = ['setting' => 'eventBlockRule'];
if ($this->request->is('post')) {
$this->AdminSetting->create();
}
}
if ($this->request->is('post')) {
if (!empty($this->request->data['Server'])) {
$this->request->data = $this->request->data['Server'];
}
$setting['AdminSetting']['setting'] = 'eventBlockRule';
$setting['AdminSetting']['value'] = $this->request->data['value'];
$result = $this->AdminSetting->save($setting);
if ($result) {
$message = __('Settings saved');
} else {
$message = __('Could not save the settings. Invalid input.');
}
if ($this->_isRest()) {
if ($result) {
return $this->RestResponse->saveFailResponse('Servers', 'eventBlockRule', false, $message, $this->response->type());
} else {
return $this->RestResponse->saveSuccessResponse('Servers', 'eventBlockRule', $message, $this->response->type());
}
} else {
if ($result) {
$this->Flash->success($message);
$this->redirect('/');
} else {
$this->Flash->error($message);
}
}
}
$this->set('setting', $setting);
}
/**
* Pull one or more events with attributes from a remote instance.
* Set $technique to
* full - download everything
* incremental - only new events
* <int> - specific id of the event to pull
*/
public function pull($id = null, $technique = 'full')
{
if (empty($id)) {
if (!empty($this->request->data['id'])) {
$id = $this->request->data['id'];
} else {
throw new NotFoundException(__('Invalid server'));
}
}
$s = $this->Server->find('first', [
'conditions' => ['id' => $id],
'recursive' => -1,
]);
if (empty($s)) {
throw new NotFoundException(__('Invalid server'));
}
$error = false;
if (false == $s['Server']['pull'] && ($technique === 'full' || $technique === 'incremental')) {
$error = __('Pull setting not enabled for this server.');
}
if (false == $s['Server']['pull_galaxy_clusters'] && ($technique === 'pull_relevant_clusters')) {
$error = __('Pull setting not enabled for this server.');
}
if (empty($error)) {
if (!Configure::read('MISP.background_jobs')) {
$result = $this->Server->pull($this->Auth->user(), $technique, $s);
if (is_array($result)) {
$success = __('Pull completed. %s events pulled, %s events could not be pulled, %s proposals pulled, %s sightings pulled, %s clusters pulled.', count($result[0]), count($result[1]), $result[2], $result[3], $result[4]);
} else {
$error = $result;
}
$this->set('successes', $result[0]);
$this->set('fails', $result[1]);
$this->set('pulledProposals', $result[2]);
$this->set('pulledSightings', $result[3]);
} else {
$this->loadModel('Job');
$jobId = $this->Job->createJob(
$this->Auth->user(),
Job::WORKER_DEFAULT,
'pull',
'Server: ' . $id,
__('Pulling.')
);
$this->Server->getBackgroundJobsTool()->enqueue(
BackgroundJobsTool::DEFAULT_QUEUE,
BackgroundJobsTool::CMD_SERVER,
[
'pull',
$this->Auth->user('id'),
$id,
$technique,
$jobId
],
false,
$jobId
);
$success = __('Pull queued for background execution. Job ID: %s', $jobId);
}
}
if ($this->_isRest()) {
if (!empty($error)) {
return $this->RestResponse->saveFailResponse('Servers', 'pull', $id, $error, $this->response->type());
} else {
return $this->RestResponse->saveSuccessResponse('Servers', 'pull', $id, $this->response->type(), $success);
}
} else {
if (!empty($error)) {
$this->Flash->error($error);
$this->redirect(array('action' => 'index'));
} else {
$this->Flash->success($success);
$this->redirect($this->referer());
}
}
}
public function push($id = null, $technique=false)
{
if (!empty($id)) {
$this->Server->id = $id;
} else if (!empty($this->request->data['id'])) {
$this->Server->id = $this->request->data['id'];
} else {
throw new NotFoundException(__('Invalid server'));
}
if (!empty($this->request->data['technique'])) {
$technique = $this->request->data['technique'];
}
if (!$this->Server->exists()) {
throw new NotFoundException(__('Invalid server'));
}
$s = $this->Server->read(null, $id);
if (!Configure::read('MISP.background_jobs')) {
App::uses('SyncTool', 'Tools');
$syncTool = new SyncTool();
$HttpSocket = $syncTool->setupHttpSocket($s);
$result = $this->Server->push($id, $technique, false, $HttpSocket, $this->Auth->user());
if ($result === false) {
$error = __('The remote server is too outdated to initiate a push towards it. Please notify the hosting organisation of the remote instance.');
} elseif (!is_array($result)) {
$error = $result;
}
if (!empty($error)) {
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Servers', 'push', false, $error, $this->response->type());
} else {
$this->Flash->info($error);
$this->redirect(array('action' => 'index'));
}
}
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Servers', 'push', $id, $this->response->type(), __('Push complete. %s events pushed, %s events could not be pushed.', count($result[0]), count($result[1])));
} else {
$this->set('successes', $result[0]);
$this->set('fails', $result[1]);
}
} else {
$this->loadModel('Job');
$jobId = $this->Job->createJob(
$this->Auth->user(),
Job::WORKER_DEFAULT,
'push',
'Server: ' . $id,
__('Pushing.')
);
$this->Server->getBackgroundJobsTool()->enqueue(
BackgroundJobsTool::DEFAULT_QUEUE,
BackgroundJobsTool::CMD_SERVER,
[
'push',
$this->Auth->user('id'),
$id,
$technique,
$jobId
],
false,
$jobId
);
$message = sprintf(__('Push queued for background execution. Job ID: %s'), $jobId);
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Servers', 'push', $message, $this->response->type());
}
$this->Flash->success($message);
$this->redirect(array('action' => 'index'));
}
}
private function __saveCert($server, $id, $client = false, $delete = false)
{
if ($client) {
$subm = 'submitted_client_cert';
$attr = 'client_cert_file';
$ins = '_client';
} else {
$subm = 'submitted_cert';
$attr = 'cert_file';
$ins = '';
}
if (!$delete) {
$ext = '';
App::uses('File', 'Utility');
App::uses('Folder', 'Utility');
App::uses('FileAccessTool', 'Tools');
if (isset($server['Server'][$subm]['name'])) {
if ($this->request->data['Server'][$subm]['size'] != 0) {
if (!$this->Server->checkFilename($server['Server'][$subm]['name'])) {
throw new Exception(__('Filename not allowed'));
}
$file = new File($server['Server'][$subm]['name']);
$ext = $file->ext();
if (!$server['Server'][$subm]['size'] > 0) {
$this->Flash->error(__('Incorrect extension or empty file.'));
$this->redirect(array('action' => 'index'));
}
// read pem file data
$pemData = FileAccessTool::readFromFile($server['Server'][$subm]['tmp_name'], $server['Server'][$subm]['size']);
} else {
return true;
}
} else {
$pemData = base64_decode($server['Server'][$subm]);
}
$destpath = APP . "files" . DS . "certs" . DS;
$dir = new Folder(APP . "files" . DS . "certs", true);
$pemfile = new File($destpath . $id . $ins . '.' . $ext);
$result = $pemfile->write($pemData);
$s = $this->Server->read(null, $id);
$s['Server'][$attr] = $s['Server']['id'] . $ins . '.' . $ext;
if ($result) {
$this->Server->save($s);
}
} else {
$s = $this->Server->read(null, $id);
$s['Server'][$attr] = '';
$this->Server->save($s);
}
return true;
}
public function serverSettingsReloadSetting($setting, $id)
{
$pathToSetting = explode('.', $setting);
if (
strpos($setting, 'Plugin.Enrichment') !== false ||
strpos($setting, 'Plugin.Import') !== false ||
strpos($setting, 'Plugin.Export') !== false ||
strpos($setting, 'Plugin.Cortex') !== false ||
strpos($setting, 'Plugin.Action') !== false ||
strpos($setting, 'Plugin.Workflow') !== false
) {
$settingObject = $this->Server->getCurrentServerSettings();
} else {
$settingObject = $this->Server->serverSettings;
}
foreach ($pathToSetting as $key) {
if (!isset($settingObject[$key])) {
throw new MethodNotAllowedException();
}
$settingObject = $settingObject[$key];
}
$result = $this->Server->serverSettingReadSingle($settingObject, $setting, $key);
$this->set('setting', $result);
$priorityErrorColours = array(0 => 'red', 1 => 'yellow', 2 => 'green');
$this->set('priorityErrorColours', $priorityErrorColours);
$priorities = array(0 => 'Critical', 1 => 'Recommended', 2 => 'Optional', 3 => 'Deprecated');
$this->set('priorities', $priorities);
$this->set('k', $id);
$this->layout = false;
$subGroup = 'general';
if ($pathToSetting[0] === 'Plugin') {
$subGroup = explode('_', $pathToSetting[1])[0];
}
$this->set('subGroup', $subGroup);
$this->render('/Elements/healthElements/settings_row');
}
public function serverSettings($tab=false)
{
if (!$this->request->is('get')) {
throw new MethodNotAllowedException('Just GET method is allowed.');
}
$tabs = array(
'MISP' => array('count' => 0, 'errors' => 0, 'severity' => 5),
'Encryption' => array('count' => 0, 'errors' => 0, 'severity' => 5),
'Proxy' => array('count' => 0, 'errors' => 0, 'severity' => 5),
'Security' => array('count' => 0, 'errors' => 0, 'severity' => 5),
'Plugin' => array('count' => 0, 'errors' => 0, 'severity' => 5),
'SimpleBackgroundJobs' => array('count' => 0, 'errors' => 0, 'severity' => 5)
);
$writeableErrors = array(0 => __('OK'), 1 => __('not found'), 2 => __('is not writeable'));
$readableErrors = array(0 => __('OK'), 1 => __('not readable'));
$gpgErrors = array(0 => __('OK'), 1 => __('FAIL: settings not set'), 2 => __('FAIL: Failed to load GnuPG'), 3 => __('FAIL: Issues with the key/passphrase'), 4 => __('FAIL: sign failed'));
$proxyErrors = array(0 => __('OK'), 1 => __('not configured (so not tested)'), 2 => __('Getting URL via proxy failed'));
$zmqErrors = array(0 => __('OK'), 1 => __('not enabled (so not tested)'), 2 => __('Python ZeroMQ library not installed correctly.'), 3 => __('ZeroMQ script not running.'));
$sessionErrors = array(
0 => __('OK'),
1 => __('Too many expired sessions in the database, please clear the expired sessions'),
2 => __('PHP session handler is using the default file storage. This is not recommended, please use the redis or database storage'),
8 => __('Alternative setting used'),
9 => __('Test failed')
);
$moduleErrors = array(0 => __('OK'), 1 => __('System not enabled'), 2 => __('No modules found'));
$backgroundJobsErrors = array(
0 => __('OK'),
1 => __('Not configured (so not tested)'),
2 => __('Error connecting to Redis.'),
3 => __('Error connecting to Supervisor.'),
4 => __('Error connecting to Redis and Supervisor.')
);
$finalSettings = $this->Server->serverSettingsRead();
$issues = array(
'errors' => array(
0 => array(
'value' => 0,
'description' => __('MISP will not operate correctly or will be unsecure until these issues are resolved.')
),
1 => array(
'value' => 0,
'description' => __('Some of the features of MISP cannot be utilised until these issues are resolved.')
),
2 => array(
'value' => 0,
'description' => __('There are some optional tweaks that could be done to improve the looks of your MISP instance.')
),
),
'deprecated' => array(),
'overallHealth' => 3,
);
$dumpResults = array();
$tempArray = array();
foreach ($finalSettings as $k => $result) {
if ($result['level'] == 3) {
$issues['deprecated']++;
}
$tabs[$result['tab']]['count']++;
if (isset($result['error']) && $result['level'] < 3) {
$issues['errors'][$result['level']]['value']++;
if ($result['level'] < $issues['overallHealth']) {
$issues['overallHealth'] = $result['level'];
}
$tabs[$result['tab']]['errors']++;
if ($result['level'] < $tabs[$result['tab']]['severity']) {
$tabs[$result['tab']]['severity'] = $result['level'];
}
}
if (isset($result['optionsSource']) && is_callable($result['optionsSource'])) {
$result['options'] = $result['optionsSource']();
}
$dumpResults[] = $result;
if ($result['tab'] == $tab) {
if (isset($result['subGroup'])) {
$tempArray[$result['subGroup']][] = $result;
} else {
$tempArray['general'][] = $result;
}
}
}
$finalSettings = $tempArray;
// Diagnostics portion
$diagnostic_errors = 0;
App::uses('File', 'Utility');
App::uses('Folder', 'Utility');
if ($tab === 'correlations') {
$this->loadModel('Correlation');
$correlation_metrics = $this->Correlation->collectMetrics();
$this->set('correlation_metrics', $correlation_metrics);
}
if ($tab === 'files') {
if (!empty(Configure::read('Security.disable_instance_file_uploads'))) {
throw new MethodNotAllowedException(__('This functionality is disabled.'));
}
$files = $this->Server->grabFiles();
$this->set('files', $files);
}
// Only run this check on the diagnostics tab
if ($tab === 'diagnostics' || $tab === 'download' || $this->_isRest()) {
$php_ini = php_ini_loaded_file();
$this->set('php_ini', $php_ini);
$attachmentTool = new AttachmentTool();
try {
$advanced_attachments = $attachmentTool->checkAdvancedExtractionStatus();
} catch (Exception $e) {
$this->log($e->getMessage(), LOG_NOTICE);
$advanced_attachments = false;
}
$this->set('advanced_attachments', $advanced_attachments);
$gitStatus = $this->Server->getCurrentGitStatus(true);
$this->set('branch', $gitStatus['branch']);
$this->set('commit', $gitStatus['commit']);
$this->set('latestCommit', $gitStatus['latestCommit']);
$this->set('version', $gitStatus['version']);
$phpSettings = array(
'max_execution_time' => array(
'explanation' => 'The maximum duration that a script can run (does not affect the background workers). A too low number will break long running scripts like comprehensive API exports',
'recommended' => 300,
'unit' => 'seconds',
),
'memory_limit' => array(
'explanation' => 'The maximum memory that PHP can consume. It is recommended to raise this number since certain exports can generate a fair bit of memory usage',
'recommended' => 2048,
'unit' => 'MB'
),
'upload_max_filesize' => array(
'explanation' => 'The maximum size that an uploaded file can be. It is recommended to raise this number to allow for the upload of larger samples',
'recommended' => 50,
'unit' => 'MB'
),
'post_max_size' => array(
'explanation' => 'The maximum size of a POSTed message, this has to be at least the same size as the upload_max_filesize setting',
'recommended' => 50,
'unit' => 'MB'
)
);
foreach ($phpSettings as $setting => $settingArray) {
$phpSettings[$setting]['value'] = $this->Server->getIniSetting($setting);
if ($phpSettings[$setting]['value'] && $settingArray['unit'] && $settingArray['unit'] === 'MB') {
// convert basic unit to M
$phpSettings[$setting]['value'] = (int) floor($phpSettings[$setting]['value'] / 1024 / 1024);
}
}
$this->set('phpSettings', $phpSettings);
if ($gitStatus['version'] && $gitStatus['version']['upToDate'] === 'older') {
$diagnostic_errors++;
}
// check if the STIX and Cybox libraries are working and the correct version using the test script stixtest.py
$stix = $this->Server->stixDiagnostics($diagnostic_errors);
$yaraStatus = $this->Server->yaraDiagnostics($diagnostic_errors);
// if GnuPG is set up in the settings, try to encrypt a test message
$gpgStatus = $this->Server->gpgDiagnostics($diagnostic_errors);
// if the message queue pub/sub is enabled, check whether the extension works
$zmqStatus = $this->Server->zmqDiagnostics($diagnostic_errors);
// if Proxy is set up in the settings, try to connect to a test URL
$proxyStatus = $this->Server->proxyDiagnostics($diagnostic_errors);
// if SimpleBackgroundJobs is set up in the settings, try to connect to Redis
$backgroundJobsStatus = $this->Server->backgroundJobsDiagnostics($diagnostic_errors);
// get the DB diagnostics
$dbDiagnostics = $this->Server->dbSpaceUsage();
$dbSchemaDiagnostics = $this->Server->dbSchemaDiagnostic();
$dbConfiguration = $this->Server->dbConfiguration();
$redisInfo = $this->Server->redisInfo();
$moduleTypes = array('Enrichment', 'Import', 'Export', 'Cortex');
foreach ($moduleTypes as $type) {
$moduleStatus[$type] = $this->Server->moduleDiagnostics($diagnostic_errors, $type);
}
// get php session diagnostics
$sessionStatus = $this->Server->sessionDiagnostics($diagnostic_errors);
$this->loadModel('AttachmentScan');
try {
$attachmentScan = ['status' => true, 'software' => $this->AttachmentScan->diagnostic()];
} catch (Exception $e) {
$attachmentScan = ['status' => false, 'error' => $e->getMessage()];
}
$securityAudit = (new SecurityAudit())->run($this->Server);
$view = compact('gpgStatus', 'sessionErrors', 'proxyStatus', 'sessionStatus', 'zmqStatus', 'moduleStatus', 'yaraStatus', 'gpgErrors', 'proxyErrors', 'zmqErrors', 'stix', 'moduleErrors', 'moduleTypes', 'dbDiagnostics', 'dbSchemaDiagnostics', 'dbConfiguration', 'redisInfo', 'attachmentScan', 'securityAudit');
} else {
$view = [];
}
// check whether the files are writeable
$writeableDirs = $this->Server->writeableDirsDiagnostics($diagnostic_errors);
$writeableFiles = $this->Server->writeableFilesDiagnostics($diagnostic_errors);
$readableFiles = $this->Server->readableFilesDiagnostics($diagnostic_errors);
$extensions = $this->Server->extensionDiagnostics();
// check if the encoding is not set to utf8
$dbEncodingStatus = $this->Server->databaseEncodingDiagnostics($diagnostic_errors);
$view = array_merge($view, compact('diagnostic_errors', 'tabs', 'tab', 'issues', 'finalSettings', 'writeableErrors', 'readableErrors', 'writeableDirs', 'writeableFiles', 'readableFiles', 'extensions', 'dbEncodingStatus'));
$this->set($view);
$workerIssueCount = 4;
$worker_array = array();
if (Configure::read('MISP.background_jobs')) {
$workerIssueCount = 0;
$worker_array = $this->Server->workerDiagnostics($workerIssueCount);
}
$this->set('worker_array', $worker_array);
if ($tab === 'download' || $this->_isRest()) {
foreach ($dumpResults as $key => $dr) {
unset($dumpResults[$key]['description']);
}
$dump = array(
'version' => $gitStatus['version'],
'phpSettings' => $phpSettings,
'gpgStatus' => $gpgErrors[$gpgStatus['status']],
'proxyStatus' => $proxyErrors[$proxyStatus],
'zmqStatus' => $zmqStatus,
'stix' => $stix,
'moduleStatus' => $moduleStatus,
'writeableDirs' => $writeableDirs,
'writeableFiles' => $writeableFiles,
'readableFiles' => $readableFiles,
'dbDiagnostics' => $dbDiagnostics,
'dbSchemaDiagnostics' => $dbSchemaDiagnostics,
'dbConfiguration' => $dbConfiguration,
'redisInfo' => $redisInfo,
'finalSettings' => $dumpResults,
'extensions' => $extensions,
'workers' => $worker_array,
'backgroundJobsStatus' => $backgroundJobsErrors[$backgroundJobsStatus]
);
foreach ($dump['finalSettings'] as $k => $v) {
if (!empty($v['redacted'])) {
$dump['finalSettings'][$k]['value'] = '*****';
}
}
$this->response->body(json_encode($dump, JSON_PRETTY_PRINT));
$this->response->type('json');
$this->response->download('MISP.report.json');
return $this->response;
}
$priorities = array(0 => 'Critical', 1 => 'Recommended', 2 => 'Optional', 3 => 'Deprecated');
$this->set('priorities', $priorities);
$this->set('workerIssueCount', $workerIssueCount);
$priorityErrorColours = array(0 => 'red', 1 => 'yellow', 2 => 'green');
$this->set('priorityErrorColours', $priorityErrorColours);
$this->set('phpversion', phpversion());
$this->set('phpmin', $this->phpmin);
$this->set('phprec', $this->phprec);
$this->set('phptoonew', $this->phptoonew);
$this->set('pythonmin', $this->pythonmin);
$this->set('pythonrec', $this->pythonrec);
$this->set('title_for_layout', __('Diagnostics'));
}
public function startWorker($type)
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException();
}
if (Configure::read('SimpleBackgroundJobs.enabled')) {
$message = __('Worker start signal sent');
$this->Server->getBackgroundJobsTool()->startWorkerByQueue($type);
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Servers', 'startWorker', $type, $this->response->type(), $message);
} else {
$this->Flash->info($message);
$this->redirect('/servers/serverSettings/workers');
}
}
// CakeResque
$validTypes = array('default', 'email', 'scheduler', 'cache', 'prio', 'update');
if (!in_array($type, $validTypes)) {
throw new MethodNotAllowedException('Invalid worker type.');
}
$prepend = '';
if ($type != 'scheduler') {
$workerIssueCount = 0;
$workerDiagnostic = $this->Server->workerDiagnostics($workerIssueCount);
if ($type == 'update' && isset($workerDiagnostic['update']['ok']) && $workerDiagnostic['update']['ok']) {
$message = __('Only one `update` worker can run at a time');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Servers', 'startWorker', false, $message, $this->response->type());
} else {
$this->Flash->error($message);
$this->redirect('/servers/serverSettings/workers');
}
}
shell_exec($prepend . APP . 'Console' . DS . 'cake CakeResque.CakeResque start --interval 5 --queue ' . $type .' > /dev/null 2>&1 &');
} else {
shell_exec($prepend . APP . 'Console' . DS . 'cake CakeResque.CakeResque startscheduler -i 5 > /dev/null 2>&1 &');
}
$message = __('Worker start signal sent');
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Servers', 'startWorker', $type, $this->response->type(), $message);
} else {
$this->Flash->info($message);
$this->redirect('/servers/serverSettings/workers');
}
}
public function stopWorker($pid)
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException();
}
$message = __('Worker stop signal sent');
if (Configure::read('SimpleBackgroundJobs.enabled')) {
$this->Server->getBackgroundJobsTool()->stopWorker($pid);
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Servers', 'stopWorker', $pid, $this->response->type(), $message);
} else {
$this->Flash->info($message);
$this->redirect('/servers/serverSettings/workers');
}
}
// CakeResque
$this->Server->killWorker($pid, $this->Auth->user());
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Servers', 'stopWorker', $pid, $this->response->type(), $message);
} else {
$this->Flash->info($message);
$this->redirect('/servers/serverSettings/workers');
}
}
public function getWorkers()
{
if (Configure::read('MISP.background_jobs')) {
$workerIssueCount = 0;
$worker_array = $this->Server->workerDiagnostics($workerIssueCount);
} else {
$worker_array = [__('Background jobs not enabled')];
}
return $this->RestResponse->viewData($worker_array);
}
public function idTranslator($localId = null)
{
// We retrieve the list of remote servers that we can query
$servers = $this->Server->find('all', [
'conditions' => ['OR' => ['pull' => true, 'push' => true]],
'recursive' => -1,
'order' => ['Server.priority ASC'],
]);
// We generate the list of servers for the dropdown
$displayServers = array();
foreach ($servers as $s) {
$displayServers[] = [
'name' => $s['Server']['name'],
'value' => $s['Server']['id'],
];
}
$this->set('servers', $displayServers);
if ($localId || $this->request->is('post')) {
if ($localId && $this->request->is('get')) {
$this->request->data['Event']['local'] = 'local';
$this->request->data['Event']['uuid'] = $localId;
}
$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->downloadEventMetadataFromServer($this->request->data['Event']['uuid'], $remote_server);
} catch (Exception $e) {
$this->Flash->error(__("Issue while contacting the remote server to retrieve event information"));
return;
}
if (empty($remote_event)) {
$this->Flash->error(__("This event could not be found or you don't have permissions to see it."));
return;
}
$local_event = $this->Event->fetchSimpleEvent($this->Auth->user(), $remote_event['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['id'],
"remote_id" => $remote_event['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 $server) {
// We check if the server was not already contacted in phase 1
if (count($remote_events) > 0 && $remote_events[0]['server_id'] == $server['Server']['id']) {
continue;
}
$exception = null;
try {
$remoteEvent = $this->Event->downloadEventMetadataFromServer($local_event['Event']['uuid'], $server);
} catch (Exception $e) {
$remoteEvent = null;
$exception = $e->getMessage();
}
$remoteEventId = isset($remoteEvent['id']) ? $remoteEvent['id'] : null;
$remote_events[] = array(
"server_id" => $server['Server']['id'],
"server_name" => $server['Server']['name'],
"url" => isset($remoteEventId) ? $server['Server']['url'] . "/events/view/" . $remoteEventId : $server['Server']['url'],
"remote_id" => isset($remoteEventId) ? $remoteEventId : false,
"exception" => $exception,
);
}
$this->set('local_event', $local_event);
$this->set('remote_events', $remote_events);
}
$this->set('title_for_layout', __('Event ID translator'));
}
public function getSubmodulesStatus()
{
$this->set('submodules', $this->Server->getSubmodulesGitStatus());
$this->render('ajax/submoduleStatus');
}
public function getSetting($settingName)
{
$setting = $this->Server->getSettingData($settingName);
if (!$setting) {
throw new NotFoundException(__('Setting %s is invalid.', $settingName));
}
if (!empty($setting["redacted"])) {
throw new ForbiddenException(__('This setting is redacted.'));
}
if (Configure::check($settingName)) {
$setting['value'] = Configure::read($settingName);
}
return $this->RestResponse->viewData($setting);
}
public function serverSettingsEdit($settingName, $id = false, $forceSave = false)
{
if (!$this->_isRest()) {
if (!isset($id)) {
throw new MethodNotAllowedException();
}
$this->set('id', $id);
}
$setting = $this->Server->getSettingData($settingName);
if ($setting === false) {
throw new NotFoundException(__('Setting %s is invalid.', $settingName));
}
if (!empty($setting['cli_only'])) {
throw new MethodNotAllowedException(__('This setting can only be edited via the CLI.'));
}
if ($this->request->is('get')) {
$value = Configure::read($setting['name']);
if (isset($value)) {
$setting['value'] = $value;
}
$setting['setting'] = $setting['name'];
if (isset($setting['optionsSource']) && is_callable($setting['optionsSource'])) {
$setting['options'] = $setting['optionsSource']();
}
$subGroup = explode('.', $setting['name']);
if ($subGroup[0] === 'Plugin') {
$subGroup = explode('_', $subGroup[1])[0];
} else {
$subGroup = 'general';
}
if ($this->_isRest()) {
if (!empty($setting['redacted'])) {
throw new ForbiddenException(__('This setting is redacted.'));
}
return $this->RestResponse->viewData([$setting['name'] => $setting['value']]);
} else {
$this->set('subGroup', $subGroup);
$this->set('setting', $setting);
$this->render('ajax/server_settings_edit');
}
} else if ($this->request->is('post')) {
if (!isset($this->request->data['Server'])) {
$this->request->data = array('Server' => $this->request->data);
}
if (!isset($this->request->data['Server']['value']) || !is_scalar($this->request->data['Server']['value'])) {
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Servers', 'serverSettingsEdit', false, 'Invalid input. Expected: {"value": "new_setting"}', $this->response->type());
}
}
if (!empty($this->request->data['Server']['force'])) {
$forceSave = $this->request->data['Server']['force'];
}
if (trim($this->request->data['Server']['value']) === '*****') {
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Servers', 'serverSettingsEdit', false, 'No change.', $this->response->type());
} else {
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'No change.')), 'status'=>200, 'type' => 'json'));
}
}
$this->autoRender = false;
if (!Configure::read('MISP.system_setting_db') && !is_writeable(APP . 'Config/config.php')) {
$this->loadModel('Log');
$this->Log->create();
$this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Server',
'model_id' => 0,
'email' => $this->Auth->user('email'),
'action' => 'serverSettingsEdit',
'user_id' => $this->Auth->user('id'),
'title' => 'Server setting issue',
'change' => 'There was an issue witch changing ' . $setting['name'] . ' to ' . $this->request->data['Server']['value'] . '. The error message returned is: app/Config.config.php is not writeable to the apache user. No changes were made.',
));
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Servers', 'serverSettingsEdit', false, 'app/Config.config.php is not writeable to the apache user.', $this->response->type());
} else {
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'app/Config.config.php is not writeable to the apache user.')), 'status'=>200, 'type' => 'json'));
}
}
$result = $this->Server->serverSettingsEditValue($this->Auth->user(), $setting, $this->request->data['Server']['value'], $forceSave);
if ($result === true) {
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Servers', 'serverSettingsEdit', false, $this->response->type(), 'Field updated');
} else {
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Field updated.')), 'status'=>200, 'type' => 'json'));
}
} else {
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Servers', 'serverSettingsEdit', false, $result, $this->response->type());
} else {
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $result)), 'status'=>200, 'type' => 'json'));
}
}
}
}
public function killAllWorkers($force = false)
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException();
}
$this->Server->killAllWorkers($this->Auth->user(), $force);
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Server', 'killAllWorkers', false, $this->response->type(), __('Killing workers.'));
}
$this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'workers'));
}
public function restartWorkers()
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException();
}
if (Configure::read('SimpleBackgroundJobs.enabled')) {
$this->Server->getBackgroundJobsTool()->restartWorkers();
} else {
// CakeResque
$this->Server->restartWorkers($this->Auth->user());
}
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Server', 'restartWorkers', false, $this->response->type(), __('Restarting workers.'));
}
$this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'workers'));
}
public function restartDeadWorkers()
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException();
}
if (Configure::read('SimpleBackgroundJobs.enabled')) {
$this->Server->getBackgroundJobsTool()->restartDeadWorkers();
} else {
// CakeResque
$this->Server->restartDeadWorkers($this->Auth->user());
}
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Server', 'restartDeadWorkers', false, $this->response->type(), __('Restarting workers.'));
}
$this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'workers'));
}
public function deleteFile($type, $filename)
{
if ($this->request->is('post')) {
$validItems = $this->Server->getFileRules();
App::uses('File', 'Utility');
$existingFile = new File($validItems[$type]['path'] . DS . $filename);
if (!$existingFile->exists()) {
$this->Flash->error(__('File not found.', true), 'default', array(), 'error');
$this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'files'));
}
if ($existingFile->delete()) {
$this->Flash->success('File deleted.');
} else {
$this->Flash->error(__('File could not be deleted.', true), 'default', array(), 'error');
}
$this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'files'));
} else {
throw new MethodNotAllowedException('This action expects a POST request.');
}
}
public function uploadFile($type)
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException();
}
if (!empty(Configure::read('Security.disable_instance_file_uploads'))) {
throw new MethodNotAllowedException(__('Feature disabled.'));
}
$validItems = $this->Server->getFileRules();
// Check if there were problems with the file upload
// only keep the last part of the filename, this should prevent directory attacks
$filename = basename($this->request->data['Server']['file']['name']);
if (!preg_match("/" . $validItems[$type]['regex'] . "/", $filename)) {
$this->Flash->error($validItems[$type]['regex_error'], 'default', array(), 'error');
$this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'files'));
}
if (empty($this->request->data['Server']['file']['tmp_name']) || !is_uploaded_file($this->request->data['Server']['file']['tmp_name'])) {
$this->Flash->error(__('Upload failed.', true), 'default', array(), 'error');
$this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'files'));
}
// check if the file already exists
App::uses('File', 'Utility');
$existingFile = new File($validItems[$type]['path'] . DS . $filename);
if ($existingFile->exists()) {
$this->Flash->info(__('File already exists. If you would like to replace it, remove the old one first.', true), 'default', array(), 'error');
$this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'files'));
}
$result = move_uploaded_file($this->request->data['Server']['file']['tmp_name'], $validItems[$type]['path'] . DS . $filename);
if ($result) {
$this->Flash->success('File uploaded.');
} else {
$this->Flash->error(__('Upload failed.', true), 'default', array(), 'error');
}
$this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'files'));
}
public function fetchServersForSG($idList = '{}')
{
$id_exclusion_list = json_decode($idList, true);
$temp = $this->Server->find('all', array(
'conditions' => array(
'id !=' => $id_exclusion_list,
),
'recursive' => -1,
'fields' => array('id', 'name', 'url')
));
$servers = array();
foreach ($temp as $server) {
$servers[] = array('id' => $server['Server']['id'], 'name' => $server['Server']['name'], 'url' => $server['Server']['url']);
}
$this->layout = false;
$this->autoRender = false;
$this->set('servers', $servers);
$this->render('ajax/fetch_servers_for_sg');
}
public function postTest()
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException('Invalid request, expecting a POST request.');
}
// Fix for PHP-FPM / Nginx / etc
// Fix via https://www.popmartian.com/tipsntricks/2015/07/14/howto-use-php-getallheaders-under-fastcgi-php-fpm-nginx-etc/
if (!function_exists('getallheaders')) {
$headers = [];
foreach ($_SERVER as $name => $value) {
$name = strtolower($name);
if (substr($name, 0, 5) === 'http_') {
$headers[str_replace('_', '-', substr($name, 5))] = $value;
}
}
} else {
$headers = getallheaders();
$headers = array_change_key_case($headers, CASE_LOWER);
}
$result = [
'body' => $this->request->data,
'headers' => [
'Content-type' => isset($headers['content-type']) ? $headers['content-type'] : 0,
'Accept' => isset($headers['accept']) ? $headers['accept'] : 0,
'Authorization' => isset($headers['authorization']) ? 'OK' : 0,
],
];
return $this->RestResponse->viewData($result, 'json');
}
public function getRemoteUser($id)
{
$user = $this->Server->getRemoteUser($id);
if ($user === null) {
throw new NotFoundException(__('Invalid server'));
}
return $this->RestResponse->viewData($user);
}
public function testConnection($id = false)
{
$server = $this->Server->find('first', ['conditions' => ['Server.id' => $id]]);
if (!$server) {
throw new NotFoundException(__('Invalid server'));
}
@session_write_close(); // close session to allow concurrent requests
$result = $this->Server->runConnectionTest($server);
if ($result['status'] == 1) {
if (isset($result['info']['version']) && preg_match('/^[0-9]+\.+[0-9]+\.[0-9]+$/', $result['info']['version'])) {
$perm_sync = isset($result['info']['perm_sync']) ? $result['info']['perm_sync'] : false;
$perm_sighting = isset($result['info']['perm_sighting']) ? $result['info']['perm_sighting'] : false;
$local_version = $this->Server->checkMISPVersion();
$version = explode('.', $result['info']['version']);
$mismatch = false;
$newer = false;
$parts = array('major', 'minor', 'hotfix');
foreach ($parts as $k => $v) {
if (!$mismatch) {
if ($version[$k] > $local_version[$v]) {
$mismatch = $v;
$newer = 'remote';
} elseif ($version[$k] < $local_version[$v]) {
$mismatch = $v;
$newer = 'local';
}
}
}
if (!$mismatch && $version[2] < 111) {
$mismatch = 'proposal';
}
if (!$perm_sync && !$perm_sighting) {
$result['status'] = 7;
return new CakeResponse(array('body'=> json_encode($result), 'type' => 'json'));
}
if (!$perm_sync && $perm_sighting) {
$result['status'] = 8;
return new CakeResponse(array('body'=> json_encode($result), 'type' => 'json'));
}
return $this->RestResponse->viewData([
'status' => 1,
'local_version' => implode('.', $local_version),
'version' => implode('.', $version),
'mismatch' => $mismatch,
'newer' => $newer,
'post' => isset($result['post']) ? $result['post']['status'] : 'too old',
'response_encoding' => isset($result['post']['content-encoding']) ? $result['post']['content-encoding'] : null,
'request_encoding' => isset($result['info']['request_encoding']) ? $result['info']['request_encoding'] : null,
'client_certificate' => $result['client_certificate'],
], 'json');
} else {
$result['status'] = 3;
}
}
return new CakeResponse(array('body'=> json_encode($result), 'type' => 'json'));
}
public function startZeroMQServer()
{
$pubSubTool = $this->Server->getPubSubTool();
$result = $pubSubTool->restartServer();
if ($result === true) {
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'ZeroMQ server successfully started.')), 'status'=>200, 'type' => 'json'));
} else {
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $result)), 'status'=>200, 'type' => 'json'));
}
}
public function stopZeroMQServer()
{
$pubSubTool = $this->Server->getPubSubTool();
$result = $pubSubTool->killService();
if ($result === true) {
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'ZeroMQ server successfully killed.')), 'status'=>200, 'type' => 'json'));
} else {
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Could not kill the previous instance of the ZeroMQ script.')), 'status'=>200, 'type' => 'json'));
}
}
public function statusZeroMQServer()
{
$pubSubTool = $this->Server->getPubSubTool();
$result = $pubSubTool->statusCheck();
if (!empty($result)) {
$this->set('events', $result['publishCount']);
$this->set('messages', $result['messageCount']);
$this->set('time', $result['timestamp']);
$this->set('time2', $result['timestampSettings']);
}
$this->render('ajax/zeromqstatus');
}
public function purgeSessions()
{
if ($this->Server->updateDatabase('cleanSessionTable') == false) {
$this->Flash->error('Could not purge the session table.');
}
$this->redirect('/servers/serverSettings/diagnostics');
}
public function clearWorkerQueue($worker)
{
if (!$this->request->is('Post') || $this->request->is('ajax')) {
throw new MethodNotAllowedException();
}
if (Configure::read('SimpleBackgroundJobs.enabled')) {
$this->Server->getBackgroundJobsTool()->purgeQueue($worker);
} else {
// CakeResque
$worker_array = array('cache', 'default', 'email', 'prio');
if (!in_array($worker, $worker_array)) {
throw new MethodNotAllowedException('Invalid worker');
}
$redis = Resque::redis();
$redis->del('queue:' . $worker);
}
$this->Flash->success('Queue cleared.');
$this->redirect($this->referer());
}
public function getVersion()
{
$user = $this->_closeSession();
$versionArray = $this->Server->checkMISPVersion();
$response = [
'version' => $versionArray['major'] . '.' . $versionArray['minor'] . '.' . $versionArray['hotfix'],
'pymisp_recommended_version' => $this->pyMispVersion,
'perm_sync' => (bool) $user['Role']['perm_sync'],
'perm_sighting' => (bool) $user['Role']['perm_sighting'],
'perm_galaxy_editor' => (bool) $user['Role']['perm_galaxy_editor'],
'request_encoding' => $this->CompressedRequestHandler->supportedEncodings(),
'filter_sightings' => true, // check if Sightings::filterSightingUuidsForPush method is supported
];
return $this->RestResponse->viewData($response, 'json');
}
/**
* @deprecated Use field `pymisp_recommended_version` from getVersion instead
*/
public function getPyMISPVersion()
{
$this->set('response', array('version' => $this->pyMispVersion));
$this->set('_serialize', 'response');
}
public function checkout()
{
$result = $this->Server->checkoutMain();
}
public function update($branch = false)
{
if ($this->request->is('post')) {
$filterData = array(
'request' => $this->request,
'named_params' => $this->params['named'],
'paramArray' => ['branch'],
'ordered_url_params' => [],
'additional_delimiters' => PHP_EOL
);
$exception = false;
$settings = $this->_harvestParameters($filterData, $exception);
$status = $this->Server->getCurrentGitStatus();
$raw = array();
if (empty($status['branch'])) { // do not try to update if you are not on branch
$msg = 'Update failed, you are not on branch';
$raw[] = $msg;
$update = $msg;
} else {
if ($settings === false) {
$settings = [];
}
$update = $this->Server->update($status, $raw, $settings);
}
if ($this->_isRest()) {
return $this->RestResponse->viewData(array('results' => $raw), $this->response->type());
} else {
return new CakeResponse(array('body' => $update, 'type' => 'txt'));
}
} else {
$this->set('isUpdatePossible', $this->Server->isUpdatePossible());
$this->set('branch', $this->Server->getCurrentBranch());
$this->render('ajax/update');
}
}
public function ondemandAction()
{
$this->AdminSetting = ClassRegistry::init('AdminSetting');
$actions = $this->Server->actions_description;
$default_fields = array(
'title' => '',
'description' => '',
'liveOff' => false,
'recommendBackup' => false,
'exitOnError' => false,
'requirements' => '',
'url' => $this->baseurl . '/'
);
foreach($actions as $id => $action) {
foreach($default_fields as $field => $value) {
if (!isset($action[$field])) {
$actions[$id][$field] = $value;
}
}
$done = $this->AdminSetting->getSetting($id);
$actions[$id]['done'] = ($done == '1');
}
$this->set('actions', $actions);
$this->set('updateLocked', $this->Server->isUpdateLocked());
}
public function updateProgress($ajaxHtml=false)
{
$this->AdminSetting = ClassRegistry::init('AdminSetting');
$dbVersion = $this->AdminSetting->getSetting('db_version');
$updateProgress = $this->Server->getUpdateProgress();
$updateProgress['db_version'] = $dbVersion;
$maxUpdateNumber = max(array_keys(Server::DB_CHANGES));
$updateProgress['complete_update_remaining'] = max($maxUpdateNumber - $dbVersion, 0);
$updateProgress['update_locked'] = $this->Server->isUpdateLocked();
$updateProgress['lock_remaining_time'] = $this->Server->getLockRemainingTime();
$updateProgress['update_fail_number_reached'] = $this->Server->UpdateFailNumberReached();
$currentIndex = $updateProgress['current'];
$currentCommand = !isset($updateProgress['commands'][$currentIndex]) ? '' : $updateProgress['commands'][$currentIndex];
$lookupString = preg_replace('/\s{2,}/', '', substr($currentCommand, 0, -1));
$sqlInfo = $this->Server->query("SELECT * FROM INFORMATION_SCHEMA.PROCESSLIST;");
if (empty($sqlInfo)) {
$updateProgress['process_list'] = array();
} else {
// retrieve current update process
foreach($sqlInfo as $row) {
if (preg_replace('/\s{2,}/', '', $row['PROCESSLIST']['INFO']) == $lookupString) {
$sqlInfo = $row['PROCESSLIST'];
break;
}
}
$updateProgress['process_list'] = array();
$updateProgress['process_list']['STATE'] = isset($sqlInfo['STATE']) ? $sqlInfo['STATE'] : '';
$updateProgress['process_list']['PROGRESS'] = isset($sqlInfo['PROGRESS']) ? $sqlInfo['PROGRESS'] : 0;
$updateProgress['process_list']['STAGE'] = isset($sqlInfo['STAGE']) ? $sqlInfo['STAGE'] : 0;
$updateProgress['process_list']['MAX_STAGE'] = isset($sqlInfo['MAX_STAGE']) ? $sqlInfo['MAX_STAGE'] : 0;
}
$this->set('ajaxHtml', $ajaxHtml);
if ($this->request->is('ajax') && $ajaxHtml) {
$this->set('updateProgress', $updateProgress);
$this->layout = false;
} elseif ($this->request->is('ajax') || $this->_isRest()) {
return $this->RestResponse->viewData(h($updateProgress), $this->response->type());
} else {
$this->set('updateProgress', $updateProgress);
}
}
public function getSubmoduleQuickUpdateForm($submodule_path=false) {
$this->set('submodule', base64_decode($submodule_path));
$this->render('ajax/submodule_quick_update_form');
}
public function updateSubmodule()
{
if ($this->request->is('post')) {
$request = $this->request->data;
$submodule = $request['Server']['submodule'];
$res = $this->Server->updateSubmodule($this->Auth->user(), $submodule);
return new CakeResponse(array('body'=> json_encode($res), 'type' => 'json'));
} else {
throw new MethodNotAllowedException();
}
}
public function getInstanceUUID()
{
return $this->RestResponse->viewData(array('uuid' => Configure::read('MISP.uuid')), $this->response->type());
}
public function cache($id = 'all')
{
if (Configure::read('MISP.background_jobs')) {
$this->loadModel('Job');
$jobId = $this->Job->createJob(
$this->Auth->user(),
Job::WORKER_DEFAULT,
'cache_servers',
intval($id) ? $id : 'all',
__('Starting server caching.')
);
$this->Server->getBackgroundJobsTool()->enqueue(
BackgroundJobsTool::DEFAULT_QUEUE,
BackgroundJobsTool::CMD_SERVER,
[
'cacheServer',
$this->Auth->user('id'),
$id,
$jobId
],
false,
$jobId
);
$message = 'Server caching job initiated.';
} else {
$result = $this->Server->cacheServerInitiator($this->Auth->user(), $id);
if (!$result) {
$this->Flash->error(__('Caching the servers has failed.'));
$this->redirect(array('action' => 'index'));
}
$message = __('Caching the servers has successfully completed.');
}
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Server', 'cache', false, $this->response->type(), $message);
} else {
$this->Flash->info($message);
$this->redirect(array('action' => 'index'));
}
}
public function updateJSON()
{
$results = $this->Server->updateJSON();
return $this->RestResponse->viewData($results, $this->response->type());
}
public function createSync()
{
if ($this->_isSiteAdmin()) {
throw new MethodNotAllowedException('Site admin accounts cannot be used to create server sync configurations.');
}
$baseurl = Configure::read('MISP.external_baseurl');
if (empty($baseurl)) {
$baseurl = Configure::read('MISP.baseurl');
if (empty($baseurl)) {
$baseurl = Router::url('/', true);
}
}
$host_org_id = Configure::read('MISP.host_org_id');
if (empty($host_org_id)) {
throw new MethodNotAllowedException(__('Cannot create sync config - no host org ID configured for the instance.'));
}
$this->loadModel('Organisation');
$host_org = $this->Organisation->find('first', array(
'conditions' => array('Organisation.id' => $host_org_id),
'recursive' => -1,
'fields' => array('name', 'uuid')
));
if (empty($host_org)) {
throw new MethodNotAllowedException(__('Configured host org not found. Please make sure that the setting is current on the instance.'));
}
if (Configure::read('Security.advanced_authkeys')) {
$this->loadModel('AuthKey');
$authkey = $this->AuthKey->createnewkey($this->Auth->user('id'), null, __('Auto generated sync key - %s', date('Y-m-d H:i:s')));
} else {
$this->loadModel('User');
$authkey = $this->User->find('column', [
'conditions' => ['User.id' => $this->Auth->user('id')],
'recursive' => -1,
'fields' => ['User.authkey']
]);
$authkey = $authkey[0];
}
$server = array(
'Server' => array(
'url' => $baseurl,
'uuid' => Configure::read('MISP.uuid'),
'authkey' => h($authkey),
'Organisation' => array(
'name' => $host_org['Organisation']['name'],
'uuid' => $host_org['Organisation']['uuid'],
)
)
);
if ($this->_isRest()) {
return $this->RestResponse->viewData($server, $this->response->type());
} else {
$this->set('server', $server);
}
}
public function import()
{
if ($this->request->is('post')) {
$server = $this->request->data;
if (isset($server['Server'])) {
$server = $server['Server'];
}
if (isset($server['json'])) {
$server = json_decode($server['json'], true)['Server'];
}
$this->loadModel('Organisation');
$org_id = $this->Organisation->captureOrg($server['Organisation'], $this->Auth->user());
$toSave = array(
'push' => 0,
'pull' => 0,
'caching_enabled' => 0,
'json' => '[]',
'push_rules' => '[]',
'pull_rules' => '[]',
'self_signed' => 0,
'org_id' => $this->Auth->user('org_id'),
'remote_org_id' => $org_id,
'name' => empty($server['name']) ? $server['url'] : $server['name'],
'url' => $server['url'],
'uuid' => $server['uuid'],
'authkey' => $server['authkey']
);
$this->Server->create();
$result = $this->Server->save($toSave);
if ($result) {
if ($this->_isRest()) {
$server = $this->Server->find('first', array(
'conditions' => array('Server.id' => $this->Server->id),
'recursive' => -1
));
return $this->RestResponse->viewData($server, $this->response->type());
} else {
$this->Flash->success(__('The server has been saved'));
$this->redirect(array('action' => 'index', $this->Server->id));
}
} else {
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Servers', 'addFromJson', false, $this->Server->validationErrors, $this->response->type());
} else {
$this->Flash->error(__('Could not save the server. Error: %s', json_encode($this->Server->validationErrors, true)));
$this->redirect(array('action' => 'index'));
}
}
}
}
public function resetRemoteAuthKey($id)
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException(__('This endpoint expects POST requests.'));
}
$result = $this->Server->resetRemoteAuthkey($id);
if ($result !== true) {
if (!$this->_isRest()) {
$this->Flash->error($result);
$this->redirect(array('action' => 'index'));
} else {
return $this->RestResponse->saveFailResponse('Servers', 'resetRemoteAuthKey', $id, $message, $this->response->type());
}
} else {
$message = __('API key updated.');
if (!$this->_isRest()) {
$this->Flash->success($message);
$this->redirect(array('action' => 'index'));
} else {
return $this->RestResponse->saveSuccessResponse('Servers', 'resetRemoteAuthKey', $message, $this->response->type());
}
}
}
public function changePriority($id = false, $direction = 'down')
{
$this->Server->id = $id;
if (!$this->Server->exists()) {
throw new InvalidArgumentException(__('ID has to be a valid server connection'));
}
if ($direction !== 'up' && $direction !== 'down') {
throw new InvalidArgumentException(__('Invalid direction. Valid options: ', 'up', 'down'));
}
$success = $this->Server->reprioritise($id, $direction);
if ($success) {
$message = __('Priority changed.');
return $this->RestResponse->saveSuccessResponse('Servers', 'changePriority', $message, $this->response->type());
} else {
$message = __('Priority could not be changed.');
return $this->RestResponse->saveFailResponse('Servers', 'changePriority', $id, $message, $this->response->type());
}
}
public function releaseUpdateLock()
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException(__('This endpoint expects POST requests.'));
}
$this->Server->changeLockState(false);
$this->Server->resetUpdateFailNumber();
$this->redirect(array('action' => 'updateProgress'));
}
public function dbSchemaDiagnostic()
{
$dbSchemaDiagnostics = $this->Server->dbSchemaDiagnostic();
if ($this->_isRest()) {
return $this->RestResponse->viewData($dbSchemaDiagnostics, $this->response->type());
} else {
$this->set('checkedTableColumn', $dbSchemaDiagnostics['checked_table_column']);
$this->set('dbSchemaDiagnostics', $dbSchemaDiagnostics['diagnostic']);
$this->set('dbIndexDiagnostics', $dbSchemaDiagnostics['diagnostic_index']);
$this->set('expectedDbVersion', $dbSchemaDiagnostics['expected_db_version']);
$this->set('actualDbVersion', $dbSchemaDiagnostics['actual_db_version']);
$this->set('error', $dbSchemaDiagnostics['error']);
$this->set('remainingLockTime', $dbSchemaDiagnostics['remaining_lock_time']);
$this->set('updateFailNumberReached', $dbSchemaDiagnostics['update_fail_number_reached']);
$this->set('updateLocked', $dbSchemaDiagnostics['update_locked']);
$this->set('dataSource', $dbSchemaDiagnostics['dataSource']);
$this->set('columnPerTable', $dbSchemaDiagnostics['columnPerTable']);
$this->set('indexes', $dbSchemaDiagnostics['indexes']);
$this->render('/Elements/healthElements/db_schema_diagnostic');
}
}
public function dbConfiguration()
{
$dbConfiguration = $this->Server->dbConfiguration();
if ($this->_isRest()) {
return $this->RestResponse->viewData($dbConfiguration, $this->response->type());
} else {
$this->set('dbConfiguration', $dbConfiguration);
$this->render('/Elements/healthElements/db_config_diagnostic');
}
}
public function cspReport()
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException('This action expects a POST request.');
}
$report = JsonTool::decode($this->request->input());
if (!isset($report['csp-report'])) {
throw new RuntimeException("Invalid report");
}
$message = 'CSP reported violation';
$remoteIp = $this->_remoteIp();
if ($remoteIp) {
$message .= ' from IP ' . $remoteIp;
}
$report = JsonTool::encode($report['csp-report'], true);
if (strlen($report) > 1024 * 1024) { // limit report to 1 kB
$report = substr($report, 0, 1024 * 1024) . '...';
}
$this->log("$message: $report");
return new CakeResponse(['status' => 204]);
}
/**
* List all tags for the rule picker.
*
* @return array
*/
private function __getTags()
{
$this->loadModel('Tag');
$list = $this->Tag->find('list', array(
'recursive' => -1,
'order' => array('LOWER(TRIM(Tag.name))' => 'ASC'),
'fields' => array('name'),
));
$allTags = array();
foreach ($list as $id => $name) {
$allTags[] = array('id' => $id, 'name' => trim($name));
}
return $allTags;
}
public function removeOrphanedCorrelations()
{
$count = $this->Server->removeOrphanedCorrelations();
$message = __('%s orphaned correlation removed', $count);
if ($this->_isRest()) {
return $this->RestResponse->viewData($message, $this->response->type());
} else {
$this->Flash->success($message);
$this->redirect(array('action' => 'serverSettings', 'diagnostics'));
}
}
public function queryAvailableSyncFilteringRules($serverID)
{
if (!$this->_isRest()) {
throw new MethodNotAllowedException(__('This method can only be access via REST'));
}
$server = $this->Server->find('first', ['conditions' => ['Server.id' => $serverID]]);
if (!$server) {
throw new NotFoundException(__('Invalid server'));
}
$syncFilteringRules = $this->Server->queryAvailableSyncFilteringRules($server);
return $this->RestResponse->viewData($syncFilteringRules);
}
public function getAvailableSyncFilteringRules()
{
if (!$this->_isRest()) {
throw new MethodNotAllowedException(__('This method can only be access via REST'));
}
$syncFilteringRules = $this->Server->getAvailableSyncFilteringRules($this->Auth->user());
return $this->RestResponse->viewData($syncFilteringRules);
}
public function pruneDuplicateUUIDs()
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException();
}
$this->loadModel('Attribute');
$duplicates = $this->Attribute->find('all', array(
'fields' => array('Attribute.uuid', 'count(*) as occurance'),
'recursive' => -1,
'group' => array('Attribute.uuid HAVING COUNT(*) > 1'),
));
$counter = 0;
foreach ($duplicates as $duplicate) {
$attributes = $this->Attribute->find('all', array(
'recursive' => -1,
'conditions' => array('uuid' => $duplicate['Attribute']['uuid'])
));
foreach ($attributes as $k => $attribute) {
if ($k > 0) {
$this->Attribute->delete($attribute['Attribute']['id']);
$counter++;
}
}
}
$this->Server->updateDatabase('makeAttributeUUIDsUnique');
$this->Flash->success('Done. Deleted ' . $counter . ' duplicate attribute(s).');
$this->redirect(array('controller' => 'pages', 'action' => 'display', 'administration'));
}
public function removeDuplicateEvents()
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException();
}
$this->loadModel('Event');
$duplicates = $this->Event->find('all', array(
'fields' => array('Event.uuid', 'count(*) as occurance'),
'recursive' => -1,
'group' => array('Event.uuid HAVING COUNT(*) > 1'),
));
$counter = 0;
// load this so we can remove the blocklist item that will be created, this is the one case when we do not want it.
if (Configure::read('MISP.enableEventBlocklisting') !== false) {
$this->EventBlocklist = ClassRegistry::init('EventBlocklist');
}
foreach ($duplicates as $duplicate) {
$events = $this->Event->find('all', array(
'recursive' => -1,
'conditions' => array('uuid' => $duplicate['Event']['uuid'])
));
foreach ($events as $k => $event) {
if ($k > 0) {
$uuid = $event['Event']['uuid'];
$this->Event->delete($event['Event']['id']);
$counter++;
// remove the blocklist entry that we just created with the event deletion, if the feature is enabled
// We do not want to block the UUID, since we just deleted a copy
if (Configure::read('MISP.enableEventBlocklisting') !== false) {
$this->EventBlocklist->deleteAll(array('EventBlocklist.event_uuid' => $uuid));
}
}
}
}
$this->Server->updateDatabase('makeEventUUIDsUnique');
$this->Flash->success('Done. Removed ' . $counter . ' duplicate events.');
$this->redirect(array('controller' => 'pages', 'action' => 'display', 'administration'));
}
public function upgrade2324()
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException();
}
if (!Configure::read('MISP.background_jobs')) {
$this->Server->upgrade2324($this->Auth->user('id'));
$this->Flash->success('Done. For more details check the audit logs.');
$this->redirect(array('controller' => 'pages', 'action' => 'display', 'administration'));
} else {
$this->loadModel('Job');
$jobId = $this->Job->createJob(
$this->Auth->user(),
Job::WORKER_DEFAULT,
'upgrade_24',
'Old database',
__('Job created.')
);
$this->Server->getBackgroundJobsTool()->enqueue(
BackgroundJobsTool::DEFAULT_QUEUE,
BackgroundJobsTool::CMD_ADMIN,
[
'jobUpgrade24',
$jobId,
$this->Auth->user('id'),
],
true,
$jobId
);
$this->Flash->success(__('Job queued. You can view the progress if you navigate to the active jobs view (administration -> jobs).'));
$this->redirect(array('controller' => 'pages', 'action' => 'display', 'administration'));
}
}
public function cleanModelCaches()
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException();
}
$this->Server->cleanCacheFiles();
$this->Flash->success('Caches cleared.');
$this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'diagnostics'));
}
public function updateDatabase($command)
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException();
}
if (is_numeric($command)) {
$command = intval($command);
}
$this->Server->updateDatabase($command);
$this->Flash->success('Done.');
if ($liveOff) {
$this->redirect(array('controller' => 'servers', 'action' => 'updateProgress'));
} else {
$this->redirect(array('controller' => 'pages', 'action' => 'display', 'administration'));
}
}
public function ipUser($input = false)
{
$params = $this->IndexFilter->harvestParameters(['ip']);
if (!empty($params['ip'])) {
$input = $params['ip'];
}
$redis = $this->Server->setupRedis();
if (!is_array($input)) {
$input = [$input];
}
$users = [];
foreach ($input as $ip) {
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
continue;
}
$user_id = $redis->get('misp:ip_user:' . $ip);
if (empty($user_id)) {
continue;
}
$this->loadModel('User');
$user = $this->User->find('first', [
'recursive' => -1,
'conditions' => ['User.id' => $user_id],
'contain' => ['Organisation.name']
]);
if (empty($user)) {
throw new NotFoundException(__('User not found (perhaps it has been removed?).'));
}
$users[$ip] = [
'id' => $user['User']['id'],
'email' => $user['User']['email'],
];
}
return $this->RestResponse->viewData($users, $this->response->type());
}
/**
* @deprecated
* @return void
*/
public function rest()
{
$this->redirect(['controller' => 'api', 'action' => 'rest']);
}
}