fix: [Sharing group] refactored and fixed

- include own org in pulled sharing groups (to avoid implicit inclusion not being visible after a pull)
- refactor the pulling method to be more maintainable
- avoid pulling proposals/sightings on each event cherry pick
pull/7147/head
iglocska 2021-02-25 11:39:20 +01:00
commit d24e2a085a
No known key found for this signature in database
GPG Key ID: BEA224F1FEF113AC
5 changed files with 241 additions and 141 deletions

View File

@ -3382,10 +3382,10 @@ class Event extends AppModel
return array($bodyevent, $body);
}
public function __captureSGForElement($element, $user, $syncLocal=false)
public function captureSGForElement($element, $user, $server=false)
{
if (isset($element['SharingGroup'])) {
$sg = $this->SharingGroup->captureSG($element['SharingGroup'], $user, $syncLocal);
$sg = $this->SharingGroup->captureSG($element['SharingGroup'], $user, $server);
unset($element['SharingGroup']);
} elseif (isset($element['sharing_group_id'])) {
$sg = $this->SharingGroup->checkIfAuthorised($user, $element['sharing_group_id']) ? $element['sharing_group_id'] : false;
@ -3402,17 +3402,17 @@ class Event extends AppModel
// When we receive an event via REST, we might end up with organisations, sharing groups, tags that we do not know
// or which we need to update. All of that is controlled in this method.
private function __captureObjects($data, $user, $syncLocal=false)
private function __captureObjects($data, $user, $server=false)
{
// First we need to check whether the event or any attributes are tied to a sharing group and whether the user is even allowed to create the sharing group / is part of it
if (isset($data['Event']['distribution']) && $data['Event']['distribution'] == 4) {
$data['Event'] = $this->__captureSGForElement($data['Event'], $user, $syncLocal);
$data['Event'] = $this->captureSGForElement($data['Event'], $user, $server);
}
if (!empty($data['Event']['Attribute'])) {
foreach ($data['Event']['Attribute'] as $k => $a) {
unset($data['Event']['Attribute']['id']);
if (isset($a['distribution']) && $a['distribution'] == 4) {
$data['Event']['Attribute'][$k] = $this->__captureSGForElement($a, $user, $syncLocal);
$data['Event']['Attribute'][$k] = $this->captureSGForElement($a, $user, $server);
if ($data['Event']['Attribute'][$k] === false) {
unset($data['Event']['Attribute']);
}
@ -3422,7 +3422,7 @@ class Event extends AppModel
if (!empty($data['Event']['Object'])) {
foreach ($data['Event']['Object'] as $k => $o) {
if (isset($o['distribution']) && $o['distribution'] == 4) {
$data['Event']['Object'][$k] = $this->__captureSGForElement($o, $user, $syncLocal);
$data['Event']['Object'][$k] = $this->captureSGForElement($o, $user, $server);
if ($data['Event']['Object'][$k] === false) {
unset($data['Event']['Object'][$k]);
continue;
@ -3430,7 +3430,7 @@ class Event extends AppModel
}
foreach ($o['Attribute'] as $k2 => $a) {
if (isset($a['distribution']) && $a['distribution'] == 4) {
$data['Event']['Object'][$k]['Attribute'][$k2] = $this->__captureSGForElement($a, $user, $syncLocal);
$data['Event']['Object'][$k]['Attribute'][$k2] = $this->captureSGForElement($a, $user, $server);
if ($data['Event']['Object'][$k]['Attribute'][$k2] === false) {
unset($data['Event']['Object'][$k]['Attribute'][$k2]);
}
@ -3754,7 +3754,7 @@ class Event extends AppModel
return $existingEvent['Event']['id'];
} else {
if ($fromXml) {
$data = $this->__captureObjects($data, $user, $server['Server']['internal']);
$data = $this->__captureObjects($data, $user, $server);
}
if ($data === false) {
$failedCapture = true;
@ -3762,7 +3762,7 @@ class Event extends AppModel
}
} else {
if ($fromXml) {
$data = $this->__captureObjects($data, $user, $server['Server']['internal']);
$data = $this->__captureObjects($data, $user, $server);
}
if ($data === false) {
$failedCapture = true;
@ -4013,7 +4013,7 @@ class Event extends AppModel
return(array('error' => 'Event could not be saved: Invalid sharing group or you don\'t have access to that sharing group.'));
}
} else {
$data['Event']['sharing_group_id'] = $this->SharingGroup->captureSG($data['Event']['SharingGroup'], $user, $server['Server']['internal']);
$data['Event']['sharing_group_id'] = $this->SharingGroup->captureSG($data['Event']['SharingGroup'], $user, $server);
unset($data['Event']['SharingGroup']);
if ($data['Event']['sharing_group_id'] === false) {
return (array('error' => 'Event could not be saved: User not authorised to create the associated sharing group.'));

View File

@ -234,7 +234,7 @@ class EventReport extends AppModel
{
$this->Event = ClassRegistry::init('Event');
if (isset($report['EventReport']['distribution']) && $report['EventReport']['distribution'] == 4) {
$report['EventReport'] = $this->Event->__captureSGForElement($report['EventReport'], $user);
$report['EventReport'] = $this->Event->captureSGForElement($report['EventReport'], $user);
}
return $report;
}

View File

@ -529,13 +529,15 @@ class Server extends AppModel
if ($jobId) {
$job->saveProgress($jobId, 'Pulling proposals.', 50);
}
$pulledProposals = $eventModel->ShadowAttribute->pullProposals($user, $server);
$pulledProposals = $pulledSightings = 0;
if ($technique === 'full' || $technique === 'update') {
$pulledProposals = $eventModel->ShadowAttribute->pullProposals($user, $server);
if ($jobId) {
$job->saveProgress($jobId, 'Pulling sightings.', 75);
if ($jobId) {
$job->saveProgress($jobId, 'Pulling sightings.', 75);
}
$pulledSightings = $eventModel->Sighting->pullSightings($user, $server);
}
$pulledSightings = $eventModel->Sighting->pullSightings($user, $server);
if ($jobId) {
$job->saveProgress($jobId, 'Pull completed.', 100);
}

View File

@ -576,8 +576,22 @@ class SharingGroup extends AppModel
return $results;
}
public function captureSG($sg, $user, $syncLocal=false)
/*
* Capture a sharing group
* Return false if something goes wrong
* Return an integer with the sharing group's ID, irregardless of the need to update or not
*
* @param array $sg
* @param array $user
* @param array $server
* @return int || false
*/
public function captureSG($sg, $user, $server = false)
{
$syncLocal = false;
if (!empty($server) && !empty($server['Server']['local'])) {
$syncLocal = true;
}
$this->Log = ClassRegistry::init('Log');
$existingSG = !isset($sg['uuid']) ? null : $this->find('first', array(
'recursive' => -1,
@ -588,136 +602,217 @@ class SharingGroup extends AppModel
'SharingGroupOrg' => array('Organisation')
)
));
$force = false;
$forceUpdate = false;
$sg_id = 0;
if (empty($existingSG)) {
if (!$user['Role']['perm_sharing_group']) {
return false;
}
// check if current user is contained in the SG and we are in a local sync setup
if (!empty($sg['uuid'])) {
if (isset($this->__sgAuthorisationCache['save'][boolval($syncLocal)][$sg['uuid']])) {
$authorisedToSave = $this->__sgAuthorisationCache['save'][boolval($syncLocal)][$sg['uuid']];
} else {
$authorisedToSave = $this->checkIfAuthorisedToSave($user, $sg);
$this->__sgAuthorisationCache['save'][boolval($syncLocal)][$sg['uuid']] = $authorisedToSave;
}
} else {
$authorisedToSave = $this->checkIfAuthorisedToSave($user, $sg);
}
if (!$user['Role']['perm_site_admin'] &&
!($user['Role']['perm_sync'] && $syncLocal ) &&
!$authorisedToSave
) {
$this->Log->create();
$entry = array(
'org' => $user['Organisation']['name'],
'model' => 'SharingGroup',
'model_id' => $sg['SharingGroup']['uuid'],
'email' => $user['email'],
'action' => 'error',
'user_id' => $user['id'],
'title' => 'Tried to save a sharing group but the user does not belong to it.'
);
$this->Log->save($entry);
$sg_id = $this->captureSGNew($user, $sg, $syncLocal);
if ($sg_id === false) {
return false;
}
$this->create();
$newSG = array();
$attributes = array(
'name' => array(),
'releasability' => array(),
'description' => array('default' => ''),
'uuid' => array('default' => CakeText::uuid()),
'organisation_uuid' => array('default' => $user['Organisation']['uuid']),
'created' => array('default' => $date = date('Y-m-d H:i:s')),
'modified' => array('default' => $date = date('Y-m-d H:i:s')),
'active' => array('default' => 1),
'roaming' => array('default' => false),
);
foreach (array_keys($attributes) as $a) {
if (isset($sg[$a])) {
$newSG[$a] = $sg[$a];
} else {
if (!isset($attributes[$a]['default'])) {
return false;
} else {
$newSG[$a] = $attributes[$a]['default'];
}
}
}
$newSG['local'] = 0;
$newSG['sync_user_id'] = $user['id'];
if (!$user['Role']['perm_sync']) {
$newSG['org_id'] = $user['org_id'];
} else {
if (!isset($sg['Organisation'])) {
if (!isset($sg['SharingGroupOrg'])) {
$sg['SharingGroupOrg'] = array(array(
'extend' => 1,
'uuid' => $user['Organisation']['uuid'],
'name' => $user['Organisation']['name'],
));
$newSG['org_id'] = $user['org_id'];
} else {
// Try to capture the creator organisation using the organisation_uuid if the org is contained in the SG (in some rare cases pre 2.4.86 the lack of this could occur)
foreach ($sg['SharingGroupOrg'] as $k => $org) {
if (!isset($org['Organisation'])) {
$org['Organisation'] = $org;
}
if (isset($org['Organisation'][0])) {
$org['Organisation'] = $org['Organisation'][0];
}
if (isset($sg['organisation_uuid'])) {
if ($org['Organisation']['uuid'] == $sg['organisation_uuid']) {
$newSG['org_id'] = $this->Organisation->captureOrg($org['Organisation'], $user);
}
} else {
$newSG['org_id'] = $user['org_id'];
}
}
}
} else {
$newSG['org_id'] = $this->Organisation->captureOrg($sg['Organisation'], $user);
}
}
if (empty($newSG['org_id'])) {
return false;
}
if (!$this->save($newSG)) {
return false;
}
$sgids = $this->id;
} else {
if (!$this->checkIfAuthorised($user, $existingSG['SharingGroup']['id']) && !$user['Role']['perm_sync']) {
return false;
$existingCaptureResult = $this->captureSGExisting($user, $existingSG, $sg, $syncLocal);
if ($existingCaptureResult !== true) {
return $existingCaptureResult;
}
if (empty($sg['modified']) || $sg['modified'] > $existingSG['SharingGroup']['modified']) {
if (
($user['Role']['perm_sync'] && isset($existingSG['SharingGroup']['local']) && $existingSG['SharingGroup']['local'] == 0) ||
((!$user['Role']['perm_sync'] && $existingSG['org_id'] == $user['org_id']) || $user['Role']['perm_site_admin'])
) {
$force = true;
}
if ($force) {
$sgids = $existingSG['SharingGroup']['id'];
$editedSG = $existingSG['SharingGroup'];
$attributes = ['name', 'releasability', 'description', 'created', 'modified', 'active', 'roaming'];
foreach ($attributes as $a) {
if (isset($sg[$a])) {
$editedSG[$a] = $sg[$a];
}
$forceUpdate = true;
}
unset($sg['Organisation']);
$creatorOrgFound = $this->captureSGOrgs($user, $sg, $sg_id, $forceUpdate);
$creatorOrgFound = $this->captureSGServers($user, $sg, $sg_id, $forceUpdate) && $creatorOrgFound;
if (!$creatorOrgFound && !empty($server)) {
$this->captureCreatorOrg($user, $sg_id);
}
if (!empty($existingSG)) {
return $existingSG[$this->alias]['id'];
}
return $this->id;
}
/*
* Capture updates for an existing sharing group
* Return true if updates are occuring
* Return false if something goes wrong
* Return an integer if no update is done but the sharing group can be attached
*
* @param array $user
* @param array $existingSG
* @param array $sg
* @param boolean syncLocal
* @return int || false || true
*/
public function captureSGExisting($user, $existingSG, $sg, $syncLocal)
{
if (!$this->checkIfAuthorised($user, $existingSG['SharingGroup']['id']) && !$user['Role']['perm_sync']) {
return false;
}
if (empty($sg['modified']) || $sg['modified'] > $existingSG['SharingGroup']['modified']) {
$isLocalSync = $user['Role']['perm_sync'] && !empty($existingSG['SharingGroup']['local']);
$isSGOwner = !$user['Role']['perm_sync'] && $existingSG['org_id'] == $user['org_id'];
if ($isLocalSync || $isSGOwner || $user['Role']['perm_site_admin']) {
$sg_id = (int)$existingSG['SharingGroup']['id'];
$editedSG = $existingSG['SharingGroup'];
$attributes = ['name', 'releasability', 'description', 'created', 'modified', 'active', 'roaming'];
foreach ($attributes as $a) {
if (isset($sg[$a])) {
$editedSG[$a] = $sg[$a];
}
$this->save($editedSG);
} else {
return $existingSG['SharingGroup']['id'];
}
$this->save($editedSG);
return true;
} else {
return $existingSG['SharingGroup']['id'];
}
} else {
return $existingSG['SharingGroup']['id'];
}
unset($sg['Organisation']);
}
/*
* Capture a new sharing group, rather than update an existing one
*
* @param array $user
* @param array $sg
* @param boolean syncLocal
* @return int || false
*/
public function captureSGNew($user, $sg, $syncLocal)
{
// check if current user is contained in the SG and we are in a local sync setup
if (!empty($sg['uuid'])) {
if (isset($this->__sgAuthorisationCache['save'][boolval($syncLocal)][$sg['uuid']])) {
$authorisedToSave = $this->__sgAuthorisationCache['save'][boolval($syncLocal)][$sg['uuid']];
} else {
$authorisedToSave = $this->checkIfAuthorisedToSave($user, $sg);
$this->__sgAuthorisationCache['save'][boolval($syncLocal)][$sg['uuid']] = $authorisedToSave;
}
} else {
$authorisedToSave = $this->checkIfAuthorisedToSave($user, $sg);
}
if (!$user['Role']['perm_site_admin'] &&
!($user['Role']['perm_sync'] && $syncLocal ) &&
!$authorisedToSave
) {
$this->Log->create();
$entry = array(
'org' => $user['Organisation']['name'],
'model' => 'SharingGroup',
'model_id' => $sg['SharingGroup']['uuid'],
'email' => $user['email'],
'action' => 'error',
'user_id' => $user['id'],
'title' => 'Tried to save a sharing group but the user does not belong to it.'
);
$this->Log->save($entry);
return false;
}
$this->create();
$newSG = array();
$date = date('Y-m-d H:i:s');
if (empty($sg['name']) || empty($sg['releasability'])) {
return false;
}
$newSG = [
'name' => $sg['name'],
'releasability' => $sg['releasability'],
'description' => !isset($sg['description']) ? '' : $sg['description'],
'uuid' => !isset($sg['uuid']) ? CakeText::uuid() : $sg['uuid'],
'organisation_uuid' => !isset($sg['organisation_uuid']) ? $user['Organisation']['uuid'] : $sg['organisation_uuid'],
'created' => !isset($sg['created']) ? $date : $sg['created'],
'modified' => !isset($sg['modified']) ? $date : $sg['modified'],
'active' => !isset($sg['active']) ? 1 : $sg['active'],
'roaming' => !isset($sg['description']) ? false : $sg['description'],
'local' => 0,
'sync_user_id' => $user['id'],
'org_id' => $user['Role']['perm_sync'] ? $this->__retrieveOrgIdFromCapturedSG($user, $sg) : $user['org_id']
];
if (empty($newSG['org_id'])) {
return false;
}
if (!$this->save($newSG)) {
return false;
}
return (int)$this->id;
}
/*
* When trying to capture a sharing group, capture the org_id
* For older MISP instances (<2.4.86) we might need to deduce it from the org list
*
* @param array $user
* @param array $sg
* @return int || false
*/
private function __retrieveOrgIdFromCapturedSG($user, $sg)
{
if (!isset($sg['Organisation'])) {
if (!isset($sg['SharingGroupOrg'])) {
$sg['SharingGroupOrg'] = array(array(
'extend' => 1,
'uuid' => $user['Organisation']['uuid'],
'name' => $user['Organisation']['name'],
));
return $user['org_id'];
} else {
foreach ($sg['SharingGroupOrg'] as $k => $org) {
if (!isset($org['Organisation'])) {
$org['Organisation'] = $org;
}
if (isset($org['Organisation'][0])) {
$org['Organisation'] = $org['Organisation'][0];
}
if (isset($sg['organisation_uuid'])) {
if ($org['Organisation']['uuid'] == $sg['organisation_uuid']) {
return $this->Organisation->captureOrg($org['Organisation'], $user);
}
} else {
return $user['org_id'];
}
}
}
} else {
return $this->Organisation->captureOrg($sg['Organisation'], $user);
}
return false;
}
/*
* we've pulled a sharing group from a remote server, but are not part of the SG
* This can happen when we have access to the data on the remote by being inherently included in the exchange
* Add our org to the sharing group!
*
* @param array $user
* @param int $sg_id
* @return void
*/
public function captureCreatorOrg(array $user, int $sg_id)
{
$this->SharingGroupOrg->create();
$this->SharingGroupOrg->save(
[
'sharing_group_id' => $sg_id,
'org_id' => $user['org_id'],
'extend' => false
]
);
}
/*
* Capture orgs of a sharing group. If the creator org is contained in the list, return true
* Otherwise return false
*
* @param array $user
* @param array $sg
* @param int $sg_id
* @param bool $force
* @return void
*/
public function captureSGOrgs(array $user, array $sg, int $sg_id, bool $force)
{
$creatorOrgFound = false;
if (!empty($sg['SharingGroupOrg'])) {
$creatorOrgFound = false;
if (isset($sg['SharingGroupOrg']['id'])) {
$temp = $sg['SharingGroupOrg'];
unset($sg['SharingGroupOrg']);
@ -746,7 +841,7 @@ class SharingGroup extends AppModel
));
if (empty($temp)) {
$this->SharingGroupOrg->create();
$this->SharingGroupOrg->save(array('sharing_group_id' => $sgids, 'org_id' => $sg['SharingGroupOrg'][$k]['org_id'], 'extend' => $org['extend']));
$this->SharingGroupOrg->save(array('sharing_group_id' => $sg_id, 'org_id' => $sg['SharingGroupOrg'][$k]['org_id'], 'extend' => $org['extend']));
} else {
if ($temp['SharingGroupOrg']['extend'] != $sg['SharingGroupOrg'][$k]['extend']) {
$temp['SharingGroupOrg']['extend'] = $sg['SharingGroupOrg'][$k]['extend'];
@ -755,10 +850,16 @@ class SharingGroup extends AppModel
}
} else {
$this->SharingGroupOrg->create();
$this->SharingGroupOrg->save(array('sharing_group_id' => $sgids, 'org_id' => $sg['SharingGroupOrg'][$k]['org_id'], 'extend' => $org['extend']));
$this->SharingGroupOrg->save(array('sharing_group_id' => $sg_id, 'org_id' => $sg['SharingGroupOrg'][$k]['org_id'], 'extend' => $org['extend']));
}
}
}
return $creatorOrgFound;
}
public function captureSGServers(array $user, array $sg, int $sg_id, bool $force)
{
$creatorOrgFound = false;
if (!empty($sg['SharingGroupServer'])) {
if (isset($sg['SharingGroupServer']['id'])) {
$temp = $sg['SharingGroupServer'];
@ -793,7 +894,7 @@ class SharingGroup extends AppModel
));
if (empty($temp)) {
$this->SharingGroupServer->create();
$this->SharingGroupServer->save(array('sharing_group_id' => $sgids, 'server_id' => $sg['SharingGroupServer'][$k]['server_id'], 'all_orgs' => empty($server['all_orgs']) ? 0 : $server['all_orgs']));
$this->SharingGroupServer->save(array('sharing_group_id' => $sg_id, 'server_id' => $sg['SharingGroupServer'][$k]['server_id'], 'all_orgs' => empty($server['all_orgs']) ? 0 : $server['all_orgs']));
} else {
if ($temp['SharingGroupServer']['all_orgs'] != $sg['SharingGroupServer'][$k]['all_orgs']) {
$temp['SharingGroupServer']['all_orgs'] = $sg['SharingGroupServer'][$k]['all_orgs'];
@ -802,15 +903,12 @@ class SharingGroup extends AppModel
}
} else {
$this->SharingGroupServer->create();
$this->SharingGroupServer->save(array('sharing_group_id' => $sgids, 'server_id' => $sg['SharingGroupServer'][$k]['server_id'], 'all_orgs' => empty($server['all_orgs']) ? 0 : $server['all_orgs']));
$this->SharingGroupServer->save(array('sharing_group_id' => $sg_id, 'server_id' => $sg['SharingGroupServer'][$k]['server_id'], 'all_orgs' => empty($server['all_orgs']) ? 0 : $server['all_orgs']));
}
}
}
}
if (!empty($existingSG)) {
return $existingSG[$this->alias]['id'];
}
return $this->id;
return $creatorOrgFound;
}
// Correct an issue that existed pre 2.4.49 where a pulled sharing group can end up not being visible to the sync user

@ -1 +1 @@
Subproject commit 36994fda1ef423e6141d2a3582fed3e6219dbf59
Subproject commit e764ed6983bac3a3171fe1a649176224d1abbf0a