From ecf52d5cb8d3ec2067d618c00db671cbf7545b5f Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 14 Dec 2018 20:25:38 +0100 Subject: [PATCH 01/31] new: [Tag collections] Added boilerplate models --- app/Model/TagCollection.php | 33 ++++++++++++++++++++++++++++++ app/Model/TagCollectionElement.php | 25 ++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 app/Model/TagCollection.php create mode 100644 app/Model/TagCollectionElement.php diff --git a/app/Model/TagCollection.php b/app/Model/TagCollection.php new file mode 100644 index 000000000..805e68851 --- /dev/null +++ b/app/Model/TagCollection.php @@ -0,0 +1,33 @@ + array( + 'roleModel' => 'Role', + 'roleKey' => 'role_id', + 'change' => 'full' + ) + ); + + public $whitelistedItems = false; + + public $validate = array( + 'name' => array( + 'valueNotEmpty' => array( + 'rule' => array('valueNotEmpty'), + ), + 'unique' => array( + 'rule' => 'isUnique', + 'message' => 'A similar name already exists.', + ), + ) + ); +} diff --git a/app/Model/TagCollectionElement.php b/app/Model/TagCollectionElement.php new file mode 100644 index 000000000..61a4babeb --- /dev/null +++ b/app/Model/TagCollectionElement.php @@ -0,0 +1,25 @@ + array( + 'roleModel' => 'Role', + 'roleKey' => 'role_id', + 'change' => 'full' + ) + ); + + public $whitelistedItems = false; + + public $validate = array( + + ); +} From 54bb5f3d01832478386c4b85efdec70609fab0ab Mon Sep 17 00:00:00 2001 From: iglocska Date: Sat, 15 Dec 2018 10:02:38 +0100 Subject: [PATCH 02/31] new: [tag_collections] Added db upgrade --- app/Model/AppModel.php | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 9ab912f74..3fb6664ae 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -71,7 +71,7 @@ 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 ); public function afterSave($created, $options = array()) @@ -1059,6 +1059,29 @@ 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_elements ( + `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 'fixNonEmptySharingGroupID': $sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;'; $sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;'; From 3ba8c360bf8a31ed45800575cdb200d3828bd9d2 Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 20 Dec 2018 21:48:04 +0100 Subject: [PATCH 03/31] new: [WIP] tag collections WIP --- app/Controller/TagsController.php | 55 ++++++++++---- app/Model/TagCollection.php | 19 ++++- app/Model/TagCollectionElement.php | 16 +++-- .../Elements/TagCollections/index_row.ctp | 42 +++++++++++ app/View/Elements/ajaxAttributeTags.ctp | 2 +- app/View/Elements/ajaxTagCollectionTags.ctp | 36 ++++++++++ app/View/Elements/generic_table.ctp | 59 +++++++++++++++ app/View/Elements/generic_table_row.ctp | 59 +++++++++++++++ app/View/Elements/global_menu.ctp | 1 + app/View/Elements/side_menu.ctp | 19 +++++ app/View/Events/json/index.ctp | 11 +-- app/View/Tags/ajax/select_tag.ctp | 27 ++++--- app/View/Tags/ajax/taxonomy_choice.ctp | 72 ++++++++++++++----- app/webroot/js/misp.js | 38 ++++++++++ 14 files changed, 403 insertions(+), 53 deletions(-) create mode 100644 app/View/Elements/TagCollections/index_row.ctp create mode 100644 app/View/Elements/ajaxTagCollectionTags.ctp create mode 100644 app/View/Elements/generic_table.ctp create mode 100644 app/View/Elements/generic_table_row.ctp diff --git a/app/Controller/TagsController.php b/app/Controller/TagsController.php index 5508a9e13..a2e7c9562 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->TagCollectionElement->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,16 +581,14 @@ 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.'); @@ -587,10 +618,12 @@ class TagsController extends AppController } 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']; + 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']; + } } } } @@ -624,9 +657,7 @@ class TagsController extends AppController unset($options[$hidden_tag]); unset($expanded[$hidden_tag]); } - if ($attributeTag !== false && $attributeTag !== "false") { - $this->set('attributeTag', true); - } + $this->set('scope', $scope); $this->set('object_id', $id); foreach ($options as $k => $v) { if (substr($v, 0, strlen('misp-galaxy:')) === 'misp-galaxy:') { diff --git a/app/Model/TagCollection.php b/app/Model/TagCollection.php index 805e68851..73b054bcb 100644 --- a/app/Model/TagCollection.php +++ b/app/Model/TagCollection.php @@ -14,7 +14,14 @@ class TagCollection extends AppModel 'roleModel' => 'Role', 'roleKey' => 'role_id', 'change' => 'full' - ) + ), + 'Containable' + ); + + public $hasMany = array( + 'TagCollectionElement' => array( + 'dependent' => true + ) ); public $whitelistedItems = false; @@ -30,4 +37,14 @@ class TagCollection extends AppModel ), ) ); + + 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; + } } diff --git a/app/Model/TagCollectionElement.php b/app/Model/TagCollectionElement.php index 61a4babeb..584460b84 100644 --- a/app/Model/TagCollectionElement.php +++ b/app/Model/TagCollectionElement.php @@ -6,20 +6,26 @@ class TagCollectionElement extends AppModel { public $useTable = 'tag_collection_elements'; - public $displayField = 'name'; - public $actsAs = array( 'Trim', 'SysLogLogable.SysLogLogable' => array( 'roleModel' => 'Role', 'roleKey' => 'role_id', 'change' => 'full' - ) + ), + 'Containable' ); - public $whitelistedItems = false; + public $belongsTo = array( + 'TagCollection' => array( + 'className' => 'TagCollection', + ), + 'Tag' => array( + 'className' => 'Tag', + ) + ); public $validate = array( - + ); } diff --git a/app/View/Elements/TagCollections/index_row.ctp b/app/View/Elements/TagCollections/index_row.ctp new file mode 100644 index 000000000..65b9d43aa --- /dev/null +++ b/app/View/Elements/TagCollections/index_row.ctp @@ -0,0 +1,42 @@ + + +   +   +   + +
+ element( + 'ajaxTagCollectionTags', + array( + 'attributeId' => $item['TagCollection']['id'], + 'attributeTags' => $item['TagCollectionElement'], + 'tagAccess' => ($isSiteAdmin || $me['org_id'] == $item['TagCollection']['org_id']), + 'context' => 'tagCollection', + 'tagCollection' => $item + ) + ); + ?> +
+ + + element('galaxyQuickViewMini', array( + 'mayModify' => true, + 'isAclTagger' => true, + 'data' => array(), + 'target_id' => h($item['TagCollection']['id']), + 'target_type' => 'tag_collection' + )); + ?> + +   + + Html->link('', array('action' => 'edit', $item['TagCollection']['id']), array('class' => 'icon-edit', 'title' => 'Edit'));?> + Form->postLink('', array('admin' => true, 'action' => 'delete', $item['TagCollection']['id']), array('class' => 'icon-trash', 'title' => 'Delete'), __('Are you sure you want to delete "%s"?', $item['TagCollection']['name']));?> + + ++', - sprintf("'%s/true', 'tags', 'selectTaxonomy'", h($attributeId)) + sprintf("'%s/attributes', 'tags', 'selectTaxonomy'", h($attributeId)) ); } echo $addTagButton; diff --git a/app/View/Elements/ajaxTagCollectionTags.ctp b/app/View/Elements/ajaxTagCollectionTags.ctp new file mode 100644 index 000000000..1cb3dfbf4 --- /dev/null +++ b/app/View/Elements/ajaxTagCollectionTags.ctp @@ -0,0 +1,36 @@ +
+ $tag); + $tagClass = $full ? 'tagFirstHalf' : 'tag'; + ?> +
+
+ +
x
+ +
+ +
+ +', + sprintf("'%s/tag_collection', 'tags', 'selectTaxonomy'", h($tagCollection['TagCollection']['id'])) + ); + } + echo $addTagButton; + ?> +
+
diff --git a/app/View/Elements/generic_table.ctp b/app/View/Elements/generic_table.ctp new file mode 100644 index 000000000..125e77a73 --- /dev/null +++ b/app/View/Elements/generic_table.ctp @@ -0,0 +1,59 @@ +Paginator->options(array( + 'update' => '.span12', + 'evalScripts' => true, + 'before' => '$(".progress").show()', + 'complete' => '$(".progress").hide()', + )); + $title = sprintf('

%s

', Inflector::humanize($controller)); + if (!empty($description)) { + $description = sprintf('

%s

', Inflector::humanize($description)); + } else { + $description = ''; + } + $pagination = sprintf( + '', + $this->Paginator->prev('« ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span')), + $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span')), + $this->Paginator->next(__('next') . ' »', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span')) + ); + $table_headers = ''; + foreach ($headers as $header => $data) { + if (!empty($data['sort'])) { + if (!empty($data['alias'])) { + $header_data = $this->Paginator->sort($header, $data['alias']); + } else { + $header_data = $this->Paginator->sort($header); + } + } else { + $header_data = Inflector::humanize(h($header)); + } + $action = ($header === 'Actions') ? ' class="actions"' : ''; + $table_headers .= '' . $header_data . ''; + } + $table_contents = $this->element($row_path, array( + 'items' => $items + )); + $table = sprintf( + '%s%s
', + $table_headers, + $table_contents + ); + $pagination_details = 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%s%s%s%s%s
', + $controller, + $title, + $description, + $pagination, + $table, + $pagination_details, + $pagination + ); diff --git a/app/View/Elements/generic_table_row.ctp b/app/View/Elements/generic_table_row.ctp new file mode 100644 index 000000000..3c8d74b9c --- /dev/null +++ b/app/View/Elements/generic_table_row.ctp @@ -0,0 +1,59 @@ +Paginator->options(array( + 'update' => '.span12', + 'evalScripts' => true, + 'before' => '$(".progress").show()', + 'complete' => '$(".progress").hide()', + )); + $title = sprintf('

%s index

', Inflector::humanize($controller)); + if (!empty($description)) { + $description = sprintf('

%s

', Inflector::humanize($description)); + } else { + $description = ''; + } + $pagination = sprintf( + '', + $this->Paginator->prev('« ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span')), + $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span')), + $this->Paginator->next(__('next') . ' »', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span')) + ); + $table_headers = ''; + foreach ($headers as $header => $data) { + if (!empty($data['sort'])) { + if (!empty($data['alias'])) { + $header_data = $this->Paginator->sort($header, $data['alias']); + } else { + $header_data = $this->Paginator->sort($header); + } + } else { + $header_data = '' . h($header) . ''; + } + $table_headers .= $header_data; + } + $table_contents = $this->element($row_path, array( + 'items' => $items + )); + $table = sprintf( + '%s%s
', + $table_header, + $table_contents + ); + $pagination_details = 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%s%s%s%s%s
', + $controller, + $title, + $description, + $pagination, + $table, + $pagination_details, + $pagination + ); + echo $this->element('side_menu', array('menuList' => 'whitelist', 'menuItem' => 'index')); diff --git a/app/View/Elements/global_menu.ctp b/app/View/Elements/global_menu.ctp index 638521bba..eb8e7cdf1 100644 --- a/app/View/Elements/global_menu.ctp +++ b/app/View/Elements/global_menu.ctp @@ -33,6 +33,7 @@
  • +
  • diff --git a/app/View/Elements/side_menu.ctp b/app/View/Elements/side_menu.ctp index 8d4f61dea..ea207f929 100644 --- a/app/View/Elements/side_menu.ctp +++ b/app/View/Elements/side_menu.ctp @@ -181,6 +181,25 @@ } break; + case 'tag-collections': + echo $this->element('/side_menu_link', array( + 'url' => '/tag_collections/index', + 'text' => __('List Tag Collections') + )); + if ($isAclTagEditor) { + echo $this->element('/side_menu_link', array( + 'url' => '/tag_collections/add', + 'text' => __('Add Tag Collection') + )); + if (($menuItem === 'edit')) { + echo $this->element('/side_menu_link', array( + 'url' => '/tag_collections/edit/' . $id, + 'text' => __('Add Tag Collection') + )); + } + } + break; + case 'event-collection': echo $this->element('/side_menu_link', array( 'element_id' => 'index', diff --git a/app/View/Events/json/index.ctp b/app/View/Events/json/index.ctp index 10fee8d9b..05e62a625 100644 --- a/app/View/Events/json/index.ctp +++ b/app/View/Events/json/index.ctp @@ -9,14 +9,9 @@ foreach ($events as $key => $event) { $events[$key]['GalaxyCluster'] = $event['GalaxyCluster']; } if (isset($event['EventTag'])) $events[$key]['EventTag'] = $event['EventTag']; - if (!empty($events[$key]['SharingGroup'])) $events[$key]['SharingGroup'] = $event['SharingGroup']; - - // cleanup the array from things we do not want to expose - unset($events[$key]['user_id']); - // hide the org field if we are not in showorg mode - if (!Configure::read('MISP.showorg')) { - unset($events[$key]['Org']); - unset($events[$key]['Orgc']); + if (!empty($event['SharingGroup'])) { + $events[$key]['SharingGroup'] = $event['SharingGroup']; } + unset($events[$key]['user_id']); } echo json_encode($events); diff --git a/app/View/Tags/ajax/select_tag.ctp b/app/View/Tags/ajax/select_tag.ctp index 8e5f5f51b..f83d5e376 100644 --- a/app/View/Tags/ajax/select_tag.ctp +++ b/app/View/Tags/ajax/select_tag.ctp @@ -2,10 +2,12 @@
    Form->create('Attribute', array('url' => '/attributes/addTag/' . $object_id, 'style' => 'margin:0px;')); - } else { + } elseif ($scope === 'event') { echo $this->Form->create('Event', array('url' => '/events/addTag/' . $object_id, 'style' => 'margin:0px;')); + } elseif ($scope === 'tag_collection') { + echo $this->Form->create('TagCollection', array('url' => '/tag_collections/addTag/' . $object_id, 'style' => 'margin:0px;')); } echo $this->Form->input('attribute_ids', array('style' => 'display:none;', 'label' => false)); echo $this->Form->input('tag', array('value' => 0)); @@ -19,16 +21,23 @@ &$option): ?> - - - - - + ' . h($option) . ''; + ?>
    -
    +
    -Html->script('tag-selection-keyboard-navigation.js'); ?> \ No newline at end of file +Html->script('tag-selection-keyboard-navigation.js'); ?> diff --git a/app/View/Tags/ajax/taxonomy_choice.ctp b/app/View/Tags/ajax/taxonomy_choice.ctp index 746ce2ac5..5bfc9e91c 100644 --- a/app/View/Tags/ajax/taxonomy_choice.ctp +++ b/app/View/Tags/ajax/taxonomy_choice.ctp @@ -5,22 +5,60 @@
    - - - - - - - - - - - - &$option): ?> - - - - + %s', + sprintf( + '', + h($object_id), + h($scope), + __('Favourite Tags') + ) + ); + } + if ($scope !== 'tag_collection') { + echo sprintf( + '%s', + sprintf( + '', + h($object_id), + h($scope), + __('Tag Collections') + ) + ); + } + echo sprintf( + '%s', + sprintf( + '', + h($object_id), + h($scope), + __('Custom Tags') + ) + ); + echo sprintf( + '%s', + sprintf( + '', + h($object_id), + h($scope), + __('All Tags') + ) + ); + foreach ($options as $k => &$option) { + echo sprintf( + '%s', + sprintf( + '', + h($object_id), + h($scope), + __('Taxonomy Library'), + h($option) + ) + ); + } + ?>
    :
    %s
    %s
    %s
    %s
    %s: %s
    @@ -50,7 +88,7 @@ } } }, 500); - + } $('#filterField').keyup(onKeyUp); diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index 0bf3f686c..dd3cb56bf 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -610,6 +610,31 @@ function quickSubmitAttributeTagForm(attribute_id, tag_id) { return false; } +function quickSubmitTagCollectionTagForm(tag_collection_id, tag_id) { + $('#TagCollectionTag').val(tag_id); + $.ajax({ + data: $('#TagCollectionSelectTagForm').closest("form").serialize(), + beforeSend: function (XMLHttpRequest) { + $(".loading").show(); + }, + success:function (data, textStatus) { + handleGenericAjaxResponse(data); + }, + error:function() { + showMessage('fail', 'Could not add tag.'); + loadTagCollectionTags(tag_collection_id); + }, + complete:function() { + $("#popover_form").fadeOut(); + $("#gray_out").fadeOut(); + $(".loading").hide(); + }, + type:"post", + url:"/tag_collections/addTag/" + tag_collection_id + }); + return false; +} + function handleAjaxEditResponse(data, name, type, id, field, event) { responseArray = data; if (type == 'Attribute') { @@ -841,6 +866,17 @@ function loadEventTags(id) { }); } +function loadTagCollectionTags(id) { + $.ajax({ + dataType:"html", + cache: false, + success:function (data, textStatus) { + $(".tagCollectionTagContainer").html(data); + }, + url:"/tags/showEventTag/" + id, + }); +} + function removeEventTag(event, tag) { var answer = confirm("Are you sure you want to remove this tag from the event?"); if (answer) { @@ -898,6 +934,8 @@ function removeObjectTag(context, object, tag) { $("#gray_out").fadeOut(); if (context == 'Attribute') { loadAttributeTags(object); + } else if (context == 'TagCollection') { + } else { loadEventTags(object); } From 15d8fd31db52724809ca990a2e2618ad660c3a70 Mon Sep 17 00:00:00 2001 From: iglocska Date: Sun, 23 Dec 2018 22:45:25 +0100 Subject: [PATCH 04/31] new: [tag collections] WIP --- app/Controller/TagsController.php | 167 ++++++++++++++++++------------ 1 file changed, 103 insertions(+), 64 deletions(-) diff --git a/app/Controller/TagsController.php b/app/Controller/TagsController.php index a2e7c9562..70b4aee65 100644 --- a/app/Controller/TagsController.php +++ b/app/Controller/TagsController.php @@ -595,80 +595,119 @@ class TagsController extends AppController } $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; + $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') + )); + if ($taxonomy_id === 'collections') { + $this->loadModel('TagCollection'); + $conditions = array(); + if (!$this->_isSiteAdmin()) { + $conditions['org_id'] = $this->Auth->user('org_id'); } - } 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); + $tagCollections = $this->TagCollection->find('all', array( + 'recursive' => -1, + 'conditions' => $conditions, + 'contain' => array('TagCollectionElement' => array('Tag')) + )); $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']; + $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['TagCollectionElement'])) { + $tagList = array(); + foreach ($tagCollection['TagCollectionElement'] as $k => $tce) { + if (in_array($tce['tag_id'], $banned_tags)) { + unset($tagCollection['TagCollectionElement'][$k]); + } else { + $tagList[] = $tce['Tag']['name']; + } + $tagCollection['TagCollectionElement'] = array_values($tagCollection['TagCollectionElement']); + } + $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()) { - $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') - ) - ) - ), + // 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 ($banned_tags as $banned_tag) { - unset($options[$banned_tag]); - unset($expanded[$banned_tag]); + foreach ($hidden_tags as $hidden_tag) { + unset($options[$hidden_tag]); + unset($expanded[$hidden_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('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) From 6f8e03c657e9747f1e0907e70a22b0659ef70f5a Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 24 Dec 2018 05:51:03 +0100 Subject: [PATCH 05/31] chg: [generic index] Fixed scoping issue with rows --- .../Elements/TagCollections/index_row.ctp | 79 +++++++++---------- app/View/Elements/generic_table.ctp | 9 ++- 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/app/View/Elements/TagCollections/index_row.ctp b/app/View/Elements/TagCollections/index_row.ctp index 65b9d43aa..123d082e9 100644 --- a/app/View/Elements/TagCollections/index_row.ctp +++ b/app/View/Elements/TagCollections/index_row.ctp @@ -1,42 +1,37 @@ - - -   -   -   - -
    - element( - 'ajaxTagCollectionTags', - array( - 'attributeId' => $item['TagCollection']['id'], - 'attributeTags' => $item['TagCollectionElement'], - 'tagAccess' => ($isSiteAdmin || $me['org_id'] == $item['TagCollection']['org_id']), - 'context' => 'tagCollection', - 'tagCollection' => $item - ) - ); - ?> -
    - - - element('galaxyQuickViewMini', array( - 'mayModify' => true, - 'isAclTagger' => true, - 'data' => array(), - 'target_id' => h($item['TagCollection']['id']), - 'target_type' => 'tag_collection' - )); - ?> - -   - - Html->link('', array('action' => 'edit', $item['TagCollection']['id']), array('class' => 'icon-edit', 'title' => 'Edit'));?> - Form->postLink('', array('admin' => true, 'action' => 'delete', $item['TagCollection']['id']), array('class' => 'icon-trash', 'title' => 'Delete'), __('Are you sure you want to delete "%s"?', $item['TagCollection']['name']));?> - - -"> +   +   +   + +
    + element( + 'ajaxTagCollectionTags', + array( + 'attributeId' => $item['TagCollection']['id'], + 'attributeTags' => $item['TagCollectionElement'], + 'tagAccess' => ($isSiteAdmin || $me['org_id'] == $item['TagCollection']['org_id']), + 'context' => 'tagCollection', + 'tagCollection' => $item + ) + ); + ?> +
    + + + element('galaxyQuickViewMini', array( + 'mayModify' => true, + 'isAclTagger' => true, + 'data' => array(), + 'target_id' => h($item['TagCollection']['id']), + 'target_type' => 'tag_collection' + )); + ?> + +   + + Html->link('', array('action' => 'edit', $item['TagCollection']['id']), array('class' => 'icon-edit', 'title' => 'Edit'));?> + Form->postLink('', array('admin' => true, 'action' => 'delete', $item['TagCollection']['id']), array('class' => 'icon-trash', 'title' => 'Delete'), __('Are you sure you want to delete "%s"?', $item['TagCollection']['name']));?> + + diff --git a/app/View/Elements/generic_table.ctp b/app/View/Elements/generic_table.ctp index 125e77a73..b17e5fd78 100644 --- a/app/View/Elements/generic_table.ctp +++ b/app/View/Elements/generic_table.ctp @@ -31,9 +31,12 @@ $action = ($header === 'Actions') ? ' class="actions"' : ''; $table_headers .= '' . $header_data . ''; } - $table_contents = $this->element($row_path, array( - 'items' => $items - )); + $table_contents = ''; + foreach ($items as $item) { + $table_contents .= $this->element($row_path, array( + 'item' => $item + )); + } $table = sprintf( '%s%s
    ', $table_headers, From 856a76951f099e662955c3546c800d40b7fb77c0 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 25 Dec 2018 14:04:01 +0100 Subject: [PATCH 06/31] fix: [js] Various fixes with adding/removing tags --- app/webroot/js/misp.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index dd3cb56bf..32d0b7fab 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -619,6 +619,7 @@ function quickSubmitTagCollectionTagForm(tag_collection_id, tag_id) { }, success:function (data, textStatus) { handleGenericAjaxResponse(data); + refreshTagCollectionRow(tag_collection_id); }, error:function() { showMessage('fail', 'Could not add tag.'); @@ -635,6 +636,20 @@ function quickSubmitTagCollectionTagForm(tag_collection_id, tag_id) { return false; } +function refreshTagCollectionRow(tag_collection_id) { + $.ajax({ + type:"get", + url:"/tag_collections/getRow/" + tag_collection_id, + error:function() { + showMessage('fail', 'Could not fetch updates to the modified row.'); + }, + success: function (data, textStatus) { + $('[data-row-id="' + tag_collection_id + '"]').replaceWith(data); + } + }); + +} + function handleAjaxEditResponse(data, name, type, id, field, event) { responseArray = data; if (type == 'Attribute') { @@ -934,8 +949,8 @@ function removeObjectTag(context, object, tag) { $("#gray_out").fadeOut(); if (context == 'Attribute') { loadAttributeTags(object); - } else if (context == 'TagCollection') { - + } else if (context == 'tag_collection') { + refreshTagCollectionRow(object); } else { loadEventTags(object); } From aefb824d5c82f7df4ab35d46d7119bc3ff391b5a Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 25 Dec 2018 14:44:26 +0100 Subject: [PATCH 07/31] new: [tag collections] Renamed tagCollectionElement to tagCollectionTag --- app/Model/TagCollectionTag.php | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 app/Model/TagCollectionTag.php 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( + + ); +} From 6a74eb0bb6a294f1dd0b118b7bbb57fc12372627 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 25 Dec 2018 14:45:12 +0100 Subject: [PATCH 08/31] fix: [cleanup] removed tagCollectionElement --- app/Model/TagCollectionElement.php | 31 ------------------------------ 1 file changed, 31 deletions(-) delete mode 100644 app/Model/TagCollectionElement.php diff --git a/app/Model/TagCollectionElement.php b/app/Model/TagCollectionElement.php deleted file mode 100644 index 584460b84..000000000 --- a/app/Model/TagCollectionElement.php +++ /dev/null @@ -1,31 +0,0 @@ - array( - 'roleModel' => 'Role', - 'roleKey' => 'role_id', - 'change' => 'full' - ), - 'Containable' - ); - - public $belongsTo = array( - 'TagCollection' => array( - 'className' => 'TagCollection', - ), - 'Tag' => array( - 'className' => 'Tag', - ) - ); - - public $validate = array( - - ); -} From d22787b263372456da9e9f20937cd1583d059f12 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 26 Dec 2018 23:48:49 +0100 Subject: [PATCH 09/31] chg: [tag collection tag] Renamed Model references in the codebase --- app/Controller/TagsController.php | 12 ++++++------ app/Model/TagCollection.php | 9 ++++++++- app/View/Elements/TagCollections/index_row.ctp | 2 +- app/View/Elements/ajaxTagCollectionTags.ctp | 2 +- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/app/Controller/TagsController.php b/app/Controller/TagsController.php index 70b4aee65..3fd538dab 100644 --- a/app/Controller/TagsController.php +++ b/app/Controller/TagsController.php @@ -533,7 +533,7 @@ class TagsController extends AppController $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->TagCollectionElement->find('all', array( + $tags = $this->TagCollection->TagCollectionTag->find('all', array( 'conditions' => array( 'tag_collection_id' => $id, 'Tag.name !=' => $cluster_names @@ -619,22 +619,22 @@ class TagsController extends AppController $tagCollections = $this->TagCollection->find('all', array( 'recursive' => -1, 'conditions' => $conditions, - 'contain' => array('TagCollectionElement' => array('Tag')) + 'contain' => array('TagCollectionTag' => array('Tag')) )); $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['TagCollectionElement'])) { + if (!empty($tagCollection['TagCollectionTag'])) { $tagList = array(); - foreach ($tagCollection['TagCollectionElement'] as $k => $tce) { + foreach ($tagCollection['TagCollectionTag'] as $k => $tce) { if (in_array($tce['tag_id'], $banned_tags)) { - unset($tagCollection['TagCollectionElement'][$k]); + unset($tagCollection['TagCollectionTag'][$k]); } else { $tagList[] = $tce['Tag']['name']; } - $tagCollection['TagCollectionElement'] = array_values($tagCollection['TagCollectionElement']); + $tagCollection['TagCollectionTag'] = array_values($tagCollection['TagCollectionTag']); } $tagList = implode(', ', $tagList); $expanded[$tagCollection['TagCollection']['id']] .= sprintf(' (%s)', $tagList); diff --git a/app/Model/TagCollection.php b/app/Model/TagCollection.php index 73b054bcb..a5a26c9fd 100644 --- a/app/Model/TagCollection.php +++ b/app/Model/TagCollection.php @@ -19,7 +19,7 @@ class TagCollection extends AppModel ); public $hasMany = array( - 'TagCollectionElement' => array( + 'TagCollectionTag' => array( 'dependent' => true ) ); @@ -47,4 +47,11 @@ class TagCollection extends AppModel } return true; } + + public function fetchTagCollection($user, $params = array()) + { + return $this->find('all', array( + $params + )); + } } diff --git a/app/View/Elements/TagCollections/index_row.ctp b/app/View/Elements/TagCollections/index_row.ctp index 123d082e9..3199649db 100644 --- a/app/View/Elements/TagCollections/index_row.ctp +++ b/app/View/Elements/TagCollections/index_row.ctp @@ -9,7 +9,7 @@ 'ajaxTagCollectionTags', array( 'attributeId' => $item['TagCollection']['id'], - 'attributeTags' => $item['TagCollectionElement'], + 'attributeTags' => $item['TagCollectionTag'], 'tagAccess' => ($isSiteAdmin || $me['org_id'] == $item['TagCollection']['org_id']), 'context' => 'tagCollection', 'tagCollection' => $item diff --git a/app/View/Elements/ajaxTagCollectionTags.ctp b/app/View/Elements/ajaxTagCollectionTags.ctp index 1cb3dfbf4..de3a1d21d 100644 --- a/app/View/Elements/ajaxTagCollectionTags.ctp +++ b/app/View/Elements/ajaxTagCollectionTags.ctp @@ -4,7 +4,7 @@ $context = 'event'; } $full = $isAclTagger && $tagAccess; - foreach ($tagCollection['TagCollectionElement'] as $tag): + foreach ($tagCollection['TagCollectionTag'] as $tag): if (!isset($tag['Tag'])) $tag = array('Tag' => $tag); $tagClass = $full ? 'tagFirstHalf' : 'tag'; ?> From 7cebaef6c8e8f98b4bd7bc49979daff59fa32a63 Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 27 Dec 2018 05:24:30 +0100 Subject: [PATCH 10/31] fix: [db update] Fixed update script for tag collection tags --- app/Model/AppModel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 3fb6664ae..bb4bee553 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -1073,7 +1073,7 @@ class AppModel extends Model INDEX `user_id` (`user_id`), INDEX `org_id` (`org_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; - $sqlArray[] = "CREATE TABLE IF NOT EXISTS tag_collection_elements ( + $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, From facedf7fc3139fa5ded60f998a242a1ab3bb5853 Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 28 Dec 2018 12:54:22 +0100 Subject: [PATCH 11/31] chg: [refactor] Refactoed attachCluster to be more model agnostic --- app/Model/Galaxy.php | 75 ++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 38 deletions(-) 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( From bf0fffcd58ccc873363b25199a7ca3dfc3379475 Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 28 Dec 2018 12:54:51 +0100 Subject: [PATCH 12/31] fix: [model linking] Made tag collection tag dependent of tag --- app/Model/Tag.php | 3 +++ 1 file changed, 3 insertions(+) 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 ) ); From cf666eec5a07e93f714ffde88769b1c29e3cffb0 Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 28 Dec 2018 12:55:17 +0100 Subject: [PATCH 13/31] new: [tag collections] Added missing views --- app/View/TagCollections/add.ctp | 27 +++++++++++++++++++++++++++ app/View/TagCollections/get_row.ctp | 11 +++++++++++ app/View/TagCollections/index.ctp | 25 +++++++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 app/View/TagCollections/add.ctp create mode 100644 app/View/TagCollections/get_row.ctp create mode 100644 app/View/TagCollections/index.ctp diff --git a/app/View/TagCollections/add.ctp b/app/View/TagCollections/add.ctp new file mode 100644 index 000000000..c43e2d07e --- /dev/null +++ b/app/View/TagCollections/add.ctp @@ -0,0 +1,27 @@ +
    +Form->create('TagCollection'); ?> +
    + %s', __('Add Tag Collection')); + } else { + echo sprintf('%s', __('Edit Tag Collection')); + } + echo $this->Form->input('name', array('class' => 'span6')); + echo '
    '; + echo $this->Form->input('description', array('class' => 'span6')); + echo '
    '; + echo $this->Form->input('all_orgs', array( + 'type' => 'checkbox', + 'label' => __('Visible to all orgs') + )); + ?> +
    +Form->button(__('Submit'), array('class' => 'btn btn-primary')); +echo $this->Form->end(); +?> +
    +element('side_menu', array('menuList' => 'tag-collections', 'menuItem' => 'add')); +?> diff --git a/app/View/TagCollections/get_row.ctp b/app/View/TagCollections/get_row.ctp new file mode 100644 index 000000000..a52ff938e --- /dev/null +++ b/app/View/TagCollections/get_row.ctp @@ -0,0 +1,11 @@ +element('TagCollections/index_row'); +?> + + diff --git a/app/View/TagCollections/index.ctp b/app/View/TagCollections/index.ctp new file mode 100644 index 000000000..de89ec3ad --- /dev/null +++ b/app/View/TagCollections/index.ctp @@ -0,0 +1,25 @@ +element('generic_table', array( + 'items' => $list, + 'controller' => 'tag_collections', + 'headers' => array( + 'id' => array('sort' => 1), + 'uuid' => array('sort' => 1), + 'name' => array('sort' => 1), + 'tags' => array(), + 'galaxies' => array(), + 'description' => array(), + 'Actions' => array() + ), + 'row_path' => 'TagCollections/index_row' + )); + + echo $this->element('side_menu', array('menuList' => 'tag-collections', 'menuItem' => 'index')); +?> + From 977d10a87f0192cc762aee131435ba23d9507427 Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 28 Dec 2018 12:55:41 +0100 Subject: [PATCH 14/31] fix: [added missing controller] tag collections controller --- app/Controller/TagCollectionsController.php | 268 ++++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 app/Controller/TagCollectionsController.php diff --git a/app/Controller/TagCollectionsController.php b/app/Controller/TagCollectionsController.php new file mode 100644 index 000000000..dcdd79c23 --- /dev/null +++ b/app/Controller/TagCollectionsController.php @@ -0,0 +1,268 @@ + 60, + 'order' => array( + 'TagCollection.name' => 'ASC' + ), + 'recursive' => -1, + 'contain' => array('TagCollectionTag' => array('Tag')) + ); + + 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() + { + + } + + 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->set('list', $this->paginate()); + } + + public function getRow($id) + { + $params = array( + 'recursive' => -1, + 'contain' => array('TagCollectionTag' => array('Tag')), + 'conditions' => array('TagCollection.id' => $id) + ); + $item = $this->TagCollection->find('first', $params); + if (empty($item)) { + throw new NotFoundException('Invalid tag collection.'); + } + $this->set('item', $item); + $this->layout = false; + } +} From 1b1fca34ceb992d5d11c93cc4f20bc7506c9e13c Mon Sep 17 00:00:00 2001 From: iglocska Date: Sat, 29 Dec 2018 04:25:57 +0100 Subject: [PATCH 15/31] fix: [tag collections] fixed galaxies not showing up --- app/Controller/TagCollectionsController.php | 8 +++++++- app/View/Elements/TagCollections/index_row.ctp | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/Controller/TagCollectionsController.php b/app/Controller/TagCollectionsController.php index dcdd79c23..80165fe6d 100644 --- a/app/Controller/TagCollectionsController.php +++ b/app/Controller/TagCollectionsController.php @@ -248,7 +248,13 @@ class TagCollectionsController extends AppController public function index() { - $this->set('list', $this->paginate()); + $list = $this->paginate(); + $this->loadModel('Event'); + foreach ($list as $k => $tag_collection) { + $list[$k] = $this->Event->massageTags($tag_collection, $dataType = 'TagCollection'); + } + + $this->set('list', $list); } public function getRow($id) diff --git a/app/View/Elements/TagCollections/index_row.ctp b/app/View/Elements/TagCollections/index_row.ctp index 3199649db..6deaac86b 100644 --- a/app/View/Elements/TagCollections/index_row.ctp +++ b/app/View/Elements/TagCollections/index_row.ctp @@ -23,7 +23,7 @@ echo $this->element('galaxyQuickViewMini', array( 'mayModify' => true, 'isAclTagger' => true, - 'data' => array(), + 'data' => $item['Galaxy'], 'target_id' => h($item['TagCollection']['id']), 'target_type' => 'tag_collection' )); From 2e38015e8eaefd95fc9a3d3eca8640f64100490e Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 1 Jan 2019 16:37:15 +0100 Subject: [PATCH 16/31] fix: [data model] fixed a bug that caused org_id fields to be tinyint(1) for org_ids on tags --- app/Model/AppModel.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index bb4bee553..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, 26 => false + 21 => false, 22 => false, 23 => false, 24 => false, 25 => false, 26 => false, + 27 => false ); public function afterSave($created, $options = array()) @@ -1082,6 +1083,9 @@ class AppModel extends Model 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;'; From 760dbed37dd5883037cb7f9728e78b3f6a4934b9 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 1 Jan 2019 16:38:57 +0100 Subject: [PATCH 17/31] new: [tag collections] First feature complete minimal version of the tag collection system --- app/Controller/AttributesController.php | 96 +++++++++++-------- app/Controller/Component/ACLComponent.php | 10 ++ app/Controller/EventsController.php | 91 +++++++++++------- app/Controller/TagCollectionsController.php | 89 +++++++++++++++-- app/Controller/TagsController.php | 11 +-- app/Model/Event.php | 3 +- app/Model/TagCollection.php | 71 +++++++++++++- app/View/Elements/Events/eventIndexTable.ctp | 1 - .../Elements/TagCollections/index_row.ctp | 17 +++- app/View/Elements/generic_table.ctp | 6 +- app/View/TagCollections/index.ctp | 3 + app/View/Tags/ajax/select_tag.ctp | 17 +++- 12 files changed, 312 insertions(+), 103 deletions(-) diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php index 018bfc127..4008b5a36 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('TagCollection.id' => $tag_id)); + 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..9f62b4108 100644 --- a/app/Controller/Component/ACLComponent.php +++ b/app/Controller/Component/ACLComponent.php @@ -382,6 +382,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'), diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 118829ef8..8b003c443 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -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('TagCollection.id' => $tag_id)); + 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,51 @@ 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')); } 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/TagCollectionsController.php b/app/Controller/TagCollectionsController.php index 80165fe6d..24bbefe30 100644 --- a/app/Controller/TagCollectionsController.php +++ b/app/Controller/TagCollectionsController.php @@ -6,7 +6,8 @@ class TagCollectionsController extends AppController { public $components = array( 'Security', - 'AdminCrud' + 'AdminCrud', + 'RequestHandler' ); public $paginate = array( @@ -15,7 +16,7 @@ class TagCollectionsController extends AppController 'TagCollection.name' => 'ASC' ), 'recursive' => -1, - 'contain' => array('TagCollectionTag' => array('Tag')) + 'contain' => array('TagCollectionTag' => array('Tag'), 'User', 'Organisation') ); public function add() @@ -104,9 +105,35 @@ class TagCollectionsController extends AppController $this->render('add'); } - public function delete() + 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) @@ -248,27 +275,69 @@ class TagCollectionsController extends AppController public function index() { - $list = $this->paginate(); + //$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->Event->massageTags($tag_collection, $dataType = 'TagCollection'); + $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); } - - $this->set('list', $list); } public function getRow($id) { $params = array( 'recursive' => -1, - 'contain' => array('TagCollectionTag' => array('Tag')), + '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.'); } - $this->set('item', $item); + 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 3fd538dab..ba5424a8f 100644 --- a/app/Controller/TagsController.php +++ b/app/Controller/TagsController.php @@ -610,17 +610,10 @@ class TagsController extends AppController ), 'fields' => array('Tag.id') )); + $this->set('taxonomy_id', $taxonomy_id); if ($taxonomy_id === 'collections') { $this->loadModel('TagCollection'); - $conditions = array(); - if (!$this->_isSiteAdmin()) { - $conditions['org_id'] = $this->Auth->user('org_id'); - } - $tagCollections = $this->TagCollection->find('all', array( - 'recursive' => -1, - 'conditions' => $conditions, - 'contain' => array('TagCollectionTag' => array('Tag')) - )); + $tagCollections = $this->TagCollection->fetchTagCollection($this->Auth->user()); $options = array(); $expanded = array(); foreach ($tagCollections as &$tagCollection) { diff --git a/app/Model/Event.php b/app/Model/Event.php index 7a001a5d8..0abb1f218 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -5215,7 +5215,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)) { @@ -5248,6 +5248,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/TagCollection.php b/app/Model/TagCollection.php index a5a26c9fd..56fdb5890 100644 --- a/app/Model/TagCollection.php +++ b/app/Model/TagCollection.php @@ -24,6 +24,13 @@ class TagCollection extends AppModel ) ); + public $belongsTo = array( + 'User', + 'Organisation' => array( + 'foreignKey' => 'org_id' + ) + ); + public $whitelistedItems = false; public $validate = array( @@ -50,8 +57,66 @@ class TagCollection extends AppModel public function fetchTagCollection($user, $params = array()) { - return $this->find('all', array( - $params - )); + 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/View/Elements/Events/eventIndexTable.ctp b/app/View/Elements/Events/eventIndexTable.ctp index 67aa76e07..b4a239289 100644 --- a/app/View/Elements/Events/eventIndexTable.ctp +++ b/app/View/Elements/Events/eventIndexTable.ctp @@ -92,7 +92,6 @@ OrgImg->getOrgImg(array('name' => $event['Orgc']['name'], 'id' => $event['Orgc']['id'], 'size' => 24)); ?> -   diff --git a/app/View/Elements/TagCollections/index_row.ctp b/app/View/Elements/TagCollections/index_row.ctp index 6deaac86b..855f07b2c 100644 --- a/app/View/Elements/TagCollections/index_row.ctp +++ b/app/View/Elements/TagCollections/index_row.ctp @@ -21,7 +21,7 @@ element('galaxyQuickViewMini', array( - 'mayModify' => true, + 'mayModify' => ($isSiteAdmin || $me['org_id'] == $item['TagCollection']['org_id']), 'isAclTagger' => true, 'data' => $item['Galaxy'], 'target_id' => h($item['TagCollection']['id']), @@ -29,9 +29,20 @@ )); ?> +   + '"> + OrgImg->getOrgImg(array('name' => $item['Organisation']['name'], 'id' => $item['Organisation']['id'], 'size' => 24)); + ?> + +     - Html->link('', array('action' => 'edit', $item['TagCollection']['id']), array('class' => 'icon-edit', 'title' => 'Edit'));?> - Form->postLink('', array('admin' => true, 'action' => 'delete', $item['TagCollection']['id']), array('class' => 'icon-trash', 'title' => 'Delete'), __('Are you sure you want to delete "%s"?', $item['TagCollection']['name']));?> + Html->link('', array('action' => 'edit', $item['TagCollection']['id']), array('class' => 'icon-edit', 'title' => 'Edit')); + echo $this->Form->postLink('', array('action' => 'delete', $item['TagCollection']['id']), array('class' => 'icon-trash', 'title' => 'Delete'), __('Are you sure you want to delete "%s"?', $item['TagCollection']['name'])); + } + ?> diff --git a/app/View/Elements/generic_table.ctp b/app/View/Elements/generic_table.ctp index b17e5fd78..c2bfd2fd9 100644 --- a/app/View/Elements/generic_table.ctp +++ b/app/View/Elements/generic_table.ctp @@ -26,7 +26,11 @@ $header_data = $this->Paginator->sort($header); } } else { - $header_data = Inflector::humanize(h($header)); + if (!empty($data['alias'])) { + $header_data = $data['alias']; + } else { + $header_data = Inflector::humanize(h($header)); + } } $action = ($header === 'Actions') ? ' class="actions"' : ''; $table_headers .= '' . $header_data . ''; diff --git a/app/View/TagCollections/index.ctp b/app/View/TagCollections/index.ctp index de89ec3ad..3fab6668e 100644 --- a/app/View/TagCollections/index.ctp +++ b/app/View/TagCollections/index.ctp @@ -8,6 +8,9 @@ 'name' => array('sort' => 1), 'tags' => array(), 'galaxies' => array(), + 'all_orgs' => array(), + 'org_id' => array('alias' => 'Organisation'), + 'user_id' => array('alias' => 'User'), 'description' => array(), 'Actions' => array() ), diff --git a/app/View/Tags/ajax/select_tag.ctp b/app/View/Tags/ajax/select_tag.ctp index f83d5e376..88c6d8248 100644 --- a/app/View/Tags/ajax/select_tag.ctp +++ b/app/View/Tags/ajax/select_tag.ctp @@ -2,7 +2,7 @@
    Form->create('Attribute', array('url' => '/attributes/addTag/' . $object_id, 'style' => 'margin:0px;')); } elseif ($scope === 'event') { echo $this->Form->create('Event', array('url' => '/events/addTag/' . $object_id, 'style' => 'margin:0px;')); @@ -19,18 +19,25 @@
    - &$option): ?> - + &$option): + $choice_id = $k; + if ($taxonomy_id === 'collections') { + $choice_id = 'collection_' . $choice_id; + } + ?> + + ' . h($option) . ''; ?> From 1bad3580ead6e204710b645dbc64d5e11327db22 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 1 Jan 2019 16:39:32 +0100 Subject: [PATCH 18/31] fix: [data model] added the fix to the org_id field in the tag table to the mysql.sql file --- INSTALL/MYSQL.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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`), From 6e8afdaa3a78a7e249b6247e1a812ecbea386134 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 1 Jan 2019 17:11:10 +0100 Subject: [PATCH 19/31] new: [tag collections] Add default tag collection per instance --- app/Controller/ServersController.php | 5 +++++ app/Model/Event.php | 33 ++++++++++++++++++++++++++++ app/Model/Server.php | 30 +++++++++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/app/Controller/ServersController.php b/app/Controller/ServersController.php index 3349e140a..ff962da34 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/Model/Event.php b/app/Model/Event.php index 0abb1f218..83cc14261 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -3259,6 +3259,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; diff --git a/app/Model/Server.php b/app/Model/Server.php index 38866b66f..c9d569006 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)) { From b608ce841b7aa85ae232656da8968bc4576260eb Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 1 Jan 2019 18:37:27 +0100 Subject: [PATCH 20/31] fix: [taxonomies] Tag list empty fixed --- app/View/Tags/ajax/select_tag.ctp | 1 + app/View/Tags/ajax/taxonomy_choice.ctp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/View/Tags/ajax/select_tag.ctp b/app/View/Tags/ajax/select_tag.ctp index 88c6d8248..11fb2e05a 100644 --- a/app/View/Tags/ajax/select_tag.ctp +++ b/app/View/Tags/ajax/select_tag.ctp @@ -1,5 +1,6 @@
    +
    %s', sprintf( - '
    ', + '', h($object_id), + h($k), h($scope), __('Taxonomy Library'), h($option) From 783214f8402e28ed1bc57cb4e7ef5d47a05c3c97 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 1 Jan 2019 19:09:46 +0100 Subject: [PATCH 21/31] fix: [tag collections] Fixed an issue where if a collection was added that already had all tags attached from before, the process would get stuck with no feedback to the user --- app/Controller/EventsController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 8b003c443..849c4d54a 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -3390,6 +3390,8 @@ class EventsController extends AppController } 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' => $fail)), 'status'=>200, 'type' => 'json')); } From 515bf89766cf1eb62aa87558a2d88c1abfcaaad7 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 1 Jan 2019 19:20:49 +0100 Subject: [PATCH 22/31] fix: [tag collections] Fixed several bugs linking to the wrong tag collection when attaching them to an event/attribute --- app/Controller/AttributesController.php | 2 +- app/Controller/EventsController.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php index 88e2d0e05..ffe2cf674 100644 --- a/app/Controller/AttributesController.php +++ b/app/Controller/AttributesController.php @@ -2841,7 +2841,7 @@ class AttributesController extends AppController 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('TagCollection.id' => $tag_id)); + $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')); } diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 849c4d54a..67061da8e 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -3321,7 +3321,7 @@ class EventsController extends AppController 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('TagCollection.id' => $tag_id)); + $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')); } From 6aa366138c23b3e4a7dc489e23cbbea860246e9d Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 2 Jan 2019 10:06:39 +0100 Subject: [PATCH 23/31] fix: [ACL] Added missing function --- app/Controller/Component/ACLComponent.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Controller/Component/ACLComponent.php b/app/Controller/Component/ACLComponent.php index 9f62b4108..db91bc41b 100644 --- a/app/Controller/Component/ACLComponent.php +++ b/app/Controller/Component/ACLComponent.php @@ -404,6 +404,7 @@ class ACLComponent extends Component 'selectTaxonomy' => array('perm_tagger'), 'showEventTag' => array('*'), 'showAttributeTag' => array('*'), + 'showTagControllerTag' => array('*'), 'tagStatistics' => array('*'), 'view' => array('*'), 'viewGraph' => array('*'), From a1332808e756fdf2fc5d4b85ad93ee9e07efadcb Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 3 Jan 2019 21:15:06 +0100 Subject: [PATCH 24/31] fix: [over-sanitisation] cleared up over-sanitised message in the events controller --- app/Controller/EventsController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 67061da8e..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); From c99f54328b67fd209a6c167be0b0f6b365a017e6 Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 4 Jan 2019 14:55:15 +0100 Subject: [PATCH 25/31] new: [galaxies] added new function to show galaxies in ajax queries --- app/Controller/GalaxiesController.php | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) 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'); + } } From 1280d20d5a3b7bd2a8961605ec8b948e93f5fa0a Mon Sep 17 00:00:00 2001 From: iglocska Date: Sat, 5 Jan 2019 05:03:04 +0100 Subject: [PATCH 26/31] new: [galaxies] adding galaxies no longer needs a full refresh of the page - use the new ajax function to get the galaxy information returned --- .../Elements/Events/View/row_attribute.ctp | 2 +- app/View/Events/ajax/ajaxTags.ctp | 11 +++++- app/View/Galaxies/ajax/cluster_choice.ctp | 2 +- app/webroot/js/misp.js | 39 ++++++++++++++++++- 4 files changed, 48 insertions(+), 6 deletions(-) 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: %s%s: %s + element('galaxyQuickViewMini', array( 'mayModify' => $mayModify, diff --git a/app/View/Events/ajax/ajaxTags.ctp b/app/View/Events/ajax/ajaxTags.ctp index c7d26fdb7..6001997d0 100644 --- a/app/View/Events/ajax/ajaxTags.ctp +++ b/app/View/Events/ajax/ajaxTags.ctp @@ -1,4 +1,11 @@ element('ajaxTags', array('event' => $event, 'tags' => $tags, 'tagAccess' => ($isSiteAdmin || $mayModify || $me['org_id'] == $event['Event']['org_id']))); + $mayModify = ( + ($isAclModify && $event['Event']['user_id'] == $me['id'] && $event['Event']['orgc_id'] == $me['org_id']) || + ($isAclModifyOrg && $event['Event']['orgc_id'] == $me['org_id']) + ); + echo $this->element('ajaxTags', array( + 'event' => $event, + 'tags' => $tags, + 'tagAccess' => ($isSiteAdmin || $mayModify) + )); ?> diff --git a/app/View/Galaxies/ajax/cluster_choice.ctp b/app/View/Galaxies/ajax/cluster_choice.ctp index 309f9c18f..d59400fa8 100644 --- a/app/View/Galaxies/ajax/cluster_choice.ctp +++ b/app/View/Galaxies/ajax/cluster_choice.ctp @@ -43,7 +43,7 @@ }); $('.clusterSelectChoice').click(function() { - quickSubmitGalaxyForm($(this).data('target-type') + '/' + $(this).data('target-id'), $(this).data('cluster-id')); + quickSubmitGalaxyForm($(this).data('target-id'), $(this).data('cluster-id'), $(this).data('target-type')); }); $('#clusterFilterField').keyup(function() { var filterString = $("#clusterFilterField").val().toLowerCase(); diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index 78e3877b9..5e78ab4e9 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -888,6 +888,21 @@ function loadEventTags(id) { }); } +function loadGalaxies(id, scope) { + $.ajax({ + dataType:"html", + cache: false, + success:function (data, textStatus) { + if (scope == 'event') { + $("#galaxies_div").html(data); + } else if ($scope == 'attribute') { + $("#attribute_" + id + "_galaxy").html(data); + } + }, + url:"/galaxies/showGalaxies/" + id + "/" + scope, + }); +} + function loadTagCollectionTags(id) { $.ajax({ dataType:"html", @@ -3276,9 +3291,29 @@ function addGalaxyListener(id) { getPopup(target_type + '/' + target_id, 'galaxies', 'selectGalaxyNamespace'); } -function quickSubmitGalaxyForm(event_id, cluster_id) { +function quickSubmitGalaxyForm(scope, cluster_id, event_id) { $('#GalaxyTargetId').val(cluster_id); - $('#GalaxySelectClusterForm').submit(); + $.ajax({ + data: $('#GalaxySelectClusterForm').closest("form").serialize(), + beforeSend: function (XMLHttpRequest) { + $(".loading").show(); + }, + success:function (data, textStatus) { + loadGalaxies(event_id, scope); + handleGenericAjaxResponse(data); + }, + error:function() { + showMessage('fail', 'Could not add cluster.'); + loadGalaxies(event_id, scope); + }, + complete:function() { + $("#popover_form").fadeOut(); + $("#gray_out").fadeOut(); + $(".loading").hide(); + }, + type:"post", + url: "/galaxies/attachCluster/" + event_id + "/" + scope + }); return false; } From 6c58b379dfa254acf87d20723edd9b4483abb90f Mon Sep 17 00:00:00 2001 From: iglocska Date: Sun, 6 Jan 2019 16:58:32 +0100 Subject: [PATCH 27/31] fix: [galaxies] added new view that wasn't finished for the previous commit (stil WIP) --- app/View/Events/ajax/ajaxGalaxies.ctp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 app/View/Events/ajax/ajaxGalaxies.ctp diff --git a/app/View/Events/ajax/ajaxGalaxies.ctp b/app/View/Events/ajax/ajaxGalaxies.ctp new file mode 100644 index 000000000..560f790f3 --- /dev/null +++ b/app/View/Events/ajax/ajaxGalaxies.ctp @@ -0,0 +1,26 @@ +

    +element($element, array( + 'mayModify' => $mayModify, + 'isAclTagger' => $isAclTagger, + 'data' => $object['Galaxy'], + 'target_id' => $object['Event']['id'], + 'target_type' => $scope + )); +?> + From 8225a0d83f50626cc837b5b88b78726841c44385 Mon Sep 17 00:00:00 2001 From: iglocska Date: Sun, 6 Jan 2019 17:04:10 +0100 Subject: [PATCH 28/31] fix: [galaxies] Some minor fixes with the ajaxification --- app/View/Events/ajax/ajaxGalaxies.ctp | 2 +- app/webroot/js/misp.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/View/Events/ajax/ajaxGalaxies.ctp b/app/View/Events/ajax/ajaxGalaxies.ctp index 560f790f3..8a0fac72d 100644 --- a/app/View/Events/ajax/ajaxGalaxies.ctp +++ b/app/View/Events/ajax/ajaxGalaxies.ctp @@ -1,9 +1,9 @@ -

    %s', __('Galaxies')); $element = 'galaxyQuickView'; } else if ($scope == 'attribute') { $element = 'galaxyQuickViewMini'; diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index 5e78ab4e9..f50b057a4 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -895,7 +895,7 @@ function loadGalaxies(id, scope) { success:function (data, textStatus) { if (scope == 'event') { $("#galaxies_div").html(data); - } else if ($scope == 'attribute') { + } else if (scope == 'attribute') { $("#attribute_" + id + "_galaxy").html(data); } }, From c09992d2d9cca9bde01130a498261341787ce603 Mon Sep 17 00:00:00 2001 From: iglocska Date: Sun, 6 Jan 2019 17:37:13 +0100 Subject: [PATCH 29/31] fix: [ACL] Added ajax function to ACL --- app/Controller/Component/ACLComponent.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Controller/Component/ACLComponent.php b/app/Controller/Component/ACLComponent.php index db91bc41b..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('*') From 8d44c78f4f1ec79293bb52c171b80851bcb98377 Mon Sep 17 00:00:00 2001 From: iglocska Date: Sun, 6 Jan 2019 21:22:01 +0100 Subject: [PATCH 30/31] fix: [tag collections] Fixed hook to reload galaxies --- app/Controller/AppController.php | 2 +- app/webroot/js/misp.js | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) 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/webroot/js/misp.js b/app/webroot/js/misp.js index f50b057a4..239668a9d 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -567,11 +567,13 @@ function quickSubmitTagForm(event_id, tag_id) { }, success:function (data, textStatus) { loadEventTags(event_id); + loadGalaxies(event_id, 'event'); handleGenericAjaxResponse(data); }, error:function() { showMessage('fail', 'Could not add tag.'); loadEventTags(event_id); + loadGalaxies(event_id, 'event'); }, complete:function() { $("#popover_form").fadeOut(); @@ -599,12 +601,14 @@ function quickSubmitAttributeTagForm(attribute_id, tag_id) { updateIndex(0, 'event'); } else { loadAttributeTags(attribute_id); + loadGalaxies(attribute_id, 'attribute'); } handleGenericAjaxResponse(data); }, error:function() { showMessage('fail', 'Could not add tag.'); loadAttributeTags(attribute_id); + loadGalaxies(attribute_id, 'attribute'); }, complete:function() { $("#popover_form").fadeOut(); From beabd3641704c9f221c18c33383562c488c41e75 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 7 Jan 2019 08:37:24 +0100 Subject: [PATCH 31/31] new: [config backup] Added logging and a second protective measure - if the current config.php is hosed, don't start the backup process and overwrite the backup --- app/Model/Server.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/app/Model/Server.php b/app/Model/Server.php index c9d569006..82ed522c9 100644 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -3191,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) { @@ -3245,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;