diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php index d921572c2..0e717cf0e 100755 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -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)); } diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 3eb740dbc..74d5e703c 100755 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -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(); } } diff --git a/app/Controller/SharingGroupsController.php b/app/Controller/SharingGroupsController.php index 2eb5a62f3..59099eb60 100644 --- a/app/Controller/SharingGroupsController.php +++ b/app/Controller/SharingGroupsController.php @@ -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( diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 77afdfd4d..0d6782923 100755 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -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: diff --git a/app/Model/Event.php b/app/Model/Event.php index fb286f6de..405e0d9b6 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -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) { diff --git a/app/Model/Server.php b/app/Model/Server.php index 6286b4a22..a380eb62a 100755 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -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']) diff --git a/app/Model/SharingGroup.php b/app/Model/SharingGroup.php index 3ff1f87bf..2d928fde6 100644 --- a/app/Model/SharingGroup.php +++ b/app/Model/SharingGroup.php @@ -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;