mirror of https://github.com/MISP/MISP
new: [WiP] edit refactor
parent
d744e6f3d5
commit
d015f9cae7
|
@ -2794,7 +2794,14 @@ class EventsController extends AppController
|
|||
$this->request->data = array('Event' => $this->request->data);
|
||||
}
|
||||
$fast_update = $this->request->param('named.fast_update');
|
||||
$result = $this->Event->_edit($this->request->data, $this->Auth->user(), $id, null, null, false, $fast_update);
|
||||
if (!empty($this->request->data['Event']['fast_update'])) {
|
||||
$fast_update = (bool)$this->request->data['Event']['fast_update'];
|
||||
}
|
||||
if ($fast_update) {
|
||||
$this->Event->fast_update = true;
|
||||
$this->Event->Attribute->fast_update = true;
|
||||
}
|
||||
$result = $this->Event->_edit($this->request->data, $this->Auth->user(), $id, null, null, false);
|
||||
if ($result === true) {
|
||||
// REST users want to see the newly created event
|
||||
$metadata = $this->request->param('named.metadata');
|
||||
|
|
|
@ -83,6 +83,8 @@ class Attribute extends AppModel
|
|||
/** @var array */
|
||||
private $old;
|
||||
|
||||
private $updateLookupTable = [];
|
||||
|
||||
public function __construct($id = false, $table = null, $ds = null)
|
||||
{
|
||||
parent::__construct($id, $table, $ds);
|
||||
|
@ -327,6 +329,8 @@ class Attribute extends AppModel
|
|||
'sha512' => 128,
|
||||
);
|
||||
|
||||
public $fast_update = false;
|
||||
|
||||
public function afterFind($results, $primary = false)
|
||||
{
|
||||
foreach ($results as &$v) {
|
||||
|
@ -347,7 +351,7 @@ class Attribute extends AppModel
|
|||
if (empty($attribute['uuid'])) {
|
||||
$attribute['uuid'] = CakeText::uuid();
|
||||
}
|
||||
if (!empty($attribute['id'])) {
|
||||
if (!$this->fast_update && !empty($attribute['id'])) {
|
||||
$this->old = $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Attribute.id' => $attribute['id']),
|
||||
|
@ -439,37 +443,41 @@ class Attribute extends AppModel
|
|||
$this->AttributeTag->attachTagToAttribute($this->id, $attribute['event_id'], $tagId);
|
||||
}
|
||||
}
|
||||
// update correlation...
|
||||
if (isset($attribute['deleted']) && $attribute['deleted']) {
|
||||
$this->Correlation->beforeSaveCorrelation($attribute);
|
||||
$this->Correlation->advancedCorrelationsUpdate($attribute);
|
||||
if (isset($attribute['event_id'])) {
|
||||
$this->__alterAttributeCount($attribute['event_id'], false);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Only recorrelate if:
|
||||
* - We are dealing with a new attribute OR
|
||||
* - The existing attribute's previous state is known AND
|
||||
* value, type, disable correlation or distribution have changed
|
||||
* This will avoid recorrelations when it's not really needed, such as adding a tag
|
||||
*/
|
||||
if (!$created) {
|
||||
if (
|
||||
empty($this->old) ||
|
||||
$attribute['value'] != $this->old['Attribute']['value'] ||
|
||||
$attribute['disable_correlation'] != $this->old['Attribute']['disable_correlation'] ||
|
||||
$attribute['type'] != $this->old['Attribute']['type'] ||
|
||||
$attribute['distribution'] != $this->old['Attribute']['distribution'] ||
|
||||
$attribute['sharing_group_id'] != $this->old['Attribute']['sharing_group_id']
|
||||
) {
|
||||
$this->Correlation->beforeSaveCorrelation($attribute);
|
||||
// Let's store all the uuid -> ID lookups so we can extract the IDs after a SaveMany() easily
|
||||
$this->updateLookupTable[$attribute['uuid']] = $attribute['id'];
|
||||
if (!$this->fast_update) {
|
||||
// update correlation...
|
||||
if (isset($attribute['deleted']) && $attribute['deleted']) {
|
||||
$this->Correlation->beforeSaveCorrelation($attribute);
|
||||
$this->Correlation->advancedCorrelationsUpdate($attribute);
|
||||
if (isset($attribute['event_id'])) {
|
||||
$this->__alterAttributeCount($attribute['event_id'], false);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Only recorrelate if:
|
||||
* - We are dealing with a new attribute OR
|
||||
* - The existing attribute's previous state is known AND
|
||||
* value, type, disable correlation or distribution have changed
|
||||
* This will avoid recorrelations when it's not really needed, such as adding a tag
|
||||
*/
|
||||
if (!$created) {
|
||||
if (
|
||||
empty($this->old) ||
|
||||
$attribute['value'] != $this->old['Attribute']['value'] ||
|
||||
$attribute['disable_correlation'] != $this->old['Attribute']['disable_correlation'] ||
|
||||
$attribute['type'] != $this->old['Attribute']['type'] ||
|
||||
$attribute['distribution'] != $this->old['Attribute']['distribution'] ||
|
||||
$attribute['sharing_group_id'] != $this->old['Attribute']['sharing_group_id']
|
||||
) {
|
||||
$this->Correlation->beforeSaveCorrelation($attribute);
|
||||
$this->Correlation->afterSaveCorrelation($attribute, false, $passedEvent);
|
||||
$this->Correlation->advancedCorrelationsUpdate($attribute);
|
||||
}
|
||||
} else {
|
||||
$this->Correlation->afterSaveCorrelation($attribute, false, $passedEvent);
|
||||
$this->Correlation->advancedCorrelationsUpdate($attribute);
|
||||
}
|
||||
} else {
|
||||
$this->Correlation->afterSaveCorrelation($attribute, false, $passedEvent);
|
||||
$this->Correlation->advancedCorrelationsUpdate($attribute);
|
||||
}
|
||||
}
|
||||
$result = true;
|
||||
|
@ -484,47 +492,49 @@ class Attribute extends AppModel
|
|||
$result = $this->saveAttachment($attribute);
|
||||
}
|
||||
}
|
||||
$pubToZmq = $this->pubToZmq('attribute');
|
||||
$kafkaTopic = $this->kafkaTopic('attribute');
|
||||
$isTriggerCallable = $this->isTriggerCallable('attribute-after-save');
|
||||
if ($pubToZmq || $kafkaTopic || $isTriggerCallable) {
|
||||
$attributeForPublish = $this->fetchAttribute($this->id);
|
||||
if (!empty($attributeForPublish)) {
|
||||
$user = array(
|
||||
'org_id' => -1,
|
||||
'Role' => array(
|
||||
'perm_site_admin' => 1
|
||||
)
|
||||
);
|
||||
$attributeForPublish['Attribute']['Sighting'] = $this->Sighting->attachToEvent($attributeForPublish, $user, $attributeForPublish);
|
||||
$action = $created ? 'add' : 'edit';
|
||||
if (!empty($attribute['deleted'])) {
|
||||
$action = 'soft-delete';
|
||||
}
|
||||
if ($pubToZmq) {
|
||||
if (Configure::read('Plugin.ZeroMQ_include_attachments') && $this->typeIsAttachment($attributeForPublish['Attribute']['type'])) {
|
||||
$attributeForPublish['Attribute']['data'] = $this->base64EncodeAttachment($attributeForPublish['Attribute']);
|
||||
if (!$this->fast_update) {
|
||||
$pubToZmq = $this->pubToZmq('attribute');
|
||||
$kafkaTopic = $this->kafkaTopic('attribute');
|
||||
$isTriggerCallable = $this->isTriggerCallable('attribute-after-save');
|
||||
if ($pubToZmq || $kafkaTopic || $isTriggerCallable) {
|
||||
$attributeForPublish = $this->fetchAttribute($this->id);
|
||||
if (!empty($attributeForPublish)) {
|
||||
$user = array(
|
||||
'org_id' => -1,
|
||||
'Role' => array(
|
||||
'perm_site_admin' => 1
|
||||
)
|
||||
);
|
||||
$attributeForPublish['Attribute']['Sighting'] = $this->Sighting->attachToEvent($attributeForPublish, $user, $attributeForPublish);
|
||||
$action = $created ? 'add' : 'edit';
|
||||
if (!empty($attribute['deleted'])) {
|
||||
$action = 'soft-delete';
|
||||
}
|
||||
$pubSubTool = $this->getPubSubTool();
|
||||
$pubSubTool->attribute_save($attributeForPublish, $action);
|
||||
unset($attributeForPublish['Attribute']['data']);
|
||||
}
|
||||
if ($kafkaTopic) {
|
||||
if (Configure::read('Plugin.Kafka_include_attachments') && $this->typeIsAttachment($attributeForPublish['Attribute']['type'])) {
|
||||
$attributeForPublish['Attribute']['data'] = $this->base64EncodeAttachment($attributeForPublish['Attribute']);
|
||||
if ($pubToZmq) {
|
||||
if (Configure::read('Plugin.ZeroMQ_include_attachments') && $this->typeIsAttachment($attributeForPublish['Attribute']['type'])) {
|
||||
$attributeForPublish['Attribute']['data'] = $this->base64EncodeAttachment($attributeForPublish['Attribute']);
|
||||
}
|
||||
$pubSubTool = $this->getPubSubTool();
|
||||
$pubSubTool->attribute_save($attributeForPublish, $action);
|
||||
unset($attributeForPublish['Attribute']['data']);
|
||||
}
|
||||
if ($kafkaTopic) {
|
||||
if (Configure::read('Plugin.Kafka_include_attachments') && $this->typeIsAttachment($attributeForPublish['Attribute']['type'])) {
|
||||
$attributeForPublish['Attribute']['data'] = $this->base64EncodeAttachment($attributeForPublish['Attribute']);
|
||||
}
|
||||
$kafkaPubTool = $this->getKafkaPubTool();
|
||||
$kafkaPubTool->publishJson($kafkaTopic, $attributeForPublish, $action);
|
||||
}
|
||||
if ($isTriggerCallable) {
|
||||
$workflowErrors = [];
|
||||
$logging = [
|
||||
'model' => 'Attribute',
|
||||
'action' => $action,
|
||||
'id' => $attributeForPublish['Attribute']['id'],
|
||||
];
|
||||
$triggerData = $attributeForPublish;
|
||||
$this->executeTrigger('attribute-after-save', $triggerData, $workflowErrors, $logging);
|
||||
}
|
||||
$kafkaPubTool = $this->getKafkaPubTool();
|
||||
$kafkaPubTool->publishJson($kafkaTopic, $attributeForPublish, $action);
|
||||
}
|
||||
if ($isTriggerCallable) {
|
||||
$workflowErrors = [];
|
||||
$logging = [
|
||||
'model' => 'Attribute',
|
||||
'action' => $action,
|
||||
'id' => $attributeForPublish['Attribute']['id'],
|
||||
];
|
||||
$triggerData = $attributeForPublish;
|
||||
$this->executeTrigger('attribute-after-save', $triggerData, $workflowErrors, $logging);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -623,11 +633,13 @@ class Attribute extends AppModel
|
|||
// make some changes to the inserted value
|
||||
$attribute['value'] = AttributeValidationTool::modifyBeforeValidation($type, $attribute['value']);
|
||||
// Run user defined regexp to attribute value
|
||||
$result = $this->runRegexp($type, $attribute['value']);
|
||||
if ($result === false) {
|
||||
$this->invalidate('value', 'This value is blocked by a regular expression in the import filters.');
|
||||
} else {
|
||||
$attribute['value'] = $result;
|
||||
if (!$this->fast_update) {
|
||||
$result = $this->runRegexp($type, $attribute['value']);
|
||||
if ($result === false) {
|
||||
$this->invalidate('value', 'This value is blocked by a regular expression in the import filters.');
|
||||
} else {
|
||||
$attribute['value'] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($attribute['comment'])) {
|
||||
|
@ -676,7 +688,6 @@ class Attribute extends AppModel
|
|||
$attribute['disable_correlation'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// return true, otherwise the object cannot be saved
|
||||
return true;
|
||||
}
|
||||
|
@ -729,6 +740,13 @@ class Attribute extends AppModel
|
|||
*/
|
||||
public function valueIsUnique($fields)
|
||||
{
|
||||
// This is somewhat dangerous, fast_update assumes that you are just updating an existing attribute's
|
||||
// non uniqueness modifying fields (first/last seen, comment, tags, timestamp, etc)
|
||||
// By ignoring this warning, you are introducing potential duplicates.
|
||||
if ($this->fast_update) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!empty($this->data['Attribute']['deleted'])) {
|
||||
return true;
|
||||
}
|
||||
|
@ -2555,8 +2573,11 @@ class Attribute extends AppModel
|
|||
return $attribute;
|
||||
}
|
||||
|
||||
public function editAttribute($attribute, array $event, $user, $objectId, $log = false, $force = false, &$nothingToChange = false, $server = null, $fast_update = false)
|
||||
public function editAttribute($attribute, array $event, $user, $objectId, $log = false, $force = false, &$nothingToChange = false, $server = null)
|
||||
{
|
||||
if ($this->fast_update) {
|
||||
$this->Behaviors->unload('SysLogLogable.SysLogLogable');
|
||||
}
|
||||
$eventId = $event['Event']['id'];
|
||||
$attribute['event_id'] = $eventId;
|
||||
$attribute['object_id'] = $objectId;
|
||||
|
@ -2632,13 +2653,16 @@ class Attribute extends AppModel
|
|||
|
||||
$saveOptions = [
|
||||
'fieldList' => $fieldList,
|
||||
'parentEvent' => $event
|
||||
'parentEvent' => $event,
|
||||
'atomic' => false
|
||||
];
|
||||
|
||||
if ($fast_update) {
|
||||
$saveOptions['callbacks'] = false;
|
||||
}
|
||||
|
||||
// This is somewhat dangerous, fast_update assumes that you are just updating an existing attribute's
|
||||
// non uniqueness modifying fields (first/last seen, comment, tags, timestamp, etc)
|
||||
// By ignoring this warning, you are introducing potential duplicates.
|
||||
if ($this->fast_update) {
|
||||
$saveOptions['skipAuditLog'] = true;
|
||||
}
|
||||
if (!$this->save(['Attribute' => $attribute], $saveOptions)) {
|
||||
$this->logDropped($user, $attribute, 'edit');
|
||||
return $this->validationErrors;
|
||||
|
@ -2677,6 +2701,197 @@ class Attribute extends AppModel
|
|||
return true;
|
||||
}
|
||||
|
||||
public function editAttribute2($attribute, array $event, $user, $objectId, $log = false, $force = false, &$nothingToChange = false, $server = null)
|
||||
{
|
||||
if ($this->fast_update) {
|
||||
$this->Behaviors->unload('SysLogLogable.SysLogLogable');
|
||||
}
|
||||
$eventId = $event['Event']['id'];
|
||||
$attribute['event_id'] = $eventId;
|
||||
$attribute['object_id'] = $objectId;
|
||||
if (isset($attribute['encrypt'])) {
|
||||
$attribute = $this->onDemandEncrypt($attribute);
|
||||
}
|
||||
unset($attribute['id']);
|
||||
if (isset($attribute['uuid'])) {
|
||||
$existingAttribute = $this->find('first', array(
|
||||
'conditions' => array('Attribute.uuid' => $attribute['uuid']),
|
||||
'recursive' => -1,
|
||||
));
|
||||
if (!empty($existingAttribute)) {
|
||||
if ($existingAttribute['Attribute']['event_id'] != $eventId || $existingAttribute['Attribute']['object_id'] != $objectId) {
|
||||
$change = 'An attribute was blocked from being saved due to a duplicate UUID. The uuid in question is: ' . $attribute['uuid'] . '. This can also be due to the same attribute (or an attribute with the same UUID) existing in a different event / object)';
|
||||
$this->loadLog()->createLogEntry($user, 'edit', 'Attribute', 0, 'Duplicate UUID found in attribute', $change);
|
||||
return true;
|
||||
}
|
||||
// If a field is not set in the request, just reuse the old value
|
||||
$recoverFields = array('value', 'to_ids', 'distribution', 'category', 'type', 'comment', 'sharing_group_id', 'object_id', 'object_relation', 'first_seen', 'last_seen');
|
||||
foreach ($recoverFields as $rF) {
|
||||
if (!isset($attribute[$rF])) {
|
||||
$attribute[$rF] = $existingAttribute['Attribute'][$rF];
|
||||
}
|
||||
}
|
||||
$attribute['id'] = $existingAttribute['Attribute']['id'];
|
||||
// Check if the attribute's timestamp is bigger than the one that already exists.
|
||||
// If yes, it means that it's newer, so insert it. If no, it means that it's the same attribute or older - don't insert it, insert the old attribute.
|
||||
// Alternatively, we could unset this attribute from the request, but that could lead with issues if we decide that we want to start deleting attributes that don't exist in a pushed event.
|
||||
if (isset($attribute['timestamp'])) {
|
||||
if (!$force && $attribute['timestamp'] <= $existingAttribute['Attribute']['timestamp']) {
|
||||
$nothingToChange = true;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
$attribute['timestamp'] = time();
|
||||
}
|
||||
} else {
|
||||
$this->create();
|
||||
}
|
||||
} else {
|
||||
$this->create();
|
||||
}
|
||||
$attribute['event_id'] = $eventId;
|
||||
if (isset($attribute['distribution']) && $attribute['distribution'] == 4) {
|
||||
if (!empty($attribute['SharingGroup'])) {
|
||||
$attribute['sharing_group_id'] = $this->SharingGroup->captureSG($attribute['SharingGroup'], $user);
|
||||
} elseif (!empty($attribute['sharing_group_id'])) {
|
||||
if (!$this->SharingGroup->checkIfAuthorised($user, $attribute['sharing_group_id'])) {
|
||||
unset($attribute['sharing_group_id']);
|
||||
}
|
||||
}
|
||||
if (empty($attribute['sharing_group_id'])) {
|
||||
$attribute_short = (isset($attribute['category']) ? $attribute['category'] : 'N/A') . '/' . (isset($attribute['type']) ? $attribute['type'] : 'N/A') . ' ' . (isset($attribute['value']) ? $attribute['value'] : 'N/A');
|
||||
$this->loadLog()->createLogEntry($user, 'edit', 'Attribute', 0,
|
||||
'Attribute dropped due to invalid sharing group for Event ' . $eventId . ' failed: ' . $attribute_short,
|
||||
'Validation errors: ' . json_encode($this->validationErrors) . ' Full Attribute: ' . json_encode($attribute)
|
||||
);
|
||||
return 'Invalid sharing group choice.';
|
||||
}
|
||||
} else if (!isset($attribute['distribution'])) {
|
||||
$attribute['distribution'] = $this->defaultDistribution();
|
||||
}
|
||||
|
||||
// This is somewhat dangerous, fast_update assumes that you are just updating an existing attribute's
|
||||
// non uniqueness modifying fields (first/last seen, comment, tags, timestamp, etc)
|
||||
// By ignoring this warning, you are introducing potential duplicates.
|
||||
if ($this->fast_update) {
|
||||
$saveOptions['skipAuditLog'] = true;
|
||||
}
|
||||
return $attribute;
|
||||
}
|
||||
|
||||
public function editAttributeBulk($attributes, $event, $user)
|
||||
{
|
||||
$fieldList = self::EDITABLE_FIELDS;
|
||||
$addableFieldList = array('event_id', 'type', 'uuid', 'object_id', 'object_relation');
|
||||
$fieldList = array_merge($fieldList, $addableFieldList);
|
||||
$saveOptions = [
|
||||
'fieldList' => $fieldList,
|
||||
'parentEvent' => $event,
|
||||
'atomic' => true,
|
||||
'validate' => true
|
||||
];
|
||||
$this->saveMany($attributes, $saveOptions);
|
||||
if (!empty($this->validationErrors)) {
|
||||
foreach ($this->validationErrors as $key => $validationError) {
|
||||
$this->logDropped($user, $attributes[$key], 'edit', $validationError);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function editAttributePostProcessing($attributes, $event, $user)
|
||||
{
|
||||
$eventId = $event['Event']['id'];
|
||||
$tagActions = [];
|
||||
foreach ($attributes as $attribute) {
|
||||
$attributeId = $this->updateLookupTable[$attribute['uuid']];
|
||||
if (!empty($attribute['Sighting'])) {
|
||||
$this->Sighting->captureSightings($attribute['Sighting'], $this->id, $eventId, $user);
|
||||
}
|
||||
if ($user['Role']['perm_tagger']) {
|
||||
/*
|
||||
We should unwrap the line below and remove the server option in the future once we have tag soft-delete
|
||||
A solution to still keep the behavior for previous instance could be to not soft-delete the Tag if the remote instance
|
||||
has a version below x
|
||||
*/
|
||||
if (isset($server) && isset($server['Server']['remove_missing_tags']) && $server['Server']['remove_missing_tags']) {
|
||||
$existingTags = $this->AttributeTag->find('all', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['attribute_id' => $attribute['id']]
|
||||
]);
|
||||
$this->AttributeTag->pruneOutdatedAttributeTagsFromSync(isset($attribute['Tag']) ? $attribute['Tag'] : array(), $existingTags['AttributeTag']);
|
||||
}
|
||||
$tag_id_store = [];
|
||||
if (isset($attribute['Tag'])) {
|
||||
foreach ($attribute['Tag'] as $tag) {
|
||||
if (empty($tag_id_store[$tag['name']])) {
|
||||
$tag_id = $this->AttributeTag->Tag->captureTag($tag, $user);
|
||||
if ($tag_id) {
|
||||
$tag_id_store[$tag['name']] = $tag_id;
|
||||
}
|
||||
} else {
|
||||
$tag_id = $tag_id_store[$tag['name']];
|
||||
}
|
||||
if ($tag_id) {
|
||||
$tag['id'] = $tag_id;
|
||||
// fix the IDs here
|
||||
$tag_result = $this->AttributeTag->handleAttributeTag($attributeId, $attribute['event_id'], $tag, true);
|
||||
if (isset($tag_result['attach'])) {
|
||||
$tagActions['attach'][$attributeId . '-' . $tag_id] = $tag_result['attach'];
|
||||
} else if(isset($tag_result['detach'])) {
|
||||
$tagActions['detach'][] = $tag_result['detach'];
|
||||
}
|
||||
} else {
|
||||
// If we couldn't attach the tag it is most likely because we couldn't create it - which could have many reasons
|
||||
// However, if a tag couldn't be added, it could also be that the user is a tagger but not a tag editor
|
||||
// In which case if no matching tag is found, no tag ID is returned. Logging these is pointless as it is the correct behaviour.
|
||||
if ($user['Role']['perm_tag_editor']) {
|
||||
$this->loadLog()->createLogEntry($user, 'edit', 'Attribute', $attributeId, 'Failed create or attach Tag ' . $tag['name'] . ' to the attribute.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($tagActions['attach'])) {
|
||||
foreach ($tagActions['attach'] as $k => $attach) {
|
||||
$existingAssociation = $this->AttributeTag->find('first', [
|
||||
'conditions' => [
|
||||
'tag_id' => $attach['tag_id'],
|
||||
'attribute_id' => $attach['attribute_id']
|
||||
],
|
||||
'recursive' => -1
|
||||
]);
|
||||
if (!empty($existingAssociation)) {
|
||||
$attach['id'] = $existingAssociation['AttributeTag']['id'];
|
||||
if ($attach['local'] == $existingAssociation['AttributeTag']['local'] && $attach['relationship_type'] == $existingAssociation['AttributeTag']['relationship_type']) {
|
||||
unset($tagActions['attach'][$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($tagActions['attach'])) {
|
||||
$this->AttributeTag->saveMany($tagActions['attach']);
|
||||
}
|
||||
|
||||
}
|
||||
if (!empty($tagActions['detach'])) {
|
||||
$conditions = [];
|
||||
foreach ($tagActions['detach'] as $detach) {
|
||||
$conditions[] = [
|
||||
'AND' => [
|
||||
'attribute_id' => $detach['attribute_id'],
|
||||
'tag_id' => $detach['tag_id']
|
||||
]
|
||||
];
|
||||
}
|
||||
if (!empty($conditions)) {
|
||||
$this->AttributeTag->deleteAll($conditions, false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int $id Attribute ID
|
||||
* @param array $user
|
||||
|
@ -3209,14 +3424,17 @@ class Attribute extends AppModel
|
|||
* @param string $action
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function logDropped(array $user, array $attribute, $action = 'add')
|
||||
public function logDropped(array $user, array $attribute, $action = 'add', $validationError = false)
|
||||
{
|
||||
$attribute_short = (isset($attribute['category']) ? $attribute['category'] : 'N/A') . '/' . (isset($attribute['type']) ? $attribute['type'] : 'N/A') . ' ' . (isset($attribute['value']) ? $attribute['value'] : 'N/A');
|
||||
if ($validationError === false) {
|
||||
$validationError = $this->validationErrors;
|
||||
}
|
||||
$eventId = $attribute['event_id'];
|
||||
$modelId = $action === 'add' ? 0 : $this->id;
|
||||
$this->loadLog()->createLogEntry($user, $action, 'Attribute', $modelId,
|
||||
"Attribute dropped due to validation for Event $eventId failed: $attribute_short",
|
||||
'Validation errors: ' . JsonTool::encode($this->validationErrors) . ' Full Attribute: ' . JsonTool::encode($attribute)
|
||||
'Validation errors: ' . JsonTool::encode($validationError) . ' Full Attribute: ' . JsonTool::encode($attribute)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -117,14 +117,36 @@ class AttributeTag extends AppModel
|
|||
}
|
||||
}
|
||||
|
||||
public function handleAttributeTag($attribute_id, $event_id, array $tag)
|
||||
public function handleAttributeTag($attribute_id, $event_id, array $tag, $mock = false)
|
||||
{
|
||||
if (empty($tag['deleted'])) {
|
||||
$local = isset($tag['local']) ? $tag['local'] : false;
|
||||
$relationship_type = isset($tag['relationship_type']) ? $tag['relationship_type'] : false;
|
||||
$this->attachTagToAttribute($attribute_id, $event_id, $tag['id'], $local, $relationship_type);
|
||||
if ($mock) {
|
||||
return [
|
||||
'attach' => [
|
||||
'attribute_id' => $attribute_id,
|
||||
'event_id' => $event_id,
|
||||
'tag_id' => $tag['id'],
|
||||
'local' => $local,
|
||||
'relationship_type' => $relationship_type
|
||||
]
|
||||
];
|
||||
} else {
|
||||
$this->attachTagToAttribute($attribute_id, $event_id, $tag['id'], $local, $relationship_type);
|
||||
}
|
||||
} else {
|
||||
$this->detachTagFromAttribute($attribute_id, $event_id, $tag['id'], null);
|
||||
if ($mock) {
|
||||
return [
|
||||
'detach' => [
|
||||
'attribute_id' => $attribute_id,
|
||||
'event_id' => $event_id,
|
||||
'tag_id' => $tag['id']
|
||||
]
|
||||
];
|
||||
} else {
|
||||
$this->detachTagFromAttribute($attribute_id, $event_id, $tag['id'], null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -373,6 +373,8 @@ class Event extends AppModel
|
|||
/** @var array|null */
|
||||
private $eventBlockRule;
|
||||
|
||||
public $fast_update = false;
|
||||
|
||||
public function beforeDelete($cascade = true)
|
||||
{
|
||||
// blocklist the event UUID if the feature is enabled
|
||||
|
@ -3983,7 +3985,6 @@ class Event extends AppModel
|
|||
$data = $this->cleanupEventArrayFromXML($data);
|
||||
unset($this->Attribute->validate['event_id']);
|
||||
unset($this->Attribute->validate['value']['unique']); // otherwise gives bugs because event_id is not set
|
||||
|
||||
// reposition to get the event.id with given uuid
|
||||
if (isset($data['Event']['uuid'])) {
|
||||
$conditions = ['Event.uuid' => $data['Event']['uuid']];
|
||||
|
@ -3993,7 +3994,6 @@ class Event extends AppModel
|
|||
throw new InvalidArgumentException("No event UUID or ID provided.");
|
||||
}
|
||||
$existingEvent = $this->find('first', ['conditions' => $conditions, 'recursive' => -1]);
|
||||
|
||||
if ($passAlong) {
|
||||
$this->Server = ClassRegistry::init('Server');
|
||||
$server = $this->Server->find('first', array(
|
||||
|
@ -4109,19 +4109,32 @@ class Event extends AppModel
|
|||
'Event'
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($data['Event']['Attribute'])) {
|
||||
$data['Event']['Attribute'] = array_values($data['Event']['Attribute']);
|
||||
foreach ($data['Event']['Attribute'] as $attribute) {
|
||||
$nothingToChange = false;
|
||||
$result = $this->Attribute->editAttribute($attribute, $saveResult, $user, 0, false, $force, $nothingToChange, $server, $fast_update);
|
||||
if ($result !== true) {
|
||||
$validationErrors['Attribute'][] = $result;
|
||||
$attributes = [];
|
||||
if (true) {
|
||||
foreach ($data['Event']['Attribute'] as $k => $attribute) {
|
||||
$nothingToChange = false;
|
||||
$attributes[] = $this->Attribute->editAttribute2($attribute, $saveResult, $user, 0, false, $force, $nothingToChange, $server);
|
||||
if (!$nothingToChange) {
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
if (!$nothingToChange) {
|
||||
$changed = true;
|
||||
$result = $this->Attribute->editAttributeBulk($attributes, $saveResult, $user);
|
||||
$result = $this->Attribute->editAttributePostProcessing($attributes, $saveResult, $user);
|
||||
} else {
|
||||
foreach ($data['Event']['Attribute'] as $k => $attribute) {
|
||||
$nothingToChange = false;
|
||||
$result = $this->Attribute->editAttribute($attribute, $saveResult, $user, 0, false, $force, $nothingToChange, $server);
|
||||
if ($result !== true) {
|
||||
$validationErrors['Attribute'][] = $result;
|
||||
}
|
||||
if (!$nothingToChange) {
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (isset($data['Event']['Object'])) {
|
||||
$data['Event']['Object'] = array_values($data['Event']['Object']);
|
||||
|
|
|
@ -1215,6 +1215,15 @@ class MispObject extends AppModel
|
|||
}
|
||||
$result = $this->Attribute->editAttribute($attribute, $event, $user, $object['id'], false, $force);
|
||||
}
|
||||
$attributes = [];
|
||||
foreach ($object['Attribute'] as $k => $attribute) {
|
||||
if (!empty($object['deleted'])) {
|
||||
$attribute['deleted'] = 1;
|
||||
}
|
||||
$attributes[] = $this->Attribute->editAttribute2($attribute, $event, $user, $object['id'], false, $force);
|
||||
}
|
||||
$result = $this->Attribute->editAttributeBulk($attributes, $event, $user);
|
||||
$result = $this->Attribute->editAttributePostProcessing($attributes, $event, $user);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -39,10 +39,18 @@ class Module_attribute_edition_operation extends WorkflowBaseActionModule
|
|||
protected function __saveAttributes(array $attributes, array $rData, array $params, array $user): array
|
||||
{
|
||||
$success = false;
|
||||
foreach ($attributes as $attribute) {
|
||||
$attributes = [];
|
||||
$newAttributes = [];
|
||||
foreach ($attributes as $k => $attribute) {
|
||||
$newAttribute = $this->_editAttribute($attribute, $rData, $params);
|
||||
$newAttributes[] = $newAttribute;
|
||||
unset($newAttribute['timestamp']);
|
||||
$saveSuccess = $this->Attribute->editAttribute($newAttribute, $rData, $user, $newAttribute['object_id']);
|
||||
$attributes[] = $this->Attribute->editAttribute($newAttribute, $rData, $user, $newAttribute['object_id']);
|
||||
}
|
||||
$this->Attribute->editAttributeBulk($attributes, $rData, $user);
|
||||
$this->Attribute->editAttributePostProcessing($attributes, $rData, $user);
|
||||
foreach ($attributes as $k => $attribute) {
|
||||
$saveSuccess = empty($this->Attribute->validationErrors[$k]);
|
||||
if ($saveSuccess) {
|
||||
$rData = $this->_overrideAttribute($attribute, $newAttribute, $rData);
|
||||
}
|
||||
|
@ -54,3 +62,14 @@ class Module_attribute_edition_operation extends WorkflowBaseActionModule
|
|||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
foreach ($object['Attribute'] as $k => $attribute) {
|
||||
if (!empty($object['deleted'])) {
|
||||
$attribute['deleted'] = 1;
|
||||
}
|
||||
$attributes[] = $this->Attribute->editAttribute2($attribute, $event, $user, $object['id'], false, $force);
|
||||
}
|
||||
$result = $this->Attribute->editAttributeBulk($attributes, $event, $user);
|
||||
$result = $this->Attribute->editAttributePostProcessing($attributes, $event, $user);
|
Loading…
Reference in New Issue