new: More work on the objects

- mostly on adding / validating / saving objects including the UI for it
pull/2489/head
iglocska 2017-07-02 20:32:30 +02:00
parent d10c7cd4b0
commit d2e1a8e259
7 changed files with 374 additions and 150 deletions

View File

@ -48,28 +48,50 @@ class ObjectsController extends AppController {
)
));
$eventId = $event['Event']['id'];
$error = false;
// If we have received a POST request
if ($this->request->is('post')) {
if (isset($this->request->data['request'])) {
$this->request->data = $this->request->data['request'];
}
if (!isset($this->request->data['Object'])) {
$this->request->data = array('Object' => $this->request->data);
if (!isset($this->request->data['Attribute'])) {
$this->request->data = array('Attribute' => $this->request->data);
}
$templateCheckResult = $this->Object->ObjectTemplate->checkTemplateConformity($templateId, $this->request->data);
if (!$templateCheckResult) {
throw new MethodNotAllowedException('Object does not meet the template requirements');
$object = $this->Object->attributeCleanup($this->request->data);
// we pre-validate the attributes before we create an object at this point
// This allows us to stop the process and return an error (API) or return
// to the add form
foreach ($object['Attribute'] as $k => $attribute) {
$object['Attribute'][$k]['event_id'] = $eventId;
$this->Object->Event->Attribute->set($attribute);
if (!$this->Object->Event->Attribute->validates()) {
$error = 'Could not save object as at least one attribute has failed validation (' . $attribute['object_relation'] . '). ' . json_encode($this->Object->Event->Attribute->validationErrors);
}
}
if (empty($error)) {
$error = $this->Object->ObjectTemplate->checkTemplateConformity($template, $object);
if ($error === true) {
$this->Object->saveObject($object, $eventId, $template, $this->Auth->user(), $errorBehaviour = 'halt');
}
}
$this->Object->saveObject($this->request->data, $eventId, $errorBehaviour = 'halt');
}
// In the case of a GET request or if the object could not be validated, show the form / the requirement
if ($this->_isRest()) {
return $this->RestResponse->viewData($orgs, $this->response->type());
if ($error) {
} else {
return $this->RestResponse->viewData($orgs, $this->response->type());
}
} else {
if (!empty($error)) {
$this->Session->setFlash($error);
}
$template = $this->Object->prepareTemplate($template);
//debug($template);
$enabledRows = array_keys($template['ObjectTemplateElement']);
$this->set('enabledRows', $enabledRows);
$distributionData = $this->Object->Event->Attribute->fetchDistributionData($this->Auth->user());
$this->set('distributionData', $distributionData);
$this->set('event', $event);
$this->set('ajax', false);
$this->set('template', $template);

View File

@ -644,6 +644,10 @@ class Attribute extends AppModel {
public function valueIsUnique ($fields) {
if (isset($this->data['Attribute']['deleted']) && $this->data['Attribute']['deleted']) return true;
// We escape this rule for objects as we can have the same category/type/value combination in different objects
if (!empty($this->data['Attribute']['object_relation'])) {
return true;
}
$value = $fields['value'];
$eventId = $this->data['Attribute']['event_id'];
$type = $this->data['Attribute']['type'];
@ -2538,4 +2542,22 @@ class Attribute extends AppModel {
}
return $cidrList;
}
public function fetchDistributionData($user) {
$initialDistribution = 5;
if (Configure::read('MISP.default_attribute_distribution') != null) {
if (Configure::read('MISP.default_attribute_distribution') === 'event') {
$initialDistribution = 5;
} else {
$initialDistribution = Configure::read('MISP.default_attribute_distribution');
}
}
$sgs = $this->SharingGroup->fetchAllAuthorised($user, 'name', 1);
$this->set('sharingGroups', $sgs);
$distributionLevels = $this->distributionLevels;
if (empty($sgs)) {
unset($distributionLevels[4]);
}
return array('sgs' => $sgs, 'levels' => $distributionLevels, 'initial' => $initialDistribution);
}
}

View File

@ -42,11 +42,25 @@ class Object extends AppModel {
public $validate = array(
);
public function saveObject($object, $eventId, $errorBehaviour = 'drop') {
$this->Object->create();
$this->request->data['Object']['event_id'] = $eventId;
$this->Object->save($this->request->data);
return $this->Object->Attribute->saveAttributes($attributes, $eventId, $objectId = 0, $errorBehaviour);
public function saveObject($object, $eventId, $template, $user, $errorBehaviour = 'drop') {
$this->create();
//id name meta-category description template_uuid template_version event_id uuid timestamp distribution sharing_group_id comment
$templateFields = array(
'name' => 'name',
'meta-category' => 'meta-category',
'description' => 'description',
'template_version' => 'version',
'template_uuid' => 'uuid'
);
foreach ($templateFields as $k => $v) {
$object['Object'][$k] = $template['ObjectTemplate'][$v];
}
$object['Object']['event_id'] = $eventId;
debug($template);
debug($object);
throw new Exception();
$this->save($object);
return $this->Attribute->saveAttributes($attributes, $eventId, $objectId = 0, $errorBehaviour);
}
public function buildEventConditions($user, $sgids = false) {
@ -212,6 +226,9 @@ class Object extends AppModel {
return $results;
}
/*
* Prepare the template form view's data, setting defaults, sorting elements
*/
public function prepareTemplate($template) {
$temp = array();
usort($template['ObjectTemplateElement'], function($a, $b) {
@ -220,13 +237,38 @@ class Object extends AppModel {
foreach ($template['ObjectTemplateElement'] as $k => $v) {
$template['ObjectTemplateElement'][$k]['default_category'] = $this->Event->Attribute->typeDefinitions[$template['ObjectTemplateElement'][$k]['type']]['default_category'];
$template['ObjectTemplateElement'][$k]['to_ids'] = $this->Event->Attribute->typeDefinitions[$template['ObjectTemplateElement'][$k]['type']]['to_ids'];
$template['ObjectTemplateElement'][$k]['categories'] = array();
foreach ($this->Event->Attribute->categoryDefinitions as $catk => $catv) {
if (in_array($template['ObjectTemplateElement'][$k]['type'], $catv['types'])) {
$template['ObjectTemplateElement'][$k]['categories'][$catk] = $catk;
if (empty($template['ObjectTemplateElement'][$k]['categories'])) {
$template['ObjectTemplateElement'][$k]['categories'] = array();
foreach ($this->Event->Attribute->categoryDefinitions as $catk => $catv) {
if (in_array($template['ObjectTemplateElement'][$k]['type'], $catv['types'])) {
$template['ObjectTemplateElement'][$k]['categories'][] = $catk;
}
}
}
}
return $template;
}
/*
* Clean the attribute list up from artifacts introduced by the object form
*/
public function attributeCleanup($attributes) {
foreach ($attributes['Attribute'] as $k => $attribute) {
if (isset($attribute['save']) && $attribute['save'] == 0) {
unset($attributes['Attribute'][$k]);
continue;
}
if (isset($attribute['value_select'])) {
if ($attribute['value_select'] !== 'Enter value manually') {
$attributes['Attribute'][$k]['value'] = $attribute['value_select'];
}
unset($attribute['value_select']);
}
unset($attribute['save']);
if ($attribute['distribution'] != 4) {
$attribute['sharing_group_id'] = 0;
}
}
return $attributes;
}
}

View File

@ -176,4 +176,32 @@ class ObjectTemplate extends AppModel {
}
return $result;
}
public function checkTemplateConformity($template, $attributes) {
if (!empty($template['ObjectTemplate']['requirements'])) {
if (!empty($template['ObjectTemplate']['requirements']['required'])) {
foreach ($template['ObjectTemplate']['requirements']['required'] as $requiredField) {
$found = false;
foreach ($attributes['Attribute'] as $attribute) {
if ($attribute['object_relation'] == $requiredField) {
$found = true;
}
}
if (!$found) return 'Could not save the object as a required attribute is not set (' . $requiredField . ')';
}
}
if (!empty($template['ObjectTemplate']['requirements']['requiredOneOf'])) {
$found = false;
foreach ($template['ObjectTemplate']['requirements']['requiredOneOf'] as $requiredField) {
foreach ($attributes['Attribute'] as $attribute) {
if ($attribute['object_relation'] == $requiredField) {
$found = true;
}
}
}
if (!$found) return 'Could not save the object as it requires at least one of the following attributes to be set: ' . implode(', ', $template['ObjectTemplate']['requirements']['requiredOneOf']);
}
}
return true;
}
}

View File

@ -75,10 +75,10 @@ foreach ($feeds as $item):
<?php
echo h($item['Feed']['name']);
if ($item['Feed']['default']):
?>
?>
<img src="<?php echo $baseurl;?>/img/orgs/MISP.png" width="24" height="24" style="padding-bottom:3px;" />
<?php
endif;
<?php
endif;
?>
</td>
<td><?php echo $feed_types[$item['Feed']['source_format']]['name']; ?>&nbsp;</td>

View File

@ -1,20 +1,16 @@
<div class="<?php if (!isset($ajax) || !$ajax) echo 'form';?>">
<div>
<?php
echo $this->Form->create('Object', array('id', 'url' => '/objects/add/' . $event['Event']['id'] . '/' . $template['ObjectTemplate']['id']));
echo $this->Form->input('data', array(
'style' => 'display:none;',
'label' => false
));
echo $this->Form->end();
?>
</div>
<?php
echo $this->Form->create('Object', array('id', 'url' => '/objects/add/' . $event['Event']['id'] . '/' . $template['ObjectTemplate']['id'], 'enctype' => 'multipart/form-data'));
?>
<h3><?php echo 'Add ' . Inflector::humanize(h($template['ObjectTemplate']['name'])) . ' Object'; ?></h3>
<div class="row-fluid">
<div class="row-fluid" style="margin-bottom:10px;">
<dl class="span8">
<dt>Object Template</dt>
<dd>
<?php echo Inflector::humanize(h($template['ObjectTemplate']['name'])); ?>&nbsp;
<?php
echo Inflector::humanize(h($template['ObjectTemplate']['name']));
?>
&nbsp;
</dd>
<dt>Description</dt>
<dd>
@ -41,67 +37,188 @@
<dd>
<?php echo Inflector::humanize(h($template['ObjectTemplate']['meta-category'])); ?>&nbsp;
</dd>
<dt>Distribution</dt>
<dd>
<?php
echo $this->Form->input('Object.distribution', array(
'class' => 'Object_distribution_select',
'options' => $distributionData['levels'],
'default' => $distributionData['initial'],
'label' => false,
'style' => 'margin-bottom:0px;',
'div' => false
));
echo $this->Form->input('Object.sharing_group_id', array(
'class' => 'Object_sharing_group_id_select',
'options' => $distributionData['sgs'],
'label' => false,
'div' => false,
'style' => 'display:none;margin-bottom:0px;'
));
?>
</dd>
</dl>
<table class="table table-striped table-condensed">
<tr>
<th>Name</th>
<th>Type</th>
<th>Category</th>
<th>Value</th>
<th>Description</th>
<th>To IDS</th>
<th>Distribution</th>
</tr>
<?php
foreach ($template['ObjectTemplateElement'] as $k => $element):
?>
<tr>
<td class="shortish bold">
<?php echo Inflector::humanize(h($element['in-object-name'])); ?>
</td>
<td class="short">
<?php echo h($element['type']); ?>
</td>
<td class="short">
<select>
<?php
foreach ($element['categories'] as $category):
?>
<option id="category_select_<?php echo h($k); ?>" value="<?php echo h($category); ?>" <?php echo $category == $element['default_category'] ? 'selected' : ''; ?>>
<?php echo h($category);?>
</option>
<?php
endforeach;
?>
</select>
</td>
<td>
<?php
if (empty($element['values_list'])):
?>
<textarea id="value_select_<?php echo h($k); ?>" class="input" style="height:20px;width:400px;" <?php echo 'list="value_select_list_' . $k . '"'; ?>></textarea>
<?php
if (!empty($elements['sane_default'])):
?>
<datalist id="exampleList">
<option value="A">
<option value="B">
</datalist>
<?php
endif;
else:
?>
<?php
endif;
?>
</td>
</tr>
<?php
endforeach;
?>
</table>
</div>
<table class="table table-striped table-condensed">
<tr>
<th>Save</th>
<th>Name :: type</th>
<th>Category</th>
<th>Value</th>
<th>IDS</th>
<th>Distribution</th>
<th>Comment</th>
</tr>
<?php
$row_list = array();
foreach ($template['ObjectTemplateElement'] as $k => $element):
$row_list[] = $k;
?>
<tr>
<td>
<?php
echo $this->Form->input('Attribute.' . $k . '.save', array(
'type' => 'checkbox',
'checked' => in_array($k, $enabledRows),
'label' => false,
'div' => false
));
?>
</td>
<td class="shortish" title="<?php echo h($element['description']); ?>">
<?php
echo $this->Form->input('Attribute.' . $k . '.object_relation', array(
'type' => 'hidden',
'value' => $element['in-object-name'],
'label' => false,
'div' => false
));
echo $this->Form->input('Attribute.' . $k . '.type', array(
'type' => 'hidden',
'value' => $element['type'],
'label' => false,
'div' => false
));
echo '<span class="bold">' . Inflector::humanize(h($element['in-object-name'])) . '</span>';
if (!empty($template['ObjectTemplate']['requirements']['required']) && in_array($element['in-object-name'], $template['ObjectTemplate']['requirements']['required'])) {
echo '<span class="bold red">' . '(*)' . '</span>';
}
echo ' :: ' . h($element['type']) . '';
?>
</td>
<td class="short">
<?php
echo $this->Form->input('Attribute.' . $k . '.category', array(
'options' => array_combine($element['categories'], $element['categories']),
'default' => $element['default_category'],
'style' => 'margin-bottom:0px;',
'label' => false,
'div' => false
));
?>
</td>
<td>
<?php
if ($element['type'] == 'malware-sample' || $element['type'] == 'attachment'):
echo $this->Form->file('Attribute.' . $k . '.Attachment', array(
'class' => 'Attribute_attachment'
));
else:
if (empty($element['values_list']) && empty($element['sane_default'])):
echo $this->Form->input('Attribute.' . $k . '.value', array(
'type' => 'textarea',
'required' => false,
'allowEmpty' => true,
'style' => 'height:20px;width:400px;',
'label' => false,
'div' => false
));
else:
if (empty($element['values_list'])) {
$list = $element['sane_default'];
$list[] = 'Enter value manually';
} else {
$list = $element['values_list'];
}
$list = array_combine($list, $list);
?>
<div class="value_select_with_manual_entry">
<?php
echo $this->Form->input('Attribute.' . $k . '.value_select', array(
'class' => 'Attribute_value_select',
'style' => 'width:414px;margin-bottom:0px;',
'options' => array_combine($list, $list),
'label' => false,
'div' => false
));
?>
<br />
<?php
echo $this->Form->input('Attribute.' . $k . '.value', array(
'class' => 'Attribute_value',
'type' => 'textarea',
'required' => false,
'allowEmpty' => true,
'style' => 'height:20px;width:400px;display:none;',
'label' => false,
'div' => false
));
?>
</div>
<?php
endif;
endif;
?>
</td>
<td>
<?php
echo $this->Form->input('Attribute.' . $k . '.to_ids', array(
'type' => 'checkbox',
'checked' => $element['to_ids'],
'label' => false,
'div' => false
));
?>
</td>
<td class="short">
<?php
echo $this->Form->input('Attribute.' . $k . '.distribution', array(
'class' => 'Attribute_distribution_select',
'options' => $distributionData['levels'],
'default' => $distributionData['initial'],
'style' => 'margin-bottom:0px;',
'label' => false,
'div' => false
));
?>
<br />
<?php
echo $this->Form->input('Attribute.' . $k . '.sharing_group_id', array(
'class' => 'Attribute_sharing_group_id_select',
'options' => $distributionData['sgs'],
'label' => false,
'div' => false,
'style' => 'display:none;margin-bottom:0px;',
));
?>
</td>
<td>
<?php
echo $this->Form->input('Attribute.' . $k . '.comment', array(
'type' => 'textarea',
'style' => 'height:20px;width:400px;',
'required' => false,
'allowEmpty' => true,
'label' => false,
'div' => false
));
?>
</td>
</tr>
<?php
endforeach;
?>
</table>
<?php if ($ajax): ?>
<div class="overlay_spacing">
<table>
@ -134,65 +251,20 @@
}
?>
<script type="text/javascript">
var fieldsArray = new Array('AttributeCategory', 'AttributeType', 'AttributeValue', 'AttributeDistribution', 'AttributeComment', 'AttributeToIds', 'AttributeBatchImport', 'AttributeSharingGroupId');
<?php
$formInfoTypes = array('distribution' => 'Distribution', 'category' => 'Category', 'type' => 'Type');
echo 'var formInfoFields = ' . json_encode($formInfoTypes) . PHP_EOL;
foreach ($formInfoTypes as $formInfoType => $humanisedName) {
echo 'var ' . $formInfoType . 'FormInfoValues = {' . PHP_EOL;
foreach ($info[$formInfoType] as $key => $formInfoData) {
echo '"' . $key . '": "<span class=\"blue bold\">' . h($formInfoData['key']) . '</span>: ' . h($formInfoData['desc']) . '<br />",' . PHP_EOL;
}
echo '}' . PHP_EOL;
}
?>
var rows = <?php echo json_encode($row_list, true); ?>;
$(document).ready(function() {
enableDisableObjectRows(rows);
//
//Generate Category / Type filtering array
//
var category_type_mapping = new Array();
<?php
foreach ($categoryDefinitions as $category => $def) {
echo "category_type_mapping['" . addslashes($category) . "'] = {";
$first = true;
foreach ($def['types'] as $type) {
if ($first) $first = false;
else echo ', ';
echo "'" . addslashes($type) . "' : '" . addslashes($type) . "'";
}
echo "}; \n";
}
?>
$(".Attribute_distribution_select").change(function() {
checkAndEnable($(this).parent().find('.Attribute_sharing_group_id_select'), $(this).val() == 4);
});
$(document).ready(function() {
initPopoverContent('Attribute');
$('#AttributeDistribution').change(function() {
if ($('#AttributeDistribution').val() == 4) $('#SGContainer').show();
else $('#SGContainer').hide();
});
$(".Object_distribution_select").change(function() {
checkAndEnable($(this).parent().find('.Object_sharing_group_id_select'), $(this).val() == 4);
});
$("#AttributeCategory").on('change', function(e) {
formCategoryChanged('Attribute');
if ($(this).val() === 'Attribution' || $(this).val() === 'Targeting data') {
$("#warning-message").show();
} else {
$("#warning-message").hide();
}
if ($(this).val() === 'Internal reference') {
$("#AttributeDistribution").val('0');
$('#SGContainer').hide();
}
});
$("#AttributeCategory, #AttributeType, #AttributeDistribution").change(function() {
initPopoverContent('Attribute');
});
<?php if ($ajax): ?>
$('#cancel_attribute_add').click(function() {
cancelPopoverForm();
});
<?php endif; ?>
});
$(".Attribute_value_select").change(function() {
checkAndEnable($(this).parent().find('.Attribute_value'), $(this).val() == 'Enter value manually');
});
});
</script>
<?php echo $this->Js->writeBuffer(); // Write cached scripts

View File

@ -3069,6 +3069,44 @@ $(".cortex-json").click(function() {
$("#gray_out").fadeIn();
});
// Show $(id) if the enable parameter evaluates to true. Hide it otherwise
function checkAndEnable(id, enable) {
if (enable) {
$(id).show();
} else {
$(id).hide();
}
}
// Show and enable checkbox $(id) if the enable parameter evaluates to true. Hide and disable it otherwise.
function checkAndEnableCheckbox(id, enable) {
if (enable) {
$(id).removeAttr("disabled");
$(id).prop('checked', true);
} else {
$(id).prop('checked', false);
$(id).attr("disabled", true);
}
}
function enableDisableObjectRows(rows) {
rows.forEach(function(i) {
if ($("#Attribute" + i + "ValueSelect").length != 0) {
checkAndEnableCheckbox("#Attribute" + i + "Save", true);
} else if ($("#Attribute" + i + "Attachment").length != 0) {
checkAndEnableCheckbox("#Attribute" + i + "Save", $("#Attribute" + i + "Attachment").val() != "");
} else {
checkAndEnableCheckbox("#Attribute" + i + "Save", $("#Attribute" + i + "Value").val() != "");
}
$("#Attribute" + i + "Value").bind('input propertychange', function() {
checkAndEnableCheckbox("#Attribute" + i + "Save", $(this).val() != "");
});
$("#Attribute" + i + "Attachment").on('change', function() {
checkAndEnableCheckbox("#Attribute" + i + "Save", $("#Attribute" + i + "Attachment").val() != "");
});
});
}
(function(){
"use strict";
$(".datepicker").datepicker({