2017-06-13 12:08:26 +02:00
< ? php
App :: uses ( 'AppModel' , 'Model' );
2018-07-19 11:48:22 +02:00
class MispObject extends AppModel
{
public $name = 'Object' ;
public $alias = 'Object' ;
2017-07-05 14:25:09 +02:00
2018-07-19 11:48:22 +02:00
public $useTable = 'objects' ;
2017-07-06 15:04:01 +02:00
2018-07-19 11:48:22 +02:00
public $actsAs = array (
'Containable' ,
2019-02-10 13:08:12 +01:00
'SysLogLogable.SysLogLogable' => array ( // TODO Audit, logable
2018-07-19 11:48:22 +02:00
'userModel' => 'User' ,
'userKey' => 'user_id' ,
'change' => 'full' ),
);
2017-07-05 14:25:09 +02:00
2018-07-19 11:48:22 +02:00
public $belongsTo = array (
'Event' => array (
'className' => 'Event' ,
'foreignKey' => 'event_id'
),
'SharingGroup' => array (
'className' => 'SharingGroup' ,
'foreignKey' => 'sharing_group_id'
),
'ObjectTemplate' => array (
'className' => 'ObjectTemplate' ,
'foreignKey' => false ,
'dependent' => false ,
'conditions' => array ( 'MispObject.template_uuid' => 'ObjectTemplate.uuid' )
)
);
2017-06-13 12:08:26 +02:00
2018-07-19 11:48:22 +02:00
public $hasMany = array (
'Attribute' => array (
'className' => 'Attribute' ,
'dependent' => true ,
),
'ObjectReference' => array (
'className' => 'ObjectReference' ,
'dependent' => true ,
'foreignKey' => 'object_id'
),
);
2017-07-05 09:02:16 +02:00
2019-06-13 09:16:34 +02:00
public $shortDist = array ( 0 => 'Organisation' , 1 => 'Community' , 2 => 'Connected' , 3 => 'All' , 4 => ' Sharing Group' , 5 => 'Inherit' );
2018-07-19 11:48:22 +02:00
public $validate = array (
2018-11-23 14:11:33 +01:00
'uuid' => array (
'uuid' => array (
'rule' => array ( 'custom' , '/^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/' ),
'message' => 'Please provide a valid UUID'
),
'unique' => array (
'rule' => 'isUnique' ,
'message' => 'The UUID provided is not unique' ,
'required' => 'create'
)
)
2018-07-19 11:48:22 +02:00
);
2017-06-13 12:08:26 +02:00
2019-06-13 09:16:34 +02:00
public function afterFind ( $results , $primary = false )
{
foreach ( $results as $k => $v ) {
2019-12-16 10:47:07 +01:00
$results [ $k ] = $this -> Attribute -> UTCToISODatetime ( $results [ $k ], $this -> alias );
2019-06-13 09:16:34 +02:00
}
return $results ;
}
public function beforeSave ( $options = array ()) {
2019-12-16 10:47:07 +01:00
$this -> data = $this -> Attribute -> ISODatetimeToUTC ( $this -> data , $this -> alias );
2019-06-13 09:16:34 +02:00
}
2018-07-19 11:48:22 +02:00
public function beforeValidate ( $options = array ())
{
parent :: beforeValidate ();
if ( empty ( $this -> data [ $this -> alias ][ 'comment' ])) {
$this -> data [ $this -> alias ][ 'comment' ] = " " ;
}
// generate UUID if it doesn't exist
if ( empty ( $this -> data [ $this -> alias ][ 'uuid' ])) {
$this -> data [ $this -> alias ][ 'uuid' ] = CakeText :: uuid ();
}
// generate timestamp if it doesn't exist
if ( empty ( $this -> data [ $this -> alias ][ 'timestamp' ])) {
$date = new DateTime ();
$this -> data [ $this -> alias ][ 'timestamp' ] = $date -> getTimestamp ();
}
2019-06-13 09:16:34 +02:00
// parse first_seen different formats
if ( isset ( $this -> data [ $this -> alias ][ 'first_seen' ])) {
$this -> data [ $this -> alias ][ 'first_seen' ] = $this -> data [ $this -> alias ][ 'first_seen' ] === '' ? null : $this -> data [ $this -> alias ][ 'first_seen' ];
}
// parse last_seen different formats
if ( isset ( $this -> data [ $this -> alias ][ 'last_seen' ])) {
$this -> data [ $this -> alias ][ 'last_seen' ] = $this -> data [ $this -> alias ][ 'last_seen' ] === '' ? null : $this -> data [ $this -> alias ][ 'last_seen' ];
}
2018-07-19 11:48:22 +02:00
if ( empty ( $this -> data [ $this -> alias ][ 'template_version' ])) {
$this -> data [ $this -> alias ][ 'template_version' ] = 1 ;
}
if ( isset ( $this -> data [ $this -> alias ][ 'deleted' ]) && empty ( $this -> data [ $this -> alias ][ 'deleted' ])) {
$this -> data [ $this -> alias ][ 'deleted' ] = 0 ;
}
if ( ! isset ( $this -> data [ $this -> alias ][ 'distribution' ]) || $this -> data [ 'Object' ][ 'distribution' ] != 4 ) {
$this -> data [ 'Object' ][ 'sharing_group_id' ] = 0 ;
}
if ( ! isset ( $this -> data [ $this -> alias ][ 'distribution' ])) {
$this -> data [ 'Object' ][ 'distribution' ] = 5 ;
}
return true ;
}
2017-06-13 12:08:26 +02:00
2018-07-19 11:48:22 +02:00
public function afterSave ( $created , $options = array ())
{
2019-03-05 12:24:56 +01:00
$pubToZmq = Configure :: read ( 'Plugin.ZeroMQ_enable' ) &&
Configure :: read ( 'Plugin.ZeroMQ_object_notifications_enable' ) &&
empty ( $this -> data [ 'Object' ][ 'skip_zmq' ]);
$kafkaTopic = Configure :: read ( 'Plugin.Kafka_object_notifications_topic' );
$pubToKafka = Configure :: read ( 'Plugin.Kafka_enable' ) &&
Configure :: read ( 'Plugin.Kafka_object_notifications_enable' ) &&
! empty ( $kafkaTopic ) &&
empty ( $this -> data [ 'Object' ][ 'skip_kafka' ]);
if ( $pubToZmq || $pubToKafka ) {
$object = $this -> find ( 'first' , array (
'conditions' => array ( 'Object.id' => $this -> id ),
'recursive' => - 1
));
$action = $created ? 'add' : 'edit' ;
if ( ! empty ( $this -> data [ 'Object' ][ 'deleted' ])) {
$action = 'soft-delete' ;
}
if ( $pubToZmq ) {
2018-07-19 11:48:22 +02:00
$pubSubTool = $this -> getPubSubTool ();
$pubSubTool -> object_save ( $object , $action );
}
2019-03-05 12:24:56 +01:00
if ( $pubToKafka ) {
$kafkaPubTool = $this -> getKafkaPubTool ();
$kafkaPubTool -> publishJson ( $kafkaTopic , $object , $action );
}
2018-07-19 11:48:22 +02:00
}
return true ;
}
2017-07-02 22:42:44 +02:00
2018-07-19 11:48:22 +02:00
public function beforeDelete ( $cascade = true )
{
if ( ! empty ( $this -> data [ 'Object' ][ 'id' ])) {
2019-03-05 12:24:56 +01:00
$pubToZmq = Configure :: read ( 'Plugin.ZeroMQ_enable' ) && Configure :: read ( 'Plugin.ZeroMQ_object_notifications_enable' );
$kafkaTopic = Configure :: read ( 'Plugin.Kafka_object_notifications_topic' );
$pubToKafka = Configure :: read ( 'Plugin.Kafka_enable' ) && Configure :: read ( 'Plugin.Kafka_object_notifications_enable' ) && ! empty ( $kafkaTopic );
if ( $pubToZmq || $pubToKafka ) {
2018-07-19 11:48:22 +02:00
$object = $this -> find ( 'first' , array (
'recursive' => - 1 ,
'conditions' => array ( 'Object.id' => $this -> data [ 'Object' ][ 'id' ])
));
2019-03-05 12:24:56 +01:00
if ( $pubToZmq ) {
$pubSubTool = $this -> getPubSubTool ();
$pubSubTool -> object_save ( $object , 'delete' );
}
if ( $pubToKafka ) {
$kafkaPubTool = $this -> getKafkaPubTool ();
$kafkaPubTool -> publishJson ( $kafkaTopic , $object , 'delete' );
}
2018-07-19 11:48:22 +02:00
}
}
}
2017-10-27 09:10:46 +02:00
2018-07-19 11:48:22 +02:00
public function afterDelete ()
{
if ( ! empty ( $this -> data [ $this -> alias ][ 'id' ])) {
$this -> ObjectReference -> deleteAll (
array (
'ObjectReference.referenced_type' => 1 ,
'ObjectReference.referenced_id' => $this -> data [ $this -> alias ][ 'id' ],
),
false
);
}
}
2017-10-27 09:10:46 +02:00
2018-07-19 11:48:22 +02:00
public function saveObject ( $object , $eventId , $template = false , $user , $errorBehaviour = 'drop' )
{
$this -> create ();
$templateFields = array (
'name' => 'name' ,
'meta-category' => 'meta-category' ,
'description' => 'description' ,
'template_version' => 'version' ,
'template_uuid' => 'uuid'
);
if ( $template ) {
foreach ( $templateFields as $k => $v ) {
$object [ 'Object' ][ $k ] = $template [ 'ObjectTemplate' ][ $v ];
}
} else {
foreach ( $templateFields as $k => $v ) {
if ( ! isset ( $object [ 'Object' ][ $k ])) {
return 'No valid template found and object lacking template information. (' . $k . ')' ;
}
}
}
$object [ 'Object' ][ 'event_id' ] = $eventId ;
$result = false ;
if ( $this -> save ( $object )) {
$result = $this -> id ;
foreach ( $object [ 'Attribute' ] as $k => $attribute ) {
$object [ 'Attribute' ][ $k ][ 'object_id' ] = $this -> id ;
2019-07-04 14:05:47 +02:00
if (
( ! array_key_exists ( 'first_seen' , $object [ 'Attribute' ][ $k ]) || is_null ( $object [ 'Attribute' ][ $k ][ 'first_seen' ])) &&
2019-07-05 10:40:00 +02:00
( array_key_exists ( 'first_seen' , $object [ 'Object' ]) && ! is_null ( $object [ 'Object' ][ 'first_seen' ]))
2019-07-04 14:05:47 +02:00
) {
$object [ 'Attribute' ][ $k ][ 'first_seen' ] = $object [ 'Object' ][ 'first_seen' ];
2019-07-04 13:52:29 +02:00
}
2019-07-04 14:05:47 +02:00
if (
( ! array_key_exists ( 'last_seen' , $object [ 'Attribute' ][ $k ]) || is_null ( $object [ 'Attribute' ][ $k ][ 'last_seen' ])) &&
2019-07-05 10:40:00 +02:00
( array_key_exists ( 'last_seen' , $object [ 'Object' ]) && ! is_null ( $object [ 'Object' ][ 'last_seen' ]))
2019-07-04 14:05:47 +02:00
) {
$object [ 'Attribute' ][ $k ][ 'last_seen' ] = $object [ 'Object' ][ 'last_seen' ];
2019-07-04 13:52:29 +02:00
}
2018-07-19 11:48:22 +02:00
}
$this -> Attribute -> saveAttributes ( $object [ 'Attribute' ]);
} else {
$result = $this -> validationErrors ;
}
return $result ;
}
2017-09-17 12:26:06 +02:00
2018-07-19 11:48:22 +02:00
public function buildEventConditions ( $user , $sgids = false )
{
if ( $user [ 'Role' ][ 'perm_site_admin' ]) {
return array ();
}
if ( $sgids == false ) {
$sgsids = $this -> SharingGroup -> fetchAllAuthorised ( $user );
}
return array (
'OR' => array (
array (
'AND' => array (
'Event.distribution >' => 0 ,
'Event.distribution <' => 4 ,
Configure :: read ( 'MISP.unpublishedprivate' ) ? array ( 'Event.published' => 1 ) : array (),
),
),
array (
'AND' => array (
'Event.sharing_group_id' => $sgids ,
'Event.distribution' => 4 ,
Configure :: read ( 'MISP.unpublishedprivate' ) ? array ( 'Event.published' => 1 ) : array (),
)
)
)
);
}
2017-06-13 12:08:26 +02:00
2018-07-19 11:48:22 +02:00
public function buildConditions ( $user , $sgids = false )
{
$conditions = array ();
if ( ! $user [ 'Role' ][ 'perm_site_admin' ]) {
if ( $sgids === false ) {
$sgsids = $this -> SharingGroup -> fetchAllAuthorised ( $user );
}
$conditions = array (
'AND' => array (
'OR' => array (
array (
'AND' => array (
'Event.org_id' => $user [ 'org_id' ],
)
),
array (
'AND' => array (
$this -> buildEventConditions ( $user , $sgids ),
'OR' => array (
'Object.distribution' => array ( '1' , '2' , '3' , '5' ),
'AND ' => array (
'Object.distribution' => 4 ,
'Object.sharing_group_id' => $sgsids ,
)
)
)
)
)
)
);
}
return $conditions ;
}
2017-06-13 12:08:26 +02:00
2019-06-13 09:16:34 +02:00
public function fetchObjectSimple ( $user , $options = array ())
{
$params = array (
'conditions' => $this -> buildConditions ( $user ),
'fields' => array (),
'recursive' => - 1
);
if ( isset ( $options [ 'conditions' ])) {
$params [ 'conditions' ][ 'AND' ][] = $options [ 'conditions' ];
}
if ( isset ( $options [ 'fields' ])) {
$params [ 'fields' ] = $options [ 'fields' ];
}
$results = $this -> find ( 'all' , array (
'conditions' => $params [ 'conditions' ],
'recursive' => - 1 ,
'fields' => $params [ 'fields' ],
2019-06-24 11:19:03 +02:00
'contain' => array ( 'Event' => array ( 'distribution' , 'id' , 'user_id' , 'orgc_id' , 'org_id' )),
2019-06-13 09:16:34 +02:00
'sort' => false
));
return $results ;
}
2017-06-13 12:08:26 +02:00
2018-07-19 11:48:22 +02:00
// Method that fetches all objects
// very flexible, it's basically a replacement for find, with the addition that it restricts access based on user
// options:
// fields
// contain
// conditions
// order
// group
public function fetchObjects ( $user , $options = array ())
{
$sgsids = $this -> SharingGroup -> fetchAllAuthorised ( $user );
$attributeConditions = array ();
if ( ! $user [ 'Role' ][ 'perm_site_admin' ]) {
$attributeConditions = array (
'OR' => array (
array (
'(SELECT events.org_id FROM events WHERE events.id = Attribute.event_id)' => $user [ 'org_id' ]
),
array (
'OR' => array (
'Attribute.distribution' => array ( 1 , 2 , 3 , 5 ),
array (
'Attribute.distribution' => 4 ,
'Attribute.sharing_group_id' => $sgsids
)
)
)
)
);
}
$params = array (
'conditions' => $this -> buildConditions ( $user ),
'recursive' => - 1 ,
'contain' => array (
'Event' => array (
'fields' => array ( 'id' , 'info' , 'org_id' , 'orgc_id' ),
),
'Attribute' => array (
'conditions' => $attributeConditions ,
//'ShadowAttribute',
'AttributeTag' => array (
'Tag'
)
)
)
);
if ( empty ( $options [ 'includeAllTags' ])) {
$params [ 'contain' ][ 'Attribute' ][ 'AttributeTag' ][ 'Tag' ][ 'conditions' ][ 'exportable' ] = 1 ;
}
if ( isset ( $options [ 'contain' ])) {
$params [ 'contain' ] = array_merge_recursive ( $params [ 'contain' ], $options [ 'contain' ]);
} else {
$option [ 'contain' ][ 'Event' ][ 'fields' ] = array ( 'id' , 'info' , 'org_id' , 'orgc_id' );
}
if ( Configure :: read ( 'MISP.proposals_block_attributes' ) && isset ( $options [ 'conditions' ][ 'AND' ][ 'Attribute.to_ids' ]) && $options [ 'conditions' ][ 'AND' ][ 'Attribute.to_ids' ] == 1 ) {
$this -> Attribute -> bindModel ( array ( 'hasMany' => array ( 'ShadowAttribute' => array ( 'foreignKey' => 'old_id' ))));
$proposalRestriction = array (
'ShadowAttribute' => array (
'conditions' => array (
'AND' => array (
'ShadowAttribute.deleted' => 0 ,
'OR' => array (
'ShadowAttribute.proposal_to_delete' => 1 ,
'ShadowAttribute.to_ids' => 0
)
)
),
'fields' => array ( 'ShadowAttribute.id' )
)
);
$params [ 'contain' ] = array_merge ( $params [ 'contain' ][ 'Attribute' ], $proposalRestriction );
}
if ( isset ( $options [ 'fields' ])) {
$params [ 'fields' ] = $options [ 'fields' ];
}
if ( isset ( $options [ 'conditions' ])) {
$params [ 'conditions' ][ 'AND' ][] = $options [ 'conditions' ];
}
if ( isset ( $options [ 'order' ])) {
$params [ 'order' ] = $options [ 'order' ];
}
if ( ! isset ( $options [ 'withAttachments' ])) {
$options [ 'withAttachments' ] = false ;
} else ( $params [ 'order' ] = array ());
if ( ! isset ( $options [ 'enforceWarninglist' ])) {
$options [ 'enforceWarninglist' ] = false ;
}
if ( ! $user [ 'Role' ][ 'perm_sync' ] || ! isset ( $options [ 'deleted' ]) || ! $options [ 'deleted' ]) {
$params [ 'contain' ][ 'Attribute' ][ 'conditions' ][ 'AND' ][ 'Attribute.deleted' ] = 0 ;
}
if ( isset ( $options [ 'group' ])) {
$params [ 'group' ] = array_merge ( array ( 'Object.id' ), $options [ 'group' ]);
}
if ( Configure :: read ( 'MISP.unpublishedprivate' )) {
$params [ 'conditions' ][ 'AND' ][] = array ( 'OR' => array ( 'Event.published' => 1 , 'Event.orgc_id' => $user [ 'org_id' ]));
}
$results = $this -> find ( 'all' , $params );
if ( $options [ 'enforceWarninglist' ]) {
$this -> Warninglist = ClassRegistry :: init ( 'Warninglist' );
$warninglists = $this -> Warninglist -> fetchForEventView ();
}
$results = array_values ( $results );
$proposals_block_attributes = Configure :: read ( 'MISP.proposals_block_attributes' );
foreach ( $results as $key => $objects ) {
foreach ( $objects as $key2 => $attribute ) {
if ( $options [ 'enforceWarninglist' ] && ! $this -> Warninglist -> filterWarninglistAttributes ( $warninglists , $attribute [ 'Attribute' ], $this -> Warninglist )) {
unset ( $results [ $key ][ $key2 ]);
continue ;
}
if ( $proposals_block_attributes ) {
if ( ! empty ( $attribute [ 'ShadowAttribute' ])) {
unset ( $results [ $key ][ $key2 ]);
} else {
unset ( $results [ $key ][ $key2 ][ 'ShadowAttribute' ]);
}
}
if ( $options [ 'withAttachments' ]) {
if ( $this -> typeIsAttachment ( $attribute [ 'Attribute' ][ 'type' ])) {
$encodedFile = $this -> base64EncodeAttachment ( $attribute [ 'Attribute' ]);
$results [ $key ][ $key2 ][ 'Attribute' ][ 'data' ] = $encodedFile ;
}
}
}
}
return $results ;
}
2017-06-13 12:08:26 +02:00
2018-07-19 11:48:22 +02:00
/*
* Prepare the template form view ' s data , setting defaults , sorting elements
*/
public function prepareTemplate ( $template , $request = array ())
{
$temp = array ();
usort ( $template [ 'ObjectTemplateElement' ], function ( $a , $b ) {
return $a [ 'ui-priority' ] < $b [ 'ui-priority' ];
});
$request_rearranged = array ();
$template_object_elements = $template [ 'ObjectTemplateElement' ];
unset ( $template [ 'ObjectTemplateElement' ]);
if ( ! empty ( $request [ 'Attribute' ])) {
foreach ( $request [ 'Attribute' ] as $attribute ) {
$request_rearranged [ $attribute [ 'object_relation' ]][] = $attribute ;
}
}
foreach ( $template_object_elements as $k => $v ) {
if ( empty ( $request_rearranged [ $v [ 'object_relation' ]])) {
if ( isset ( $this -> Event -> Attribute -> typeDefinitions [ $v [ 'type' ]])) {
$v [ 'default_category' ] = $this -> Event -> Attribute -> typeDefinitions [ $v [ 'type' ]][ 'default_category' ];
$v [ 'to_ids' ] = $this -> Event -> Attribute -> typeDefinitions [ $v [ 'type' ]][ 'to_ids' ];
if ( empty ( $v [ 'categories' ])) {
$v [ 'categories' ] = array ();
foreach ( $this -> Event -> Attribute -> categoryDefinitions as $catk => $catv ) {
if ( in_array ( $v [ 'type' ], $catv [ 'types' ])) {
$v [ 'categories' ][] = $catk ;
}
}
}
$template [ 'ObjectTemplateElement' ][] = $v ;
} else {
$template [ 'warnings' ][] = 'Missing attribute type "' . $v [ 'type' ] . '" found. Omitted template element ("' . $template_object_elements [ $k ][ 'object_relation' ] . '") that would not pass validation due to this.' ;
}
} else {
foreach ( $request_rearranged [ $v [ 'object_relation' ]] as $request_item ) {
if ( isset ( $this -> Event -> Attribute -> typeDefinitions [ $v [ 'type' ]])) {
$v [ 'default_category' ] = $request_item [ 'category' ];
$v [ 'value' ] = $request_item [ 'value' ];
$v [ 'to_ids' ] = $request_item [ 'to_ids' ];
$v [ 'comment' ] = $request_item [ 'comment' ];
if ( ! empty ( $request_item [ 'uuid' ])) {
$v [ 'uuid' ] = $request_item [ 'uuid' ];
}
if ( isset ( $request_item [ 'data' ])) {
$v [ 'data' ] = $request_item [ 'data' ];
}
if ( empty ( $v [ 'categories' ])) {
$v [ 'categories' ] = array ();
foreach ( $this -> Event -> Attribute -> categoryDefinitions as $catk => $catv ) {
if ( in_array ( $v [ 'type' ], $catv [ 'types' ])) {
$v [ 'categories' ][] = $catk ;
}
}
}
2018-11-21 15:25:03 +01:00
$v [ 'disable_correlation' ] = $request_item [ 'disable_correlation' ];
2018-07-19 11:48:22 +02:00
$template [ 'ObjectTemplateElement' ][] = $v ;
2019-08-22 15:28:25 +02:00
unset ( $v [ 'uuid' ]); // force creating a new attribute if template element entry gets reused
2018-07-19 11:48:22 +02:00
} else {
$template [ 'warnings' ][] = 'Missing attribute type "' . $v [ 'type' ] . '" found. Omitted template element ("' . $template_object_elements [ $k ][ 'object_relation' ] . '") that would not pass validation due to this.' ;
}
}
}
}
return $template ;
}
2017-07-02 00:05:15 +02:00
2018-07-19 11:48:22 +02:00
/*
* Clean the attribute list up from artifacts introduced by the object form
*/
public function attributeCleanup ( $attributes )
{
if ( empty ( $attributes [ 'Attribute' ])) {
2019-08-05 10:50:39 +02:00
return $attributes ;
2018-07-19 11:48:22 +02:00
}
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 ( $attributes [ 'Attribute' ][ $k ][ 'value_select' ]);
}
if ( isset ( $attribute [ 'Attachment' ])) {
// Check if there were problems with the file upload
// only keep the last part of the filename, this should prevent directory attacks
$filename = basename ( $attribute [ 'Attachment' ][ 'name' ]);
$tmpfile = new File ( $attribute [ 'Attachment' ][ 'tmp_name' ]);
if (( isset ( $attribute [ 'Attachment' ][ 'error' ]) && $attribute [ 'Attachment' ][ 'error' ] == 0 ) ||
( ! empty ( $attribute [ 'Attachment' ][ 'tmp_name' ]) && $attribute [ 'Attachment' ][ 'tmp_name' ] != 'none' )
) {
if ( ! is_uploaded_file ( $tmpfile -> path )) {
throw new InternalErrorException ( 'PHP says file was not uploaded. Are you attacking me?' );
}
} else {
return 'Issues with the file attachment for the ' . $attribute [ 'object_relation' ] . ' attribute. The error code returned is ' . $attribute [ 'Attachment' ][ 'error' ];
}
$attributes [ 'Attribute' ][ $k ][ 'value' ] = $attribute [ 'Attachment' ][ 'name' ];
unset ( $attributes [ 'Attribute' ][ $k ][ 'Attachment' ]);
$attributes [ 'Attribute' ][ $k ][ 'encrypt' ] = $attribute [ 'type' ] == 'malware-sample' ? 1 : 0 ;
$attributes [ 'Attribute' ][ $k ][ 'data' ] = base64_encode ( $tmpfile -> read ());
$tmpfile -> delete ();
$tmpfile -> close ();
}
unset ( $attributes [ 'Attribute' ][ $k ][ 'save' ]);
}
return $attributes ;
}
2017-07-02 20:32:30 +02:00
2019-10-28 15:45:33 +01:00
// Set Object's *-seen (and ObjectAttribute's *-seen and ObjectAttribute's value if requested) to the provided *-seen value
// Therefore, synchronizing the 3 values
public function syncObjectAndAttributeSeen ( $object , $forcedSeenOnElements , $applyOnAttribute = True ) {
if ( empty ( $forcedSeenOnElements )) {
return $object ;
}
if ( isset ( $forcedSeenOnElements [ 'first_seen' ])) {
$object [ 'Object' ][ 'first_seen' ] = $forcedSeenOnElements [ 'first_seen' ];
}
if ( isset ( $forcedSeenOnElements [ 'last_seen' ])) {
$object [ 'Object' ][ 'last_seen' ] = $forcedSeenOnElements [ 'last_seen' ];
}
if ( $applyOnAttribute ) {
if ( isset ( $object [ 'Attribute' ])) {
$attributes = $object [ 'Attribute' ];
} else {
$attributes = $this -> find ( 'first' , array (
'conditions' => array ( 'id' => $object [ 'Object' ][ 'id' ]),
'contain' => array ( 'Attribute' )
))[ 'Attribute' ];
}
foreach ( $attributes as $i => $attribute ) {
if ( isset ( $forcedSeenOnElements [ 'first_seen' ])) {
$attributes [ $i ][ 'first_seen' ] = $forcedSeenOnElements [ 'first_seen' ];
if ( $attribute [ 'object_relation' ] == 'first-seen' ) {
$attributes [ $i ][ 'value' ] = $forcedSeenOnElements [ 'first_seen' ];
2019-06-13 09:16:34 +02:00
}
2019-10-28 15:45:33 +01:00
} elseif ( isset ( $forcedSeenOnElements [ 'last_seen' ])) {
$attributes [ $i ][ 'last_seen' ] = $forcedSeenOnElements [ 'last_seen' ];
if ( $attribute [ 'object_relation' ] == 'last-seen' ) {
$attributes [ $i ][ 'value' ] = $forcedSeenOnElements [ 'last_seen' ];
2019-06-13 09:16:34 +02:00
}
}
}
2019-10-28 15:45:33 +01:00
$object [ 'Attribute' ] = $attributes ;
2019-06-13 09:16:34 +02:00
}
2019-10-28 15:45:33 +01:00
return $object ;
2019-06-13 09:16:34 +02:00
}
public function deltaMerge ( $object , $objectToSave , $onlyAddNewAttribute = false )
2018-07-19 11:48:22 +02:00
{
if ( ! isset ( $objectToSave [ 'Object' ])) {
$dataToBackup = array ( 'ObjectReferences' , 'Attribute' , 'ShadowAttribute' );
$backup = array ();
foreach ( $dataToBackup as $dtb ) {
if ( isset ( $objectToSave [ $dtb ])) {
$backup [ $dtb ] = $objectToSave [ $dtb ];
unset ( $objectToSave [ $dtb ]);
}
}
$objectToSave = array ( 'Object' => $objectToSave );
foreach ( $dataToBackup as $dtb ) {
if ( isset ( $backup [ $dtb ])) {
$objectToSave [ $dtb ] = $backup [ $dtb ];
}
}
unset ( $dataToBackup );
}
2019-06-13 09:16:34 +02:00
if ( isset ( $objectToSave [ 'Object' ][ 'comment' ])) {
$object [ 'Object' ][ 'comment' ] = $objectToSave [ 'Object' ][ 'comment' ];
}
if ( isset ( $objectToSave [ 'Object' ][ 'distribution' ])) {
$object [ 'Object' ][ 'distribution' ] = $objectToSave [ 'Object' ][ 'distribution' ];
if ( $object [ 'Object' ][ 'distribution' ] == 4 ) {
$object [ 'Object' ][ 'sharing_group_id' ] = $objectToSave [ 'Object' ][ 'sharing_group_id' ];
}
2018-07-19 11:48:22 +02:00
}
$date = new DateTime ();
$object [ 'Object' ][ 'timestamp' ] = $date -> getTimestamp ();
2019-10-28 15:45:33 +01:00
$forcedSeenOnElements = array ();
2019-06-13 09:16:34 +02:00
if ( isset ( $objectToSave [ 'Object' ][ 'first_seen' ])) {
2019-10-28 15:45:33 +01:00
$forcedSeenOnElements [ 'first_seen' ] = $objectToSave [ 'Object' ][ 'first_seen' ];
2019-06-13 09:16:34 +02:00
}
if ( isset ( $objectToSave [ 'Object' ][ 'last_seen' ])) {
2019-10-28 15:45:33 +01:00
$forcedSeenOnElements [ 'last_seen' ] = $objectToSave [ 'Object' ][ 'last_seen' ];
2019-06-13 09:16:34 +02:00
}
2019-10-28 15:45:33 +01:00
$object = $this -> syncObjectAndAttributeSeen ( $object , $forcedSeenOnElements , false );
2018-07-19 11:48:22 +02:00
$this -> save ( $object );
2019-06-13 09:16:34 +02:00
if ( ! $onlyAddNewAttribute ) {
2019-10-28 15:45:33 +01:00
$checkFields = array ( 'category' , 'value' , 'to_ids' , 'distribution' , 'sharing_group_id' , 'comment' , 'disable_correlation' , 'first_seen' , 'last_seen' );
2019-10-02 11:23:04 +02:00
if ( ! empty ( $objectToSave [ 'Attribute' ])) {
foreach ( $objectToSave [ 'Attribute' ] as $newKey => $newAttribute ) {
foreach ( $object [ 'Attribute' ] as $origKey => $originalAttribute ) {
if ( ! empty ( $newAttribute [ 'uuid' ])) {
if ( $newAttribute [ 'uuid' ] == $originalAttribute [ 'uuid' ]) {
$different = false ;
foreach ( $checkFields as $f ) {
if ( $f == 'sharing_group_id' && empty ( $newAttribute [ $f ])) {
$newAttribute [ $f ] = 0 ;
}
if ( $newAttribute [ $f ] != $originalAttribute [ $f ]) {
$different = true ;
}
// Set seen of object at attribute level
2019-10-28 15:45:33 +01:00
if ( isset ( $forcedSeenOnElements [ 'first_seen' ])) {
$newAttribute [ 'first_seen' ] = $forcedSeenOnElements [ 'first_seen' ];
if ( $newAttribute [ 'object_relation' ] == 'first-seen' ) {
// $newAttribute['value'] = $forcedSeenOnElements['first_seen'];
}
2019-10-02 11:23:04 +02:00
$different = true ;
}
2019-10-28 15:45:33 +01:00
if ( isset ( $forcedSeenOnElements [ 'last_seen' ])) {
$newAttribute [ 'last_seen' ] = $forcedSeenOnElements [ 'last_seen' ];
if ( $newAttribute [ 'object_relation' ] == 'last-seen' ) {
// $newAttribute['value'] = $forcedSeenOnElements['last_seen'];
}
2019-10-02 11:23:04 +02:00
$different = true ;
}
2019-08-05 10:50:39 +02:00
}
2019-10-02 11:23:04 +02:00
if ( $different ) {
$newAttribute [ 'id' ] = $originalAttribute [ 'id' ];
$newAttribute [ 'event_id' ] = $object [ 'Object' ][ 'event_id' ];
$newAttribute [ 'object_id' ] = $object [ 'Object' ][ 'id' ];
$newAttribute [ 'timestamp' ] = $date -> getTimestamp ();
$result = $this -> Event -> Attribute -> save ( array ( 'Attribute' => $newAttribute ), array (
'category' ,
'value' ,
'to_ids' ,
'distribution' ,
'sharing_group_id' ,
'comment' ,
'timestamp' ,
'object_id' ,
'event_id' ,
'disable_correlation' ,
'first_seen' ,
'last_seen'
));
2019-08-05 10:50:39 +02:00
}
2019-10-02 11:23:04 +02:00
unset ( $object [ 'Attribute' ][ $origKey ]);
continue 2 ;
2018-07-19 11:48:22 +02:00
}
}
}
2019-10-02 11:23:04 +02:00
$this -> Event -> Attribute -> create ();
$newAttribute [ 'event_id' ] = $object [ 'Object' ][ 'event_id' ];
$newAttribute [ 'object_id' ] = $object [ 'Object' ][ 'id' ];
// Set seen of object at attribute level
2019-10-28 15:45:33 +01:00
if ( isset ( $forcedSeenOnElements [ 'first_seen' ])) {
$newAttribute [ 'first_seen' ] = $forcedSeenOnElements [ 'first_seen' ];
if ( $newAttribute [ 'object_relation' ] == 'first-seen' ) {
$newAttribute [ 'value' ] = $forcedSeenOnElements [ 'first_seen' ];
}
2019-08-05 10:50:39 +02:00
}
2019-10-28 15:45:33 +01:00
if ( isset ( $forcedSeenOnElements [ 'last_seen' ])) {
$newAttribute [ 'last_seen' ] = $forcedSeenOnElements [ 'last_seen' ];
if ( $newAttribute [ 'object_relation' ] == 'last-seen' ) {
$newAttribute [ 'value' ] = $forcedSeenOnElements [ 'last_seen' ];
}
2019-10-02 11:23:04 +02:00
}
if ( ! isset ( $newAttribute [ 'timestamp' ])) {
$newAttribute [ 'distribution' ] = Configure :: read ( 'MISP.default_attribute_distribution' );
if ( $newAttribute [ 'distribution' ] == 'event' ) {
$newAttribute [ 'distribution' ] = 5 ;
}
2019-06-13 09:16:34 +02:00
}
2019-10-02 11:23:04 +02:00
$this -> Event -> Attribute -> save ( $newAttribute );
$attributeArrays [ 'add' ][] = $newAttribute ;
unset ( $objectToSave [ 'Attribute' ][ $newKey ]);
}
foreach ( $object [ 'Attribute' ] as $origKey => $originalAttribute ) {
$originalAttribute [ 'deleted' ] = 1 ;
$this -> Event -> Attribute -> save ( $originalAttribute );
2019-02-10 13:08:12 +01:00
}
2019-08-05 10:50:39 +02:00
}
2019-06-13 09:16:34 +02:00
} else { // we only add the new attribute
$newAttribute = $objectToSave [ 'Attribute' ][ 0 ];
2019-10-28 15:45:33 +01:00
$this -> Event -> Attribute -> create ();
$newAttribute [ 'event_id' ] = $object [ 'Object' ][ 'event_id' ];
$newAttribute [ 'object_id' ] = $object [ 'Object' ][ 'id' ];
// Set seen of object at attribute level
if (
( ! array_key_exists ( 'first_seen' , $newAttribute ) || is_null ( $newAttribute [ 'first_seen' ])) &&
( ! array_key_exists ( 'first_seen' , $object [ 'Object' ]) && ! is_null ( $object [ 'Object' ][ 'first_seen' ]))
) {
$newAttribute [ 'first_seen' ] = $object [ 'Object' ][ 'first_seen' ];
}
if (
( ! array_key_exists ( 'last_seen' , $newAttribute ) || is_null ( $newAttribute [ 'last_seen' ])) &&
( ! array_key_exists ( 'last_seen' , $object [ 'Object' ]) && ! is_null ( $object [ 'Object' ][ 'last_seen' ]))
) {
$newAttribute [ 'last_seen' ] = $object [ 'Object' ][ 'last_seen' ];
$different = true ;
}
if ( ! isset ( $newAttribute [ 'timestamp' ])) {
$newAttribute [ 'distribution' ] = Configure :: read ( 'MISP.default_attribute_distribution' );
if ( $newAttribute [ 'distribution' ] == 'event' ) {
$newAttribute [ 'distribution' ] = 5 ;
2019-02-10 13:08:12 +01:00
}
}
2019-10-28 15:45:33 +01:00
$saveAttributeResult = $this -> Attribute -> saveAttributes ( array ( $newAttribute ));
return $saveAttributeResult ? $this -> id : $this -> validationErrors ;
2018-07-19 11:48:22 +02:00
}
return $this -> id ;
}
2017-07-06 15:04:01 +02:00
2018-07-19 11:48:22 +02:00
public function captureObject ( $object , $eventId , $user , $log = false )
{
$this -> create ();
if ( ! isset ( $object [ 'Object' ])) {
$object = array ( 'Object' => $object );
}
if ( empty ( $log )) {
$log = ClassRegistry :: init ( 'Log' );
}
if ( isset ( $object [ 'Object' ][ 'id' ])) {
unset ( $object [ 'Object' ][ 'id' ]);
}
$object [ 'Object' ][ 'event_id' ] = $eventId ;
if ( $this -> save ( $object )) {
$this -> Event -> unpublishEvent ( $eventId );
$objectId = $this -> id ;
$partialFails = array ();
2019-12-02 08:47:07 +01:00
if ( ! empty ( $object [ 'Object' ][ 'Attribute' ])) {
foreach ( $object [ 'Object' ][ 'Attribute' ] as $attribute ) {
$this -> Attribute -> captureAttribute ( $attribute , $eventId , $user , $objectId , $log );
}
2018-07-19 11:48:22 +02:00
}
return true ;
} else {
$log -> create ();
$log -> save ( array (
'org' => $user [ 'Organisation' ][ 'name' ],
'model' => 'Object' ,
'model_id' => 0 ,
'email' => $user [ 'email' ],
'action' => 'add' ,
'user_id' => $user [ 'id' ],
'title' => 'Object dropped due to validation for Event ' . $eventId . ' failed: ' . $object [ 'Object' ][ 'name' ],
2019-07-18 15:40:20 +02:00
'change' => 'Validation errors: ' . json_encode ( $this -> validationErrors ) . ' Full Object: ' . json_encode ( $object ),
2018-07-19 11:48:22 +02:00
));
}
return 'fail' ;
}
2017-08-28 09:18:40 +02:00
2018-07-19 11:48:22 +02:00
public function editObject ( $object , $eventId , $user , $log )
{
$object [ 'event_id' ] = $eventId ;
if ( isset ( $object [ 'uuid' ])) {
$existingObject = $this -> find ( 'first' , array (
'recursive' => - 1 ,
'conditions' => array ( 'Object.uuid' => $object [ 'uuid' ])
));
if ( empty ( $existingObject )) {
return $this -> captureObject ( $object , $eventId , $user , $log );
} else {
if ( $existingObject [ 'Object' ][ 'event_id' ] != $eventId ) {
$log -> create ();
$log -> save ( array (
'org' => $user [ 'Organisation' ][ 'name' ],
'model' => 'Object' ,
'model_id' => 0 ,
'email' => $user [ 'email' ],
'action' => 'edit' ,
'user_id' => $user [ 'id' ],
'title' => 'Duplicate UUID found in object' ,
'change' => 'An object was blocked from being saved due to a duplicate UUID. The uuid in question is: ' . $object [ 'uuid' ] . '. This can also be due to the same object (or an object with the same UUID) existing in a different event)' ,
));
return true ;
}
if ( isset ( $object [ 'timestamp' ])) {
if ( $existingObject [ 'Object' ][ 'timestamp' ] >= $object [ 'timestamp' ]) {
return true ;
}
} else {
$date = new DateTime ();
$object [ 'timestamp' ] = $date -> getTimestamp ();
}
}
} else {
return $this -> captureObject ( $object , $eventId , $user , $log );
}
// At this point we have an existingObject that we can edit
$recoverFields = array (
'name' ,
'meta-category' ,
'description' ,
'template_uuid' ,
'template_version' ,
'distribution' ,
'sharing_group_id' ,
'comment' ,
'deleted'
);
foreach ( $recoverFields as $rF ) {
if ( ! isset ( $object [ $rF ])) {
$object [ $rF ] = $existingObject [ 'Object' ][ $rF ];
}
}
$object [ 'id' ] = $existingObject [ 'Object' ][ 'id' ];
$object [ 'uuid' ] = $existingObject [ 'Object' ][ 'uuid' ];
$object [ 'event_id' ] = $eventId ;
if ( $object [ 'distribution' ] == 4 ) {
$object [ 'sharing_group_id' ] = $this -> SharingGroup -> captureSG ( $object [ 'SharingGroup' ], $user );
}
if ( ! $this -> save ( $object )) {
$log -> create ();
$log -> save ( array (
'org' => $user [ 'Organisation' ][ 'name' ],
'model' => 'Object' ,
'model_id' => 0 ,
'email' => $user [ 'email' ],
'action' => 'edit' ,
'user_id' => $user [ 'id' ],
'title' => 'Attribute dropped due to validation for Event ' . $eventId . ' failed: ' . $object [ 'name' ],
'change' => 'Validation errors: ' . json_encode ( $this -> validationErrors ) . ' Full Object: ' . json_encode ( $attribute ),
));
return $this -> validationErrors ;
} else {
$this -> Event -> unpublishEvent ( $eventId );
}
if ( ! empty ( $object [ 'Attribute' ])) {
foreach ( $object [ 'Attribute' ] as $attribute ) {
$result = $this -> Attribute -> editAttribute ( $attribute , $eventId , $user , $object [ 'id' ], $log );
}
}
return true ;
}
2017-09-04 17:26:45 +02:00
2018-07-19 11:48:22 +02:00
public function updateTimestamp ( $id )
{
$date = new DateTime ();
$object = $this -> find ( 'first' , array (
'recursive' => - 1 ,
'conditions' => array ( 'Object.id' => $id )
));
$object [ 'Object' ][ 'timestamp' ] = $date -> getTimestamp ();
$object [ 'Object' ][ 'skip_zmq' ] = 1 ;
2019-03-05 12:24:56 +01:00
$object [ 'Object' ][ 'skip_kafka' ] = 1 ;
2018-07-19 11:48:22 +02:00
$result = $this -> save ( $object );
return $result ;
}
2017-09-04 23:12:46 +02:00
2018-07-19 11:48:22 +02:00
// Hunt down all LEDA and CASTOR clones
public function removeOrphanedObjects ()
{
$orphans = $this -> find ( 'list' , array (
'fields' => array ( 'Object.id' , 'Object.id' ),
'conditions' => array ( 'Event.id' => null ),
'contain' => array ( 'Event' => array ( 'fields' => array ( 'id' )))
));
foreach ( $orphans as $orphan ) {
$this -> delete ( $orphan );
}
return count ( $orphans );
}
2019-05-08 16:56:19 +02:00
2019-05-17 16:28:58 +02:00
public function validObjectsFromAttributeTypes ( $user , $event_id , $selected_attribute_ids )
2019-05-08 16:56:19 +02:00
{
2019-06-12 11:17:17 +02:00
$attributes = $this -> Attribute -> fetchAttributes ( $user ,
2019-05-08 16:56:19 +02:00
array (
'conditions' => array (
2019-06-12 11:17:17 +02:00
'Attribute.id' => $selected_attribute_ids ,
'Attribute.event_id' => $event_id ,
'Attribute.object_id' => 0
2019-05-08 16:56:19 +02:00
),
)
);
2019-05-17 16:02:06 +02:00
if ( empty ( $attributes )) {
2019-05-21 08:35:19 +02:00
return array ( 'templates' => array (), 'types' => array ());
2019-05-17 16:02:06 +02:00
}
2019-05-08 16:56:19 +02:00
$attribute_types = array ();
foreach ( $attributes as $i => $attribute ) {
$attribute_types [ $attribute [ 'Attribute' ][ 'type' ]] = 1 ;
$attributes [ $i ][ 'Attribute' ][ 'object_relation' ] = $attribute [ 'Attribute' ][ 'type' ];
}
$attribute_types = array_keys ( $attribute_types );
2019-05-17 16:02:06 +02:00
2019-06-18 15:50:48 +02:00
$potential_templates = $this -> ObjectTemplate -> find ( 'list' , array (
2019-05-17 16:02:06 +02:00
'recursive' => - 1 ,
'fields' => array (
'ObjectTemplate.id' ,
'COUNT(ObjectTemplateElement.type) as type_count'
),
'conditions' => array (
'ObjectTemplate.active' => true ,
'ObjectTemplateElement.type' => $attribute_types
),
'joins' => array (
array (
2019-06-12 12:05:13 +02:00
'table' => 'object_template_elements' ,
2019-05-17 16:02:06 +02:00
'alias' => 'ObjectTemplateElement' ,
'type' => 'RIGHT' ,
'fields' => array ( 'ObjectTemplateElement.object_relation' , 'ObjectTemplateElement.type' ),
'conditions' => array ( 'ObjectTemplate.id = ObjectTemplateElement.object_template_id' )
)
),
2019-06-18 15:50:48 +02:00
'group' => 'ObjectTemplate.id' ,
2019-05-17 16:02:06 +02:00
'order' => 'type_count DESC'
));
2019-06-18 15:50:48 +02:00
$potential_template_ids = array_keys ( $potential_templates );
2019-05-08 16:56:19 +02:00
$templates = $this -> ObjectTemplate -> find ( 'all' , array (
'recursive' => - 1 ,
'conditions' => array ( 'id' => $potential_template_ids ),
'contain' => 'ObjectTemplateElement'
));
foreach ( $templates as $i => $template ) {
2019-05-17 16:02:06 +02:00
$res = $this -> ObjectTemplate -> checkTemplateConformityBasedOnTypes ( $template , $attributes );
$templates [ $i ][ 'ObjectTemplate' ][ 'compatibility' ] = $res [ 'valid' ] ? true : $res [ 'missingTypes' ];
$templates [ $i ][ 'ObjectTemplate' ][ 'invalidTypes' ] = $res [ 'invalidTypes' ];
$templates [ $i ][ 'ObjectTemplate' ][ 'invalidTypesMultiple' ] = $res [ 'invalidTypesMultiple' ];
2019-05-08 16:56:19 +02:00
}
2019-05-20 14:30:20 +02:00
return array ( 'templates' => $templates , 'types' => $attribute_types );
2019-05-08 16:56:19 +02:00
}
2019-06-06 15:11:34 +02:00
2019-06-12 11:00:17 +02:00
public function groupAttributesIntoObject ( $user , $event_id , $object , $template , $selected_attribute_ids , $selected_object_relation_mapping , $hard_delete_attribute )
2019-06-06 15:11:34 +02:00
{
$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' ]);
2019-06-12 11:38:19 +02:00
// Duplicate the attribute and its context, otherwise connected instances will drop the duplicated UUID
2019-06-06 15:11:34 +02:00
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' ]);
2019-06-12 11:56:42 +02:00
$this -> Attribute -> deleteAttribute ( $existing_attribute [ 'Attribute' ][ 'id' ], $user , $hard_delete_attribute );
2019-06-06 15:11:34 +02:00
}
}
return $saved_object [ 'Object' ][ 'id' ];
}
2017-06-13 12:08:26 +02:00
}