fix: [object:fromAttributes] SYNC support for older instances

(duplicate attributes and their contexts)
pull/4672/head
mokaddem 2019-06-06 15:11:34 +02:00
parent 8d2c55fa69
commit fe4740abd9
4 changed files with 133 additions and 111 deletions

View File

@ -1277,7 +1277,7 @@ class AttributesController extends AppController
}
if ($this->request->is('ajax')) {
if ($this->request->is('post')) {
if ($this->__delete($id, $hard)) {
if ($this->Attribute->__delete($id, $this->Auth->user(), $hard)) {
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Attribute deleted.')), 'status'=>200, 'type' => 'json'));
} else {
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Attribute was not deleted.')), 'status'=>200, 'type' => 'json'));
@ -1291,7 +1291,7 @@ class AttributesController extends AppController
if (!$this->request->is('post') && !$this->_isRest()) {
throw new MethodNotAllowedException();
}
if ($this->__delete($id, $hard)) {
if ($this->Attribute->__delete($id, $this->Auth->user(), $hard)) {
if ($this->_isRest() || $this->response->type() === 'application/json') {
$this->set('message', 'Attribute deleted.');
$this->set('_serialize', array('message'));
@ -1360,77 +1360,6 @@ class AttributesController extends AppController
}
}
// unification of the actual delete for the multi-select
private function __delete($id, $hard = false)
{
$this->Attribute->id = $id;
if (!$this->Attribute->exists()) {
return false;
}
$result = $this->Attribute->find('first', array(
'conditions' => array('Attribute.id' => $id),
'fields' => array('Attribute.*'),
'contain' => array('Event' => array(
'fields' => array('Event.*')
)),
));
if (empty($result)) {
throw new MethodNotAllowedException(__('Attribute not found or not authorised.'));
}
// check for permissions
if (!$this->_isSiteAdmin()) {
if ($result['Event']['locked']) {
if ($this->Auth->user('org_id') != $result['Event']['org_id'] || !$this->userRole['perm_sync']) {
throw new MethodNotAllowedException(__('Attribute not found or not authorised.'));
}
} else {
if ($this->Auth->user('org_id') != $result['Event']['orgc_id']) {
throw new MethodNotAllowedException(__('Attribute not found or not authorised.'));
}
}
}
$date = new DateTime();
if ($hard) {
$save = $this->Attribute->delete($id);
} else {
if (Configure::read('Security.sanitise_attribute_on_delete')) {
$result['Attribute']['category'] = 'Other';
$result['Attribute']['type'] = 'comment';
$result['Attribute']['value'] = 'deleted';
$result['Attribute']['comment'] = '';
$result['Attribute']['to_ids'] = 0;
}
$result['Attribute']['deleted'] = 1;
$result['Attribute']['timestamp'] = $date->getTimestamp();
$save = $this->Attribute->save($result);
$object_refs = $this->Attribute->Object->ObjectReference->find('all', array(
'conditions' => array(
'ObjectReference.referenced_type' => 0,
'ObjectReference.referenced_id' => $id,
),
'recursive' => -1
));
foreach ($object_refs as $ref) {
$ref['ObjectReference']['deleted'] = 1;
$this->Attribute->Object->ObjectReference->save($ref);
}
}
// attachment will be deleted with the beforeDelete() function in the Model
if ($save) {
// We have just deleted the attribute, let's also check if there are any shadow attributes that were attached to it and delete them
$this->loadModel('ShadowAttribute');
$this->ShadowAttribute->deleteAll(array('ShadowAttribute.old_id' => $id), false);
// remove the published flag from the event
$this->Attribute->Event->unpublishEvent($result['Event']['id']);
return true;
} else {
return false;
}
}
public function deleteSelected($id = false, $hard = false)
{
if (!$this->request->is('post')) {
@ -1497,11 +1426,11 @@ class AttributesController extends AppController
$successes = array();
foreach ($attributes as $a) {
if ($hard) {
if ($this->__delete($a['Attribute']['id'], true)) {
if ($this->Attribute->__delete($a['Attribute']['id'], true)) {
$successes[] = $a['Attribute']['id'];
}
} else {
if ($this->__delete($a['Attribute']['id'], $a['Attribute']['deleted'] == 1 ? true : false)) {
if ($this->Attribute->__delete($a['Attribute']['id'], $a['Attribute']['deleted'] == 1 ? true : false)) {
$successes[] = $a['Attribute']['id'];
}
}

View File

@ -1053,25 +1053,15 @@ class ObjectsController extends AppController
} else {
$sharing_group_id = 0;
}
$object = array('Object' => array(
'distribution' => $distribution,
'sharing_group_id' => $sharing_group_id,
'comment' => $comment
));
$temp = $this->MispObject->Attribute->fetchAttributes($this->Auth->user(), array('conditions' => array(
'Attribute.id' => $selected_attribute_ids,
'Attribute.event_id' => $event_id,
'Attribute.object_id' => 0
)));
foreach ($temp as $i => $attribute) {
if (isset($selected_object_relation_mapping[$attribute['Attribute']['id']])) {
$object_relation = $selected_object_relation_mapping[$attribute['Attribute']['id']];
$attribute['Attribute']['object_relation'] = $object_relation;
$object['Attribute'][$i] = $attribute['Attribute'];
unset($object['Attribute'][$i]['timestamp']);
}
}
$result = $this->MispObject->saveObject($object, $event_id, $template, $this->Auth->user());
$object = array(
'Object' => array(
'distribution' => $distribution,
'sharing_group_id' => $sharing_group_id,
'comment' => $comment,
),
'Attribute' => array()
);
$result = $this->MispObject->groupAttributesIntoObject($this->Auth->user(), $event_id, $object, $template, $selected_attribute_ids, $selected_object_relation_mapping);
if (is_numeric($result)) {
$this->MispObject->Event->unpublishEvent($event_id);
return $this->RestResponse->saveSuccessResponse('Objects', 'Created from Attributes', $result, $this->response->type());

View File

@ -3768,14 +3768,7 @@ class Attribute extends AppModel
));
$this->Log = ClassRegistry::init('Log');
if (count($existingAttribute)) {
if (
$existingAttribute['Attribute']['event_id'] != $eventId ||
(
$existingAttribute['Attribute']['object_id'] != $objectId &&
// Still take into account the attribute if it was merged into an object
$existingAttribute['Attribute']['object_id'] != 0
)
) {
if ($existingAttribute['Attribute']['event_id'] != $eventId || $existingAttribute['Attribute']['object_id'] != $objectId) {
$this->Log->create();
$result = $this->Log->save(array(
'org' => $user['Organisation']['name'],
@ -3912,6 +3905,74 @@ class Attribute extends AppModel
return true;
}
public function __delete($id, $user, $hard = false)
{
$this->id = $id;
if (!$this->exists()) {
return false;
}
$result = $this->find('first', array(
'conditions' => array('Attribute.id' => $id),
'fields' => array('Attribute.*'),
'contain' => array('Event' => array(
'fields' => array('Event.*')
)),
));
if (empty($result)) {
throw new MethodNotAllowedException(__('Attribute not found or not authorised.'));
}
// check for permissions
if (!$user['Role']['perm_site_admin']) {
if ($result['Event']['locked']) {
if ($user['org_id'] != $result['Event']['org_id'] || !$user['Role']['perm_sync']) {
throw new MethodNotAllowedException(__('Attribute not found or not authorised.'));
}
} else {
if ($user['org_id'] != $result['Event']['orgc_id']) {
throw new MethodNotAllowedException(__('Attribute not found or not authorised.'));
}
}
}
$date = new DateTime();
if ($hard) {
$save = $this->delete($id);
} else {
if (Configure::read('Security.sanitise_attribute_on_delete')) {
$result['Attribute']['category'] = 'Other';
$result['Attribute']['type'] = 'comment';
$result['Attribute']['value'] = 'deleted';
$result['Attribute']['comment'] = '';
$result['Attribute']['to_ids'] = 0;
}
$result['Attribute']['deleted'] = 1;
$result['Attribute']['timestamp'] = $date->getTimestamp();
$save = $this->save($result);
$object_refs = $this->Object->ObjectReference->find('all', array(
'conditions' => array(
'ObjectReference.referenced_type' => 0,
'ObjectReference.referenced_id' => $id,
),
'recursive' => -1
));
foreach ($object_refs as $ref) {
$ref['ObjectReference']['deleted'] = 1;
$this->Object->ObjectReference->save($ref);
}
}
// attachment will be deleted with the beforeDelete() function in the Model
if ($save) {
// We have just deleted the attribute, let's also check if there are any shadow attributes that were attached to it and delete them
$this->Event->ShadowAttribute->deleteAll(array('ShadowAttribute.old_id' => $id), false);
// remove the published flag from the event
$this->Event->unpublishEvent($result['Event']['id']);
return true;
} else {
return false;
}
}
public function attachValidationWarnings($adata)
{
if (!$this->__fTool) {

View File

@ -595,15 +595,7 @@ class MispObject extends AppModel
$objectId = $this->id;
$partialFails = array();
foreach ($object['Object']['Attribute'] as $attribute) {
$existing_attribute = $this->Attribute->find('first', array(
'recursive' => -1,
'conditions' => array('uuid' => $attribute['uuid'])
));
if (empty($existing_attribute)) {
$this->Attribute->captureAttribute($attribute, $eventId, $user, $objectId, $log);
} else { // The attribute may just have been grouped into an object
$this->Attribute->editAttribute($attribute, $eventId, $user, $objectId, $log);
}
$this->Attribute->captureAttribute($attribute, $eventId, $user, $objectId, $log);
}
return true;
} else {
@ -826,4 +818,54 @@ class MispObject extends AppModel
}
return array('templates' => $templates, 'types' => $attribute_types);
}
public function groupAttributesIntoObject($user, $event_id, $object, $template, $selected_attribute_ids, $selected_object_relation_mapping)
{
$saved_object_id = $this->saveObject($object, $event_id, $template, $user);
if (!is_numeric($saved_object_id)) {
return $saved_object_id;
}
$saved_object = $this->find('first', array(
'recursive' => -1,
'conditions' => array('Object.id' => $saved_object_id)
));
$existing_attributes = $this->Attribute->fetchAttributes($user, array('conditions' => array(
'Attribute.id' => $selected_attribute_ids,
'Attribute.event_id' => $event_id,
'Attribute.object_id' => 0
)));
if (empty($existing_attributes)) {
return __('Selected Attributes do not exist.');
}
$event = array('Event' => $existing_attributes[0]['Event']);
// Duplicate the attribute and its context, otherwise connected instance will drop the duplicated UUID
foreach ($existing_attributes as $i => $existing_attribute) {
if (isset($selected_object_relation_mapping[$existing_attribute['Attribute']['id']])) {
$sightings = $this->Event->Sighting->attachToEvent($event, $user, $existing_attribute['Attribute']['id']);
$object_relation = $selected_object_relation_mapping[$existing_attribute['Attribute']['id']];
$created_attribute = $existing_attribute['Attribute'];
unset($created_attribute['timestamp']);
unset($created_attribute['id']);
unset($created_attribute['uuid']);
$created_attribute['object_relation'] = $object_relation;
$created_attribute['object_id'] = $saved_object['Object']['id'];
if (isset($existing_attribute['AttributeTag'])) {
$created_attribute['AttributeTag'] = $existing_attribute['AttributeTag'];
}
if (!empty($sightings)) {
$created_attribute['Sighting'] = $sightings;
}
$saved_object['Attribute'][$i] = $created_attribute;
$this->Attribute->captureAttribute($created_attribute, $event_id, $user, $saved_object['Object']['id']);
$this->Attribute->__delete($existing_attribute['Attribute']['id'], $user);
}
}
return $saved_object['Object']['id'];
}
}