feature: Support of Tags in the event graph

pull/3154/head
Sami Mokaddem 2018-04-06 14:44:40 +00:00
parent c76160e16b
commit f3b2741843
3 changed files with 284 additions and 46 deletions

View File

@ -4307,14 +4307,14 @@ class EventsController extends AppController {
return new CakeResponse(array('body' => json_encode($json), 'status' => 200, 'type' => 'json'));
}
public function getReferences($id, $type = 'references') {
$validTools = array('references');
public function getEventGraphReferences($id, $type = 'event') {
$validTools = array('event');
if (!in_array($type, $validTools)) throw new MethodNotAllowedException('Invalid type.');
App::uses('EventGraphTool', 'Tools');
$grapher = new EventGraphTool();
$data = $this->request->is('post') ? $this->request->data : array();
$grapher->construct($this->Event, $this->Auth->user());
$json = $grapher->get_references($id, $data);
$grapher->construct($this->Event, $this->Auth->user(), $data);
$json = $grapher->get_references($id);
array_walk_recursive($json, function(&$item, $key){
if(!mb_detect_encoding($item, 'utf-8', true)){
@ -4325,6 +4325,23 @@ class EventsController extends AppController {
return new CakeResponse(array('body' => json_encode($json), 'status' => 200, 'type' => 'json'));
}
public function getEventGraphTags($id, $type = 'event') {
$validTools = array('event');
if (!in_array($type, $validTools)) throw new MethodNotAllowedException('Invalid type.');
App::uses('EventGraphTool', 'Tools');
$grapher = new EventGraphTool();
$data = $this->request->is('post') ? $this->request->data : array();
$grapher->construct($this->Event, $this->Auth->user(), $data);
$json = $grapher->get_tags($id);
array_walk_recursive($json, function(&$item, $key){
if(!mb_detect_encoding($item, 'utf-8', true)){
$item = utf8_encode($item);
}
});
$this->response->type('json');
return new CakeResponse(array('body' => json_encode($json), 'status' => 200, 'type' => 'json'));
}
public function getReferenceData($uuid, $type = 'reference') {
$validTools = array('reference');
if (!in_array($type, $validTools)) throw new MethodNotAllowedException('Invalid type.');

View File

@ -10,9 +10,10 @@
private $__related_events = array();
private $__related_attributes = array();
public function construct($eventModel, $user) {
public function construct($eventModel, $user, $filterRules) {
$this->__eventModel = $eventModel;
$this->__user = $user;
$this->__filterRules = $filterRules;
$this->__json = array();
$this->__lookupTables = array(
'analysisLevels' => $this->__eventModel->analysisLevels,
@ -44,7 +45,7 @@
private function __get_filtered_event($id) {
$event = $this->__get_event($id);
if (empty($this->filterRules)) return $event;
if (empty($this->__filterRules)) return $event;
$filtered = array('Object' => array(), 'Attribute' => array());
// perform filtering
@ -66,7 +67,7 @@
private function __satisfy_obj_filtering($obj) {
// presence rule - search in the object's attribute
$presenceMatch = true;
foreach ($this->filterRules['presence'] as $rule) {
foreach ($this->__filterRules['presence'] as $rule) {
$relation = $rule[0];
$obj_rel = $rule[1];
if($relation === "Contains") {
@ -91,9 +92,9 @@
}
private function __satisfy_val_filtering($attr, $different_type_return=true) {
if (count($this->filterRules['value']) == 0) return true;
if (count($this->__filterRules['value']) == 0) return true;
foreach ($this->filterRules['value'] as $rule) {
foreach ($this->__filterRules['value'] as $rule) {
$attr_type = $rule[0];
$comparison = $rule[1];
$attr_value = $rule[2];
@ -130,8 +131,7 @@
return false;
}
public function get_references($id, $filterRules=array()) {
$this->filterRules = $filterRules;
public function get_references($id) {
$event = $this->__get_filtered_event($id);
$this->__json['items'] = array();
$this->__json['relations'] = array();
@ -196,8 +196,103 @@
return $this->__json;
}
public function get_tag($id) {
// to do
public function get_tags($id) {
$event = $this->__get_filtered_event($id);
$this->__json['items'] = array();
$this->__json['relations'] = array();
$this->__json['existing_object_relation'] = array();
if (empty($event)) return $this->__json;
if (!empty($event['Object'])) {
$object = $event['Object'];
} else {
$object = array();
}
if (!empty($event['Attribute'])) {
$attribute = $event['Attribute'];
} else {
$attribute = array();
}
$tagSet = array();
$i = 0;
// extract links and node type
foreach ($attribute as $attr) {
$Tags = $attr['AttributeTag'];
$toPush = array(
'id' => $attr['id'],
'uuid' => $attr['uuid'],
'type' => $attr['type'],
'label' => $attr['value'],
'node_type' => 'attribute',
//'Tag' => $Tags,
);
array_push($this->__json['items'], $toPush);
foreach($Tags as $tag) {
$tag = $tag['Tag'];
$toPush = array(
'id' => 'tag_edge_id_' . $i,
'from' => $attr['id'],
'to' => $tag['name'],
);
$tagSet[$tag['name']] = $tag;
array_push($this->__json['relations'], $toPush);
$i = $i+1;
}
}
foreach ($object as $obj) {
$toPush = array(
'id' => $obj['id'],
'uuid' => $obj['uuid'],
'type' => $obj['name'],
'Attribute' => $obj['Attribute'],
'label' => '',
'node_type' => 'object',
'meta-category' => $obj['meta-category'],
'template_uuid' => $obj['template_uuid'],
);
array_push($this->__json['items'], $toPush);
// Record existing object_relation
foreach ($obj['Attribute'] as $attr) {
$this->__json['existing_object_relation'][$attr['object_relation']] = 0; // set-alike
}
// get all tags in the Object's Attributes
foreach($obj['Attribute'] as $ObjAttr) {
$Tags = $ObjAttr['AttributeTag'];
foreach($Tags as $tag) {
$tag = $tag['Tag'];
$toPush = array(
'id' => "tag_edge_id_" . $i,
'from' => $obj['id'],
'to' => $tag['name'],
);
$tagSet[$tag['name']] = $tag;
array_push($this->__json['relations'], $toPush);
$i = $i + 1;
}
}
}
// Add tags as nodes
foreach($tagSet as $tag) {
$toPush = array(
'id' => $tag['name'],
'uuid' => $tag['id'], // id is used for linking edges in vis.js, this uuid (which is not the tag uuid) is used to store the real tag id
'type' => 'tag',
'label' => $tag['name'],
'node_type' => 'tag',
'tagContent' => $tag,
);
array_push($this->__json['items'], $toPush);
}
return $this->__json;
}
public function get_distribution($id) {

View File

@ -13,9 +13,11 @@ var container = document.getElementById('eventgraph_network');
var user_manipulation = $('#eventgraph_network').data('user-manipulation');
var root_id_attr = "rootNode:attribute"
var root_id_object = "rootNode:object"
var root_id_tag = "rootNode:tag"
var mapping_root_id_to_type = {};
mapping_root_id_to_type[root_id_attr] = 'attribute';
mapping_root_id_to_type[root_id_object] = 'object';
mapping_root_id_to_type[root_id_tag] = 'tag';
var root_node_x_pos = 800;
var cluster_expand_threshold = 100;
@ -34,6 +36,7 @@ class EventGraph {
this.mapping_meta_fa.set('misc', {"meta-category": "misc","fa_text": "cube","fa-hex": "f1b2"}); // Also considered as default
// FIXME
this.network_options = network_options;
this.scope_name;
this.globalCounter = 0;
this.first_draw = true;
this.root_node_shown = false;
@ -86,11 +89,13 @@ class EventGraph {
this.globalCounter++;
return this.globalCounter-1;
}
update_scope_badge(value) {
update_scope(value) {
value = value === undefined ? $("#select_graph_scope").val() : value;
$("#network-scope-badge").text(value);
this.scope_name = value;
dataHandler.scope_name = value;
}
init_scope_menu() {
var menu_scope = new ContextualMenu({
trigger_container: document.getElementById("network-scope"),
@ -101,9 +106,10 @@ class EventGraph {
label: "Scope",
tooltip: "The scope represented by the network",
event: function(value) {
eventGraph.update_scope_badge(value);
eventGraph.update_scope(value);
dataHandler.fetch_data_and_update();
},
options: ["Event", "Reference", "Correlation", "Tag", "Distribution"],
options: ["Reference", "Correlation", "Tag", "Distribution"],
default: "Reference"
});
return menu_scope;
@ -145,6 +151,7 @@ class EventGraph {
});
return menu_physic;
}
init_display_menu() {
var menu_display = new ContextualMenu({
trigger_container: document.getElementById("network-display"),
@ -156,16 +163,16 @@ class EventGraph {
event: function(value) {
switch(value) {
case "default":
eventGraph.change_layout_type('default');
eventGraph.change_layout_type("default");
break;
case "hierarchical.directed":
eventGraph.change_layout_type('directed');
eventGraph.change_layout_type("directed");
break;
case "hierarchical.hubsize":
eventGraph.change_layout_type('hubsize');
eventGraph.change_layout_type("hubsize");
break;
default:
eventGraph.change_layout_type("default");
}
},
options: [
@ -227,6 +234,7 @@ class EventGraph {
});
return menu_display;
}
init_filter_menu() {
var menu_filter = new ContextualMenu({
trigger_container: document.getElementById("network-filter"),
@ -324,13 +332,16 @@ class EventGraph {
}
}
reset_graphs() {
reset_graphs(hard) {
this.nodes.clear();
this.edges.clear();
if (hard) {
this.backup_connection_edges = {};
}
}
update_graph(data) {
this.network_loading(true, loadingText_creating);
setTimeout(function() { eventGraph.network_loading(true, loadingText_creating); });
// New nodes will be automatically added
// removed references will be deleted
@ -341,8 +352,6 @@ class EventGraph {
var group, label;
if ( node.node_type == 'object' ) {
var group = 'object';
var label = node.type;
var label = node.label;
var label = dataHandler.generate_label(node);
var striped_value = this.strip_text_value(label);
node_conf = {
@ -360,6 +369,31 @@ class EventGraph {
}
};
dataHandler.mapping_value_to_nodeID.set(striped_value, node.id);
} else if (node.node_type == 'tag') {
var tag_color = node.tagContent.colour;
group = 'tag';
label = node.label;
node_conf = {
id: node.id,
uuid: node.uuid,
label: label,
title: label,
group: group,
mass: 20,
color: {
background: tag_color,
border: tag_color
},
font: {
color: getTextColour(tag_color),
bold: true,
size: 28
},
shapeProperties: {
borderRadius: 6
}
};
dataHandler.mapping_value_to_nodeID.set(striped_value, node.id);
} else {
group = 'attribute';
label = node.type + ': ' + node.label;
@ -382,7 +416,7 @@ class EventGraph {
var old_node_ids = this.nodes.getIds();
for (var old_id of old_node_ids) {
// Ignore root node
if (old_id == "rootNode:attribute" || old_id == "rootNode:object") {
if (old_id == "rootNode:attribute" || old_id == "rootNode:object" || old_id == "rootNode:tag") {
continue;
}
// This old node got removed
@ -422,16 +456,24 @@ class EventGraph {
this.edges.update(newRelations);
// remove root node if graph is filtered
if (this.is_filtered) {
this.remove_unreferenced_root_node();
} else {
this.remove_root_nodes();
if (this.scope_name == 'Reference') {
this.add_unreferenced_root_node();
// links unreferenced attributes and object to root nodes
if (this.first_draw) {
this.link_not_referenced_nodes();
this.first_draw = !this.first_draw
}
} else if (this.scope_name == 'Tag') {
this.add_tag_root_node();
// links untagged attributes and object to root nodes
if (this.first_draw) {
this.link_not_referenced_nodes();
this.first_draw = !this.first_draw
}
} else if (this.scope_name == 'Distribution') {
} else if (this.scope_name == 'Correlation') {
} else {
}
this.network_loading(false, "");
@ -515,7 +557,7 @@ class EventGraph {
collapse_node(parent_id) {
if(parent_id === undefined) { return; }
if (!(parent_id == root_id_attr || parent_id == root_id_object)) { // Is not a root node
if (!(parent_id == root_id_attr || parent_id == root_id_object || parent_id == root_id_tag)) { // Is not a root node
var node_group = this.nodes.get(parent_id).group;
if (parent_id === undefined || node_group != 'object') { // No node selected or collapse not permitted
return
@ -620,7 +662,6 @@ class EventGraph {
this.nodes.forEach(function(nodeData) {
var cur_id = nodeData.id;
var cur_group = nodeData.group;
var prev_nodeID = cur_group == 'attribute' ? root_id_attr : root_id_object;
// Do not link already connected nodes
if (that.network.getConnectedEdges(cur_id).length > 0) {
@ -637,21 +678,35 @@ class EventGraph {
},
length: 150
}
if (cur_group == 'attribute') {
new_edge.from = that.layout == 'default' ? root_id_attr : prev_nodeID;
that.nodes.update({id: nodeData.id, unreferenced: 'attribute'});
} else if (cur_group == 'object') {
new_edge.from = that.layout == 'default' ? root_id_object : prev_nodeID;
that.nodes.update({id: nodeData.id, unreferenced: 'object'});
if (that.scope_name == 'Reference') {
if (cur_group == 'attribute' || cur_group == 'object') {
new_edge.from = cur_group == 'attribute' ? root_id_attr : root_id_object;
that.nodes.update({id: nodeData.id, unreferenced: cur_group});
}
} else if (that.scope_name == 'Tag') {
if (cur_group == 'attribute' || cur_group == 'object') {
new_edge.from = root_id_tag;
that.nodes.update({id: nodeData.id, unreferenced: 'tag'});
}
} else if (that.scope_name == 'Distribution') {
} else if (that.scope_name == 'Correlation') {
} else {
}
newEdges.push(new_edge);
that.new_edges_for_unreferenced_nodes.push(new_edge.id);
prev_nodeID = cur_id;
});
this.edges.add(newEdges);
this.init_clusterize();
}
remove_root_nodes() {
this.remove_unreferenced_root_node();
this.remove_tag_root_node();
}
add_unreferenced_root_node() {
if (this.root_node_shown) {
return;
@ -680,12 +735,38 @@ class EventGraph {
this.root_node_shown = false;
}
add_tag_root_node() {
if (this.root_node_shown) {
return;
}
var root_node_tag = {
id: root_id_tag,
x: -root_node_x_pos,
y: 0,
label: 'Untagged Attribute',
title: 'All Attributes not being tagged',
group: 'rootNodeTag'
};
this.nodes.add([root_node_tag]);
this.root_node_shown = true;
}
remove_tag_root_node() {
this.nodes.remove([root_id_tag]);
this.root_node_shown = false;
}
switch_unreferenced_nodes_connection() {
var that = eventGraph;
var to_update = [];
for(var root_id of [root_id_attr, root_id_object]) {
var root_ids = that.scope_name == 'Reference' ? [root_id_attr, root_id_object] : [root_id_tag];
for(var root_id of root_ids) {
if(that.layout == 'default') {
var all_edgesID = that.backup_connection_edges[root_id]
if (all_edgesID === undefined) { // edgesID was not saved (happen if we switch scope then layout)
// redraw everything
eventGraph.destroy_and_redraw();
return;
}
} else {
that.network.storePositions();
var prev_node = root_id;
@ -755,6 +836,23 @@ class DataHandler {
this.mapping_obj_relation_value_to_nodeID = new Map();
this.mapping_uuid_to_template = new Map();
this.selected_type_to_display = "";
this.scope_name;
}
get_scope_url() {
switch(this.scope_name) {
case "Reference":
return "getEventGraphReferences";
case "Tag":
return "getEventGraphTags";
case "Correlation":
return "getEventGraphReferences";
case "Distribution":
return "getEventGraphReferences";
default:
return "getEventGraphReferences";
}
}
extract_references(data) {
@ -815,7 +913,6 @@ class DataHandler {
var template_uuid = obj.template_uuid;
var template_req = this.mapping_uuid_to_template.get(template_uuid);
if (template_req === undefined) { // template not known
console.log(template_uuid);
return label;
}
// search if this field exists in the object
@ -840,13 +937,14 @@ class DataHandler {
$.when(this.fetch_objects_template()).done(function() {
var filtering_rules = eventGraph.get_filtering_rules();
$.ajax({
url: "/events/getReferences/"+scope_id+"/references.json",
url: "/events/"+dataHandler.get_scope_url()+"/"+scope_id+"/event.json",
dataType: 'json',
type: 'post',
contentType: 'application/json',
data: JSON.stringify( filtering_rules ),
processData: false,
success: function( data, textStatus, jQxhr ){
eventGraph.reset_graphs(true);
eventGraph.is_filtered = (filtering_rules.presence.length > 0 || filtering_rules.value.length > 0);
eventGraph.first_draw = true;
var available_object_references = Object.keys(data.existing_object_relation);
@ -1182,7 +1280,7 @@ function enable_interactive_graph() {
});
eventGraph.update_scope_badge();
eventGraph.update_scope();
dataHandler.fetch_data_and_update();
}, 1);
}
@ -1275,6 +1373,7 @@ var network_options = {
},
},
obj_relation: {
mass: 3,
size: 10,
color: {
border:'black'
@ -1288,6 +1387,15 @@ var network_options = {
},
size: 15
},
tag: {
shape: 'box',
size: 15,
shadow: {
enabled: true,
size: 3,
x: 3, y: 3
}
},
rootNodeObject: {
shape: 'icon',
icon: {
@ -1298,7 +1406,7 @@ var network_options = {
size: 18, // px
background: 'rgba(255, 255, 255, 0.7)'
},
mass: 5
},
rootNodeAttribute: {
shape: 'icon',
@ -1310,6 +1418,19 @@ var network_options = {
size: 18, // px
background: 'rgba(255, 255, 255, 0.7)'
},
mass: 5
},
rootNodeTag: {
shape: 'icon',
icon: {
face: 'FontAwesome',
code: '\uf02b',
},
font: {
size: 22, // px
background: 'rgba(255, 255, 255, 0.7)'
},
mass: 5
},
clustered_object: {
shape: 'icon',
@ -1321,6 +1442,7 @@ var network_options = {
size: 18, // px
background: 'rgba(255, 255, 255, 0.7)'
},
mass: 5
}
},
locales: {
@ -1391,7 +1513,7 @@ function global_processProperties(clusterOptions, childNodes) {
var childrenCount = 0;
for (var i = 0; i < childNodes.length; i++) {
var childNodeID = childNodes[i].id
if ( childNodeID == "rootNode:object" || childNodeID == "rootNode:attribute") {
if ( childNodeID.includes("rootNode:")) {
concerned_root_node = childNodeID;
}
childrenCount += childNodes[i].childrenCount || 1;
@ -1410,6 +1532,10 @@ function global_processProperties(clusterOptions, childNodes) {
clusterOptions.label = "Unreferenced Attributes (" + childrenCount + ")";
clusterOptions.x = -root_node_x_pos;
clusterOptions.group = 'rootNodeAttribute';
} else if (concerned_root_node == "rootNode:tag") {
clusterOptions.label = "Untagged elements (" + childrenCount + ")";
clusterOptions.x = -root_node_x_pos;
clusterOptions.group = 'rootNodeTag';
}
}
clusterOptions.y = 0