Further progress on the sync

- also, added maintenance mode
- various fixes
pull/762/head
Iglocska 2015-12-09 01:43:54 +01:00
parent 24fc2399cc
commit af92e29e3c
7 changed files with 179 additions and 49 deletions

View File

@ -180,6 +180,26 @@ class AppController extends Controller {
$base_dir = '';
}
// check if MISP is live
if ($this->Auth->user() && !Configure::read('MISP.live')) {
$role = $this->getActions();
if (!$role['perm_site_admin']) {
$message = Configure::read('MISP.maintenance_message');
if (empty($messaage)) {
$this->loadModel('Server');
$message = $this->Server->serverSettings['MISP']['maintenance_message']['value'];
}
if (strpos($message, '$email') && Configure::read('MISP.email')) {
$email = Configure::read('MISP.email');
$message = str_replace('$email', $email, $message);
}
$this->Auth->logout();
throw new MethodNotAllowedException($message);
} else {
$this->Session->setFlash('Warning: MISP is currently disabled for all users. Enable it in Server Settings (Administration -> Server Settings -> MISP tab -> live)');
}
}
if ($this->Session->check(AuthComponent::$sessionKey) && !$this->Auth->user('termsaccepted') && (!in_array($this->request->here, array($base_dir.'/users/terms', $base_dir.'/users/logout', $base_dir.'/users/login')))) {
$this->redirect(array('controller' => 'users', 'action' => 'terms', 'admin' => false));
}

View File

