2013-04-25 14:04:08 +02:00
|
|
|
<?php
|
|
|
|
App::uses('AppModel', 'Model');
|
|
|
|
App::uses('Folder', 'Utility');
|
|
|
|
App::uses('File', 'Utility');
|
2020-05-05 15:23:26 +02:00
|
|
|
App::uses('AttachmentTool', 'Tools');
|
2020-10-11 15:20:20 +02:00
|
|
|
App::uses('ComplexTypeTool', 'Tools');
|
2021-08-11 20:47:52 +02:00
|
|
|
App::uses('ServerSyncTool', 'Tools');
|
2021-10-28 09:40:28 +02:00
|
|
|
App::uses('AttributeValidationTool', 'Tools');
|
2013-04-25 14:04:08 +02:00
|
|
|
|
2020-07-24 15:46:59 +02:00
|
|
|
/**
|
|
|
|
* @property Event $Event
|
2020-10-11 15:20:20 +02:00
|
|
|
* @property Attribute $Attribute
|
2021-01-09 21:12:57 +01:00
|
|
|
* @property-read array $typeDefinitions
|
|
|
|
* @property-read array $categoryDefinitions
|
2020-07-24 15:46:59 +02:00
|
|
|
*/
|
2018-07-19 11:48:22 +02:00
|
|
|
class ShadowAttribute extends AppModel
|
|
|
|
{
|
|
|
|
public $combinedKeys = array('event_id', 'category', 'type');
|
|
|
|
|
2023-05-21 10:09:05 +02:00
|
|
|
public $name = 'ShadowAttribute';
|
2018-07-19 11:48:22 +02:00
|
|
|
|
2019-09-30 08:23:36 +02:00
|
|
|
public $recursive = -1;
|
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
public $actsAs = array(
|
2021-01-22 13:01:23 +01:00
|
|
|
'AuditLog',
|
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'),
|
|
|
|
'Trim',
|
|
|
|
'Containable',
|
|
|
|
'Regexp' => array('fields' => array('value', 'value2')),
|
|
|
|
);
|
|
|
|
|
|
|
|
public $belongsTo = array(
|
|
|
|
'Event' => array(
|
|
|
|
'className' => 'Event',
|
|
|
|
'foreignKey' => 'event_id',
|
|
|
|
'conditions' => '',
|
|
|
|
'fields' => '',
|
|
|
|
'order' => '',
|
|
|
|
'counterCache' => true
|
|
|
|
),
|
|
|
|
'Org' => array(
|
|
|
|
'className' => 'Organisation',
|
|
|
|
'foreignKey' => 'org_id'
|
|
|
|
),
|
|
|
|
'EventOrg' => array(
|
|
|
|
'className' => 'Organisation',
|
|
|
|
'foreignKey' => 'event_org_id'
|
|
|
|
),
|
2019-07-11 22:57:58 +02:00
|
|
|
'Attribute' => array(
|
|
|
|
'className' => 'Attribute',
|
|
|
|
'foreignKey' => 'old_id'
|
|
|
|
)
|
2018-07-19 11:48:22 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
public $displayField = 'value';
|
|
|
|
|
|
|
|
public $virtualFields = array(
|
|
|
|
'value' => "CASE WHEN ShadowAttribute.value2 = '' THEN ShadowAttribute.value1 ELSE CONCAT(ShadowAttribute.value1, '|', ShadowAttribute.value2) END",
|
|
|
|
); // TODO hardcoded
|
|
|
|
|
|
|
|
// explanations of certain fields to be used in various views
|
|
|
|
public $fieldDescriptions = array(
|
|
|
|
'signature' => array('desc' => 'Is this attribute eligible to automatically create an IDS signature (network IDS or host IDS) out of it ?'),
|
|
|
|
);
|
|
|
|
|
|
|
|
public $order = array("ShadowAttribute.event_id" => "DESC", "ShadowAttribute.type" => "ASC");
|
|
|
|
|
|
|
|
public $validate = array(
|
|
|
|
'event_id' => array(
|
|
|
|
'numeric' => array(
|
|
|
|
'rule' => array('numeric')
|
|
|
|
)
|
|
|
|
),
|
|
|
|
'org_id' => array(
|
|
|
|
'numeric' => array(
|
|
|
|
'rule' => array('numeric')
|
|
|
|
)
|
|
|
|
),
|
|
|
|
'event_org_id' => array(
|
|
|
|
'numeric' => array(
|
|
|
|
'rule' => array('numeric')
|
|
|
|
)
|
|
|
|
),
|
|
|
|
'type' => array(
|
|
|
|
// currently when adding a new attribute type we need to change it in both places
|
|
|
|
'rule' => array('validateTypeValue'),
|
|
|
|
'message' => 'Options depend on the selected category.',
|
|
|
|
//'allowEmpty' => false,
|
|
|
|
'required' => true,
|
|
|
|
//'last' => false, // Stop validation after this rule
|
|
|
|
//'on' => 'create', // Limit validation to 'create' or 'update' operations
|
|
|
|
|
|
|
|
),
|
|
|
|
// this could be initialized from categoryDefinitions but dunno how at the moment
|
|
|
|
'category' => array(
|
|
|
|
'validCategory' => array(
|
|
|
|
'rule' => array('validCategory'),
|
|
|
|
'message' => 'Options : Payload delivery, Antivirus detection, Payload installation, Files dropped ...'
|
|
|
|
),
|
|
|
|
),
|
|
|
|
'value' => array(
|
|
|
|
'stringNotEmpty' => array(
|
|
|
|
'rule' => array('stringNotEmpty'),
|
|
|
|
),
|
|
|
|
'userdefined' => array(
|
|
|
|
'rule' => array('validateAttributeValue'),
|
|
|
|
'message' => 'Value not in the right type/format. Please double check the value or select type "other".',
|
|
|
|
),
|
|
|
|
),
|
|
|
|
'to_ids' => array(
|
|
|
|
'boolean' => array(
|
2020-10-11 15:20:20 +02:00
|
|
|
'rule' => 'boolean',
|
2018-07-19 11:48:22 +02:00
|
|
|
'required' => false,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
'uuid' => array(
|
|
|
|
'uuid' => array(
|
2020-09-01 19:05:06 +02:00
|
|
|
'rule' => 'uuid',
|
2020-09-18 10:55:52 +02:00
|
|
|
'message' => 'Please provide a valid RFC 4122 UUID'
|
2018-07-19 11:48:22 +02:00
|
|
|
),
|
|
|
|
),
|
|
|
|
'proposal_to_delete' => array(
|
2020-10-11 15:20:20 +02:00
|
|
|
'boolean' => array(
|
|
|
|
'rule' => 'boolean',
|
|
|
|
),
|
2018-07-19 11:48:22 +02:00
|
|
|
),
|
2019-06-24 10:28:55 +02:00
|
|
|
'first_seen' => array(
|
|
|
|
'rule' => array('datetimeOrNull'),
|
|
|
|
'required' => false,
|
2021-10-23 20:48:03 +02:00
|
|
|
'message' => array('Invalid ISO 8601 format'),
|
2019-06-24 10:28:55 +02:00
|
|
|
),
|
|
|
|
'last_seen' => array(
|
2021-05-06 15:01:58 +02:00
|
|
|
'datetimeOrNull' => array(
|
|
|
|
'rule' => array('datetimeOrNull'),
|
|
|
|
'required' => false,
|
2021-10-23 20:48:03 +02:00
|
|
|
'message' => array('Invalid ISO 8601 format'),
|
2021-05-06 15:01:58 +02:00
|
|
|
),
|
|
|
|
'validateLastSeenValue' => array(
|
|
|
|
'rule' => array('validateLastSeenValue'),
|
|
|
|
'required' => false,
|
|
|
|
'message' => array('Last seen value should be greater than first seen value')
|
|
|
|
),
|
2019-06-24 10:28:55 +02:00
|
|
|
)
|
2018-07-19 11:48:22 +02:00
|
|
|
);
|
|
|
|
|
2021-01-09 21:12:57 +01:00
|
|
|
public function __isset($name)
|
|
|
|
{
|
|
|
|
if ($name === 'typeDefinitions' || $name === 'categoryDefinitions') {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return parent::__isset($name);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function __get($name)
|
2018-07-19 11:48:22 +02:00
|
|
|
{
|
2021-01-09 21:12:57 +01:00
|
|
|
if ($name === 'categoryDefinitions') {
|
|
|
|
return $this->Attribute->categoryDefinitions;
|
|
|
|
} else if ($name === 'typeDefinitions') {
|
|
|
|
return $this->Attribute->typeDefinitions;
|
|
|
|
}
|
|
|
|
return parent::__get($name);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// The Associations below have been created with all possible keys, those that are not needed can be removed
|
|
|
|
|
|
|
|
public function beforeSave($options = array())
|
|
|
|
{
|
|
|
|
// explode value of composite type in value1 and value2
|
|
|
|
// or copy value to value1 if not composite type
|
|
|
|
if (!empty($this->data['ShadowAttribute']['type'])) {
|
|
|
|
$compositeTypes = $this->getCompositeTypes();
|
|
|
|
// explode composite types in value1 and value2
|
|
|
|
$pieces = explode('|', $this->data['ShadowAttribute']['value']);
|
2021-10-23 20:48:03 +02:00
|
|
|
if (in_array($this->data['ShadowAttribute']['type'], $compositeTypes, true)) {
|
2018-07-19 11:48:22 +02:00
|
|
|
if (2 != count($pieces)) {
|
|
|
|
throw new InternalErrorException('Composite type, but value not explodable');
|
|
|
|
}
|
|
|
|
$this->data['ShadowAttribute']['value1'] = $pieces[0];
|
|
|
|
$this->data['ShadowAttribute']['value2'] = $pieces[1];
|
|
|
|
} else {
|
|
|
|
$total = implode('|', $pieces);
|
|
|
|
$this->data['ShadowAttribute']['value1'] = $total;
|
|
|
|
$this->data['ShadowAttribute']['value2'] = '';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!isset($this->data['ShadowAttribute']['deleted'])) {
|
|
|
|
$this->data['ShadowAttribute']['deleted'] = 0;
|
|
|
|
}
|
|
|
|
if ($this->data['ShadowAttribute']['deleted']) {
|
2022-08-05 12:55:04 +02:00
|
|
|
// correlations for proposals are deprecated.
|
|
|
|
//$this->__beforeDeleteCorrelation($this->data['ShadowAttribute']);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
2019-06-24 10:28:55 +02:00
|
|
|
|
|
|
|
// convert into utc and micro sec
|
2019-12-16 10:47:07 +01:00
|
|
|
$this->data = $this->Attribute->ISODatetimeToUTC($this->data, $this->alias);
|
2018-07-19 11:48:22 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function __beforeDeleteCorrelation($sa)
|
|
|
|
{
|
|
|
|
if (isset($sa['ShadowAttribute'])) {
|
|
|
|
$sa = $sa['ShadowAttribute'];
|
|
|
|
}
|
|
|
|
$this->ShadowAttributeCorrelation = ClassRegistry::init('ShadowAttributeCorrelation');
|
|
|
|
$this->ShadowAttributeCorrelation->deleteAll(array('ShadowAttributeCorrelation.1_shadow_attribute_id' => $sa['id']));
|
|
|
|
}
|
|
|
|
|
|
|
|
private function __afterSaveCorrelation($sa)
|
|
|
|
{
|
|
|
|
if (isset($sa['ShadowAttribute'])) {
|
|
|
|
$sa = $sa['ShadowAttribute'];
|
|
|
|
}
|
2021-07-27 15:19:41 +02:00
|
|
|
if (in_array($sa['type'], Attribute::NON_CORRELATING_TYPES, true)) {
|
2018-07-19 11:48:22 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
$this->ShadowAttributeCorrelation = ClassRegistry::init('ShadowAttributeCorrelation');
|
|
|
|
$shadow_attribute_correlations = array();
|
|
|
|
$correlatingValues = array($sa['value1']);
|
|
|
|
if (!empty($sa['value2'])) {
|
|
|
|
$correlatingValues[] = $sa['value2'];
|
|
|
|
}
|
|
|
|
foreach ($correlatingValues as $k => $cV) {
|
2020-10-11 15:20:20 +02:00
|
|
|
$correlatingAttributes[$k] = $this->Attribute->find('all', array(
|
2018-07-19 11:48:22 +02:00
|
|
|
'conditions' => array(
|
|
|
|
'AND' => array(
|
|
|
|
'OR' => array(
|
|
|
|
'Attribute.value1' => $cV,
|
|
|
|
'Attribute.value2' => $cV
|
|
|
|
),
|
2021-07-27 15:19:41 +02:00
|
|
|
'Attribute.type !=' => Attribute::NON_CORRELATING_TYPES,
|
2018-07-19 11:48:22 +02:00
|
|
|
'Attribute.deleted' => 0,
|
|
|
|
'Attribute.event_id !=' => $sa['event_id']
|
|
|
|
),
|
|
|
|
),
|
|
|
|
'recursive => -1',
|
|
|
|
'fields' => array('Attribute.event_id', 'Attribute.id', 'Attribute.distribution', 'Attribute.sharing_group_id'),
|
|
|
|
'contain' => array('Event' => array('fields' => array('Event.id', 'Event.date', 'Event.info', 'Event.org_id', 'Event.distribution', 'Event.sharing_group_id'))),
|
|
|
|
'order' => array(),
|
|
|
|
));
|
|
|
|
foreach ($correlatingAttributes as $key => $cA) {
|
|
|
|
foreach ($cA as $corr) {
|
|
|
|
$shadow_attribute_correlations[] = array(
|
|
|
|
'value' => $correlatingValues[$key],
|
|
|
|
'1_event_id' => $sa['event_id'],
|
|
|
|
'1_shadow_attribute_id' => $sa['id'],
|
|
|
|
'event_id' => $corr['Attribute']['event_id'],
|
|
|
|
'attribute_id' => $corr['Attribute']['id'],
|
|
|
|
'org_id' => $corr['Event']['org_id'],
|
|
|
|
'distribution' => $corr['Event']['distribution'],
|
|
|
|
'a_distribution' => $corr['Attribute']['distribution'],
|
|
|
|
'sharing_group_id' => $corr['Event']['sharing_group_id'],
|
|
|
|
'a_sharing_group_id' => $corr['Attribute']['sharing_group_id'],
|
|
|
|
'date' => $corr['Event']['date'],
|
|
|
|
'info' => $corr['Event']['info'],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!empty($shadow_attribute_correlations)) {
|
|
|
|
$this->ShadowAttributeCorrelation->saveMany($shadow_attribute_correlations);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function afterSave($created, $options = array())
|
|
|
|
{
|
|
|
|
$result = true;
|
|
|
|
// if the 'data' field is set on the $this->data then save the data to the correct file
|
|
|
|
if (isset($this->data['ShadowAttribute']['deleted']) && $this->data['ShadowAttribute']['deleted']) {
|
|
|
|
$sa = $this->find('first', array('conditions' => array('ShadowAttribute.id' => $this->data['ShadowAttribute']['id']), 'recursive' => -1, 'fields' => array('ShadowAttribute.id', 'ShadowAttribute.event_id', 'ShadowAttribute.type')));
|
|
|
|
if ($this->typeIsAttachment($sa['ShadowAttribute']['type'])) {
|
2020-08-13 15:58:42 +02:00
|
|
|
$this->loadAttachmentTool()->deleteShadow($sa['ShadowAttribute']['event_id'], $sa['ShadowAttribute']['id']);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (isset($this->data['ShadowAttribute']['type']) && $this->typeIsAttachment($this->data['ShadowAttribute']['type']) && !empty($this->data['ShadowAttribute']['data'])) {
|
|
|
|
$result = $result && $this->saveBase64EncodedAttachment($this->data['ShadowAttribute']);
|
|
|
|
}
|
|
|
|
}
|
2022-08-05 12:55:04 +02:00
|
|
|
/*
|
|
|
|
* correlations are deprecated for proposals
|
2018-07-19 11:48:22 +02:00
|
|
|
if ((isset($this->data['ShadowAttribute']['deleted']) && $this->data['ShadowAttribute']['deleted']) || (isset($this->data['ShadowAttribute']['proposal_to_delete']) && $this->data['ShadowAttribute']['proposal_to_delete'])) {
|
|
|
|
// this is a deletion
|
|
|
|
// Could be a proposal to delete or flagging a proposal that it was discarded / accepted - either way, we don't want to correlate here for now
|
|
|
|
} else {
|
|
|
|
$this->__afterSaveCorrelation($this->data['ShadowAttribute']);
|
|
|
|
}
|
2022-08-05 12:55:04 +02:00
|
|
|
*/
|
2019-03-05 12:24:56 +01:00
|
|
|
if (empty($this->data['ShadowAttribute']['deleted'])) {
|
|
|
|
$action = $created ? 'add' : 'edit';
|
|
|
|
$this->publishKafkaNotification('shadow_attribute', $this->data, $action);
|
|
|
|
}
|
2018-07-19 11:48:22 +02:00
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function beforeDelete($cascade = true)
|
|
|
|
{
|
|
|
|
// delete attachments from the disk
|
|
|
|
$this->read(); // first read the attribute from the db
|
|
|
|
if ($this->typeIsAttachment($this->data['ShadowAttribute']['type'])) {
|
2020-08-13 15:58:42 +02:00
|
|
|
$this->loadAttachmentTool()->deleteShadow($this->data['ShadowAttribute']['event_id'], $this->data['ShadowAttribute']['id']);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function beforeValidate($options = array())
|
|
|
|
{
|
2021-10-23 20:48:03 +02:00
|
|
|
$proposal = &$this->data['ShadowAttribute'];
|
|
|
|
if (!isset($proposal['type'])) {
|
|
|
|
$this->invalidate('type', 'No value provided.');
|
|
|
|
return false;
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
2021-10-23 20:48:03 +02:00
|
|
|
if (!isset($proposal['comment'])) {
|
|
|
|
$proposal['comment'] = '';
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
2020-10-11 15:20:20 +02:00
|
|
|
// make some changes to the inserted value
|
2021-10-23 20:48:03 +02:00
|
|
|
if (isset($proposal['value'])) {
|
|
|
|
$value = trim($proposal['value']);
|
|
|
|
$value = ComplexTypeTool::refangValue($value, $proposal['type']);
|
2021-10-28 09:40:28 +02:00
|
|
|
$value = AttributeValidationTool::modifyBeforeValidation($proposal['type'], $value);
|
2021-10-23 20:48:03 +02:00
|
|
|
$proposal['value'] = $value;
|
2020-10-11 15:20:20 +02:00
|
|
|
}
|
|
|
|
|
2021-10-23 20:48:03 +02:00
|
|
|
if (!isset($proposal['org'])) {
|
|
|
|
$proposal['org'] = '';
|
2020-09-30 09:54:00 +02:00
|
|
|
}
|
|
|
|
|
2021-10-23 20:48:03 +02:00
|
|
|
if (empty($proposal['timestamp'])) {
|
|
|
|
$proposal['timestamp'] = time();
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
2021-10-23 20:48:03 +02:00
|
|
|
if (!isset($proposal['proposal_to_delete'])) {
|
|
|
|
$proposal['proposal_to_delete'] = 0;
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// generate UUID if it doesn't exist
|
2021-10-23 20:48:03 +02:00
|
|
|
if (empty($proposal['uuid'])) {
|
|
|
|
$proposal['uuid'] = CakeText::uuid();
|
2020-09-01 19:05:06 +02:00
|
|
|
} else {
|
2021-10-23 20:48:03 +02:00
|
|
|
$proposal['uuid'] = strtolower($proposal['uuid']);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
2021-10-23 20:48:03 +02:00
|
|
|
if (empty($proposal['category'])) {
|
|
|
|
$proposal['category'] = $this->Attribute->typeDefinitions[$proposal['type']]['default_category'];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isset($proposal['first_seen'])) {
|
|
|
|
$proposal['first_seen'] = $proposal['first_seen'] === '' ? null : $proposal['first_seen'];
|
|
|
|
}
|
|
|
|
if (isset($proposal['last_seen'])) {
|
|
|
|
$proposal['last_seen'] = $proposal['last_seen'] === '' ? null : $proposal['last_seen'];
|
2019-07-29 11:17:18 +02:00
|
|
|
}
|
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-06-24 10:28:55 +02:00
|
|
|
public function afterFind($results, $primary = false)
|
|
|
|
{
|
2021-10-28 13:19:20 +02:00
|
|
|
foreach ($results as &$v) {
|
|
|
|
$proposal = &$v['ShadowAttribute'];
|
|
|
|
if (!empty($proposal['first_seen'])) {
|
|
|
|
$proposal['first_seen'] = $this->microTimestampToIso($proposal['first_seen']);
|
|
|
|
}
|
|
|
|
if (!empty($proposal['last_seen'])) {
|
|
|
|
$proposal['last_seen'] = $this->microTimestampToIso($proposal['last_seen']);
|
|
|
|
}
|
2019-06-24 10:28:55 +02:00
|
|
|
}
|
|
|
|
return $results;
|
|
|
|
}
|
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
public function validateTypeValue($fields)
|
|
|
|
{
|
|
|
|
$category = $this->data['ShadowAttribute']['category'];
|
|
|
|
if (isset($this->categoryDefinitions[$category]['types'])) {
|
2021-01-09 21:12:57 +01:00
|
|
|
return in_array($fields['type'], $this->categoryDefinitions[$category]['types'], true);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function validCategory($fields)
|
|
|
|
{
|
2020-10-11 15:20:20 +02:00
|
|
|
return $this->Attribute->validCategory($fields);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function validateAttributeValue($fields)
|
|
|
|
{
|
|
|
|
$value = $fields['value'];
|
2021-10-28 09:47:55 +02:00
|
|
|
return AttributeValidationTool::validate($this->data['ShadowAttribute']['type'], $value);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getCompositeTypes()
|
|
|
|
{
|
2020-10-11 15:20:20 +02:00
|
|
|
return $this->Attribute->getCompositeTypes();
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function typeIsMalware($type)
|
|
|
|
{
|
2021-07-20 17:30:17 +02:00
|
|
|
return $this->Attribute->typeIsMalware($type);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function typeIsAttachment($type)
|
|
|
|
{
|
2020-10-11 15:20:20 +02:00
|
|
|
return $this->Attribute->typeIsAttachment($type);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
2020-08-28 16:48:16 +02:00
|
|
|
public function base64EncodeAttachment(array $attribute)
|
2018-07-19 11:48:22 +02:00
|
|
|
{
|
2020-08-28 16:48:16 +02:00
|
|
|
try {
|
|
|
|
return base64_encode($this->getAttachment($attribute));
|
|
|
|
} catch (NotFoundException $e) {
|
|
|
|
$this->log($e->getMessage(), LOG_NOTICE);
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getAttachment($attribute, $path_suffix='')
|
|
|
|
{
|
|
|
|
return $this->loadAttachmentTool()->getShadowContent($attribute['event_id'], $attribute['id'], $path_suffix);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function saveBase64EncodedAttachment($attribute)
|
|
|
|
{
|
2020-05-05 15:23:26 +02:00
|
|
|
$data = base64_decode($attribute['data']);
|
2020-10-08 19:27:49 +02:00
|
|
|
$result = $this->loadAttachmentTool()->saveShadow($attribute['event_id'], $attribute['id'], $data);
|
|
|
|
if ($result) {
|
|
|
|
$this->loadAttachmentScan()->backgroundScan(AttachmentScan::TYPE_SHADOW_ATTRIBUTE, $attribute);
|
|
|
|
}
|
|
|
|
return $result;
|
2020-08-13 15:58:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $shadowAttribute
|
|
|
|
* @param string $path_suffix
|
|
|
|
* @return File
|
|
|
|
* @throws Exception
|
|
|
|
*/
|
|
|
|
public function getAttachmentFile(array $shadowAttribute, $path_suffix='')
|
|
|
|
{
|
|
|
|
return $this->loadAttachmentTool()->getShadowFile($shadowAttribute['event_id'], $shadowAttribute['id'], $path_suffix);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function checkComposites()
|
|
|
|
{
|
|
|
|
$compositeTypes = $this->getCompositeTypes();
|
|
|
|
$fails = array();
|
|
|
|
$attributes = $this->find('all', array('recursive' => 0));
|
|
|
|
|
|
|
|
foreach ($attributes as $attribute) {
|
|
|
|
if ((in_array($attribute['ShadowAttribute']['type'], $compositeTypes)) && (!strlen($attribute['ShadowAttribute']['value1']) || !strlen($attribute['ShadowAttribute']['value2']))) {
|
|
|
|
$fails[] = $attribute['ShadowAttribute']['event_id'] . ':' . $attribute['ShadowAttribute']['id'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $fails;
|
|
|
|
}
|
|
|
|
|
2019-06-24 10:28:55 +02:00
|
|
|
// check whether the variable is null or datetime
|
|
|
|
public function datetimeOrNull($fields)
|
|
|
|
{
|
2020-10-11 15:20:20 +02:00
|
|
|
return $this->Attribute->datetimeOrNull($fields);
|
2019-06-24 10:28:55 +02:00
|
|
|
}
|
|
|
|
|
2021-05-06 15:01:58 +02:00
|
|
|
public function validateLastSeenValue($fields)
|
|
|
|
{
|
|
|
|
$ls = $fields['last_seen'];
|
2021-07-27 14:33:54 +02:00
|
|
|
if (!isset($this->data['ShadowAttribute']['first_seen']) || is_null($ls)) {
|
2021-05-06 15:01:58 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
$converted = $this->Attribute->ISODatetimeToUTC(['ShadowAttribute' => [
|
|
|
|
'first_seen' => $this->data['ShadowAttribute']['first_seen'],
|
|
|
|
'last_seen' => $ls
|
|
|
|
]], 'ShadowAttribute');
|
2021-05-11 11:22:19 +02:00
|
|
|
if ($converted['ShadowAttribute']['first_seen'] > $converted['ShadowAttribute']['last_seen']) {
|
2021-05-06 15:01:58 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
public function setDeleted($id)
|
|
|
|
{
|
|
|
|
$this->Behaviors->detach('SysLogLogable.SysLogLogable');
|
|
|
|
$sa = $this->find('first', array('conditions' => array('ShadowAttribute.id' => $id), 'recusive' => -1));
|
|
|
|
if (empty($sa)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
$date = new DateTime();
|
|
|
|
$sa['ShadowAttribute']['deleted'] = 1;
|
|
|
|
$sa['ShadowAttribute']['timestamp'] = $date->getTimestamp();
|
|
|
|
$this->save($sa);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function findOldProposal($sa)
|
|
|
|
{
|
|
|
|
$oldsa = $this->find('first', array(
|
|
|
|
'conditions' => array(
|
2019-07-14 08:24:10 +02:00
|
|
|
'ShadowAttribute.event_uuid' => $sa['event_uuid'],
|
|
|
|
'ShadowAttribute.uuid' => $sa['uuid'],
|
|
|
|
'ShadowAttribute.value' => $sa['value'],
|
|
|
|
'ShadowAttribute.type' => $sa['type'],
|
|
|
|
'ShadowAttribute.category' => $sa['category'],
|
|
|
|
'ShadowAttribute.to_ids' => $sa['to_ids'],
|
|
|
|
'ShadowAttribute.comment' => $sa['comment']
|
2018-07-19 11:48:22 +02:00
|
|
|
),
|
|
|
|
));
|
|
|
|
if (empty($oldsa)) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
return $oldsa['ShadowAttribute'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-11 20:29:50 +02:00
|
|
|
/**
|
|
|
|
* @param int $eventId
|
|
|
|
* @return array Key is organisation ID, value is an organisation name
|
|
|
|
*/
|
|
|
|
public function getEventContributors($eventId)
|
2018-07-19 11:48:22 +02:00
|
|
|
{
|
2020-12-22 23:43:17 +01:00
|
|
|
$orgIds = $this->find('column', array(
|
|
|
|
'fields' => array('ShadowAttribute.org_id'),
|
2020-10-11 20:29:50 +02:00
|
|
|
'conditions' => array('event_id' => $eventId),
|
2020-12-22 23:43:17 +01:00
|
|
|
'unique' => true,
|
2019-09-30 08:23:36 +02:00
|
|
|
'order' => false
|
|
|
|
));
|
2020-12-22 23:43:17 +01:00
|
|
|
if (empty($orgIds)) {
|
2020-10-11 20:29:50 +02:00
|
|
|
return [];
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
2020-10-11 20:29:50 +02:00
|
|
|
$this->Organisation = ClassRegistry::init('Organisation');
|
|
|
|
return $this->Organisation->find('list', array(
|
|
|
|
'recursive' => -1,
|
|
|
|
'fields' => array('id', 'name'),
|
2020-12-22 23:43:17 +01:00
|
|
|
'conditions' => array('Organisation.id' => $orgIds)
|
|
|
|
));
|
2020-10-11 20:29:50 +02:00
|
|
|
}
|
2018-07-19 11:48:22 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Sends an email to members of the organization that owns the event
|
|
|
|
* @param int $id The event id
|
|
|
|
* @return boolean False if no email at all was sent, true if at least an email was sent
|
|
|
|
*/
|
|
|
|
public function sendProposalAlertEmail($id)
|
|
|
|
{
|
|
|
|
$this->Event->recursive = -1;
|
|
|
|
$event = $this->Event->read(null, $id);
|
|
|
|
|
|
|
|
// If the event has an e-mail lock, return
|
|
|
|
if ($event['Event']['proposal_email_lock'] == 1) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
$this->setProposalLock($id);
|
|
|
|
}
|
|
|
|
$this->User = ClassRegistry::init('User');
|
|
|
|
$this->User->recursive = -1;
|
|
|
|
$orgMembers = $this->User->find('all', array(
|
|
|
|
'conditions' => array(
|
|
|
|
'org_id' => $event['Event']['orgc_id'],
|
|
|
|
'contactalert' => 1,
|
|
|
|
'disabled' => 0
|
|
|
|
),
|
2020-08-14 13:40:05 +02:00
|
|
|
'fields' => array('email', 'gpgkey', 'certif_public', 'contactalert', 'id', 'disabled'),
|
2018-07-19 11:48:22 +02:00
|
|
|
));
|
|
|
|
|
|
|
|
$body = "Hello, \n\n";
|
|
|
|
$body .= "A user of another organisation has proposed a change to an event created by you or your organisation. \n\n";
|
|
|
|
$body .= 'To view the event in question, follow this link: ' . Configure::read('MISP.baseurl') . '/events/view/' . $id . "\n";
|
2022-02-16 14:45:57 +01:00
|
|
|
$subject = "[" . Configure::read('MISP.org') . " MISP] Proposal to event #" . $id . ' (uuid: ' . $event['Event']['uuid'] . ')';
|
2018-07-19 11:48:22 +02:00
|
|
|
$result = false;
|
|
|
|
foreach ($orgMembers as $user) {
|
|
|
|
$result = $this->User->sendEmail($user, $body, $body, $subject) or $result;
|
|
|
|
}
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function setProposalLock($id, $lock = true)
|
|
|
|
{
|
|
|
|
$this->Event->recursive = -1;
|
|
|
|
$event = $this->Event->read(null, $id);
|
|
|
|
if ($lock) {
|
|
|
|
$event['Event']['proposal_email_lock'] = 1;
|
|
|
|
} else {
|
|
|
|
$event['Event']['proposal_email_lock'] = 0;
|
|
|
|
}
|
|
|
|
$fieldList = array('proposal_email_lock', 'id', 'info');
|
|
|
|
$event['Event']['skip_zmq'] = 1;
|
2019-03-05 12:24:56 +01:00
|
|
|
$event['Event']['skip_kafka'] = 1;
|
2018-07-19 11:48:22 +02:00
|
|
|
$this->Event->save($event, array('fieldList' => $fieldList));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function generateCorrelation($jobId = false)
|
|
|
|
{
|
|
|
|
$this->ShadowAttributeCorrelation = ClassRegistry::init('ShadowAttributeCorrelation');
|
|
|
|
$this->ShadowAttributeCorrelation->deleteAll(array('id !=' => 0), false);
|
|
|
|
// get all proposals..
|
|
|
|
$proposals = $this->find('all', array('recursive' => -1, 'conditions' => array('ShadowAttribute.deleted' => 0, 'ShadowAttribute.proposal_to_delete' => 0)));
|
|
|
|
$proposalCount = count($proposals);
|
|
|
|
if ($jobId && Configure::read('MISP.background_jobs')) {
|
|
|
|
$this->Job = ClassRegistry::init('Job');
|
|
|
|
$this->Job->id = $jobId;
|
|
|
|
}
|
|
|
|
if ($proposalCount > 0) {
|
|
|
|
foreach ($proposals as $k => $proposal) {
|
|
|
|
$this->__afterSaveCorrelation($proposal['ShadowAttribute']);
|
|
|
|
if ($jobId && Configure::read('MISP.background_jobs') && $k > 0 && $proposalCount % $k == 10) {
|
|
|
|
$this->Job->saveField('progress', ($k / $proposalCount * 100));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ($jobId && Configure::read('MISP.background_jobs')) {
|
|
|
|
$this->Job->saveField('progress', 100);
|
|
|
|
$this->Job->saveField('status', 4);
|
|
|
|
$this->Job->saveField('message', 'Job done.');
|
|
|
|
}
|
|
|
|
return $proposalCount;
|
|
|
|
}
|
|
|
|
|
2021-08-11 20:47:52 +02:00
|
|
|
/**
|
|
|
|
* @param array $proposal
|
|
|
|
* @return array|false
|
|
|
|
*/
|
|
|
|
private function __preCaptureMassage(array $proposal)
|
2019-07-12 08:56:06 +02:00
|
|
|
{
|
|
|
|
if (empty($proposal['event_uuid']) || empty($proposal['Org'])) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-07-12 16:03:08 +02:00
|
|
|
if (isset($proposal['id'])) {
|
2019-07-12 08:56:06 +02:00
|
|
|
unset($proposal['id']);
|
|
|
|
}
|
|
|
|
$event = $this->Event->find('first', array(
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => array('Event.uuid' => $proposal['event_uuid']),
|
|
|
|
'fields' => array('Event.id', 'Event.uuid', 'Event.org_id')
|
|
|
|
));
|
|
|
|
if (empty($event)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
$proposal['event_id'] = $event['Event']['id'];
|
|
|
|
$proposal['event_org_id'] = $event['Event']['org_id'];
|
|
|
|
return $proposal;
|
|
|
|
}
|
|
|
|
|
2019-07-11 22:57:58 +02:00
|
|
|
public function capture($proposal, $user)
|
|
|
|
{
|
2019-07-12 08:56:06 +02:00
|
|
|
$proposal = $this->__preCaptureMassage($proposal);
|
|
|
|
if ($proposal === false) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-07-11 22:57:58 +02:00
|
|
|
$oldsa = $this->findOldProposal($proposal);
|
|
|
|
if (!$oldsa || $oldsa['timestamp'] < $proposal['timestamp']) {
|
|
|
|
if ($oldsa) {
|
2019-07-12 16:03:08 +02:00
|
|
|
$this->delete($oldsa['id']);
|
2019-07-11 22:57:58 +02:00
|
|
|
}
|
|
|
|
if (isset($proposal['old_id'])) {
|
2019-07-14 08:24:10 +02:00
|
|
|
$oldAttribute = $this->Attribute->find('first', array('recursive' => -1, 'conditions' => array('Attribute.uuid' => $proposal['uuid'])));
|
2019-07-11 22:57:58 +02:00
|
|
|
if ($oldAttribute) {
|
|
|
|
$proposal['old_id'] = $oldAttribute['Attribute']['id'];
|
|
|
|
} else {
|
|
|
|
$proposal['old_id'] = 0;
|
|
|
|
}
|
2019-07-12 16:03:08 +02:00
|
|
|
} else {
|
|
|
|
$proposal['old_id'] = 0;
|
2019-07-11 22:57:58 +02:00
|
|
|
}
|
2019-07-12 16:03:08 +02:00
|
|
|
$proposal['org_id'] = $this->Event->Orgc->captureOrg($proposal['Org'], $user);
|
2019-07-11 22:57:58 +02:00
|
|
|
unset($proposal['Org']);
|
|
|
|
$this->create();
|
2019-07-12 08:56:06 +02:00
|
|
|
if ($this->save($proposal)) {
|
|
|
|
if (!isset($proposal['deleted']) || !$proposal['deleted']) {
|
2019-07-11 22:57:58 +02:00
|
|
|
$this->sendProposalAlertEmail($proposal['event_id']);
|
|
|
|
}
|
2019-07-12 16:03:08 +02:00
|
|
|
return true;
|
2019-07-11 22:57:58 +02:00
|
|
|
}
|
|
|
|
}
|
2019-07-12 16:03:08 +02:00
|
|
|
return false;
|
2019-07-11 22:57:58 +02:00
|
|
|
}
|
|
|
|
|
2021-08-11 20:47:52 +02:00
|
|
|
/**
|
|
|
|
* @param array $user
|
|
|
|
* @param ServerSyncTool $serverSync
|
|
|
|
* @return int
|
|
|
|
* @throws HttpSocketHttpException
|
|
|
|
* @throws HttpSocketJsonException
|
|
|
|
*/
|
|
|
|
public function pullProposals(array $user, ServerSyncTool $serverSync)
|
2019-07-12 08:56:06 +02:00
|
|
|
{
|
2021-08-11 20:47:52 +02:00
|
|
|
if (!$serverSync->isSupported(ServerSyncTool::FEATURE_PROPOSALS)) {
|
2019-07-12 08:56:06 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2021-08-11 20:47:52 +02:00
|
|
|
|
2019-07-12 08:56:06 +02:00
|
|
|
$i = 1;
|
|
|
|
$fetchedCount = 0;
|
2021-08-11 20:47:52 +02:00
|
|
|
$chunkSize = 1000;
|
2019-07-12 16:03:08 +02:00
|
|
|
$timestamp = strtotime("-90 day");
|
2021-08-11 20:47:52 +02:00
|
|
|
while (true) {
|
|
|
|
try {
|
|
|
|
$data = $serverSync->fetchProposals([
|
|
|
|
'all' => 1,
|
|
|
|
'timestamp' => $timestamp,
|
|
|
|
'limit' => $chunkSize,
|
|
|
|
'page' => $i,
|
|
|
|
'deleted' => [0, 1],
|
|
|
|
])->json();
|
|
|
|
} catch (Exception $e) {
|
|
|
|
$this->logException("Could not fetch page $i of proposals from remote server {$serverSync->server()['Server']['id']}", $e);
|
|
|
|
return $fetchedCount;
|
|
|
|
}
|
|
|
|
$returnSize = count($data);
|
|
|
|
if ($returnSize === 0) {
|
|
|
|
return $fetchedCount;
|
|
|
|
}
|
|
|
|
foreach ($data as $proposal) {
|
|
|
|
$result = $this->capture($proposal['ShadowAttribute'], $user);
|
|
|
|
if ($result) {
|
|
|
|
$fetchedCount++;
|
2019-07-12 08:56:06 +02:00
|
|
|
}
|
2021-08-11 20:47:52 +02:00
|
|
|
}
|
|
|
|
if ($returnSize < $chunkSize) {
|
2019-07-12 08:56:06 +02:00
|
|
|
return $fetchedCount;
|
|
|
|
}
|
2021-08-11 20:47:52 +02:00
|
|
|
$i++;
|
2019-07-12 08:56:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-11 22:57:58 +02:00
|
|
|
public function buildConditions($user)
|
|
|
|
{
|
|
|
|
$conditions = array();
|
|
|
|
if (!$user['Role']['perm_site_admin']) {
|
2022-05-22 17:09:00 +02:00
|
|
|
$sgids = $this->Event->SharingGroup->authorizedIds($user);
|
2019-07-11 22:57:58 +02:00
|
|
|
$attributeDistribution = array(
|
|
|
|
'Attribute.distribution' => array(1,2,3,5)
|
|
|
|
);
|
|
|
|
$objectDistribution = array(
|
|
|
|
'(SELECT distribution FROM objects WHERE objects.id = Attribute.object_id)' => array(1,2,3,5)
|
|
|
|
);
|
|
|
|
if (!empty($sgids) && (!isset($sgids[0]) || $sgids[0] != -1)) {
|
|
|
|
$objectDistribution['(SELECT sharing_group_id FROM objects WHERE objects.id = Attribute.object_id)'] = $sgids;
|
|
|
|
$attributeDistribution['Attribute.sharing_group_id'] = $sgids;
|
|
|
|
}
|
2020-07-24 15:46:59 +02:00
|
|
|
$unpublishedPrivate = Configure::read('MISP.unpublishedprivate');
|
2019-07-11 22:57:58 +02:00
|
|
|
$conditions = array(
|
|
|
|
'AND' => array(
|
|
|
|
'OR' => array(
|
|
|
|
'Event.org_id' => $user['org_id'],
|
2020-07-24 15:46:59 +02:00
|
|
|
['AND' => [
|
2020-10-11 15:20:20 +02:00
|
|
|
'Event.distribution' => array(1,2,3),
|
2020-07-24 15:46:59 +02:00
|
|
|
$unpublishedPrivate ? ['Event.published' => 1] : [],
|
|
|
|
]],
|
|
|
|
['AND' => [
|
|
|
|
'Event.distribution' => 4,
|
|
|
|
'Event.sharing_group_id' => $sgids,
|
|
|
|
$unpublishedPrivate ? ['Event.published' => 1] : [],
|
|
|
|
]],
|
2019-07-11 22:57:58 +02:00
|
|
|
),
|
|
|
|
array(
|
|
|
|
'OR' => array(
|
|
|
|
'ShadowAttribute.old_id' => '0',
|
|
|
|
'AND' => array(
|
|
|
|
array(
|
|
|
|
'OR' => array(
|
|
|
|
'Attribute.object_id' => '0',
|
|
|
|
array(
|
|
|
|
'OR' => $objectDistribution
|
|
|
|
)
|
|
|
|
)
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
'OR' => $attributeDistribution
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return $conditions;
|
|
|
|
}
|
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
public function upgradeToProposalCorrelation()
|
|
|
|
{
|
|
|
|
$this->Log = ClassRegistry::init('Log');
|
|
|
|
if (!Configure::read('MISP.background_jobs')) {
|
|
|
|
$this->Log->create();
|
2023-12-07 15:17:58 +01:00
|
|
|
$this->Log->saveOrFailSilently(array(
|
2018-07-19 11:48:22 +02:00
|
|
|
'org' => 'SYSTEM',
|
|
|
|
'model' => 'Server',
|
|
|
|
'model_id' => 0,
|
|
|
|
'email' => 'SYSTEM',
|
|
|
|
'action' => 'update_database',
|
|
|
|
'user_id' => 0,
|
|
|
|
'title' => 'Starting proposal correlation generation',
|
|
|
|
'change' => 'The generation of Proposal correlations as part of the 2.4.20 datamodel upgrade has started'
|
|
|
|
));
|
|
|
|
$count = $this->generateCorrelation();
|
|
|
|
$this->Log->create();
|
|
|
|
if (is_numeric($count)) {
|
2023-12-07 15:17:58 +01:00
|
|
|
$this->Log->saveOrFailSilently(array(
|
2018-07-19 11:48:22 +02:00
|
|
|
'org' => 'SYSTEM',
|
|
|
|
'model' => 'Server',
|
|
|
|
'model_id' => 0,
|
|
|
|
'email' => 'SYSTEM',
|
|
|
|
'action' => 'update_database',
|
|
|
|
'user_id' => 0,
|
|
|
|
'title' => 'Proposal correlation generation complete',
|
|
|
|
'change' => 'The generation of Proposal correlations as part of the 2.4.20 datamodel upgrade is completed. ' . $count . ' proposals used.'
|
|
|
|
));
|
|
|
|
} else {
|
2023-12-07 15:17:58 +01:00
|
|
|
$this->Log->saveOrFailSilently(array(
|
2018-07-19 11:48:22 +02:00
|
|
|
'org' => 'SYSTEM',
|
|
|
|
'model' => 'Server',
|
|
|
|
'model_id' => 0,
|
|
|
|
'email' => 'SYSTEM',
|
|
|
|
'action' => 'update_database',
|
|
|
|
'user_id' => 0,
|
|
|
|
'title' => 'Proposal correlation generation failed',
|
|
|
|
'change' => 'The generation of Proposal correlations as part of the 2.4.20 has failed. You can rerun it from the administrative tools.'
|
|
|
|
));
|
|
|
|
}
|
|
|
|
} else {
|
2021-11-03 11:38:44 +01:00
|
|
|
|
|
|
|
/** @var Job $job */
|
2018-07-19 11:48:22 +02:00
|
|
|
$job = ClassRegistry::init('Job');
|
2021-11-03 11:38:44 +01:00
|
|
|
$jobId = $job->createJob(
|
|
|
|
'SYSTEM',
|
|
|
|
Job::WORKER_DEFAULT,
|
|
|
|
'generate proposal correlation',
|
|
|
|
'All attributes',
|
|
|
|
'Correlating Proposals.'
|
2018-07-19 11:48:22 +02:00
|
|
|
);
|
2021-11-03 11:38:44 +01:00
|
|
|
|
|
|
|
$this->getBackgroundJobsTool()->enqueue(
|
|
|
|
BackgroundJobsTool::DEFAULT_QUEUE,
|
|
|
|
BackgroundJobsTool::CMD_ADMIN,
|
|
|
|
[
|
|
|
|
'jobGenerateShadowAttributeCorrelation',
|
|
|
|
$jobId
|
|
|
|
],
|
|
|
|
true,
|
|
|
|
$jobId
|
2018-07-19 11:48:22 +02:00
|
|
|
);
|
2021-11-03 11:38:44 +01:00
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
$this->Log->create();
|
2023-12-07 15:17:58 +01:00
|
|
|
$this->Log->saveOrFailSilently(array(
|
2018-07-19 11:48:22 +02:00
|
|
|
'org' => 'SYSTEM',
|
|
|
|
'model' => 'Server',
|
|
|
|
'model_id' => 0,
|
|
|
|
'email' => 'SYSTEM',
|
|
|
|
'action' => 'update_database',
|
|
|
|
'user_id' => 0,
|
|
|
|
'title' => 'Proposal correlation generation job queued',
|
|
|
|
'change' => 'The job for the generation of Proposal correlations as part of the 2.4.20 datamodel upgrade has been queued'
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
2021-01-25 15:26:53 +01:00
|
|
|
|
|
|
|
public function saveAttachment($shadowAttribute, $path_suffix='')
|
|
|
|
{
|
|
|
|
$result = $this->loadAttachmentTool()->saveShadow($shadowAttribute['event_id'], $shadowAttribute['id'], $shadowAttribute['data'], $path_suffix);
|
|
|
|
if ($result) {
|
|
|
|
$this->loadAttachmentScan()->backgroundScan(AttachmentScan::TYPE_SHADOW_ATTRIBUTE, $shadowAttribute);
|
|
|
|
}
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $shadowAttribute
|
|
|
|
* @param bool $thumbnail
|
|
|
|
* @param int $maxWidth - When $thumbnail is true
|
|
|
|
* @param int $maxHeight - When $thumbnail is true
|
|
|
|
* @return string
|
|
|
|
* @throws Exception
|
|
|
|
*/
|
|
|
|
public function getPictureData(array $shadowAttribute, $thumbnail=false, $maxWidth=200, $maxHeight=200)
|
|
|
|
{
|
|
|
|
if ($thumbnail && extension_loaded('gd')) {
|
|
|
|
if ($maxWidth == 200 && $maxHeight == 200) {
|
|
|
|
// Return thumbnail directly if already exists
|
|
|
|
try {
|
|
|
|
return $this->getAttachment($shadowAttribute['ShadowAttribute'], $path_suffix = '_thumbnail');
|
|
|
|
} catch (NotFoundException $e) {
|
|
|
|
// pass
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Thumbnail doesn't exists, we need to generate it
|
|
|
|
$imageData = $this->getAttachment($shadowAttribute['ShadowAttribute']);
|
2021-10-10 11:13:00 +02:00
|
|
|
$imageData = $this->loadAttachmentTool()->resizeImage($imageData, $maxWidth, $maxHeight);
|
2021-01-25 15:26:53 +01:00
|
|
|
|
|
|
|
// Save just when requested default thumbnail size
|
|
|
|
if ($maxWidth == 200 && $maxHeight == 200) {
|
|
|
|
$shadowAttribute['ShadowAttribute']['data'] = $imageData;
|
|
|
|
$this->saveAttachment($shadowAttribute['ShadowAttribute'], $path_suffix='_thumbnail');
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$imageData = $this->getAttachment($shadowAttribute['ShadowAttribute']);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $imageData;
|
|
|
|
}
|
2016-06-06 10:09:55 +02:00
|
|
|
}
|