mirror of https://github.com/MISP/MISP
309 lines
11 KiB
PHP
309 lines
11 KiB
PHP
<?php
|
|
App::uses('AppModel', 'Model');
|
|
App::uses('EncryptedValue', 'Tools');
|
|
|
|
class SharingGroupBlueprint extends AppModel
|
|
{
|
|
public $actsAs = [
|
|
'AuditLog',
|
|
'SysLogLogable.SysLogLogable' => [
|
|
'roleModel' => 'Role',
|
|
'roleKey' => 'role_id',
|
|
'change' => 'full'
|
|
],
|
|
'Containable'
|
|
];
|
|
|
|
public $belongsTo = array(
|
|
'SharingGroup',
|
|
'Organisation' => array(
|
|
'className' => 'Organisation',
|
|
'foreignKey' => 'org_id'
|
|
)
|
|
);
|
|
|
|
public $validFilters = [
|
|
'org' => [
|
|
'org_id' => 'id',
|
|
'org_uuid' => 'uuid',
|
|
'org_name' => 'name',
|
|
'org_nationality' => 'nationality',
|
|
'org_sector' => 'sector',
|
|
'org_type' => 'type'
|
|
],
|
|
'sharing_group' => [
|
|
'sharing_group_id' => 'id',
|
|
'sharing_group_uuid' => 'uuid'
|
|
]
|
|
];
|
|
|
|
public $operands = [
|
|
'OR',
|
|
'AND',
|
|
'NOT'
|
|
];
|
|
|
|
public function beforeSave($options = array())
|
|
{
|
|
$this->data['SharingGroupBlueprint']['timestamp'] = time();
|
|
$this->data['SharingGroupBlueprint']['rules'] = json_decode($this->data['SharingGroupBlueprint']['rules']);
|
|
$this->data['SharingGroupBlueprint']['rules'] = json_encode($this->data['SharingGroupBlueprint']['rules']);
|
|
return true;
|
|
}
|
|
|
|
public function afterFind($results, $primary = false)
|
|
{
|
|
foreach ($results as &$v) {
|
|
$v['SharingGroupBlueprint']['rules'] = json_encode(json_decode($v['SharingGroupBlueprint']['rules']), JSON_PRETTY_PRINT);
|
|
}
|
|
return $results;
|
|
}
|
|
|
|
public function execute($sharingGroupBlueprints)
|
|
{
|
|
$stats = [
|
|
'changed' => 0,
|
|
'created' => 0,
|
|
'failed' => 0
|
|
];
|
|
$updated = $failed = 0;
|
|
foreach ($sharingGroupBlueprints as $sharingGroupBlueprint) {
|
|
// we create a fake user to restrict the visible sharing groups to the creator of the SharingGroupBlueprint, in case an admin wants to update it
|
|
$fake_user = [
|
|
'Role' => [
|
|
'perm_site_admin' => false
|
|
],
|
|
'org_id' => $sharingGroupBlueprint['SharingGroupBlueprint']['org_id'],
|
|
'id' => 1
|
|
];
|
|
$result = $this->updateSharingGroup($sharingGroupBlueprint, $fake_user);
|
|
foreach (array_keys($stats) as $field) {
|
|
$stats[$field] += $result[$field];
|
|
}
|
|
|
|
}
|
|
return $stats;
|
|
}
|
|
|
|
public function updateSharingGroup($sharingGroupBlueprint, $user)
|
|
{
|
|
$this->Organisation = ClassRegistry::init('Organisation');
|
|
$data = $this->evaluateSharingGroupBlueprint($sharingGroupBlueprint, $user);
|
|
$failed = 0;
|
|
if (empty($sharingGroupBlueprint['SharingGroupBlueprint']['sharing_group_id'])) {
|
|
$created = true;
|
|
$this->SharingGroup->create();
|
|
$org_uuid = $this->SharingGroup->Organisation->find('first', [
|
|
'recursive' => -1,
|
|
'conditions' => ['Organisation.id' => $sharingGroupBlueprint['SharingGroupBlueprint']['org_id']],
|
|
'fields' => ['Organisation.uuid']
|
|
]);
|
|
if (empty($org_uuid)) {
|
|
throw new MethodNotAllowedException(__('Invalid owner organisation.'));
|
|
}
|
|
$org_uuid = $org_uuid['Organisation']['uuid'];
|
|
$sg = [
|
|
'name' => $sharingGroupBlueprint['SharingGroupBlueprint']['name'],
|
|
'description' => __('Generated based on Sharing Group Blueprint rules'),
|
|
'org_id' => $user['org_id'],
|
|
'organisation_uuid' => $org_uuid,
|
|
'releasability' => __('Generated based on Sharing Group Blueprint rules'),
|
|
'local' => 1,
|
|
'roaming' => 1,
|
|
'active' => 1
|
|
];
|
|
if ($this->SharingGroup->save($sg)) {
|
|
$id = $this->SharingGroup->id;
|
|
$sharingGroupBlueprint['SharingGroupBlueprint']['sharing_group_id'] = $id;
|
|
$existingOrgs = [];
|
|
$this->save($sharingGroupBlueprint);
|
|
} else {
|
|
$failed++;
|
|
}
|
|
|
|
} else {
|
|
$created = false;
|
|
$sg = $this->SharingGroup->find('first', [
|
|
'recursive' => -1,
|
|
'contain' => ['SharingGroupOrg'],
|
|
'conditions' => ['SharingGroup.id' => $sharingGroupBlueprint['SharingGroupBlueprint']['sharing_group_id']]
|
|
]);
|
|
$existingOrgs = [];
|
|
foreach ($sg['SharingGroupOrg'] as $sgo) {
|
|
$existingOrgs[] = $sgo['org_id'];
|
|
}
|
|
$existingOrgs = array_unique($existingOrgs);
|
|
$id = $sg['SharingGroup']['id'];
|
|
}
|
|
return [
|
|
'id' => $id,
|
|
'changed' => $this->__handleSharingGroupOrgs($existingOrgs, $data['orgs'], $id) || $created,
|
|
'created' => $created,
|
|
'failed' => $failed
|
|
];
|
|
}
|
|
|
|
private function __handleSharingGroupOrgs($existingOrgs, $newOrgs, $id)
|
|
{
|
|
$added = 0;
|
|
$removed = 0;
|
|
$this->Log = ClassRegistry::init('Log');
|
|
foreach ($existingOrgs as $existingOrg) {
|
|
if (!in_array($existingOrg, $newOrgs)) {
|
|
$this->SharingGroup->SharingGroupOrg->deleteAll([
|
|
'sharing_group_id' => $id,
|
|
'org_id' => $existingOrg
|
|
], false);
|
|
$removed++;
|
|
}
|
|
}
|
|
foreach ($newOrgs as $newOrg) {
|
|
if (!in_array($newOrg, $existingOrgs)) {
|
|
$sgo = [
|
|
'sharing_group_id' => $id,
|
|
'org_id' => $newOrg,
|
|
'extend' => false
|
|
];
|
|
$this->SharingGroup->SharingGroupOrg->create();
|
|
$this->SharingGroup->SharingGroupOrg->save($sgo);
|
|
$added++;
|
|
}
|
|
}
|
|
if ($added || $removed) {
|
|
$this->Log->create();
|
|
$entry = array(
|
|
'org' => 'SYSTEM',
|
|
'model' => 'SharingGroup',
|
|
'model_id' => $id,
|
|
'email' => 'SYSTEM',
|
|
'action' => 'execute_blueprint',
|
|
'user_id' => 0,
|
|
'title' => 'Updated the sharing group.',
|
|
'change' => __('Updated sharing group. Added %s and removed %s organisations', $added, $removed)
|
|
);
|
|
$this->Log->saveOrFailSilently($entry);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Walking on water and developing software from a specification are easy if both are frozen - Edward V Berard
|
|
public function evaluateSharingGroupBlueprint($sharingGroupBlueprint, $user)
|
|
{
|
|
$data = [];
|
|
$rules = json_decode($sharingGroupBlueprint['SharingGroupBlueprint']['rules'], true);
|
|
$data = $this->__recursiveEvaluate($user, $rules, 'OR');
|
|
return $data;
|
|
}
|
|
|
|
private function __recursiveEvaluate($user, $rules, $operand)
|
|
{
|
|
if (!empty($rules)) {
|
|
$data = [];
|
|
foreach ($rules as $key => $value) {
|
|
if (in_array($key, $this->operands)) {
|
|
if ($operand === 'NOT') {
|
|
throw new MethodNotAllwedException(__('Boolean branches within a NOT branch are not supported.'));
|
|
}
|
|
$temp = $this->__recursiveEvaluate($user, $rules[$key], $key);
|
|
} else {
|
|
$negation = $operand === 'NOT';
|
|
$temp = $this->__evaluateLeaf($user, $key, $value, $negation);
|
|
}
|
|
if ($operand === 'OR') {
|
|
if (!isset($data['orgs'])) {
|
|
$data['orgs'] = [];
|
|
}
|
|
$data['orgs'] = array_merge(
|
|
$data['orgs'],
|
|
isset($temp['orgs']) ? $temp['orgs'] : []
|
|
);
|
|
} else if ($operand === 'AND' || $operand === 'NOT') {
|
|
if (!isset($data['orgs'])) {
|
|
$data['orgs'] = $temp['orgs'];
|
|
} else {
|
|
$data['orgs'] = array_intersect($data['orgs'], $temp['orgs']);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
private function __evaluateLeaf($user, $key, $value, $negation = false)
|
|
{
|
|
if (substr($key, 0, strlen('org')) === 'org') {
|
|
return $this->__evaluateOrgLeaf(
|
|
$user,
|
|
substr($key, (strlen('org_'))),
|
|
$value,
|
|
$negation
|
|
);
|
|
} else if (substr($key, 0, strlen('sharing_group')) === 'sharing_group') {
|
|
return $this->__evaluateSGLeaf(
|
|
$user,
|
|
substr($key, (strlen('sharing_group_'))),
|
|
$value,
|
|
$negation
|
|
);
|
|
}
|
|
return [];
|
|
}
|
|
|
|
private function __evaluateOrgLeaf($user, $key, $value, $negation)
|
|
{
|
|
if (in_array($key, $this->validFilters['org'])) {
|
|
$conditions = [$key => $value];
|
|
if ($negation) {
|
|
$conditions = ['NOT' => $conditions];
|
|
}
|
|
$orgs = $this->SharingGroup->Organisation->find('list', [
|
|
'fields' => ['id', 'id'],
|
|
'recursive' => -1,
|
|
'conditions' => $conditions
|
|
]);
|
|
$orgs = array_values($orgs);
|
|
if (empty($orgs)) {
|
|
$orgs = [-1];
|
|
}
|
|
return [
|
|
'orgs' => $orgs
|
|
];
|
|
}
|
|
return [];
|
|
}
|
|
|
|
private function __evaluateSGLeaf($user, $key, $value, $negation)
|
|
{
|
|
$orgs = [];
|
|
if (in_array($key, $this->validFilters['sharing_group'])) {
|
|
$conditions = [$key => $value];
|
|
if ($negation) {
|
|
$conditions = ['NOT' => $conditions];
|
|
}
|
|
$sgs = $this->SharingGroup->find('all', [
|
|
'fields' => ['id', 'uuid', 'name', 'org_id'],
|
|
'contain' => ['SharingGroupOrg.org_id'],
|
|
'recursive' => -1,
|
|
'conditions' => $conditions
|
|
]);
|
|
foreach ($sgs as $sg) {
|
|
if ($this->SharingGroup->checkIfAuthorised($user, $sg['SharingGroup']['id'])) {
|
|
$orgs[$sg['SharingGroup']['org_id']] = true;
|
|
foreach ($sg['SharingGroupOrg'] as $sgo) {
|
|
$orgs[$sgo['org_id']] = true;
|
|
}
|
|
}
|
|
}
|
|
$orgs = array_keys($orgs);
|
|
if (empty($orgs)) {
|
|
$orgs = [-1];
|
|
}
|
|
return [
|
|
'orgs' => $orgs
|
|
];
|
|
}
|
|
return [];
|
|
}
|
|
}
|