@ -702,9 +702,9 @@ class EventsController extends AppController {
$conditions['includeAllTags'] = true;
}
$results = $this->Event->fetchEvent($this->Auth->user(), $conditions);
if (empty($results)) throw new NotFoundException('Invalid event');
$event = &$results[0];
if (empty($results)) throw new NotFoundException('Invalid event');
if ($this->_isRest()) $this->set('event', $event);
if (!$this->_isRest()) $this->__viewUI($event, $continue, $fromEvent);
}
@ -820,7 +820,8 @@ class EventsController extends AppController {
throw new MethodNotAllowedException('You don\'t have permissions to create events');
}
$this->loadModel('SharingGroup');
$sgs = $this->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'name', 1);
if ($this->userRole['perm_sync']) $sguuids = $this->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'uuid', 1);
$sgs = $this->SharingGroup->fetchAllAuthorised($this->Auth->user(), false, 1);
if ($this->request->is('post')) {
if ($this->_isRest()) {
@ -843,7 +844,12 @@ class EventsController extends AppController {
} else {
// If the distribution is set to sharing group, check if the id provided is really visible to the user, if not throw an error.
if ($this->request->data['Event']['distribution'] == 4) {
if (!isset($sgs[$this->request->data['Event']['sharing_group_id']])) throw new MethodNotAllowedException('Invalid Sharing Group or not authorised.');
if ($this->userRole['perm_sync']) {
if (!$this->SharingGroup->checkIfAuthorisedToSave($this->Auth->user(), $this->request->data['Event']['SharingGroup'])) throw new MethodNotAllowedException('Invalid Sharing Group or not authorised. (Sync user is not contained in the Sharing group)');
//if (!isset($this->request->data['Event']['SharingGroup']))
} else {
if (!isset($sgs[$this->request->data['Event']['sharing_group_id']])) throw new MethodNotAllowedException('Invalid Sharing Group or not authorised.');
}
} else {
// If the distribution is set to something "traditional", set the SG id to 0.
$this->request->data['Event']['sharing_group_id'] = 0;
@ -1285,7 +1291,7 @@ class EventsController extends AppController {
$this->Event->read();
if (!$this->_isSiteAdmin()) {
if ($this->Event->data['Event']['orgc_id'] != $this->_checkOrg()) {
if ($this->Event->data['Event']['org_id'] != $this->_checkOrg() || !$this->userRole['perm_modify']) {
throw new MethodNotAllowedException();
}
}

View File

@ -113,7 +113,7 @@ class SharingGroupsController extends AppController {
$failedField = array_keys($validationErrors)[0];
$reason = reset($this->SharingGroup->validationErrors)[0];
foreach ($validationReplacements as $k => $vR) if ($reason == $k) $reason = $vR;
$this->Session->setFlash('The sharing group could not be added. ' . ucfirst($failedField) . ': ' . $reason);
$this->Session->setFlash('The sharing group could not be edited. ' . ucfirst($failedField) . ': ' . $reason);
}
}
$orgs = $this->SharingGroup->Organisation->find('all', array(

View File

@ -112,7 +112,11 @@ class AppModel extends Model {
KEY `taxonomy_id` (`taxonomy_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;";
$sqlArray[] = 'ALTER TABLE `jobs` ADD `org` text COLLATE utf8_bin NOT NULL';
$sqlArray[] = 'ALTER TABLE `jobs` ADD `org` text COLLATE utf8_bin NOT NULL;';
$sqlArray[] = 'ALTER TABLE `servers` ADD `name` varchar(255) NOT NULL;';
$sqlArray[] = 'ALTER TABLE `sharing_groups` ADD `sync_user_id` INT( 11 ) NOT NULL DEFAULT \'0\' AFTER `org_id`;';
break;
default:

View File

@ -598,15 +598,11 @@ class Event extends AppModel {
foreach ($objects as $object) {
// Workaround for different structure in XML/array than what CakePHP expects
if (isset($data['Event'][$object]) && is_array($data['Event'][$object]) && count($data['Event'][$object])) {
if (is_numeric(implode(array_keys($data['Event'][$object]), ''))) {
// normal array of multiple Attributes
$data[$object] = $data['Event'][$object];
} else {
if (!is_numeric(implode(array_keys($data['Event'][$object]), ''))) {
// single attribute
$data[$object][0] = $data['Event'][$object];
$data['Event'][$object][0] = $data['Event'][$object];
}
}
unset($data['Event'][$object]);
}
return $data;
}
@ -1008,6 +1004,7 @@ class Event extends AppModel {
//restricting to non-private or same org if the user is not a site-admin.
if (!$isSiteAdmin) {
$sgids = $this->SharingGroup->fetchAllAuthorised($user);
if (empty($sgids)) $sgids = array(-1);
$conditions['AND']['OR'] = array(
'Event.org_id' => $user['org_id'],
array(
@ -1071,10 +1068,6 @@ class Event extends AppModel {
if ($options['to_ids']) {
$conditionsAttributes['AND'][] = array('Attribute.to_ids' => 1);
}
if (isset($user['Server']) && $user['Server']['push_rules']) {
$conditions['AND'][] = $this->filterRulesToConditions($user['Server']['push_rules']);
}
// removing this for now, we export the to_ids == 0 attributes too, since there is a to_ids field indicating it in the .xml
// $conditionsAttributes['AND'] = array('Attribute.to_ids =' => 1);
@ -1132,12 +1125,11 @@ class Event extends AppModel {
),
)
);
if ($user['Role']['perm_site_admin']) {
$params['contain']['User'] = array('fields' => 'email');
}
$results = $this->find('all', $params);
if (empty($results)) throw new NotFoundException(__('Invalid event'));
if (empty($results)) return array();
// Do some refactoring with the event
$sgsids = $this->SharingGroup->fetchAllAuthorised($user);
foreach ($results as $eventKey => &$event) {
@ -1553,6 +1545,27 @@ class Event extends AppModel {
// When we receive an event via REST, we might end up with organisations, sharing groups, tags that we do not know
// or which we need to update. All of that is controller in this method.
private function __captureObjects($data, $user) {
// First we need to check whether the event or any attributes are tied to a sharing group and whether the user is even allowed to create the sharing group / is part of it
// For this we first collect all the sharing groups
$sgs = array();
if ($data['Event']['distribution'] == 4) $sgs[$data['Event']['SharingGroup']['uuid']] = $data['Event']['SharingGroup'];
if (isset($data['Event']['Attribute'])) {
foreach ($data['Event']['Attribute'] as &$attribute) {
if ($attribute['distribution'] == 4 && !isset($sgs[$attribute['SharingGroup']['uuid']])) $sgs[$attribute['SharingGroup']['uuid']] = $attribute['SharingGroup'];
}
}
if ($data['Event']['distribution'] == 4) {
$data['Event']['sharing_group_id'] = $this->SharingGroup->captureSG($data['Event']['SharingGroup'], $user);
unset ($data['Event']['SharingGroup']);
}
if(isset($data['Attribute'])) foreach ($data['Attribute'] as $k => &$a) {
unset($data['Attribute']['id']);
if($a['distribution'] == 4) {
$data['Attribute'][$k]['sharing_group_id'] = $this->SharingGroup->captureSG($data['Attribute'][$k]['SharingGroup'], $user);
}
unset($data['Attribute'][$k]['SharingGroup']);
}
// first we want to see how the creator organisation is encoded
// The options here are either by passing an organisation object along or simply passing a string along
if (isset($data['Event']['Orgc'])) {
@ -1577,17 +1590,6 @@ class Event extends AppModel {
}
$data['Event']['EventTag'] = $eventTags;
}
if ($data['Event']['distribution'] == 4) {
$data['Event']['sharing_group_id'] = $this->SharingGroup->captureSG($data['Event']['SharingGroup'], $user);
unset ($data['Event']['SharingGroup']);
}
if(isset($data['Attribute'])) foreach ($data['Attribute'] as $k => &$a) {
unset($data['Attribute']['id']);
if($a['distribution'] == 4) {
$data['Attribute'][$k]['sharing_group_id'] = $this->SharingGroup->captureSG($data['Attribute'][$k]['SharingGroup'], $user);
}
unset($data['Attribute'][$k]['SharingGroup']);
}
return $data;
}
@ -1659,13 +1661,15 @@ class Event extends AppModel {
$saveResult = $this->save(array('Event' => $data['Event']), array('fieldList' => $fieldList['Event']));
$this->Log = ClassRegistry::init('Log');
if ($saveResult) {
if (isset($data['Event']['EventTag'])) foreach ($data['Event']['EventTag'] as $et) {
if (isset($data['Event']['EventTag'])) {
foreach ($data['Event']['EventTag'] as $et) {
$this->EventTag->create();
$et['event_id'] = $this->id;
$this->EventTag->save($et);
}
}
if (isset($data['Attribute'])) {
foreach ($data['Attribute'] as $k => &$attribute) {
if (isset($data['Event']['Attribute'])) {
foreach ($data['Event']['Attribute'] as $k => &$attribute) {
$attribute['event_id'] = $this->id;
unset ($attribute['id']);
$this->Attribute->create();
@ -1686,7 +1690,7 @@ class Event extends AppModel {
}
}
}
if ($fromXml) $created_id = $this->id;
if (!empty($data['Event']['published']) && 1 == $data['Event']['published']) {
// do the necessary actions to publish the event (email, upload,...)
@ -1724,28 +1728,28 @@ class Event extends AppModel {
$data = $this->cleanupEventArrayFromXML($data);
$saveResult = $this->save($data, array('fieldList' => $fieldList['Event']));
if ($saveResult) {
if (isset($data['Attribute'])) {
foreach ($data['Attribute'] as $k => &$attribute) {
if (isset($data['Event']['Attribute'])) {
foreach ($data['Event']['Attribute'] as $k => &$attribute) {
$mode = 'add';
$data['Attribute'][$k]['event_id'] = $this->id;
$data['Event']['Attribute'][$k]['event_id'] = $this->id;
$existingAttribute = $this->__searchUuidInAttributeArray($attribute['uuid'], $localEvent);
if (count($existingAttribute)) {
// Check if the attribute's timestamp is bigger than the one that already exists.
// If yes, it means that it's newer, so insert it. If no, it means that it's the same attribute or older - don't insert it, insert the old attribute.
// Alternatively, we could unset this attribute from the request, but that could lead with issues if we decide that we want to start deleting attributes that don't exist in a pushed event.
if ($data['Attribute'][$k]['timestamp'] > $existingAttribute['Attribute']['timestamp']) {
$data['Attribute'][$k]['id'] = $existingAttribute['Attribute']['id'];
if ($data['Event']['Attribute'][$k]['timestamp'] > $existingAttribute['Attribute']['timestamp']) {
$data['Event']['Attribute'][$k]['id'] = $existingAttribute['Attribute']['id'];
} else {
unset($data['Attribute'][$k]);
unset($data['Event']['Attribute'][$k]);
continue;
}
} else {
unset($data['Attribute'][$k]['id']);
unset($data['Event']['Attribute'][$k]['id']);
$this->Attribute->create();
}
if (!$this->Attribute->save($data['Attribute'][$k], array('fieldList' => $fieldList['Attribute']))) {
if (!$this->Attribute->save($data['Event']['Attribute'][$k], array('fieldList' => $fieldList['Attribute']))) {
$validationErrors[] = $this->Attribute->validationErrors;
$attribute_short = (isset($data['Attribute'][$k]['category']) ? $data['Attribute'][$k]['category'] : 'N/A') . '/' . (isset($data['Attribute'][$k]['type']) ? $data['Attribute'][$k]['type'] : 'N/A') . ' ' . (isset($data['Attribute'][$k]['value']) ? $data['Attribute'][$k]['value'] : 'N/A');
$attribute_short = (isset($data['Event']['Attribute'][$k]['category']) ? $data['Event']['Attribute'][$k]['category'] : 'N/A') . '/' . (isset($data['Event']['Attribute'][$k]['type']) ? $data['Event']['Attribute'][$k]['type'] : 'N/A') . ' ' . (isset($data['Event']['Attribute'][$k]['value']) ? $data['Event']['Attribute'][$k]['value'] : 'N/A');
$this->Log->create();
$this->Log->save(array(
'org' => $user['Organisation']['name'],
@ -1933,7 +1937,7 @@ class Event extends AppModel {
if (!empty($hostOrg)) {
$user = array('org_id' => $hostOrg['Org']['id'], 'Role' => array('perm_sync' => false, 'perm_site_admin' => false), 'Organisation' => $hostOrg['Org']);
$fullEvent = $this->fetchEvent($user, array('eventid' => $id));
$pubSubTool->publishEvent($fullEvent[0]);
if (!empty($fullEvent)) $pubSubTool->publishEvent($fullEvent[0]);
}
}
if ($event['Event']['distribution'] > 1) {

View File

@ -136,6 +136,22 @@ class Server extends AppModel {
'test' => 'testBaseURL',
'type' => 'string',
),
'live' => array(
'level' => 0,
'description' => 'Unless set to true, the instance will only be accessible by site admins.',
'value' => false,
'errorMessage' => '',
'test' => 'testBool',
'type' => 'boolean',
),
'maintenance_message' => array(
'level' => 2,
'description' => 'The message that users will see if the instance is not live.',
'value' => 'Great things are happening! MISP is undergoing maintenance, but will return shortly. You can contact the administration at $email.',
'errorMessage' => 'If this is not set the default value will be used.',
'test' => 'testForEmpty',
'type' => 'string',
),
'name' => array(
'level' => 3,
'description' => 'This setting is deprecated and can be safely removed.',
@ -1785,6 +1801,7 @@ class Server extends AppModel {
}
public function captureServer($server, $user) {
if ($server['url'] == Configure::read('MISP.baseurl')) return 0;
$existingServer = $this->find('first', array(
'recursive' => -1,
'conditions' => array('url' => $server['url'])

View File

@ -109,8 +109,9 @@ class SharingGroup extends AppModel {
} else {
$ids = array_unique(array_merge($this->SharingGroupServer->fetchAllAuthorised(), $this->SharingGroupOrg->fetchAllAuthorised($user['Organisation']['id'])));
}
if (!empty($ids)) $conditions['And'][] = array('SharingGroup.id' => $ids);
else return array();
if ($scope === 'full') {
if (!empty($ids)) $conditions['And'][] = array('SharingGroup.id' => $ids);
$sgs = $this->find('all', array(
'contain' => array('SharingGroupServer' => array('Server'), 'SharingGroupOrg' => array('Organisation'), 'Organisation'),
'conditions' => $conditions,
@ -118,7 +119,6 @@ class SharingGroup extends AppModel {
));
return $sgs;
} else if ($scope == 'name') {
if (!empty($ids)) $conditions['And'][] = array('SharingGroup.id' => $ids);
$sgs = $this->find('list', array(
'recursive' => -1,
'fields' => array('id', 'name'),
@ -126,16 +126,75 @@ class SharingGroup extends AppModel {
'conditions' => $conditions,
));
return $sgs;
} else if ($scope == 'uuid') {
$sgs = $this->find('list', array(
'recursive' => -1,
'fields' => array('id', 'uuid'),
'conditions' => $conditions,
));
} else {
return $ids;
}
}
// Who can create a new sharing group with the elements pre-defined (via REST for example)?
// 1. site admins
// 2. Sharing group enabled users
// a. as long as they are creator or extender of the SG object
// 3. Sync users
// a. as long as they are at least users of the SG (they can circumvent the extend rule to
// avoid situations where no one can create / edit an SG on an instance after a push)
public function checkIfAuthorisedToSave($user, $sg) {
if ($user['Role']['perm_site_admin']) return true;
if (!$user['Role']['perm_sharing_group']) return false;
// First let us find out if we already have the SG
$local = $this->find('first', array(
'recursive' => -1,
'conditions' => array('uuid' => $sg['uuid'])
));
if (empty($local)) {
$found = false;
$orgCheck = false;
$serverCheck = false;
if (isset($sg['SharingGroupOrg'])) {
foreach ($sg['SharingGroupOrg'] as $org) {
if ($org['Organisation']['uuid'] == $user['Organisation']['uuid']) {
if ($user['Role']['perm_sync'] || $org['extend'] == 1) {
$orgCheck = true;
continue;
}
}
}
}
if (isset($sg['SharingGroupServer'])) {
foreach ($sg['SharingGroupServer'] as $server) {
if ($server['Server']['url'] == Configure::read('MISP.baseurl')) {
$serverCheck = true;
continue;
}
}
} else $serverCheck = true;
if ($serverCheck && $orgCheck) return true;
} else {
return $this->checkIfAuthorisedExtend($user, $local['SharingGroup']['id']);
}
return false;
}
// Who is authorised to extend a sharing group?
// 1. Site admins
// 2. Sharing group permission enabled users that:
// a. Belong to the organisation that created the SG
// b. Have an organisation entry in the SG with the extend flag set
// 3. Sync users that have synced the SG to the local instance
public function checkIfAuthorisedExtend($user, $id) {
if ($user['Role']['perm_site_admin']) return true;
if (!$user['Role']['perm_sharing_group']) return false;
if ($this->checkIfOwner($user, $id)) return true;
$this->id = $id;
if (!$this->exists()) return false;
$sg = $this->SharingGroupOrg->find('first', array(
$sgo = $this->SharingGroupOrg->find('first', array(
'conditions' => array(
'sharing_group_id' => $id,
'org_id' => $user['org_id'],
@ -144,7 +203,20 @@ class SharingGroup extends AppModel {
'recursive' => -1,
'fields' => array('id', 'org_id', 'extend')
));
if (empty($sg)) return false;
if (empty($sgo)) {
if ($user['Role']['perm_sync']) {
$sg = $this->find('first', array(
'conditions' => array(
'id' => $id,
'sync_user_id' => $user['id'],
),
'recursive' => -1,
));
if (empty($sg)) return false;
else return true;
}
return false;
}
else return true;
}
@ -255,7 +327,9 @@ class SharingGroup extends AppModel {
$attributes = array('name', 'releasability', 'description', 'uuid', 'organisation_uuid', 'created', 'modified');
foreach ($attributes as $a) $newSG[$a] = $sg[$a];
$newSG['local'] = 0;
$this->save($newSG);
$newSG['sync_user_id'] = $user['id'];
if (!$this->save($newSG)) return false;
$sgids = $this->id;
}
$sg['org_id'] = $this->Organisation->captureOrg($sg['Organisation'], $user);
unset ($sg['Organisation']);
@ -268,6 +342,8 @@ class SharingGroup extends AppModel {
foreach ($sg['SharingGroupOrg'] as $k => $org) {
$sg['SharingGroupOrg'][$k]['org_id'] = $this->Organisation->captureOrg($org['Organisation'], $user);
unset ($sg['SharingGroupOrg'][$k]['Organisation']);
$this->SharingGroupOrg->create();
$this->SharingGroupOrg->save(array('sharing_group_id' => $sgids, 'org_id' => $sg['SharingGroupOrg'][$k]['org_id'], 'extend' => $org['extend']));
}
if (isset($sg['SharingGroupServer']['id'])) {
@ -278,7 +354,10 @@ class SharingGroup extends AppModel {
foreach ($sg['SharingGroupServer'] as $k => $server) {
$sg['SharingGroupServer'][$k]['server_id'] = $this->SharingGroupServer->Server->captureServer($server['Server'], $user);
if ($sg['SharingGroupServer'][$k]['server_id'] === false) unset ($sg['SharingGroupServer'][$k]);
else unset ($sg['SharingGroupServer'][$k]['Server']);
else {
$this->SharingGroupServer->create();
$this->SharingGroupServer->save(array('sharing_group_id' => $sgids, 'server_id' => $sg['SharingGroupServer'][$k]['server_id'], 'all_orgs' => $server['all_orgs']));
}
}
if (!empty($existingSG)) return $existingSG[$this->alias]['id'];
return $this->id;