diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index 3ab640863..ad1adfd6a 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -904,7 +904,7 @@ CREATE TABLE IF NOT EXISTS `tags` ( `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `colour` varchar(7) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, `exportable` tinyint(1) NOT NULL, - `org_id` tinyint(1) NOT NULL DEFAULT 0, + `org_id` int(11) NOT NULL DEFAULT 0, `user_id` int(11) NOT NULL DEFAULT 0, `hide_tag` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (`id`), diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php index 439b90b16..9fe02627d 100755 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -46,7 +46,7 @@ class AppController extends Controller public $helpers = array('Utility', 'OrgImg'); - private $__queryVersion = '50'; + private $__queryVersion = '51'; public $pyMispVersion = '2.4.99'; public $phpmin = '5.6.5'; public $phprec = '7.0.16'; diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php index dce7db014..ffe2cf674 100644 --- a/app/Controller/AttributesController.php +++ b/app/Controller/AttributesController.php @@ -2838,15 +2838,31 @@ class AttributesController extends AppController $tag_id = $this->request->data['tag']; } if (!is_numeric($tag_id)) { - $tag = $this->Attribute->AttributeTag->Tag->find('first', array('recursive' => -1, 'conditions' => array('LOWER(Tag.name) LIKE' => strtolower(trim($tag_id))))); - if (empty($tag)) { - return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag.')), 'status' => 200, 'type' => 'json')); + if (preg_match('/^collection_[0-9]+$/i', $tag_id)) { + $tagChoice = explode('_', $tag_id)[1]; + $this->loadModel('TagCollection'); + $tagCollection = $this->TagCollection->fetchTagCollection($this->Auth->user(), array('conditions' => array('TagCollection.id' => $tagChoice))); + if (empty($tagCollection)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag Collection.')), 'status'=>200, 'type' => 'json')); + } + $tag_id_list = array(); + foreach ($tagCollection[0]['TagCollectionTag'] as $tagCollectionTag) { + $tag_id_list[] = $tagCollectionTag['tag_id']; + } + } else { + $tag = $this->Event->EventTag->Tag->find('first', array('recursive' => -1, 'conditions' => $conditions)); + if (empty($tag)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag.')), 'status'=>200, 'type' => 'json')); + } + $tag_id = $tag['Tag']['id']; } - $tag_id = $tag['Tag']['id']; } if (!isset($idList)) { $idList = array($id); } + if (empty($tag_id_list)) { + $tag_id_list = array($tag_id); + } $success = 0; $fails = 0; foreach ($idList as $id) { @@ -2874,40 +2890,44 @@ class AttributesController extends AppController $this->Attribute->Event->insertLock($this->Auth->user(), $eventId); } $this->Attribute->recursive = -1; - $this->Attribute->AttributeTag->Tag->id = $tag_id; - if (!$this->Attribute->AttributeTag->Tag->exists()) { - return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag.')), 'status' => 200, 'type' => 'json')); - } - $tag = $this->Attribute->AttributeTag->Tag->find('first', array( - 'conditions' => array('Tag.id' => $tag_id), - 'recursive' => -1, - 'fields' => array('Tag.name') - )); - $found = $this->Attribute->AttributeTag->find('first', array( - 'conditions' => array( - 'attribute_id' => $id, - 'tag_id' => $tag_id - ), - 'recursive' => -1, - )); - $this->autoRender = false; - if (!empty($found)) { - $fails++; - continue; - } - $this->Attribute->AttributeTag->create(); - if ($this->Attribute->AttributeTag->save(array('attribute_id' => $id, 'tag_id' => $tag_id, 'event_id' => $eventId))) { - $event['Event']['published'] = 0; - $date = new DateTime(); - $event['Event']['timestamp'] = $date->getTimestamp(); - $this->Attribute->Event->save($event); - $this->Attribute->data['Attribute']['timestamp'] = $date->getTimestamp(); - $this->Attribute->save($this->Attribute->data); - $log = ClassRegistry::init('Log'); - $log->createLogEntry($this->Auth->user(), 'tag', 'Attribute', $id, 'Attached tag (' . $tag_id . ') "' . $tag['Tag']['name'] . '" to attribute (' . $id . ')', 'Attribute (' . $id . ') tagged as Tag (' . $tag_id . ')'); - $success++; - } else { - $fails++; + + foreach ($tag_id_list as $tag_id) { + $this->Attribute->AttributeTag->Tag->id = $tag_id; + if (!$this->Attribute->AttributeTag->Tag->exists()) { + $fails++; + continue; + } + $tag = $this->Attribute->AttributeTag->Tag->find('first', array( + 'conditions' => array('Tag.id' => $tag_id), + 'recursive' => -1, + 'fields' => array('Tag.name') + )); + $found = $this->Attribute->AttributeTag->find('first', array( + 'conditions' => array( + 'attribute_id' => $id, + 'tag_id' => $tag_id + ), + 'recursive' => -1, + )); + $this->autoRender = false; + if (!empty($found)) { + $fails++; + continue; + } + $this->Attribute->AttributeTag->create(); + if ($this->Attribute->AttributeTag->save(array('attribute_id' => $id, 'tag_id' => $tag_id, 'event_id' => $eventId))) { + $event['Event']['published'] = 0; + $date = new DateTime(); + $event['Event']['timestamp'] = $date->getTimestamp(); + $this->Attribute->Event->save($event); + $this->Attribute->data['Attribute']['timestamp'] = $date->getTimestamp(); + $this->Attribute->save($this->Attribute->data); + $log = ClassRegistry::init('Log'); + $log->createLogEntry($this->Auth->user(), 'tag', 'Attribute', $id, 'Attached tag (' . $tag_id . ') "' . $tag['Tag']['name'] . '" to attribute (' . $id . ')', 'Attribute (' . $id . ') tagged as Tag (' . $tag_id . ')'); + $success++; + } else { + $fails++; + } } } if ($fails == 0) { diff --git a/app/Controller/Component/ACLComponent.php b/app/Controller/Component/ACLComponent.php index 0ab2a6d9e..f321bab8e 100644 --- a/app/Controller/Component/ACLComponent.php +++ b/app/Controller/Component/ACLComponent.php @@ -181,6 +181,7 @@ class ACLComponent extends Component 'selectGalaxy' => array('perm_tagger'), 'selectGalaxyNamespace' => array('perm_tagger'), 'selectCluster' => array('perm_tagger'), + 'showGalaxies' => array('*'), 'update' => array(), 'view' => array('*'), 'viewGraph' => array('*') @@ -382,6 +383,16 @@ class ACLComponent extends Component 'viewSightings' => array('perm_sighting'), 'quickAdd' => array('perm_sighting') ), + 'tagCollections' => array( + 'add' => array('perm_tag_editor'), + 'addTag' => array('perm_tag_editor'), + 'delete' => array('perm_tag_editor'), + 'edit' => array('perm_tag_editor'), + 'getRow' => array('perm_tag_editor'), + 'index' => array('*'), + 'removeTag' => array('perm_tag_editor'), + 'view' => array('*') + ), 'tags' => array( 'add' => array('perm_tag_editor'), 'attachTagToObject' => array('perm_tagger'), @@ -394,6 +405,7 @@ class ACLComponent extends Component 'selectTaxonomy' => array('perm_tagger'), 'showEventTag' => array('*'), 'showAttributeTag' => array('*'), + 'showTagControllerTag' => array('*'), 'tagStatistics' => array('*'), 'view' => array('*'), 'viewGraph' => array('*'), diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 118829ef8..a70e6c109 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -1848,7 +1848,7 @@ class EventsController extends AppController $this->Event->read(null, $target_id); // check if private and user not authorised to edit if (!$this->_isSiteAdmin() && ($this->Event->data['Event']['orgc_id'] != $this->_checkOrg() || !($this->userRole['perm_modify']))) { - $this->Flash->error(__('You are not authorised to do that. Please consider using the \'propose attribute\' feature.')); + $this->Flash->error(__("You are not authorised to do that. Please consider using the 'propose attribute' feature.")); $this->redirect(array('action' => 'view', $target_id)); } $this->Event->insertLock($this->Auth->user(), $target_id); @@ -3318,11 +3318,24 @@ class EventsController extends AppController $conditions['Tag.user_id'] = array('0', $this->Auth->user('id')); } if (!is_numeric($tag_id)) { - $tag = $this->Event->EventTag->Tag->find('first', array('recursive' => -1, 'conditions' => $conditions)); - if (empty($tag)) { - return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag.')), 'status'=>200, 'type' => 'json')); + if (preg_match('/^collection_[0-9]+$/i', $tag_id)) { + $tagChoice = explode('_', $tag_id)[1]; + $this->loadModel('TagCollection'); + $tagCollection = $this->TagCollection->fetchTagCollection($this->Auth->user(), array('conditions' => array('TagCollection.id' => $tagChoice))); + if (empty($tagCollection)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag Collection.')), 'status'=>200, 'type' => 'json')); + } + $tag_id_list = array(); + foreach ($tagCollection[0]['TagCollectionTag'] as $tagCollectionTag) { + $tag_id_list[] = $tagCollectionTag['tag_id']; + } + } else { + $tag = $this->Event->EventTag->Tag->find('first', array('recursive' => -1, 'conditions' => $conditions)); + if (empty($tag)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag.')), 'status'=>200, 'type' => 'json')); + } + $tag_id = $tag['Tag']['id']; } - $tag_id = $tag['Tag']['id']; } $this->Event->recursive = -1; $event = $this->Event->read(array(), $id); @@ -3334,37 +3347,53 @@ class EventsController extends AppController return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'You don\'t have permission to do that.')), 'status'=>200, 'type' => 'json')); } } - $this->Event->EventTag->Tag->id = $tag_id; - if (!$this->Event->EventTag->Tag->exists()) { - return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag.')), 'status'=>200, 'type' => 'json')); - } - $tag = $this->Event->EventTag->Tag->find('first', array( - 'conditions' => array('Tag.id' => $tag_id), - 'recursive' => -1, - 'fields' => array('Tag.name') - )); - $found = $this->Event->EventTag->find('first', array( - 'conditions' => array( - 'event_id' => $id, - 'tag_id' => $tag_id - ), - 'recursive' => -1, - )); $this->autoRender = false; - if (!empty($found)) { - return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Tag is already attached to this event.')), 'status'=>200, 'type' => 'json')); + $error = false; + $success = false; + if (empty($tag_id_list)) { + $tag_id_list = array($tag_id); } - $this->Event->EventTag->create(); - if ($this->Event->EventTag->save(array('event_id' => $id, 'tag_id' => $tag_id))) { - $event['Event']['published'] = 0; - $date = new DateTime(); - $event['Event']['timestamp'] = $date->getTimestamp(); - $this->Event->save($event); - $log = ClassRegistry::init('Log'); - $log->createLogEntry($this->Auth->user(), 'tag', 'Event', $id, 'Attached tag (' . $tag_id . ') "' . $tag['Tag']['name'] . '" to event (' . $id . ')', 'Event (' . $id . ') tagged as Tag (' . $tag_id . ')'); - return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Tag added.', 'check_publish' => true)), 'status'=>200, 'type' => 'json')); + foreach ($tag_id_list as $tag_id) { + $this->Event->EventTag->Tag->id = $tag_id; + if (!$this->Event->EventTag->Tag->exists()) { + $error = __('Invalid Tag.'); + continue; + } + $tag = $this->Event->EventTag->Tag->find('first', array( + 'conditions' => array('Tag.id' => $tag_id), + 'recursive' => -1, + 'fields' => array('Tag.name') + )); + $found = $this->Event->EventTag->find('first', array( + 'conditions' => array( + 'event_id' => $id, + 'tag_id' => $tag_id + ), + 'recursive' => -1, + )); + if (!empty($found)) { + $error = __('Tag is already attached to this event.'); + continue; + } + $this->Event->EventTag->create(); + if ($this->Event->EventTag->save(array('event_id' => $id, 'tag_id' => $tag_id))) { + $event['Event']['published'] = 0; + $date = new DateTime(); + $event['Event']['timestamp'] = $date->getTimestamp(); + $this->Event->save($event); + $log = ClassRegistry::init('Log'); + $log->createLogEntry($this->Auth->user(), 'tag', 'Event', $id, 'Attached tag (' . $tag_id . ') "' . $tag['Tag']['name'] . '" to event (' . $id . ')', 'Event (' . $id . ') tagged as Tag (' . $tag_id . ')'); + $success = __('Tag(s) added.'); + } else { + $fail = __('Tag could not be added.'); + } + } + if ($success) { + return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => __('Tag(s) added.'), 'check_publish' => true)), 'status'=>200, 'type' => 'json')); + } elseif (empty($fail)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => __('All tags are already present, nothing to add.'), 'check_publish' => true)), 'status'=>200, 'type' => 'json')); } else { - return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Tag could not be added.')), 'status'=>200, 'type' => 'json')); + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $fail)), 'status'=>200, 'type' => 'json')); } } diff --git a/app/Controller/GalaxiesController.php b/app/Controller/GalaxiesController.php index a84bde6c2..f06baa52a 100644 --- a/app/Controller/GalaxiesController.php +++ b/app/Controller/GalaxiesController.php @@ -173,8 +173,7 @@ class GalaxiesController extends AppController { $cluster_id = $this->request->data['Galaxy']['target_id']; $result = $this->Galaxy->attachCluster($this->Auth->user(), $target_type, $target_id, $cluster_id); - $this->Flash->info($result); - $this->redirect($this->referer()); + return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => $result, 'check_publish' => true)), 'status'=>200, 'type' => 'json')); } public function attachMultipleClusters($target_id, $target_type = 'event') @@ -204,4 +203,28 @@ class GalaxiesController extends AppController $this->set('galaxy_id', $cluster['Galaxy']['id']); $this->render('/Events/view_graph'); } + + public function showGalaxies($id, $scope = 'event') + { + $this->layout = 'ajax'; + $this->set('scope', $scope); + if ($scope == 'event') { + $this->loadModel('Event'); + $object = $this->Event->fetchEvent($this->Auth->user(), array('eventid' => $id, 'metadata' => 1)); + if (empty($object)) { + throw new MethodNotAllowedException('Invalid event.'); + } + $this->set('object', $object[0]); + + } elseif ($scope == 'attribute') { + $this->loadModel('Attribute'); + $object = $this->Attribute->fetchAttributes($this->Auth->user(), array('conditions' => array('Attribute.id' => $id))); + if (empty($object)) { + throw new MethodNotAllowedException('Invalid attribute.'); + } + $object[0] = $this->Attribute->Event->massageTags($object[0], 'Attribute'); + } + $this->set('object', $object[0]); + $this->render('/Events/ajax/ajaxGalaxies'); + } } diff --git a/app/Controller/ServersController.php b/app/Controller/ServersController.php index b71cb84aa..9bc216500 100644 --- a/app/Controller/ServersController.php +++ b/app/Controller/ServersController.php @@ -821,6 +821,11 @@ class ServersController extends AppController return $this->Server->loadAvailableLanguages(); } + private function __loadTagCollections() + { + return $this->Server->loadTagCollections($this->Auth->user()); + } + private function __loadLocalOrgs() { $this->loadModel('Organisation'); diff --git a/app/Controller/TagCollectionsController.php b/app/Controller/TagCollectionsController.php new file mode 100644 index 000000000..24bbefe30 --- /dev/null +++ b/app/Controller/TagCollectionsController.php @@ -0,0 +1,343 @@ + 60, + 'order' => array( + 'TagCollection.name' => 'ASC' + ), + 'recursive' => -1, + 'contain' => array('TagCollectionTag' => array('Tag'), 'User', 'Organisation') + ); + + public function add() + { + if ($this->request->is('post')) { + $this->TagCollection->create(); + if (!isset($this->request->data['TagCollection'])) { + $this->request->data = array('TagCollection' => $this->request->data); + } + $this->request->data['TagCollection']['org_id'] = $this->Auth->user('org_id'); + $this->request->data['TagCollection']['user_id'] = $this->Auth->user('id'); + if ($this->TagCollection->save($this->request->data)) { + if ($this->_isRest()) { + $tagCollection = $this->TagCollection->find('first', array( + 'recursive' => -1, + 'conditions' => array('TagCollection.id' => $this->TagCollection->id) + )); + return $this->RestResponse->viewData($tagCollection, $this->response->type()); + } else { + $this->Flash->success(__('The tag collection has been saved')); + $this->redirect(array('action' => 'index')); + } + } else { + $message = json_encode($this->TagCollection->validationErrors); + if ($this->_isRest()) { + return $this->RestResponse->saveFailResponse('TagCollection', 'add', false, $message, $this->response->type()); + } else { + $this->Flash->error(__('The tag collection could not be added. Reason: ') . $message); + } + } + } elseif ($this->_isRest()) { + return $this->RestResponse->describe('TagCollection', 'add', false, $this->response->type()); + } + $this->set('action', 'add'); + } + + public function view() + { + + } + + public function edit($id) + { + $this->TagCollection->id = $id; + if (!$this->TagCollection->exists()) { + throw new NotFoundException(__('Invalid Tag Collection')); + } + $tagCollection = $this->TagCollection->find('first', array( + 'conditions' => array('TagCollection.id' => $id), + 'recursive' => -1 + )); + if (!$this->_isSiteAdmin() && $tagCollection['TagCollection']['org_id'] !== $this->Auth->user('org_id')) { + throw new MethodNotAllowedException(__('You don\'t have editing rights on this Tag Collection.')); + } + if ($this->request->is('post') || $this->request->is('put')) { + if (!isset($this->request->data['TagCollection'])) { + $this->request->data = array('TagCollection' => $this->request->data); + } + $this->request->data['TagCollection']['id'] = $tagCollection['TagCollection']['id']; + $this->request->data['TagCollection']['uuid'] = $tagCollection['TagCollection']['uuid']; + if ($this->TagCollection->save($this->request->data)) { + if ($this->_isRest()) { + $tagCollection = $this->TagCollection->find('first', array( + 'recursive' => -1, + 'conditions' => array('TagCollection.id' => $this->TagCollection->id) + )); + return $this->RestResponse->viewData($tagCollection, $this->response->type()); + } else { + $this->Flash->success(__('The tag collection has been saved')); + $this->redirect(array('action' => 'index')); + } + } else { + $message = json_encode($this->TagCollection->validationErrors); + if ($this->_isRest()) { + return $this->RestResponse->saveFailResponse('TagCollection', 'add', false, $message, $this->response->type()); + } else { + $this->Flash->error(__('The tag collection could not be added. Reason: ') . $message); + } + } + } elseif ($this->_isRest()) { + return $this->RestResponse->describe('TagCollection', 'add', false, $this->response->type()); + } else { + $this->request->data = $tagCollection; + } + $this->set('action', 'edit'); + $this->render('add'); + } + + public function delete($id) + { + $tagCollection = $this->TagCollection->fetchTagCollection($this->Auth->user(), array('conditions' => array('TagCollection.id' => $id))); + if (empty($tagCollection)) { + throw new NotFoundException(__('Invalid tag collection.')); + } + $tagCollection = $tagCollection[0]; + if ($this->TagCollection->checkAccess($this->Auth->user(), $tagCollection, 'write')) { + $result = $this->TagCollection->delete($id); + if ($result) { + $message = __('Tag collection deleted.'); + if ($this->_isRest()) { + return $this->RestResponse->saveSuccessResponse('TagCollections', 'delete', false, $this->response->type(), $message); + } else { + $this->Flash->success($message); + $this->redirect('index'); + } + } else { + $message = __('Tag collection could not be deleted.'); + if ($this->_isRest()) { + return $this->RestResponse->saveFailResponse('TagCollections', 'delete', false, $message, $this->response->type()); + } else { + $this->Flash->error($message); + $this->redirect('index'); + } + } + } else { + throw new NotFoundException(__('You are not allowed to delete that.')); + } + } + + public function addTag($id = false, $tag_id = false) + { + if (!$this->request->is('post')) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'You don\'t have permission to do that.')), 'status'=>200, 'type' => 'json')); + } + $rearrangeRules = array( + 'request' => false, + 'TagCollection' => false, + 'tag_id' => 'tag', + 'tag_collection_id' => 'tag_collection', + 'id' => 'tag_collection' + ); + $RearrangeTool = new RequestRearrangeTool(); + $this->request->data = $RearrangeTool->rearrangeArray($this->request->data, $rearrangeRules); + if ($id === false) { + $id = $this->request->data['tag_collection']; + } + if ($tag_id === false) { + $tag_id = $this->request->data['tag']; + } + $conditions = array('LOWER(Tag.name) LIKE' => strtolower(trim($tag_id))); + if (!$this->_isSiteAdmin()) { + $conditions['Tag.org_id'] = array('0', $this->Auth->user('org_id')); + $conditions['Tag.user_id'] = array('0', $this->Auth->user('id')); + } + if (!is_numeric($tag_id)) { + $tag = $this->TagCollection->Tag->find('first', array('recursive' => -1, 'conditions' => $conditions)); + if (empty($tag)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag.')), 'status'=>200, 'type' => 'json')); + } + $tag_id = $tag['Tag']['id']; + } + $tagCollection = $this->TagCollection->find('first', array( + 'recursive' => -1, + 'conditions' => array('TagCollection.id' => $id) + )); + if (empty($tagCollection)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid tag collection.')), 'status'=>200, 'type' => 'json')); + } + if (!$this->_isSiteAdmin()) { + if (!$this->userRole['perm_tagger'] || ($this->Auth->user('org_id') !== $tag_collection['TagCollection']['org_id'])) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'You don\'t have permission to do that.')), 'status'=>200, 'type' => 'json')); + } + } + $this->TagCollection->TagCollectionTag->Tag->id = $tag_id; + if (!$this->TagCollection->TagCollectionTag->Tag->exists()) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Invalid Tag.')), 'status'=>200, 'type' => 'json')); + } + $tag = $this->TagCollection->TagCollectionTag->Tag->find('first', array( + 'conditions' => array('Tag.id' => $tag_id), + 'recursive' => -1, + 'fields' => array('Tag.name') + )); + $found = $this->TagCollection->TagCollectionTag->find('first', array( + 'conditions' => array( + 'tag_collection_id' => $id, + 'tag_id' => $tag_id + ), + 'recursive' => -1, + )); + $this->autoRender = false; + if (!empty($found)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Tag is already attached to this collection.')), 'status'=>200, 'type' => 'json')); + } + $this->TagCollection->TagCollectionTag->create(); + if ($this->TagCollection->TagCollectionTag->save(array('tag_collection_id' => $id, 'tag_id' => $tag_id))) { + $log = ClassRegistry::init('Log'); + $log->createLogEntry($this->Auth->user(), 'tag', 'TagCollection', $id, 'Attached tag (' . $tag_id . ') "' . $tag['Tag']['name'] . '" to collection (' . $id . ')', 'Event (' . $id . ') tagged as Tag (' . $tag_id . ')'); + return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Tag added.')), 'status'=>200, 'type' => 'json')); + } else { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Tag could not be added.')), 'status'=>200, 'type' => 'json')); + } + } + + public function removeTag($id = false, $tag_id = false) + { + if (!$this->request->is('post')) { + $this->set('id', $id); + $this->set('tag_id', $tag_id); + $this->set('model', 'tag_collection'); + $this->layout = 'ajax'; + $this->render('/Attributes/ajax/tagRemoveConfirmation'); + } else { + $rearrangeRules = array( + 'request' => false, + 'TagCollection' => false, + 'tag_id' => 'tag', + 'tag_collection_id' => 'tag_collection', + 'id' => 'tag_collection' + ); + $RearrangeTool = new RequestRearrangeTool(); + $this->request->data = $RearrangeTool->rearrangeArray($this->request->data, $rearrangeRules); + if ($id === false) { + $id = $this->request->data['tag_collection']; + } + if ($tag_id === false) { + $tag_id = $this->request->data['tag']; + } + $tagCollection = $this->TagCollection->find('first', array( + 'recursive' => -1, + 'conditions' => array( + 'TagCollection.id' => $id + ), + 'contain' => array( + 'TagCollectionTag' => array( + 'Tag' + ) + ) + )); + if (empty($tagCollection)) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => __('Invalid tag collection.'))), 'status' => 200, 'type' => 'json')); + } + $found = false; + if (!$this->_isSiteAdmin() && $this->Auth->user('org_id') !== $tagCollection['TagCollection']['org_id']) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => __('Insufficient privileges to remove the tag from the collection.'))), 'status' => 200, 'type' => 'json')); + } + foreach ($tagCollection['TagCollectionTag'] as $TagCollectionTag) { + if ((is_numeric($tag_id) && $TagCollectionTag['Tag']['id'] == $tag_id) || $TagCollectionTag['Tag']['name'] === $tag_id) { + $found = true; + $tag = $TagCollectionTag; + $result = $this->TagCollection->TagCollectionTag->delete($TagCollectionTag['id']); + break; + } + } + if (!$found) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => __('Invalid tag or tag not associated with the collection.'))), 'status' => 200, 'type' => 'json')); + } + + if (!$result) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => __('Failed to remove tag from the collection.'))), 'status' => 200, 'type' => 'json')); + } + $log = ClassRegistry::init('Log'); + $log->createLogEntry($this->Auth->user(), 'tag', 'TagCollection', $id, 'Removed tag (' . $tag['Tag']['id'] . ') "' . $tag['Tag']['name'] . '" from tag collection (' . $id . ')', 'Tag collection (' . $id . ') - untagged Tag (' . $tag_id . ')'); + return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Tag removed.')), 'status' => 200)); + } + } + + public function index() + { + //$this->Auth->user('Role')['perm_site_admin']); + $conditions = array(); + if (!$this->_isSiteAdmin()) { + $conditions = array( + 'OR' => array( + 'TagCollection.all_orgs' => 1, + 'TagCollection.org_id' => $this->Auth->user('org_id') + ) + ); + $this->paginate['conditions'] = $conditions; + } + if ($this->_isRest()) { + $params = array( + 'recursive' => -1, + 'contain' => array('TagCollectionTag' => array('Tag'), 'Organisation', 'User') + ); + if (!empty($conditions)) { + $params['conditions'] = $conditions; + } + $namedParams = array('limit', 'page'); + foreach ($namedParams as $namedParam) { + if (!empty($this->params['named'][$namedParam])) { + $params['limit'] = $this->params['named'][$namedParam]; + } + } + $list = $this->TagCollection->find('all', $params); + } else { + $list = $this->paginate(); + } + $this->loadModel('Event'); + foreach ($list as $k => $tag_collection) { + $list[$k] = $this->TagCollection->cullBlockedTags($this->Auth->user(), $tag_collection); + $list[$k] = $this->Event->massageTags($list[$k], 'TagCollection', false, true); + if (!$this->_isSiteAdmin() && $list[$k]['TagCollection']['org_id'] !== $this->Auth->user('org_id')) { + unset($list[$k]['User']); + unset($list[$k]['TagCollection']['user_id']); + } + } + if ($this->_isRest()) { + return $this->RestResponse->viewData($list, $this->response->type()); + } else { + $this->set('list', $list); + } + } + + public function getRow($id) + { + $params = array( + 'recursive' => -1, + 'contain' => array('TagCollectionTag' => array('Tag'), 'User', 'Organisation'), + 'conditions' => array('TagCollection.id' => $id) + ); + $item = $this->TagCollection->find('first', $params); + if (empty($item)) { + throw new NotFoundException('Invalid tag collection.'); + } + if (!$this->_isSiteAdmin() && $item['TagCollection']['org_id'] !== $this->Auth->user('org_id')) { + unset($item['User']); + unset($item['TagCollection']['user_id']); + } + $this->loadModel('Event'); + $item = $this->Event->massageTags($item, 'TagCollection', false, true); + $this->layout = false; + $this->set('item', $item); + } +} diff --git a/app/Controller/TagsController.php b/app/Controller/TagsController.php index 5508a9e13..ba5424a8f 100644 --- a/app/Controller/TagsController.php +++ b/app/Controller/TagsController.php @@ -519,6 +519,39 @@ class TagsController extends AppController $this->render('/Attributes/ajax/ajaxAttributeTags'); } + public function showTagControllerTag($id) + { + $this->loadModel('TagCollection'); + $tagCollection = $this->TagCollection->find('first', array( + 'recursive' => -1, + 'contain' => array('TagCollection'), + 'conditions' => array('TagCollection.id' => $id) + )); + if (empty($tagCollection) || (!$this->_isSiteAdmin() && $tagCollection['org_id'] !== $this->Auth->user('org_id'))) { + throw new MethodNotAllowedException('Invalid tag_collection.'); + } + $this->loadModel('GalaxyCluster'); + $cluster_names = $this->GalaxyCluster->find('list', array('fields' => array('GalaxyCluster.tag_name'), 'group' => array('GalaxyCluster.id', 'GalaxyCluster.tag_name'))); + $this->helpers[] = 'TextColour'; + $tags = $this->TagCollection->TagCollectionTag->find('all', array( + 'conditions' => array( + 'tag_collection_id' => $id, + 'Tag.name !=' => $cluster_names + ), + 'contain' => array('Tag'), + 'fields' => array('Tag.id', 'Tag.colour', 'Tag.name'), + )); + $this->set('tags', $tags); + $event = $this->Tag->EventTag->Event->find('first', array( + 'recursive' => -1, + 'fields' => array('Event.id', 'Event.orgc_id', 'Event.org_id', 'Event.user_id'), + 'conditions' => array('Event.id' => $id) + )); + $this->set('event', $event); + $this->layout = 'ajax'; + $this->render('/Events/ajax/ajaxTags'); + } + public function viewTag($id) { $tag = $this->Tag->find('first', array( @@ -534,7 +567,7 @@ class TagsController extends AppController } - public function selectTaxonomy($id, $attributeTag = false) + public function selectTaxonomy($id, $scope = 'event') { if (!$this->_isSiteAdmin() && !$this->userRole['perm_tagger']) { throw new NotFoundException('You don\'t have permission to do that.'); @@ -548,96 +581,126 @@ class TagsController extends AppController unset($options[$k]); } } - if ($attributeTag !== false) { - $this->set('attributeTag', true); - } + $this->set('scope', $scope); $this->set('object_id', $id); $this->set('options', $options); $this->set('favourites', $favourites); $this->render('ajax/taxonomy_choice'); } - public function selectTag($id, $taxonomy_id, $attributeTag = false, $filterData = '') + public function selectTag($id, $taxonomy_id, $scope = 'event', $filterData = '') { if (!$this->_isSiteAdmin() && !$this->userRole['perm_tagger']) { throw new NotFoundException('You don\'t have permission to do that.'); } $this->loadModel('Taxonomy'); $expanded = array(); - if ($taxonomy_id === '0') { - $options = $this->Taxonomy->getAllTaxonomyTags(true); - $expanded = $options; - } elseif ($taxonomy_id === 'favourites') { - $conditions = array('FavouriteTag.user_id' => $this->Auth->user('id')); - $tags = $this->Tag->FavouriteTag->find('all', array( - 'conditions' => $conditions, - 'recursive' => -1, - 'contain' => array('Tag.name') - )); - foreach ($tags as $tag) { - $options[$tag['FavouriteTag']['tag_id']] = $tag['Tag']['name']; - $expanded = $options; - } - } elseif ($taxonomy_id === 'all') { - $conditions = array('Tag.org_id' => array(0, $this->Auth->user('org_id'))); - $conditions = array('Tag.user_id' => array(0, $this->Auth->user('id'))); - $conditions['Tag.hide_tag'] = 0; - $options = $this->Tag->find('list', array('fields' => array('Tag.name'), 'conditions' => $conditions)); - $expanded = $options; - } else { - $taxonomies = $this->Taxonomy->getTaxonomy($taxonomy_id); - $options = array(); - foreach ($taxonomies['entries'] as $entry) { - if (!empty($entry['existing_tag']['Tag'])) { - $options[$entry['existing_tag']['Tag']['id']] = $entry['existing_tag']['Tag']['name']; - $expanded[$entry['existing_tag']['Tag']['id']] = $entry['expanded']; - } - } - } - // Unset all tags that this user cannot use for tagging, determined by the org restriction on tags - if (!$this->_isSiteAdmin()) { - $banned_tags = $this->Tag->find('list', array( - 'conditions' => array( - 'NOT' => array( - 'Tag.org_id' => array( - 0, - $this->Auth->user('org_id') - ), - 'Tag.user_id' => array( - 0, - $this->Auth->user('id') - ) - ) - ), - 'fields' => array('Tag.id') - )); - foreach ($banned_tags as $banned_tag) { - unset($options[$banned_tag]); - unset($expanded[$banned_tag]); - } - } - $hidden_tags = $this->Tag->find('list', array( - 'conditions' => array('Tag.hide_tag' => 1), + $banned_tags = $this->Tag->find('list', array( + 'conditions' => array( + 'NOT' => array( + 'Tag.org_id' => array( + 0, + $this->Auth->user('org_id') + ), + 'Tag.user_id' => array( + 0, + $this->Auth->user('id') + ) + ) + ), 'fields' => array('Tag.id') )); - foreach ($hidden_tags as $hidden_tag) { - unset($options[$hidden_tag]); - unset($expanded[$hidden_tag]); - } - if ($attributeTag !== false && $attributeTag !== "false") { - $this->set('attributeTag', true); - } - $this->set('object_id', $id); - foreach ($options as $k => $v) { - if (substr($v, 0, strlen('misp-galaxy:')) === 'misp-galaxy:') { - unset($options[$k]); + $this->set('taxonomy_id', $taxonomy_id); + if ($taxonomy_id === 'collections') { + $this->loadModel('TagCollection'); + $tagCollections = $this->TagCollection->fetchTagCollection($this->Auth->user()); + $options = array(); + $expanded = array(); + foreach ($tagCollections as &$tagCollection) { + $options[$tagCollection['TagCollection']['id']] = $tagCollection['TagCollection']['name']; + $expanded[$tagCollection['TagCollection']['id']] = empty($tagCollection['TagCollection']['description']) ? $tagCollection['TagCollection']['name'] : $tagCollection['TagCollection']['description']; + if (!empty($tagCollection['TagCollectionTag'])) { + $tagList = array(); + foreach ($tagCollection['TagCollectionTag'] as $k => $tce) { + if (in_array($tce['tag_id'], $banned_tags)) { + unset($tagCollection['TagCollectionTag'][$k]); + } else { + $tagList[] = $tce['Tag']['name']; + } + $tagCollection['TagCollectionTag'] = array_values($tagCollection['TagCollectionTag']); + } + $tagList = implode(', ', $tagList); + $expanded[$tagCollection['TagCollection']['id']] .= sprintf(' (%s)', $tagList); + } } + $this->set('scope', $scope); + $this->set('object_id', $id); + $this->set('options', $options); + $this->set('expanded', $expanded); + $this->set('custom', $taxonomy_id == 0 ? true : false); + $this->set('filterData', $filterData); + $this->render('ajax/select_tag'); + } else { + if ($taxonomy_id === '0') { + $options = $this->Taxonomy->getAllTaxonomyTags(true); + $expanded = $options; + } elseif ($taxonomy_id === 'favourites') { + $conditions = array('FavouriteTag.user_id' => $this->Auth->user('id')); + $tags = $this->Tag->FavouriteTag->find('all', array( + 'conditions' => $conditions, + 'recursive' => -1, + 'contain' => array('Tag.name') + )); + foreach ($tags as $tag) { + $options[$tag['FavouriteTag']['tag_id']] = $tag['Tag']['name']; + $expanded = $options; + } + } elseif ($taxonomy_id === 'all') { + $conditions = array('Tag.org_id' => array(0, $this->Auth->user('org_id'))); + $conditions = array('Tag.user_id' => array(0, $this->Auth->user('id'))); + $conditions['Tag.hide_tag'] = 0; + $options = $this->Tag->find('list', array('fields' => array('Tag.name'), 'conditions' => $conditions)); + $expanded = $options; + } else { + $taxonomies = $this->Taxonomy->getTaxonomy($taxonomy_id); + $options = array(); + if (!empty($taxonomies['entries'])) { + foreach ($taxonomies['entries'] as $entry) { + if (!empty($entry['existing_tag']['Tag'])) { + $options[$entry['existing_tag']['Tag']['id']] = $entry['existing_tag']['Tag']['name']; + $expanded[$entry['existing_tag']['Tag']['id']] = $entry['expanded']; + } + } + } + } + // Unset all tags that this user cannot use for tagging, determined by the org restriction on tags + if (!$this->_isSiteAdmin()) { + foreach ($banned_tags as $banned_tag) { + unset($options[$banned_tag]); + unset($expanded[$banned_tag]); + } + } + $hidden_tags = $this->Tag->find('list', array( + 'conditions' => array('Tag.hide_tag' => 1), + 'fields' => array('Tag.id') + )); + foreach ($hidden_tags as $hidden_tag) { + unset($options[$hidden_tag]); + unset($expanded[$hidden_tag]); + } + $this->set('scope', $scope); + $this->set('object_id', $id); + foreach ($options as $k => $v) { + if (substr($v, 0, strlen('misp-galaxy:')) === 'misp-galaxy:') { + unset($options[$k]); + } + } + $this->set('options', $options); + $this->set('expanded', $expanded); + $this->set('custom', $taxonomy_id == 0 ? true : false); + $this->set('filterData', $filterData); + $this->render('ajax/select_tag'); } - $this->set('options', $options); - $this->set('expanded', $expanded); - $this->set('custom', $taxonomy_id == 0 ? true : false); - $this->set('filterData', $filterData); - $this->render('ajax/select_tag'); } public function tagStatistics($percentage = false, $keysort = false) diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 9ab912f74..9aeb3cf23 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -71,7 +71,8 @@ class AppModel extends Model 1 => false, 2 => false, 3 => false, 4 => true, 5 => false, 6 => false, 7 => false, 8 => false, 9 => false, 10 => false, 11 => false, 12 => false, 13 => false, 14 => false, 15 => false, 18 => false, 19 => false, 20 => false, - 21 => false, 22 => false, 23 => false, 24 => false, 25 => false + 21 => false, 22 => false, 23 => false, 24 => false, 25 => false, 26 => false, + 27 => false ); public function afterSave($created, $options = array()) @@ -1059,6 +1060,32 @@ class AppModel extends Model $this->__addIndex('galaxy_clusters', 'uuid'); $this->__addIndex('galaxy_clusters', 'collection_uuid'); break; + case 26: + $sqlArray[] = "CREATE TABLE IF NOT EXISTS tag_collections ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `user_id` int(11) NOT NULL, + `org_id` int(11) NOT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `description` TEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, + `all_orgs` tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY (id), + INDEX `uuid` (`uuid`), + INDEX `user_id` (`user_id`), + INDEX `org_id` (`org_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; + $sqlArray[] = "CREATE TABLE IF NOT EXISTS tag_collection_tags ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `tag_collection_id` int(11) NOT NULL, + `tag_id` int(11) NOT NULL, + PRIMARY KEY (id), + INDEX `uuid` (`tag_collection_id`), + INDEX `user_id` (`tag_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; + break; + case 27: + $sqlArray[] = 'ALTER TABLE `tags` CHANGE `org_id` `org_id` int(11) NOT NULL DEFAULT 0;'; + break; case 'fixNonEmptySharingGroupID': $sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;'; $sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;'; diff --git a/app/Model/Event.php b/app/Model/Event.php index 1a90abfd4..5730b70f6 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -3288,6 +3288,39 @@ class Event extends AppModel } $this->publish($this->getID(), $passAlong); } + if (empty($data['Event']['locked']) && !empty(Configure::read('MISP.default_event_tag_collection'))) { + $this->TagCollection = ClassRegistry::init('TagCollection'); + $tagCollection = $this->TagCollection->fetchTagCollection($user, array('conditions' => array('TagCollection.id' => Configure::read('MISP.default_event_tag_collection')))); + if (!empty($tagCollection)) { + $tag_id_list = array(); + foreach ($tagCollection[0]['TagCollectionTag'] as $tagCollectionTag) { + $tag_id_list[] = $tagCollectionTag['tag_id']; + } + foreach ($tag_id_list as $tag_id) { + $tag = $this->EventTag->Tag->find('first', array( + 'conditions' => array('Tag.id' => $tag_id), + 'recursive' => -1, + 'fields' => array('Tag.name') + )); + if (!empty($tag)) { + $found = $this->EventTag->find('first', array( + 'conditions' => array( + 'event_id' => $this->id, + 'tag_id' => $tag_id + ), + 'recursive' => -1, + )); + if (empty($found)) { + $this->EventTag->create(); + if ($this->EventTag->save(array('event_id' => $this->id, 'tag_id' => $tag_id))) { + $log = ClassRegistry::init('Log'); + $log->createLogEntry($user, 'tag', 'Event', $this->id, 'Attached tag (' . $tag_id . ') "' . $tag['Tag']['name'] . '" to event (' . $this->id . ')', 'Event (' . $this->id . ') tagged as Tag (' . $tag_id . ')'); + } + } + } + } + } + } return true; } else { $validationErrors['Event'] = $this->validationErrors; @@ -5244,7 +5277,7 @@ class Event extends AppModel return $attributes_added; } - public function massageTags($data, $dataType = 'Event', $excludeGalaxy = false) + public function massageTags($data, $dataType = 'Event', $excludeGalaxy = false, $cullGalaxyTags = false) { $data['Galaxy'] = array(); if (empty($this->GalaxyCluster)) { @@ -5277,6 +5310,7 @@ class Event extends AppModel unset($temp['GalaxyCluster']['Galaxy']); $data['Galaxy'][count($data['Galaxy']) - 1]['GalaxyCluster'][] = $temp['GalaxyCluster']; } + unset($data[$dataType . 'Tag'][$k]); } } } diff --git a/app/Model/Galaxy.php b/app/Model/Galaxy.php index 82adc6be3..39d763a79 100644 --- a/app/Model/Galaxy.php +++ b/app/Model/Galaxy.php @@ -179,55 +179,54 @@ class Galaxy extends AppModel return true; } + private function __attachClusterToEvent($user, $target_id, $cluster_id) { + + } + public function attachCluster($user, $target_type, $target_id, $cluster_id) { + $connectorModel = Inflector::camelize($target_type) . 'Tag'; $cluster = $this->GalaxyCluster->find('first', array('recursive' => -1, 'conditions' => array('id' => $cluster_id), 'fields' => array('tag_name', 'id', 'value'))); $this->Tag = ClassRegistry::init('Tag'); - if ($target_type == 'event') { - $event = $this->Tag->EventTag->Event->fetchEvent($user, array('eventid' => $target_id, 'metadata' => 1)); - if (empty($event)) { - throw new NotFoundException('Invalid event.'); - } - $event = $event[0]; - $tag_id = $this->Tag->captureTag(array('name' => $cluster['GalaxyCluster']['tag_name'], 'colour' => '#0088cc', 'exportable' => 1), $user); - if ($tag_id === false) { - throw new MethodNotAllowedException('Could not attach cluster.'); - } - $this->Tag->EventTag->create(); - $existingTag = $this->Tag->EventTag->find('first', array('conditions' => array('event_id' => $target_id, 'tag_id' => $tag_id))); - if (!empty($existingTag)) { - return 'Cluster already attached.'; - } - $result = $this->Tag->EventTag->save(array('event_id' => $target_id, 'tag_id' => $tag_id)); - } elseif ($target_type == 'attribute') { - $attribute = $this->Tag->AttributeTag->Attribute->fetchAttributes($user, array('conditions' => array('Attribute.id' => $target_id), 'flatten' => 1)); - if (empty($attribute)) { - throw new NotFoundException('Invalid attribute.'); - } - $attribute = $attribute[0]; - $tag_id = $this->Tag->captureTag(array('name' => $cluster['GalaxyCluster']['tag_name'], 'colour' => '#0088cc', 'exportable' => 1), $user); - if ($tag_id === false) { - throw new MethodNotAllowedException('Could not attach cluster.'); - } - $this->Tag->AttributeTag->create(); - $existingTag = $this->Tag->AttributeTag->find('first', array('conditions' => array('attribute_id' => $target_id, 'tag_id' => $tag_id))); - if (!empty($existingTag)) { - return 'Cluster already attached.'; - } + if ($target_type === 'event') { + $target = $this->Tag->EventTag->Event->fetchEvent($user, array('eventid' => $target_id, 'metadata' => 1)); + } elseif ($target_type === 'attribute') { + $target = $this->Tag->AttributeTag->Attribute->fetchAttributes($user, array('conditions' => array('Attribute.id' => $target_id), 'flatten' => 1)); + } elseif ($target_type === 'tag_collection') { + $target = $this->Tag->TagCollectionTag->TagCollection->fetchTagCollection($user, array('conditions' => array('TagCollection.id' => $target_id))); + } + if (empty($target)) { + throw new NotFoundException(__('Invalid %s.', $target_type)); + } + $target = $target[0]; + $tag_id = $this->Tag->captureTag(array('name' => $cluster['GalaxyCluster']['tag_name'], 'colour' => '#0088cc', 'exportable' => 1), $user); + $existingTag = $this->Tag->$connectorModel->find('first', array('conditions' => array($target_type . '_id' => $target_id, 'tag_id' => $tag_id))); + if (!empty($existingTag)) { + return 'Cluster already attached.'; + } + $this->Tag->$connectorModel->create(); + $toSave = array($target_type . '_id' => $target_id, 'tag_id' => $tag_id); + if ($target_type === 'attribute') { $event = $this->Tag->EventTag->Event->find('first', array( 'conditions' => array( - 'Event.id' => $attribute['Attribute']['event_id'] + 'Event.id' => $target['Attribute']['event_id'] ), 'recursive' => -1 )); - $result = $this->Tag->AttributeTag->save(array('attribute_id' => $target_id, 'tag_id' => $tag_id, 'event_id' => $attribute['Attribute']['event_id'])); + $toSave['event_id'] = $target['Attribute']['event_id']; } + $result = $this->Tag->$connectorModel->save($toSave); if ($result) { - $this->Tag->EventTag->Event->insertLock($user, $event['Event']['id']); - $event['Event']['published'] = 0; - $date = new DateTime(); - $event['Event']['timestamp'] = $date->getTimestamp(); - $this->Tag->EventTag->Event->save($event); + if ($target_type !== 'tag_collection') { + if ($target_type === 'event') { + $event = $target; + } + $this->Tag->EventTag->Event->insertLock($user, $event['Event']['id']); + $event['Event']['published'] = 0; + $date = new DateTime(); + $event['Event']['timestamp'] = $date->getTimestamp(); + $this->Tag->EventTag->Event->save($event); + } $this->Log = ClassRegistry::init('Log'); $this->Log->create(); $this->Log->save(array( diff --git a/app/Model/Server.php b/app/Model/Server.php index 38866b66f..82ed522c9 100644 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -522,6 +522,15 @@ class Server extends AppModel 'type' => 'string', 'options' => array('1' => 'High', '2' => 'Medium', '3' => 'Low', '4' => 'undefined'), ), + 'default_event_tag_collection' => array( + 'level' => 0, + 'description' => __('The tag collection to be applied to all events created manually.'), + 'value' => 0, + 'errorMessage' => '', + 'test' => 'testTagCollections', + 'type' => 'numeric', + 'optionsSource' => 'TagCollections', + ), 'tagging' => array( 'level' => 1, 'description' => __('Enable the tagging feature of MISP. This is highly recommended.'), @@ -2643,6 +2652,27 @@ class Server extends AppModel return true; } + public function loadTagCollections() + { + $this->TagCollection = ClassRegistry::init('TagCollection'); + $user = array('Role' => array('perm_site_admin' => 1)); + $tagCollections = $this->TagCollection->fetchTagCollection($user); + $options = array(0 => 'None'); + foreach ($tagCollections as $tagCollection) { + $options[intval($tagCollection['TagCollection']['id'])] = $tagCollection['TagCollection']['name']; + } + return $options; + } + + public function testTagCollections($value) + { + $tag_collections = $this->loadTagCollections(); + if (!isset($tag_collections[intval($value)])) { + return 'Invalid tag_collection.'; + } + return true; + } + public function testForNumeric($value) { if (!is_numeric($value)) { @@ -3161,6 +3191,23 @@ class Server extends AppModel public function serverSettingsSaveValue($setting, $value) { + // validate if current config.php is intact: + $current = file_get_contents(APP . 'Config' . DS . 'config.php'); + $current = trim($current); + if (strlen($current) < 20) { + $this->Log = ClassRegistry::init('Log'); + $this->Log->create(); + $this->Log->save(array( + 'org' => 'SYSTEM', + 'model' => 'Server', + 'model_id' => $id, + 'email' => 'SYSTEM', + 'action' => 'error', + 'user_id' => 0, + 'title' => 'Error: Tried to modify server settings but current config is broken.', + )); + return false; + } copy(APP . 'Config' . DS . 'config.php', APP . 'Config' . DS . 'config.php.bk'); $settingObject = $this->getCurrentServerSettings(); foreach ($settingObject as $branchName => $branch) { @@ -3215,6 +3262,17 @@ class Server extends AppModel // if the saved config file is empty, restore the backup. if (strlen($config_saved) < 20) { copy(APP . 'Config' . DS . 'config.php.bk', APP . 'Config' . DS . 'config.php'); + $this->Log = ClassRegistry::init('Log'); + $this->Log->create(); + $this->Log->save(array( + 'org' => 'SYSTEM', + 'model' => 'Server', + 'model_id' => $id, + 'email' => 'SYSTEM', + 'action' => 'error', + 'user_id' => 0, + 'title' => 'Error: Something went wrong saving the config file, reverted to backup file.', + )); return false; } return true; diff --git a/app/Model/Tag.php b/app/Model/Tag.php index dc9fbac0b..5b63e0f76 100644 --- a/app/Model/Tag.php +++ b/app/Model/Tag.php @@ -52,6 +52,9 @@ class Tag extends AppModel ), 'AttributeTag' => array( 'dependent' => true + ), + 'TagCollectionTag' => array( + 'dependent' => true ) ); diff --git a/app/Model/TagCollection.php b/app/Model/TagCollection.php new file mode 100644 index 000000000..56fdb5890 --- /dev/null +++ b/app/Model/TagCollection.php @@ -0,0 +1,122 @@ + array( + 'roleModel' => 'Role', + 'roleKey' => 'role_id', + 'change' => 'full' + ), + 'Containable' + ); + + public $hasMany = array( + 'TagCollectionTag' => array( + 'dependent' => true + ) + ); + + public $belongsTo = array( + 'User', + 'Organisation' => array( + 'foreignKey' => 'org_id' + ) + ); + + public $whitelistedItems = false; + + public $validate = array( + 'name' => array( + 'valueNotEmpty' => array( + 'rule' => array('valueNotEmpty'), + ), + 'unique' => array( + 'rule' => 'isUnique', + 'message' => 'A similar name already exists.', + ), + ) + ); + + public function beforeValidate($options = array()) + { + parent::beforeValidate(); + // generate UUID if it doesn't exist + if (empty($this->data['TagCollection']['uuid'])) { + $this->data['TagCollection']['uuid'] = CakeText::uuid(); + } + return true; + } + + public function fetchTagCollection($user, $params = array()) + { + if (empty($user['Role']['perm_site_admin'])) { + $params['conditions']['AND'][] = array( + 'OR' => array( + 'TagCollection.org_id' => $user['org_id'], + 'TagCollection.all_orgs' => 1 + ) + ); + } + if (empty($params['contain'])) { + $params['contain'] = array( + 'Organisation', + 'User', + 'TagCollectionTag' => array('Tag') + ); + } + $tagCollections = $this->find('all', $params); + $tagCollections = $this->cullBlockedTags($user, $tagCollections); + return $tagCollections; + } + + public function checkAccess($user, $tagCollection, $accessLevel = 'read') + { + if (isset($tagCollection['TagCollection'])) { + $tagCollection = $tagCollection['TagCollection']; + } + if (!empty($user['Role']['admin'])) { + return true; + } + if (!$tagCollection['all_orgs'] && $user['org_id'] != $tagCollection['org_id']) { + return false; + } + if ($accessLevel === 'write') { + if ($tagCollection['org_id'] !== $user['org_id']) { + return false; + } + } + return true; + } + + public function cullBlockedTags($user, $tagCollections) + { + $single = false; + if (!isset($tagCollections[0])) { + $tagCollections = array(0 => $tagCollections); + $single = true; + } + if (empty($user['Role']['perm_site_admin'])) { + foreach ($tagCollections as $k => $tagCollection) { + foreach ($tagCollection['TagCollectionTag'] as $k2 => $tagCollectionTag) { + if ( + (!empty($tagCollectionTag['Tag']['org_id']) && $tagCollectionTag['Tag']['org_id'] != $user['org_id']) || + (!empty($tagCollectionTag['Tag']['user_id']) && $tagCollectionTag['Tag']['user_id'] != $user['id']) || + $tagCollectionTag['Tag']['hide_tag'] + ) { + unset($tagCollections[$k]['TagCollectionTag'][$k2]); + } + } + $tagCollections[$k]['TagCollectionTag'] = array_values($tagCollections[$k]['TagCollectionTag']); + } + } + return $single ? $tagCollections[0] : $tagCollections; + } +} diff --git a/app/Model/TagCollectionTag.php b/app/Model/TagCollectionTag.php new file mode 100644 index 000000000..fde336c09 --- /dev/null +++ b/app/Model/TagCollectionTag.php @@ -0,0 +1,31 @@ + array( + 'roleModel' => 'Role', + 'roleKey' => 'role_id', + 'change' => 'full' + ), + 'Containable' + ); + + public $belongsTo = array( + 'TagCollection' => array( + 'className' => 'TagCollection', + ), + 'Tag' => array( + 'className' => 'Tag', + ) + ); + + public $validate = array( + + ); +} diff --git a/app/View/Elements/Events/View/row_attribute.ctp b/app/View/Elements/Events/View/row_attribute.ctp index cae6a8f23..8fe7426e7 100644 --- a/app/View/Elements/Events/View/row_attribute.ctp +++ b/app/View/Elements/Events/View/row_attribute.ctp @@ -163,7 +163,7 @@ ); } ?> -
%s
', Inflector::humanize($description)); + } else { + $description = ''; + } + $pagination = sprintf( + '%s
', + $this->Paginator->counter( + array( + 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') + ) + ) + ); + echo sprintf( + '%s
', Inflector::humanize($description)); + } else { + $description = ''; + } + $pagination = sprintf( + '%s
', + $this->Paginator->counter( + array( + 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') + ) + ) + ); + echo sprintf( + '- - | - + &$option): + $choice_id = $k; + if ($taxonomy_id === 'collections') { + $choice_id = 'collection_' . $choice_id; + } + ?> + + |
- |
- |
- |
: | -%s | ', + h($object_id), + h($scope), + __('Favourite Tags') + ) + ); + } + if ($scope !== 'tag_collection') { + echo sprintf( + '
%s | ', + h($object_id), + h($scope), + __('Tag Collections') + ) + ); + } + echo sprintf( + '
%s | ', + h($object_id), + h($scope), + __('Custom Tags') + ) + ); + echo sprintf( + '
%s | ', + h($object_id), + h($scope), + __('All Tags') + ) + ); + foreach ($options as $k => &$option) { + echo sprintf( + '
%s: %s | ', + h($object_id), + h($k), + h($scope), + __('Taxonomy Library'), + h($option) + ) + ); + } + ?>