mirror of https://github.com/MISP/MISP
chg: [wip] migrate galaxies controller
parent
ce159c7bb7
commit
2ca194169d
|
@ -4,3 +4,6 @@
|
|||
[submodule "libraries/misp-objects"]
|
||||
path = libraries/misp-objects
|
||||
url = https://github.com/MISP/misp-objects.git
|
||||
[submodule "libraries/misp-galaxy"]
|
||||
path = libraries/misp-galaxy
|
||||
url = https://github.com/MISP/misp-galaxy
|
||||
|
|
|
@ -121,7 +121,7 @@ RUN apt-get update \
|
|||
# Clone MISP repository
|
||||
RUN git clone --branch ${MISP_TAG_OR_BRANCH} --depth 1 https://github.com/MISP/MISP.git /var/www/html
|
||||
WORKDIR /var/www/html
|
||||
RUN git submodule update --init --recursive .;
|
||||
RUN git submodule update --init --recursive .
|
||||
|
||||
USER www-data
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ init_user() {
|
|||
ADMIN_USER_ID=$(echo "SELECT id FROM users WHERE EMAIL='${ADMIN_EMAIL}';" | ${MYSQLCMD} | tr -d '\n')
|
||||
|
||||
# Insert Admin user API key
|
||||
if [ -z "$ADMIN_API_KEY" ]; then
|
||||
if [ ! -z "$ADMIN_API_KEY" ]; then
|
||||
echo >&2 "Creating admin user API key..."
|
||||
ADMIN_API_KEY_START=$(echo ${ADMIN_API_KEY} | head -c 4)
|
||||
ADMIN_API_KEY_END=$(echo ${ADMIN_API_KEY} | tail -c 5)
|
||||
|
@ -143,9 +143,7 @@ php-fpm -t
|
|||
# Finished bootstrapping, create ready flag file
|
||||
touch "${MISP_READY_STATUS_FLAG}"
|
||||
|
||||
if [ -z "$DISABLE_BACKGROUND_WORKERS" ]; then
|
||||
DISABLE_BACKGROUND_WORKERS=0
|
||||
fi
|
||||
[ -z "$DISABLE_BACKGROUND_WORKERS" ] && DISABLE_BACKGROUND_WORKERS=0
|
||||
|
||||
if [ "$DISABLE_BACKGROUND_WORKERS" -eq 1 ]; then
|
||||
echo >&2 "Background workers disabled, skipping..."
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit e24fecbd403106395ed806ace0a946f8f6343c0b
|
|
@ -24,6 +24,7 @@ use Cake\Core\Configure;
|
|||
use Cake\Event\EventInterface;
|
||||
use Cake\Http\Exception\HttpException;
|
||||
use Cake\Http\Exception\MethodNotAllowedException;
|
||||
use Cake\ORM\Locator\LocatorAwareTrait;
|
||||
use Cake\Utility\Text;
|
||||
use Exception;
|
||||
|
||||
|
@ -37,6 +38,7 @@ use Exception;
|
|||
*/
|
||||
class AppController extends Controller
|
||||
{
|
||||
use LocatorAwareTrait;
|
||||
|
||||
public $isRest = null;
|
||||
public $restResponsePayload = null;
|
||||
|
@ -122,6 +124,7 @@ class AppController extends Controller
|
|||
if ($this->ParamHandler->isRest()) {
|
||||
$this->authApiUser();
|
||||
$this->Security->setConfig('unlockedActions', [$this->request->getParam('action')]);
|
||||
$this->response = $this->setResponseType();
|
||||
}
|
||||
$this->ACL->setPublicInterfaces();
|
||||
if (!empty($this->request->getAttribute('identity'))) {
|
||||
|
@ -203,12 +206,12 @@ class AppController extends Controller
|
|||
private function authApiUser(): void
|
||||
{
|
||||
if (!empty($_SERVER['HTTP_AUTHORIZATION']) && strlen($_SERVER['HTTP_AUTHORIZATION'])) {
|
||||
$this->loadModel('AuthKeys');
|
||||
$AuthKeysTable = $this->fetchTable('AuthKeys');
|
||||
$logModel = $this->Users->auditLogs();
|
||||
$authKey = $this->AuthKeys->checkKey($_SERVER['HTTP_AUTHORIZATION']);
|
||||
$authKey = $AuthKeysTable->checkKey($_SERVER['HTTP_AUTHORIZATION']);
|
||||
if (!empty($authKey)) {
|
||||
$this->loadModel('Users');
|
||||
$user = $this->Users->get($authKey['user_id']);
|
||||
$UsersTable = $this->fetchTable('Users');
|
||||
$user = $UsersTable->get($authKey['user_id']);
|
||||
$logModel->insert(
|
||||
[
|
||||
'request_action' => 'login',
|
||||
|
@ -400,4 +403,13 @@ class AppController extends Controller
|
|||
throw new HttpException('Invalid JSON input. Make sure that the JSON input is a correctly formatted JSON string. This request has been blocked to avoid an unfiltered request.', 405, $e);
|
||||
}
|
||||
}
|
||||
|
||||
private function setResponseType()
|
||||
{
|
||||
foreach ($this->request->getHeader('Accept') as $accept) {
|
||||
if (strpos($accept, 'application/json') !== false) {
|
||||
return $this->response->withType('json');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller\Component;
|
||||
|
||||
use Cake\Controller\Component;
|
||||
use Cake\Http\Exception\NotFoundException;
|
||||
use Cake\Validation\Validation;
|
||||
|
||||
class ToolboxComponent extends Component
|
||||
{
|
||||
public function findIdByUuid($model, $id, $allowEmpty = false)
|
||||
{
|
||||
if (empty($id) && $allowEmpty) {
|
||||
return $id;
|
||||
}
|
||||
if (Validation::uuid($id)) {
|
||||
$data = $model->find('first', array(
|
||||
'conditions' => array($model->alias . '.uuid' => $id),
|
||||
'recursive' => -1,
|
||||
'fields' => array($model->alias . '.id')
|
||||
));
|
||||
if (empty($data)) {
|
||||
throw new NotFoundException(__('Invalid %s.', $model->alias));
|
||||
}
|
||||
return $data[$model->alias]['id'];
|
||||
} else {
|
||||
if (!is_numeric($id)) {
|
||||
throw new NotFoundException(__('Invalid %s.', $model->alias));
|
||||
}
|
||||
$data = $model->find('first', array(
|
||||
'conditions' => array($model->alias . '.id' => $id),
|
||||
'recursive' => -1,
|
||||
'fields' => array($model->alias . '.id')
|
||||
));
|
||||
if (empty($data)) {
|
||||
throw new NotFoundException(__('Invalid %s.', $model->alias));
|
||||
} else {
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,41 +4,39 @@ namespace App\Controller;
|
|||
|
||||
use App\Controller\AppController;
|
||||
use App\Lib\Tools\ClusterRelationsGraphTool;
|
||||
use App\Lib\Tools\FileAccessTool;
|
||||
use App\Lib\Tools\JsonTool;
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Http\Exception\MethodNotAllowedException;
|
||||
use Cake\Http\Exception\NotFoundException;
|
||||
use Cake\Http\Exception\BadRequestException;
|
||||
use Cake\Http\Exception\ForbiddenException;
|
||||
use Cake\Http\Exception\MethodNotAllowedException;
|
||||
use Cake\Http\Exception\NotFoundException;
|
||||
use Cake\Http\Response;
|
||||
use Cake\ORM\Locator\LocatorAwareTrait;
|
||||
use Cake\Validation\Validation;
|
||||
use App\Lib\Tools\FileAccessTool;
|
||||
use App\Lib\Tools\JsonTool;
|
||||
use Exception;
|
||||
|
||||
class JobsController extends AppController
|
||||
class GalaxiesController extends AppController
|
||||
{
|
||||
use LocatorAwareTrait;
|
||||
|
||||
|
||||
public $paginate = [
|
||||
'limit' => 60,
|
||||
'recursive' => 0,
|
||||
'order' => [
|
||||
'Galaxy.id' => 'DESC'
|
||||
'Galaxies.id' => 'DESC'
|
||||
]
|
||||
];
|
||||
|
||||
public function index()
|
||||
{
|
||||
$aclConditions = array();
|
||||
$filterData = array(
|
||||
$filterData = [
|
||||
'request' => $this->request,
|
||||
'named_params' => $this->params['named'],
|
||||
'named_params' => $this->request->getParam('named'),
|
||||
'paramArray' => ['value', 'enabled'],
|
||||
'ordered_url_params' => [],
|
||||
'additional_delimiters' => PHP_EOL
|
||||
);
|
||||
];
|
||||
$exception = false;
|
||||
$filters = $this->harvestParameters($filterData, $exception);
|
||||
$searchConditions = [];
|
||||
|
@ -46,33 +44,32 @@ class JobsController extends AppController
|
|||
$filters['value'] = '';
|
||||
} else {
|
||||
$searchall = '%' . strtolower($filters['value']) . '%';
|
||||
$searchConditions = array(
|
||||
'OR' => array(
|
||||
'LOWER(Galaxy.name) LIKE' => $searchall,
|
||||
'LOWER(Galaxy.namespace) LIKE' => $searchall,
|
||||
'LOWER(Galaxy.description) LIKE' => $searchall,
|
||||
'LOWER(Galaxy.kill_chain_order) LIKE' => $searchall,
|
||||
'Galaxy.uuid LIKE' => $searchall
|
||||
)
|
||||
);
|
||||
$searchConditions = [
|
||||
'OR' => [
|
||||
'LOWER(Galaxies.name) LIKE' => $searchall,
|
||||
'LOWER(Galaxies.namespace) LIKE' => $searchall,
|
||||
'LOWER(Galaxies.description) LIKE' => $searchall,
|
||||
'LOWER(Galaxies.kill_chain_order) LIKE' => $searchall,
|
||||
'Galaxies.uuid LIKE' => $searchall
|
||||
]
|
||||
];
|
||||
}
|
||||
if (isset($filters['enabled'])) {
|
||||
$searchConditions[]['enabled'] = $filters['enabled'] ? 1 : 0;
|
||||
}
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
$galaxies = $this->Galaxy->find(
|
||||
$galaxies = $this->Galaxies->find(
|
||||
'all',
|
||||
array(
|
||||
[
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'AND' => array($searchConditions, $aclConditions)
|
||||
)
|
||||
)
|
||||
);
|
||||
'conditions' => [
|
||||
'AND' => $searchConditions
|
||||
]
|
||||
]
|
||||
)->disableHydration()->toArray();
|
||||
return $this->RestResponse->viewData($galaxies, $this->response->getType());
|
||||
} else {
|
||||
$this->paginate['conditions']['AND'][] = $searchConditions;
|
||||
$this->paginate['conditions']['AND'][] = $aclConditions;
|
||||
$galaxies = $this->paginate();
|
||||
$this->set('galaxyList', $galaxies);
|
||||
$this->set('passedArgsArray', $this->passedArgs);
|
||||
|
@ -85,18 +82,18 @@ class JobsController extends AppController
|
|||
if (!$this->request->is('post')) {
|
||||
throw new MethodNotAllowedException('This action is only accessible via POST requests.');
|
||||
}
|
||||
if (!empty($this->params['named']['force'])) {
|
||||
if (!empty($this->request->getParam('named')['force'])) {
|
||||
$force = 1;
|
||||
} else {
|
||||
$force = 0;
|
||||
}
|
||||
$result = $this->Galaxy->update($force);
|
||||
$result = $this->Galaxies->update($force);
|
||||
$message = __('Galaxies updated.');
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('Galaxy', 'update', false, $this->response->getType(), $message);
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(array('controller' => 'galaxies', 'action' => 'index'));
|
||||
$this->redirect(['controller' => 'galaxies', 'action' => 'index']);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,35 +108,41 @@ class JobsController extends AppController
|
|||
return $this->RestResponse->saveSuccessResponse('Galaxy', 'wipe_default', false, $this->response->getType(), $message);
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(array('controller' => 'galaxies', 'action' => 'index'));
|
||||
$this->redirect(['controller' => 'galaxies', 'action' => 'index']);
|
||||
}
|
||||
}
|
||||
|
||||
public function view($id)
|
||||
{
|
||||
$id = $this->Toolbox->findIdByUuid($this->Galaxy, $id);
|
||||
$passedArgsArray = array(
|
||||
$passedArgsArray = [
|
||||
'context' => isset($this->params['named']['context']) ? $this->params['named']['context'] : 'all'
|
||||
);
|
||||
];
|
||||
if (isset($this->params['named']['searchall']) && strlen($this->params['named']['searchall']) > 0) {
|
||||
$passedArgsArray['searchall'] = $this->params['named']['searchall'];
|
||||
}
|
||||
$this->set('passedArgsArray', $passedArgsArray);
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
$galaxy = $this->Galaxy->find('first', array(
|
||||
'contain' => array('GalaxyCluster' => array('GalaxyElement'/*, 'GalaxyReference'*/)),
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Galaxy.id' => $id)
|
||||
));
|
||||
$galaxy = $this->Galaxy->find(
|
||||
'first',
|
||||
[
|
||||
'contain' => ['GalaxyCluster' => ['GalaxyElement'/*, 'GalaxyReference'*/]],
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Galaxy.id' => $id]
|
||||
]
|
||||
);
|
||||
if (empty($galaxy)) {
|
||||
throw new NotFoundException('Galaxy not found.');
|
||||
}
|
||||
return $this->RestResponse->viewData($galaxy, $this->response->getType());
|
||||
} else {
|
||||
$galaxy = $this->Galaxy->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Galaxy.id' => $id)
|
||||
));
|
||||
$galaxy = $this->Galaxy->find(
|
||||
'first',
|
||||
[
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Galaxy.id' => $id]
|
||||
]
|
||||
);
|
||||
if (empty($galaxy)) {
|
||||
throw new NotFoundException('Galaxy not found.');
|
||||
}
|
||||
|
@ -155,10 +158,13 @@ class JobsController extends AppController
|
|||
throw new NotFoundException('Invalid galaxy.');
|
||||
}
|
||||
|
||||
$galaxy = $this->Galaxy->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Galaxy.id' => $id)
|
||||
));
|
||||
$galaxy = $this->Galaxy->find(
|
||||
'first',
|
||||
[
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Galaxy.id' => $id]
|
||||
]
|
||||
);
|
||||
if (empty($galaxy)) {
|
||||
throw new NotFoundException('Invalid galaxy.');
|
||||
}
|
||||
|
@ -169,7 +175,7 @@ class JobsController extends AppController
|
|||
return $this->RestResponse->saveSuccessResponse('Galaxy', 'delete', false, $this->response->getType(), $message);
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(array('controller' => 'galaxies', 'action' => 'index'));
|
||||
$this->redirect(['controller' => 'galaxies', 'action' => 'index']);
|
||||
}
|
||||
} else {
|
||||
$message = __('Could not delete Galaxy.');
|
||||
|
@ -200,10 +206,13 @@ class JobsController extends AppController
|
|||
throw new NotFoundException('Invalid galaxy.');
|
||||
}
|
||||
|
||||
$galaxy = $this->Galaxy->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Galaxy.id' => $id)
|
||||
));
|
||||
$galaxy = $this->Galaxy->find(
|
||||
'first',
|
||||
[
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Galaxy.id' => $id]
|
||||
]
|
||||
);
|
||||
if (empty($galaxy)) {
|
||||
throw new NotFoundException('Invalid galaxy.');
|
||||
}
|
||||
|
@ -219,7 +228,7 @@ class JobsController extends AppController
|
|||
return $this->RestResponse->saveSuccessResponse('Galaxy', 'toggle', false, $this->response->getType(), $message);
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(array('controller' => 'galaxies', 'action' => 'index'));
|
||||
$this->redirect(['controller' => 'galaxies', 'action' => 'index']);
|
||||
}
|
||||
} else {
|
||||
$message = __('Could not enable Galaxy.');
|
||||
|
@ -253,7 +262,7 @@ class JobsController extends AppController
|
|||
return $this->RestResponse->saveSuccessResponse('Galaxy', 'import', false, $this->response->getType(), $message);
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(array('controller' => 'galaxies', 'action' => 'index'));
|
||||
$this->redirect(['controller' => 'galaxies', 'action' => 'index']);
|
||||
}
|
||||
} else {
|
||||
$message = __('Could not import galaxy clusters. %s imported, %s ignored, %s failed. %s', $saveResult['imported'], $saveResult['ignored'], $saveResult['failed'], !empty($saveResult['errors']) ? implode(', ', $saveResult['errors']) : '');
|
||||
|
@ -292,29 +301,32 @@ class JobsController extends AppController
|
|||
|
||||
public function export($galaxyId)
|
||||
{
|
||||
$galaxy = $this->Galaxy->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Galaxy.id' => $galaxyId)
|
||||
));
|
||||
$galaxy = $this->Galaxy->find(
|
||||
'first',
|
||||
[
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Galaxy.id' => $galaxyId]
|
||||
]
|
||||
);
|
||||
if (empty($galaxy) && $galaxyId !== null) {
|
||||
throw new NotFoundException('Galaxy not found.');
|
||||
}
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$requestData = $this->request->getData()['Galaxy'] ? $this->request->getData()['Galaxy'] : $this->request->getData();
|
||||
$clusterType = array();
|
||||
$clusterType = [];
|
||||
if ($requestData['default']) {
|
||||
$clusterType[] = true;
|
||||
}
|
||||
if ($requestData['custom']) {
|
||||
$clusterType[] = false;
|
||||
}
|
||||
$options = array(
|
||||
'conditions' => array(
|
||||
$options = [
|
||||
'conditions' => [
|
||||
'GalaxyCluster.galaxy_id' => $galaxyId,
|
||||
'GalaxyCluster.distribution' => $requestData['distribution'],
|
||||
'GalaxyCluster.default' => $clusterType
|
||||
)
|
||||
);
|
||||
]
|
||||
];
|
||||
$clusters = $this->Galaxy->GalaxyCluster->fetchGalaxyClusters($this->Auth->user(), $options, $full = true);
|
||||
$clusters = $this->Galaxy->GalaxyCluster->unsetFieldsForExport($clusters);
|
||||
if ($requestData['format'] == 'misp-galaxy') {
|
||||
|
@ -352,32 +364,35 @@ class JobsController extends AppController
|
|||
if (!$local) {
|
||||
$conditions['local_only'] = false;
|
||||
}
|
||||
$galaxies = $this->Galaxy->find('all', array(
|
||||
'recursive' => -1,
|
||||
'fields' => array('MAX(Galaxy.version) as latest_version', 'id', 'kill_chain_order', 'name', 'icon', 'description'),
|
||||
'conditions' => $conditions,
|
||||
'group' => array('name', 'id', 'kill_chain_order', 'icon', 'description'),
|
||||
'order' => array('name asc')
|
||||
));
|
||||
$items = array(
|
||||
array(
|
||||
'name' => __('All clusters'),
|
||||
'value' => $this->baseurl . "/galaxies/selectCluster/" . h($target_id) . '/' . h($target_type) . '/0' . '/local:' . h($local) . '/eventid:' . h($eventid)
|
||||
)
|
||||
$galaxies = $this->Galaxy->find(
|
||||
'all',
|
||||
[
|
||||
'recursive' => -1,
|
||||
'fields' => ['MAX(Galaxy.version) as latest_version', 'id', 'kill_chain_order', 'name', 'icon', 'description'],
|
||||
'conditions' => $conditions,
|
||||
'group' => ['name', 'id', 'kill_chain_order', 'icon', 'description'],
|
||||
'order' => ['name asc']
|
||||
]
|
||||
);
|
||||
$items = [
|
||||
[
|
||||
'name' => __('All clusters'),
|
||||
'value' => $this->baseurl . "/galaxies/selectCluster/" . h($target_id) . '/' . h($target_type) . '/0/local:' . h($local) . '/eventid:' . h($eventid)
|
||||
]
|
||||
];
|
||||
foreach ($galaxies as $galaxy) {
|
||||
if (!isset($galaxy['Galaxy']['kill_chain_order']) || $noGalaxyMatrix) {
|
||||
$items[] = array(
|
||||
$items[] = [
|
||||
'name' => h($galaxy['Galaxy']['name']),
|
||||
'value' => $this->baseurl . "/galaxies/selectCluster/" . h($target_id) . '/' . h($target_type) . '/' . $galaxy['Galaxy']['id'] . '/local:' . h($local) . '/eventid:' . h($eventid),
|
||||
'template' => array(
|
||||
'template' => [
|
||||
'preIcon' => 'fa-' . $galaxy['Galaxy']['icon'],
|
||||
'name' => $galaxy['Galaxy']['name'],
|
||||
'infoExtra' => $galaxy['Galaxy']['description'],
|
||||
)
|
||||
);
|
||||
]
|
||||
];
|
||||
} else { // should use matrix instead
|
||||
$param = array(
|
||||
$param = [
|
||||
'name' => $galaxy['Galaxy']['name'],
|
||||
'value' => $this->baseurl . "/galaxies/selectCluster/" . h($target_id) . '/' . h($target_type) . '/' . $galaxy['Galaxy']['id'] . '/local:' . h($local) . '/eventid:' . h($eventid),
|
||||
'functionName' => sprintf(
|
||||
|
@ -390,7 +405,7 @@ class JobsController extends AppController
|
|||
),
|
||||
'isPill' => true,
|
||||
'isMatrix' => true
|
||||
);
|
||||
];
|
||||
if ($galaxy['Galaxy']['id'] == $mitreAttackGalaxyId) {
|
||||
$param['img'] = $this->baseurl . "/img/mitre-attack-icon.ico";
|
||||
}
|
||||
|
@ -405,67 +420,83 @@ class JobsController extends AppController
|
|||
public function selectGalaxyNamespace($target_id, $target_type = 'event', $noGalaxyMatrix = false)
|
||||
{
|
||||
$this->closeSession();
|
||||
$namespaces = $this->Galaxy->find('column', array(
|
||||
'recursive' => -1,
|
||||
'fields' => array('namespace'),
|
||||
'conditions' => array('enabled' => 1),
|
||||
'unique' => true,
|
||||
'order' => array('namespace asc')
|
||||
));
|
||||
$namespaces = $this->Galaxy->find(
|
||||
'column',
|
||||
[
|
||||
'recursive' => -1,
|
||||
'fields' => ['namespace'],
|
||||
'conditions' => ['enabled' => 1],
|
||||
'unique' => true,
|
||||
'order' => ['namespace asc']
|
||||
]
|
||||
);
|
||||
$local = !empty($this->params['named']['local']) ? '1' : '0';
|
||||
$eventid = !empty($this->params['named']['eventid']) ? $this->params['named']['eventid'] : '0';
|
||||
$noGalaxyMatrix = $noGalaxyMatrix ? '1' : '0';
|
||||
$items = [[
|
||||
'name' => __('All namespaces'),
|
||||
'value' => $this->baseurl . "/galaxies/selectGalaxy/" . h($target_id) . '/' . h($target_type) . '/0' . '/' . h($noGalaxyMatrix) . '/local:' . h($local) . '/eventid:' . h($eventid)
|
||||
]];
|
||||
'value' => $this->baseurl . "/galaxies/selectGalaxy/" . h($target_id) . '/' . h($target_type) . '/0/' . h($noGalaxyMatrix) . '/local:' . h($local) . '/eventid:' . h($eventid)
|
||||
]
|
||||
];
|
||||
foreach ($namespaces as $namespace) {
|
||||
$items[] = array(
|
||||
$items[] = [
|
||||
'name' => $namespace,
|
||||
'value' => $this->baseurl . "/galaxies/selectGalaxy/" . h($target_id) . '/' . h($target_type) . '/' . h($namespace) . '/' . h($noGalaxyMatrix) . '/local:' . h($local) . '/eventid:' . h($eventid)
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
$this->set('items', $items);
|
||||
$this->set('options', array( // set chosen (select picker) options
|
||||
'multiple' => 0,
|
||||
));
|
||||
$this->set(
|
||||
'options',
|
||||
[ // set chosen (select picker) options
|
||||
'multiple' => 0,
|
||||
]
|
||||
);
|
||||
$this->render('/Elements/generic_picker');
|
||||
}
|
||||
|
||||
public function selectCluster($target_id, $target_type = 'event', $selectGalaxy = false)
|
||||
{
|
||||
$user = $this->closeSession();
|
||||
$conditions = array(
|
||||
'OR' => array(
|
||||
$conditions = [
|
||||
'OR' => [
|
||||
'GalaxyCluster.published' => true,
|
||||
'GalaxyCluster.default' => true,
|
||||
),
|
||||
'AND' => array(
|
||||
],
|
||||
'AND' => [
|
||||
'GalaxyCluster.deleted' => false,
|
||||
)
|
||||
);
|
||||
]
|
||||
];
|
||||
if ($target_type == 'galaxyClusterRelation') {
|
||||
$conditions['OR']['GalaxyCluster.published'] = [true, false];
|
||||
}
|
||||
if ($selectGalaxy) {
|
||||
$conditions['GalaxyCluster.galaxy_id'] = $selectGalaxy;
|
||||
}
|
||||
$data = array_column($this->Galaxy->GalaxyCluster->fetchGalaxyClusters($user, array(
|
||||
'conditions' => $conditions,
|
||||
'fields' => array('value', 'description', 'source', 'type', 'id', 'uuid'),
|
||||
'order' => array('value asc'),
|
||||
)), 'GalaxyCluster');
|
||||
$synonyms = $this->Galaxy->GalaxyCluster->GalaxyElement->find('all', array(
|
||||
'conditions' => array(
|
||||
'GalaxyElement.key' => 'synonyms',
|
||||
$conditions
|
||||
$data = array_column(
|
||||
$this->Galaxy->GalaxyCluster->fetchGalaxyClusters(
|
||||
$user,
|
||||
[
|
||||
'conditions' => $conditions,
|
||||
'fields' => ['value', 'description', 'source', 'type', 'id', 'uuid'],
|
||||
'order' => ['value asc'],
|
||||
]
|
||||
),
|
||||
'fields' => ['GalaxyElement.galaxy_cluster_id', 'GalaxyElement.value'],
|
||||
'contain' => 'GalaxyCluster',
|
||||
'recursive' => -1
|
||||
));
|
||||
$sortedSynonyms = array();
|
||||
'GalaxyCluster'
|
||||
);
|
||||
$synonyms = $this->Galaxy->GalaxyCluster->GalaxyElement->find(
|
||||
'all',
|
||||
[
|
||||
'conditions' => [
|
||||
'GalaxyElement.key' => 'synonyms',
|
||||
$conditions
|
||||
],
|
||||
'fields' => ['GalaxyElement.galaxy_cluster_id', 'GalaxyElement.value'],
|
||||
'contain' => 'GalaxyCluster',
|
||||
'recursive' => -1
|
||||
]
|
||||
);
|
||||
$sortedSynonyms = [];
|
||||
foreach ($synonyms as $synonym) {
|
||||
$sortedSynonyms[$synonym['GalaxyElement']['galaxy_cluster_id']][] = $synonym['GalaxyElement']['value'];
|
||||
}
|
||||
|
@ -478,24 +509,24 @@ class JobsController extends AppController
|
|||
}
|
||||
ksort($clusters);
|
||||
|
||||
$items = array();
|
||||
$items = [];
|
||||
foreach ($clusters as $cluster_data) {
|
||||
foreach ($cluster_data as $cluster) {
|
||||
$optionName = $cluster['value'];
|
||||
if (isset($cluster['synonyms_string'])) {
|
||||
$optionName .= ' (' . $cluster['synonyms_string'] . ')';
|
||||
}
|
||||
$itemParam = array(
|
||||
$itemParam = [
|
||||
'name' => $optionName,
|
||||
'value' => $cluster['id'],
|
||||
'template' => array(
|
||||
'template' => [
|
||||
'name' => $cluster['value'],
|
||||
'infoExtra' => $cluster['description'],
|
||||
),
|
||||
'additionalData' => array(
|
||||
],
|
||||
'additionalData' => [
|
||||
'uuid' => $cluster['uuid']
|
||||
)
|
||||
);
|
||||
]
|
||||
];
|
||||
if (isset($cluster['synonyms_string'])) {
|
||||
$itemParam['template']['infoContextual'] = __('Synonyms: ') . $cluster['synonyms_string'];
|
||||
}
|
||||
|
@ -512,17 +543,20 @@ class JobsController extends AppController
|
|||
$this->set('mirrorOnEvent', $mirrorOnEvent);
|
||||
$this->set('items', $items);
|
||||
$local = !empty($this->params['named']['local']) ? $this->params['named']['local'] : '0';
|
||||
$this->set('options', array( // set chosen (select picker) options
|
||||
'functionName' => 'quickSubmitGalaxyForm',
|
||||
'multiple' => $target_type == 'galaxyClusterRelation' ? 0 : '-1',
|
||||
'select_options' => array(
|
||||
'additionalData' => array(
|
||||
'target_id' => $target_id,
|
||||
'target_type' => $target_type,
|
||||
'local' => $local
|
||||
)
|
||||
),
|
||||
));
|
||||
$this->set(
|
||||
'options',
|
||||
[ // set chosen (select picker) options
|
||||
'functionName' => 'quickSubmitGalaxyForm',
|
||||
'multiple' => $target_type == 'galaxyClusterRelation' ? 0 : '-1',
|
||||
'select_options' => [
|
||||
'additionalData' => [
|
||||
'target_id' => $target_id,
|
||||
'target_type' => $target_type,
|
||||
'local' => $local
|
||||
]
|
||||
],
|
||||
]
|
||||
);
|
||||
$this->render('ajax/cluster_choice');
|
||||
}
|
||||
|
||||
|
@ -547,7 +581,7 @@ class JobsController extends AppController
|
|||
}
|
||||
|
||||
$result = $this->Galaxy->attachCluster($user, $target_type, $target, $cluster_id, $local);
|
||||
return new Response(array('body' => json_encode(array('saved' => true, 'success' => $result, 'check_publish' => true)), 'status' => 200, 'type' => 'json'));
|
||||
return new Response(['body' => json_encode(['saved' => true, 'success' => $result, 'check_publish' => true]), 'status' => 200, 'type' => 'json']);
|
||||
}
|
||||
|
||||
public function attachMultipleClusters($target_id, $target_type = 'event')
|
||||
|
@ -561,32 +595,32 @@ class JobsController extends AppController
|
|||
if ($target_id === 'selected') {
|
||||
$target_id_list = $this->_jsonDecode($this->request->getData()['Galaxy']['attribute_ids']);
|
||||
} else {
|
||||
$target_id_list = array($target_id);
|
||||
$target_id_list = [$target_id];
|
||||
}
|
||||
$cluster_ids = $this->request->getData()['Galaxy']['target_ids'];
|
||||
$mirrorOnEventRequested = $mirrorOnEvent && !empty($this->request->getData()['Galaxy']['mirror_on_event']);
|
||||
if (strlen($cluster_ids) > 0) {
|
||||
$cluster_ids = $this->_jsonDecode($cluster_ids);
|
||||
if (empty($cluster_ids)) {
|
||||
return new Response(array('body' => json_encode(array('saved' => false, 'errors' => __('No clusters picked.'))), 'status' => 200, 'type' => 'json'));
|
||||
return new Response(['body' => json_encode(['saved' => false, 'errors' => __('No clusters picked.')]), 'status' => 200, 'type' => 'json']);
|
||||
}
|
||||
} else {
|
||||
return new Response(array('body' => json_encode(array('saved' => false, 'errors' => __('Failed to parse request.'))), 'status' => 200, 'type' => 'json'));
|
||||
return new Response(['body' => json_encode(['saved' => false, 'errors' => __('Failed to parse request.')]), 'status' => 200, 'type' => 'json']);
|
||||
}
|
||||
if ($mirrorOnEventRequested && !empty($target_id_list)) {
|
||||
$first_attribute_id = $target_id_list[0]; // We consider that all attributes to be tagged are contained in the same event.
|
||||
|
||||
$AttributeTable = $this->fetchTable('Attributes');
|
||||
$attribute = $AttributeTable->fetchAttributeSimple($user, array('conditions' => array('Attribute.id' => $first_attribute_id)));
|
||||
$attribute = $AttributeTable->fetchAttributeSimple($user, ['conditions' => ['Attribute.id' => $first_attribute_id]]);
|
||||
if (!empty($attribute['Attribute']['event_id'])) {
|
||||
$event_id = $attribute['Attribute']['event_id'];
|
||||
} else {
|
||||
return new Response(array('body' => json_encode(array('saved' => false, 'errors' => __('Failed to parse request. Could not fetch attribute'))), 'status' => 200, 'type' => 'json'));
|
||||
return new Response(['body' => json_encode(['saved' => false, 'errors' => __('Failed to parse request. Could not fetch attribute')]), 'status' => 200, 'type' => 'json']);
|
||||
}
|
||||
}
|
||||
$result = "";
|
||||
if (!is_array($cluster_ids)) { // in case we only want to attach 1
|
||||
$cluster_ids = array($cluster_ids);
|
||||
$cluster_ids = [$cluster_ids];
|
||||
}
|
||||
foreach ($cluster_ids as $cluster_id) {
|
||||
foreach ($target_id_list as $target_id) {
|
||||
|
@ -610,7 +644,7 @@ class JobsController extends AppController
|
|||
}
|
||||
}
|
||||
if ($this->request->is('ajax')) {
|
||||
return new Response(array('body' => json_encode(array('saved' => true, 'success' => $result, 'check_publish' => true)), 'status' => 200, 'type' => 'json'));
|
||||
return new Response(['body' => json_encode(['saved' => true, 'success' => $result, 'check_publish' => true]), 'status' => 200, 'type' => 'json']);
|
||||
}
|
||||
|
||||
$this->Flash->info($result);
|
||||
|
@ -628,11 +662,14 @@ class JobsController extends AppController
|
|||
|
||||
public function viewGraph($id)
|
||||
{
|
||||
$cluster = $this->Galaxy->GalaxyCluster->find('first', array(
|
||||
'conditions' => array('GalaxyCluster.id' => $id),
|
||||
'contain' => array('Galaxy'),
|
||||
'recursive' => -1
|
||||
));
|
||||
$cluster = $this->Galaxy->GalaxyCluster->find(
|
||||
'first',
|
||||
[
|
||||
'conditions' => ['GalaxyCluster.id' => $id],
|
||||
'contain' => ['Galaxy'],
|
||||
'recursive' => -1
|
||||
]
|
||||
);
|
||||
if (empty($cluster)) {
|
||||
throw new MethodNotAllowedException('Invalid Galaxy.');
|
||||
}
|
||||
|
@ -648,31 +685,34 @@ class JobsController extends AppController
|
|||
{
|
||||
if ($scope === 'event') {
|
||||
$EventsTable = $this->fetchTable('Events');
|
||||
$object = $EventsTable->fetchEvent($this->Auth->user(), array('eventid' => $id, 'metadata' => 1));
|
||||
$object = $EventsTable->fetchEvent($this->Auth->user(), ['eventid' => $id, 'metadata' => 1]);
|
||||
if (empty($object)) {
|
||||
throw new NotFoundException('Invalid event.');
|
||||
}
|
||||
$object = $object[0];
|
||||
} elseif ($scope === 'attribute') {
|
||||
$AttributesTable = $this->fetchTable('Attributes');
|
||||
$object = $AttributesTable->fetchAttributeSimple($this->Auth->user(), [
|
||||
'conditions' => ['Attribute.id' => $id],
|
||||
'contain' => [
|
||||
'Event',
|
||||
'Object',
|
||||
'AttributeTag' => [
|
||||
'fields' => ['AttributeTag.id', 'AttributeTag.tag_id', 'AttributeTag.relationship_type', 'AttributeTag.local'],
|
||||
'Tag' => ['fields' => ['Tag.id', 'Tag.name', 'Tag.colour', 'Tag.exportable']],
|
||||
$object = $AttributesTable->fetchAttributeSimple(
|
||||
$this->Auth->user(),
|
||||
[
|
||||
'conditions' => ['Attribute.id' => $id],
|
||||
'contain' => [
|
||||
'Event',
|
||||
'Object',
|
||||
'AttributeTag' => [
|
||||
'fields' => ['AttributeTag.id', 'AttributeTag.tag_id', 'AttributeTag.relationship_type', 'AttributeTag.local'],
|
||||
'Tag' => ['fields' => ['Tag.id', 'Tag.name', 'Tag.colour', 'Tag.exportable']],
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
]
|
||||
);
|
||||
if (empty($object)) {
|
||||
throw new NotFoundException('Invalid attribute.');
|
||||
}
|
||||
$object = $AttributesTable->Event->massageTags($this->Auth->user(), $object, 'Attribute');
|
||||
} elseif ($scope === 'tag_collection') {
|
||||
$TagsCollectionTable = $this->fetchTable('TagCollections');
|
||||
$object = $TagsCollectionTable->fetchTagCollection($this->Auth->user(), array('conditions' => array('TagCollection.id' => $id)));
|
||||
$object = $TagsCollectionTable->fetchTagCollection($this->Auth->user(), ['conditions' => ['TagCollection.id' => $id]]);
|
||||
if (empty($object)) {
|
||||
throw new NotFoundException('Invalid Tag Collection.');
|
||||
}
|
||||
|
@ -689,7 +729,7 @@ class JobsController extends AppController
|
|||
|
||||
public function forkTree($galaxyId, $pruneRootLeaves = true)
|
||||
{
|
||||
$clusters = $this->Galaxy->GalaxyCluster->fetchGalaxyClusters($this->Auth->user(), array('conditions' => array('GalaxyCluster.galaxy_id' => $galaxyId)), $full = true);
|
||||
$clusters = $this->Galaxy->GalaxyCluster->fetchGalaxyClusters($this->Auth->user(), ['conditions' => ['GalaxyCluster.galaxy_id' => $galaxyId]], $full = true);
|
||||
if (empty($clusters)) {
|
||||
throw new MethodNotAllowedException('Invalid Galaxy.');
|
||||
}
|
||||
|
@ -697,10 +737,13 @@ class JobsController extends AppController
|
|||
foreach ($clusters as $k => $cluster) {
|
||||
$clusters[$k] = $this->Galaxy->GalaxyCluster->attachExtendFromInfo($this->Auth->user(), $clusters[$k]);
|
||||
}
|
||||
$galaxy = $this->Galaxy->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Galaxy.id' => $galaxyId)
|
||||
));
|
||||
$galaxy = $this->Galaxy->find(
|
||||
'first',
|
||||
[
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Galaxy.id' => $galaxyId]
|
||||
]
|
||||
);
|
||||
$tree = $this->Galaxy->generateForkTree($clusters, $galaxy, $pruneRootLeaves = $pruneRootLeaves);
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->viewData($tree, $this->response->getType());
|
||||
|
@ -712,14 +755,17 @@ class JobsController extends AppController
|
|||
|
||||
public function relationsGraph($galaxyId, $includeInbound = 0)
|
||||
{
|
||||
$clusters = $this->Galaxy->GalaxyCluster->fetchGalaxyClusters($this->Auth->user(), array('conditions' => array('GalaxyCluster.galaxy_id' => $galaxyId)), $full = true);
|
||||
$clusters = $this->Galaxy->GalaxyCluster->fetchGalaxyClusters($this->Auth->user(), ['conditions' => ['GalaxyCluster.galaxy_id' => $galaxyId]], $full = true);
|
||||
if (empty($clusters)) {
|
||||
throw new MethodNotAllowedException('Invalid Galaxy.');
|
||||
}
|
||||
$galaxy = $this->Galaxy->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Galaxy.id' => $galaxyId)
|
||||
));
|
||||
$galaxy = $this->Galaxy->find(
|
||||
'first',
|
||||
[
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Galaxy.id' => $galaxyId]
|
||||
]
|
||||
);
|
||||
$grapher = new ClusterRelationsGraphTool($this->Auth->user(), $this->Galaxy->GalaxyCluster);
|
||||
$relations = $grapher->getNetwork($clusters, $includeInbound, $includeInbound);
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
|
|
|
@ -0,0 +1,934 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Cake\ORM\Locator\LocatorAwareTrait;
|
||||
use Cake\Validation\Validation;
|
||||
use Cake\Http\Exception\MethodNotAllowedException;
|
||||
use Cake\Http\Exception\NotFoundException;
|
||||
use Cake\Routing\Router;
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Utility\Hash;
|
||||
use App\Lib\Tools\ColourGradientTool;
|
||||
use App\Lib\Tools\ClusterRelationsTreeTool;
|
||||
use Cake\Http\Response;
|
||||
|
||||
class GalaxyClustersController extends AppController
|
||||
{
|
||||
use LocatorAwareTrait;
|
||||
|
||||
public $components = array('Session', 'RequestHandler');
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 60,
|
||||
'recursive' => -1,
|
||||
'order' => array(
|
||||
'GalaxyCluster.version' => 'DESC',
|
||||
'GalaxyCluster.value' => 'ASC'
|
||||
),
|
||||
'contain' => array(
|
||||
'Tag' => array(
|
||||
'fields' => array('Tag.id'),
|
||||
/*
|
||||
'EventTag' => array(
|
||||
'fields' => array('EventTag.event_id')
|
||||
),
|
||||
'AttributeTag' => array(
|
||||
'fields' => array('AttributeTag.event_id', 'AttributeTag.attribute_id')
|
||||
)
|
||||
*/
|
||||
),
|
||||
'GalaxyElement' => array(
|
||||
'conditions' => array('GalaxyElement.key' => 'synonyms'),
|
||||
'fields' => array('value')
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
public function initialize(): void
|
||||
{
|
||||
$this->loadComponent('Toolbox');
|
||||
}
|
||||
|
||||
|
||||
public function index($galaxyId)
|
||||
{
|
||||
$galaxyId = $this->Toolbox->findIdByUuid($this->GalaxyCluster->Galaxy, $galaxyId);
|
||||
$filterData = array(
|
||||
'request' => $this->request,
|
||||
'named_params' => $this->params['named'],
|
||||
'paramArray' => ['context', 'searchall'],
|
||||
'ordered_url_params' => [],
|
||||
'additional_delimiters' => PHP_EOL
|
||||
);
|
||||
$exception = false;
|
||||
$filters = $this->harvestParameters($filterData, $exception);
|
||||
$aclConditions = $this->GalaxyCluster->buildConditions($this->Auth->user());
|
||||
$contextConditions = array();
|
||||
if (empty($filters['context'])) {
|
||||
$filters['context'] = 'all';
|
||||
} else {
|
||||
$contextConditions = array('GalaxyCluster.deleted' => false);
|
||||
}
|
||||
|
||||
if ($filters['context'] == 'default') {
|
||||
$contextConditions['GalaxyCluster.default'] = true;
|
||||
} elseif ($filters['context'] == 'custom') {
|
||||
$contextConditions['GalaxyCluster.default'] = false;
|
||||
} elseif ($filters['context'] == 'org') {
|
||||
$contextConditions['GalaxyCluster.org_id'] = $this->Auth->user('org_id');
|
||||
} elseif ($filters['context'] == 'deleted') {
|
||||
$contextConditions['GalaxyCluster.deleted'] = true;
|
||||
}
|
||||
|
||||
$this->set('passedArgs', json_encode(array('context' => $filters['context'], 'searchall' => isset($filters['searchall']) ? $filters['searchall'] : '')));
|
||||
$this->set('context', $filters['context']);
|
||||
$searchConditions = array();
|
||||
if (empty($filters['searchall'])) {
|
||||
$filters['searchall'] = '';
|
||||
}
|
||||
if (strlen($filters['searchall']) > 0) {
|
||||
$searchall = '%' . strtolower($filters['searchall']) . '%';
|
||||
$synonym_hits = $this->GalaxyCluster->GalaxyElement->find(
|
||||
'list',
|
||||
array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'LOWER(GalaxyElement.value) LIKE' => $searchall,
|
||||
'GalaxyElement.key' => 'synonyms'
|
||||
),
|
||||
'fields' => array(
|
||||
'GalaxyElement.galaxy_cluster_id'
|
||||
)
|
||||
)
|
||||
);
|
||||
$searchConditions = array(
|
||||
'OR' => array(
|
||||
'LOWER(GalaxyCluster.value) LIKE' => $searchall,
|
||||
'LOWER(GalaxyCluster.description) LIKE' => $searchall,
|
||||
'GalaxyCluster.uuid' => $filters['searchall'],
|
||||
'GalaxyCluster.id' => array_values($synonym_hits),
|
||||
),
|
||||
);
|
||||
}
|
||||
$searchConditions['GalaxyCluster.galaxy_id'] = $galaxyId;
|
||||
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
$clusters = $this->GalaxyCluster->find(
|
||||
'all',
|
||||
array(
|
||||
'conditions' => array(
|
||||
'AND' => array($contextConditions, $searchConditions, $aclConditions)
|
||||
),
|
||||
)
|
||||
);
|
||||
return $this->RestResponse->viewData($clusters, $this->response->getType());
|
||||
}
|
||||
|
||||
$this->paginate['conditions']['AND'][] = $contextConditions;
|
||||
$this->paginate['conditions']['AND'][] = $searchConditions;
|
||||
$this->paginate['conditions']['AND'][] = $aclConditions;
|
||||
$this->paginate['contain'] = array_merge($this->paginate['contain'], array('Org', 'Orgc', 'SharingGroup', 'GalaxyClusterRelation', 'TargetingClusterRelation'));
|
||||
$clusters = $this->paginate();
|
||||
|
||||
$this->GalaxyCluster->attachExtendByInfo($this->Auth->user(), $clusters);
|
||||
|
||||
$tagIds = array();
|
||||
foreach ($clusters as $k => $cluster) {
|
||||
$clusters[$k] = $this->GalaxyCluster->attachExtendFromInfo($this->Auth->user(), $clusters[$k]);
|
||||
$clusters[$k]['GalaxyCluster']['relation_counts'] = array(
|
||||
'out' => count($clusters[$k]['GalaxyClusterRelation']),
|
||||
'in' => count($clusters[$k]['TargetingClusterRelation']),
|
||||
);
|
||||
|
||||
if (isset($cluster['Tag']['id'])) {
|
||||
$tagIds[] = $cluster['Tag']['id'];
|
||||
$clusters[$k]['GalaxyCluster']['tag_id'] = $cluster['Tag']['id'];
|
||||
}
|
||||
$clusters[$k]['GalaxyCluster']['synonyms'] = array();
|
||||
foreach ($cluster['GalaxyElement'] as $element) {
|
||||
$clusters[$k]['GalaxyCluster']['synonyms'][] = $element['value'];
|
||||
}
|
||||
$clusters[$k]['GalaxyCluster']['event_count'] = 0; // real number is assigned later
|
||||
}
|
||||
|
||||
$eventCountsForTags = $this->GalaxyCluster->Tag->EventTag->countForTags($tagIds, $this->Auth->user());
|
||||
|
||||
$SightingsTable = $this->fetchTable('Sightings');
|
||||
|
||||
$csvForTags = $SightingsTable->tagsSparkline($tagIds, $this->Auth->user(), '0');
|
||||
foreach ($clusters as $k => $cluster) {
|
||||
if (isset($cluster['GalaxyCluster']['tag_id'])) {
|
||||
if (isset($csvForTags[$cluster['GalaxyCluster']['tag_id']])) {
|
||||
$clusters[$k]['csv'] = $csvForTags[$cluster['GalaxyCluster']['tag_id']];
|
||||
}
|
||||
if (isset($eventCountsForTags[$cluster['GalaxyCluster']['tag_id']])) {
|
||||
$clusters[$k]['GalaxyCluster']['event_count'] = $eventCountsForTags[$cluster['GalaxyCluster']['tag_id']];
|
||||
}
|
||||
}
|
||||
}
|
||||
$customClusterCount = $this->GalaxyCluster->fetchGalaxyClusters($this->Auth->user(), [
|
||||
'count' => true,
|
||||
'conditions' => [
|
||||
'AND' => [$searchConditions, $aclConditions],
|
||||
'GalaxyCluster.default' => 0,
|
||||
]
|
||||
]);
|
||||
|
||||
$EventsTable = $this->fetchTable('Events');
|
||||
|
||||
$distributionLevels = $EventsTable->shortDist;
|
||||
$this->set('distributionLevels', $distributionLevels);
|
||||
$this->set('list', $clusters);
|
||||
$this->set('galaxy_id', $galaxyId);
|
||||
$this->set('custom_cluster_count', $customClusterCount);
|
||||
|
||||
if ($this->request->is('ajax')) {
|
||||
$this->layout = false;
|
||||
$this->render('ajax/index');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $id ID or UUID of the cluster
|
||||
*/
|
||||
public function view($id)
|
||||
{
|
||||
$cluster = $this->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $id, 'view', $throwErrors = true, $full = true);
|
||||
$tag = $this->GalaxyCluster->Tag->find('first', array(
|
||||
'conditions' => array(
|
||||
'LOWER(name)' => strtolower($cluster['GalaxyCluster']['tag_name']),
|
||||
),
|
||||
'fields' => array('id'),
|
||||
'recursive' => -1,
|
||||
'contain' => array('EventTag.event_id')
|
||||
));
|
||||
if (!empty($tag)) {
|
||||
$cluster['GalaxyCluster']['tag_count'] = $this->GalaxyCluster->Tag->EventTag->countForTag($tag['Tag']['id'], $this->Auth->user());
|
||||
$cluster['GalaxyCluster']['tag_id'] = $tag['Tag']['id'];
|
||||
}
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->viewData($cluster, $this->response->getType());
|
||||
}
|
||||
|
||||
$clusters = [$cluster];
|
||||
$this->GalaxyCluster->attachExtendByInfo($this->Auth->user(), $clusters);
|
||||
$cluster = $clusters[0];
|
||||
$cluster = $this->GalaxyCluster->attachExtendFromInfo($this->Auth->user(), $cluster);
|
||||
$this->set('id', $cluster['GalaxyCluster']['id']);
|
||||
$this->set('galaxy', ['Galaxy' => $cluster['GalaxyCluster']['Galaxy']]);
|
||||
$this->set('galaxy_id', $cluster['GalaxyCluster']['galaxy_id']);
|
||||
$this->set('cluster', $cluster);
|
||||
$this->set('defaultCluster', $cluster['GalaxyCluster']['default']);
|
||||
if (!empty($cluster['GalaxyCluster']['extended_from'])) {
|
||||
$newVersionAvailable = $cluster['GalaxyCluster']['extended_from']['GalaxyCluster']['version'] > $cluster['GalaxyCluster']['extends_version'];
|
||||
} else {
|
||||
$newVersionAvailable = false;
|
||||
}
|
||||
$this->set('newVersionAvailable', $newVersionAvailable);
|
||||
$AttributesTable = $this->fetchTable('Attributes');
|
||||
$distributionLevels = $AttributesTable->distributionLevels;
|
||||
$this->set('distributionLevels', $distributionLevels);
|
||||
if (!$cluster['GalaxyCluster']['default'] && !$cluster['GalaxyCluster']['published'] && $cluster['GalaxyCluster']['orgc_id'] == $this->Auth->user()['org_id']) {
|
||||
$this->Flash->warning(__('This cluster is not published. Users will not be able to use it'));
|
||||
}
|
||||
$this->set('title_for_layout', __('Galaxy cluster %s', $cluster['GalaxyCluster']['value']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $galaxyId ID of the galaxy to which the cluster will be added
|
||||
*/
|
||||
public function add($galaxyId)
|
||||
{
|
||||
if (Validation::uuid($galaxyId)) {
|
||||
$temp = $this->GalaxyCluster->Galaxy->find('first', array(
|
||||
'recursive' => -1,
|
||||
'fields' => array('Galaxy.id', 'Galaxy.uuid'),
|
||||
'conditions' => array('Galaxy.uuid' => $galaxyId)
|
||||
));
|
||||
if ($temp === null) {
|
||||
throw new NotFoundException(__('Invalid galaxy'));
|
||||
}
|
||||
$galaxyId = $temp['Galaxy']['id'];
|
||||
} elseif (!is_numeric($galaxyId)) {
|
||||
throw new NotFoundException(__('Invalid galaxy'));
|
||||
}
|
||||
$AttributesTable = $this->fetchTable('Attributes');
|
||||
$distributionLevels = $AttributesTable->distributionLevels;
|
||||
unset($distributionLevels[5]);
|
||||
$initialDistribution = 3;
|
||||
$configuredDistribution = Configure::check('MISP.default_attribute_distribution');
|
||||
if ($configuredDistribution != null && $configuredDistribution != 'event') {
|
||||
$initialDistribution = $configuredDistribution;
|
||||
}
|
||||
$SharingGroupsTable = $this->fetchTable('SharingGroups');
|
||||
$sgs = $SharingGroupsTable->fetchAllAuthorised($this->Auth->user(), 'name', 1);
|
||||
|
||||
if (isset($this->params['named']['forkUuid'])) {
|
||||
$forkUuid = $this->params['named']['forkUuid'];
|
||||
$forkedCluster = $this->GalaxyCluster->fetchGalaxyClusters($this->Auth->user(), array(
|
||||
'conditions' => array('GalaxyCluster.uuid' => $forkUuid),
|
||||
), true);
|
||||
if (!empty($forkedCluster)) {
|
||||
$forkedCluster = $forkedCluster[0];
|
||||
$forkedClusterMeta = $forkedCluster['GalaxyCluster'];
|
||||
if (empty($this->request->getData())) {
|
||||
$data = $forkedCluster;
|
||||
unset($data['GalaxyCluster']['id']);
|
||||
unset($data['GalaxyCluster']['uuid']);
|
||||
foreach ($forkedCluster['GalaxyCluster']['GalaxyElement'] as $k => $element) {
|
||||
unset($forkedCluster['GalaxyCluster']['GalaxyElement'][$k]['id']);
|
||||
unset($forkedCluster['GalaxyCluster']['GalaxyElement'][$k]['galaxy_cluster_id']);
|
||||
}
|
||||
$data['GalaxyCluster']['extends_uuid'] = $forkedCluster['GalaxyCluster']['uuid'];
|
||||
$data['GalaxyCluster']['extends_version'] = $forkedCluster['GalaxyCluster']['version'];
|
||||
$data['GalaxyCluster']['elements'] = json_encode($forkedCluster['GalaxyCluster']['GalaxyElement']);
|
||||
$data['GalaxyCluster']['elementsDict'] = $forkedCluster['GalaxyCluster']['GalaxyElement'];
|
||||
$data['GalaxyCluster']['authors'] = json_encode($forkedCluster['GalaxyCluster']['authors']);
|
||||
}
|
||||
unset($forkedClusterMeta['Galaxy']);
|
||||
unset($forkedClusterMeta['Org']);
|
||||
unset($forkedClusterMeta['Orgc']);
|
||||
$this->set('forkedCluster', $forkedCluster);
|
||||
$this->set('forkedClusterMeta', $forkedClusterMeta);
|
||||
} else {
|
||||
throw new NotFoundException('Forked cluster not found.');
|
||||
}
|
||||
}
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$cluster = $this->request->getData();
|
||||
if (!isset($cluster['GalaxyCluster'])) {
|
||||
$cluster = array('GalaxyCluster' => $cluster);
|
||||
}
|
||||
$cluster['GalaxyCluster']['galaxy_id'] = $galaxyId;
|
||||
$cluster['GalaxyCluster']['published'] = false;
|
||||
$errors = array();
|
||||
if (empty($cluster['GalaxyCluster']['elements'])) {
|
||||
if (empty($cluster['GalaxyCluster']['GalaxyElement'])) {
|
||||
$cluster['GalaxyCluster']['GalaxyElement'] = array();
|
||||
}
|
||||
} else {
|
||||
$decoded = json_decode($cluster['GalaxyCluster']['elements'], true);
|
||||
if (is_null($decoded)) {
|
||||
$this->GalaxyCluster->validationErrors['values'][] = __('Invalid JSON');
|
||||
$errors[] = sprintf(__('Invalid JSON'));
|
||||
}
|
||||
$cluster['GalaxyCluster']['GalaxyElement'] = $decoded;
|
||||
}
|
||||
if (!empty($cluster['GalaxyCluster']['extends_uuid'])) {
|
||||
$extendId = $this->Toolbox->findIdByUuid($this->GalaxyCluster, $cluster['GalaxyCluster']['extends_uuid']);
|
||||
$forkedCluster = $this->GalaxyCluster->fetchGalaxyClusters(
|
||||
$this->Auth->user(),
|
||||
array('conditions' => array('GalaxyCluster.id' => $extendId))
|
||||
);
|
||||
if (!empty($forkedCluster)) {
|
||||
$cluster['GalaxyCluster']['extends_uuid'] = $forkedCluster[0]['GalaxyCluster']['uuid'];
|
||||
if (empty($cluster['GalaxyCluster']['extends_version'])) {
|
||||
$cluster['GalaxyCluster']['extends_version'] = $forkedCluster[0]['GalaxyCluster']['version'];
|
||||
}
|
||||
} else {
|
||||
$cluster['GalaxyCluster']['extends_uuid'] = null;
|
||||
}
|
||||
} else {
|
||||
$cluster['GalaxyCluster']['extends_uuid'] = null;
|
||||
}
|
||||
$errors = $this->GalaxyCluster->saveCluster($this->Auth->user(), $cluster);
|
||||
if (!empty($errors)) {
|
||||
$message = implode(', ', $errors);
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('GalaxyCluster', 'add', $this->GalaxyCluster->id, $message, $this->response->getType());
|
||||
} else {
|
||||
$this->Flash->error($message);
|
||||
}
|
||||
} else {
|
||||
$message = __('Galaxy cluster saved');
|
||||
if ($this->request->is('ajax')) {
|
||||
return $this->RestResponse->saveSuccessResponse('GalaxyCluster', 'add', $this->GalaxyCluster->id, $this->response->getType());
|
||||
} else if ($this->ParamHandler->isRest()) {
|
||||
$saved_cluster = $this->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $this->GalaxyCluster->id, 'view', $throwErrors = true, $full = true);
|
||||
return $this->RestResponse->viewData($saved_cluster);
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(array('controller' => 'galaxy_clusters', 'action' => 'view', $this->GalaxyCluster->id));
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->set('galaxy', ['Galaxy' => ['id' => $galaxyId]]);
|
||||
$this->set('galaxy_id', $galaxyId);
|
||||
$this->set('distributionLevels', $distributionLevels);
|
||||
$this->set('initialDistribution', $initialDistribution);
|
||||
$this->set('sharingGroups', $sgs);
|
||||
$this->set('action', 'add');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $id ID or UUID of the cluster
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
$cluster = $this->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $id, 'edit', $throwErrors = true, $full = true);
|
||||
if ($cluster['GalaxyCluster']['default']) {
|
||||
throw new MethodNotAllowedException('Default galaxy cluster cannot be edited');
|
||||
}
|
||||
$this->GalaxyCluster->data = array('GalaxyCluster' => $cluster['GalaxyCluster'], 'GalaxyElement' => $cluster['GalaxyCluster']['GalaxyElement']);
|
||||
|
||||
$AttributesTable = $this->fetchTable('Attributes');
|
||||
$distributionLevels = $AttributesTable->distributionLevels;
|
||||
unset($distributionLevels[5]);
|
||||
$initialDistribution = 3;
|
||||
$configuredDistribution = Configure::check('MISP.default_attribute_distribution');
|
||||
if ($configuredDistribution != null && $configuredDistribution != 'event') {
|
||||
$initialDistribution = $configuredDistribution;
|
||||
}
|
||||
$SharingGroupsTable = $this->fetchTable('SharingGroups');
|
||||
$sgs = $SharingGroupsTable->fetchAllAuthorised($this->Auth->user(), 'name', 1);
|
||||
|
||||
if (!empty($cluster['GalaxyCluster']['extends_uuid'])) {
|
||||
$forkedCluster = $this->GalaxyCluster->fetchGalaxyClusters($this->Auth->user(), array(
|
||||
'conditions' => array('uuid' => $cluster['GalaxyCluster']['extends_uuid']),
|
||||
), false);
|
||||
} else {
|
||||
$forkedCluster = array();
|
||||
}
|
||||
|
||||
if (!empty($forkedCluster)) {
|
||||
$forkedCluster = $forkedCluster[0];
|
||||
$this->set('forkUuid', $cluster['GalaxyCluster']['extends_uuid']);
|
||||
$forkedClusterMeta = $forkedCluster['GalaxyCluster'];
|
||||
$this->set('forkedCluster', $forkedCluster);
|
||||
$this->set('forkedClusterMeta', $forkedClusterMeta);
|
||||
}
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$cluster = $this->request->getData();
|
||||
if (!isset($cluster['GalaxyCluster'])) {
|
||||
$cluster = array('GalaxyCluster' => $cluster);
|
||||
}
|
||||
$errors = array();
|
||||
if (!isset($cluster['GalaxyCluster']['uuid'])) {
|
||||
$cluster['GalaxyCluster']['uuid'] = $this->GalaxyCluster->data['GalaxyCluster']['uuid']; // freeze the uuid
|
||||
}
|
||||
if (!isset($cluster['GalaxyCluster']['id'])) {
|
||||
$cluster['GalaxyCluster']['id'] = $id;
|
||||
}
|
||||
|
||||
if (empty($cluster['GalaxyCluster']['elements'])) {
|
||||
if (empty($cluster['GalaxyCluster']['GalaxyElement'])) {
|
||||
$cluster['GalaxyCluster']['GalaxyElement'] = array();
|
||||
}
|
||||
} else {
|
||||
$decoded = json_decode($cluster['GalaxyCluster']['elements'], true);
|
||||
if (is_null($decoded)) {
|
||||
$this->GalaxyCluster->validationErrors['values'][] = __('Invalid JSON');
|
||||
$errors[] = sprintf(__('Invalid JSON'));
|
||||
}
|
||||
$cluster['GalaxyCluster']['GalaxyElement'] = $decoded;
|
||||
}
|
||||
|
||||
if (empty($cluster['GalaxyCluster']['authors'])) {
|
||||
$cluster['GalaxyCluster']['authors'] = [];
|
||||
} else if (is_array($cluster['GalaxyCluster']['authors'])) {
|
||||
// This is as intended, move on
|
||||
} else {
|
||||
$decoded = json_decode($cluster['GalaxyCluster']['authors'], true);
|
||||
if (is_null($decoded)) { // authors might be comma separated
|
||||
$decoded = array_map('trim', explode(',', $cluster['GalaxyCluster']['authors']));
|
||||
}
|
||||
$cluster['GalaxyCluster']['authors'] = $decoded;
|
||||
}
|
||||
$cluster['GalaxyCluster']['authors'] = json_encode($cluster['GalaxyCluster']['authors']);
|
||||
$cluster['GalaxyCluster']['published'] = false;
|
||||
if (!empty($errors)) {
|
||||
$message = implode(', ', $errors);
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('GalaxyCluster', 'edit', $cluster['GalaxyCluster']['id'], $message, $this->response->getType());
|
||||
} else {
|
||||
$this->Flash->error($message);
|
||||
}
|
||||
} else {
|
||||
$errors = $this->GalaxyCluster->editCluster($this->Auth->user(), $cluster);
|
||||
if (!empty($errors)) {
|
||||
$message = implode(', ', $errors);
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('GalaxyCluster', 'edit', $cluster['GalaxyCluster']['id'], $message, $this->response->getType());
|
||||
} else {
|
||||
$this->Flash->error($message);
|
||||
}
|
||||
} else {
|
||||
$message = __('Galaxy cluster saved');
|
||||
if ($this->request->is('ajax')) {
|
||||
return $this->RestResponse->saveSuccessResponse('GalaxyCluster', 'edit', $cluster['GalaxyCluster']['id'], $this->response->getType());
|
||||
} else if ($this->ParamHandler->isRest()) {
|
||||
$saved_cluster = $this->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $id, 'view', $throwErrors = true, $full = true);
|
||||
return $this->RestResponse->viewData($saved_cluster);
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(array('controller' => 'galaxy_clusters', 'action' => 'view', $this->GalaxyCluster->id));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->GalaxyCluster->data['GalaxyCluster']['elements'] = json_encode($this->GalaxyCluster->data['GalaxyElement']);
|
||||
$this->GalaxyCluster->data['GalaxyCluster']['elementsDict'] = $this->GalaxyCluster->data['GalaxyElement'];
|
||||
$this->GalaxyCluster->data['GalaxyCluster']['authors'] = !empty($this->GalaxyCluster->data['GalaxyCluster']['authors']) ? json_encode($this->GalaxyCluster->data['GalaxyCluster']['authors']) : '';
|
||||
$data = $this->GalaxyCluster->data;
|
||||
}
|
||||
$fieldDesc = array(
|
||||
'authors' => __('Valid JSON array or comma separated'),
|
||||
'elements' => __('Valid JSON array composed from Object of the form {key: keyname, value: actualValue}'),
|
||||
'distribution' => Hash::extract($AttributesTable->distributionDescriptions, '{n}.formdesc'),
|
||||
);
|
||||
$this->set('id', $cluster['GalaxyCluster']['id']);
|
||||
$this->set('cluster', $cluster);
|
||||
$this->set('fieldDesc', $fieldDesc);
|
||||
$this->set('distributionLevels', $distributionLevels);
|
||||
$this->set('initialDistribution', $initialDistribution);
|
||||
$this->set('sharingGroups', $sgs);
|
||||
$this->set('galaxy_id', $cluster['GalaxyCluster']['galaxy_id']);
|
||||
$this->set('clusterId', $id);
|
||||
$this->set('defaultCluster', $cluster['GalaxyCluster']['default']);
|
||||
$this->set('action', 'edit');
|
||||
$this->render('add');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $id ID or UUID of the cluster
|
||||
*/
|
||||
public function publish($id)
|
||||
{
|
||||
$cluster = $this->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $id, 'publish', $throwErrors = true, $full = false);
|
||||
if ($cluster['GalaxyCluster']['published']) {
|
||||
throw new MethodNotAllowedException(__('You can\'t publish a galaxy cluster that is already published'));
|
||||
}
|
||||
if ($cluster['GalaxyCluster']['default']) {
|
||||
throw new MethodNotAllowedException(__('Default galaxy cluster cannot be published'));
|
||||
}
|
||||
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$success = $this->GalaxyCluster->publishRouter($this->Auth->user(), $cluster, $passAlong = null);
|
||||
if (Configure::read('MISP.BackgroundJobs.enabled')) {
|
||||
$message = __('Publish job queued. Job ID: %s', $success);
|
||||
$this->Flash->success($message);
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->viewData(array('message' => $message), $this->response->getType());
|
||||
}
|
||||
} else {
|
||||
if (!$success) {
|
||||
$message = __('Could not publish galaxy cluster');
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('GalaxyCluster', 'publish', $cluster['GalaxyCluster']['id'], $message, $this->response->getType());
|
||||
} else {
|
||||
$this->Flash->error($message);
|
||||
}
|
||||
} else {
|
||||
$message = __('Galaxy cluster published');
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('GalaxyCluster', 'publish', $cluster['GalaxyCluster']['id'], $this->response->getType());
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->redirect(array('controller' => 'galaxy_clusters', 'action' => 'view', $cluster['GalaxyCluster']['id']));
|
||||
} else {
|
||||
$this->set('cluster', $cluster);
|
||||
$this->set('type', 'publish');
|
||||
$this->render('ajax/publishConfirmationForm');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $id ID or UUID of the cluster
|
||||
*/
|
||||
public function unpublish($id)
|
||||
{
|
||||
$cluster = $this->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $id, 'publish', $throwErrors = true, $full = false);
|
||||
if (!$cluster['GalaxyCluster']['published']) {
|
||||
throw new MethodNotAllowedException(__('You can\'t unpublish a galaxy cluster that is not published'));
|
||||
}
|
||||
if ($cluster['GalaxyCluster']['default']) {
|
||||
throw new MethodNotAllowedException(__('Default galaxy cluster cannot be unpublished'));
|
||||
}
|
||||
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$success = $this->GalaxyCluster->unpublish($cluster);
|
||||
if (!$success) {
|
||||
$message = __('Could not unpublish galaxy cluster');
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('GalaxyCluster', 'unpublish', $cluster['GalaxyCluster']['id'], $message, $this->response->getType());
|
||||
} else {
|
||||
$this->Flash->error($message);
|
||||
}
|
||||
} else {
|
||||
$message = __('Galaxy cluster unpublished');
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('GalaxyCluster', 'unpublish', $cluster['GalaxyCluster']['id'], $this->response->getType());
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
}
|
||||
}
|
||||
$this->redirect(array('controller' => 'galaxy_clusters', 'action' => 'view', $cluster['GalaxyCluster']['id']));
|
||||
} else {
|
||||
$this->set('cluster', $cluster);
|
||||
$this->set('type', 'unpublish');
|
||||
$this->render('ajax/publishConfirmationForm');
|
||||
}
|
||||
}
|
||||
|
||||
public function detach($target_id, $target_type, $tag_id)
|
||||
{
|
||||
if ($this->request->is('ajax') && $this->request->is('get')) {
|
||||
$this->set('url', Router::url());
|
||||
return $this->render('/Elements/emptyForm', false);
|
||||
}
|
||||
|
||||
$this->request->allowMethod(['post']);
|
||||
|
||||
try {
|
||||
$this->GalaxyCluster->Galaxy->detachClusterByTagId($this->Auth->user(), $target_id, $target_type, $tag_id);
|
||||
} catch (NotFoundException $e) {
|
||||
if (!$this->request->is('ajax')) {
|
||||
$this->Flash->error($e->getMessage());
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
$message = __('Galaxy successfully detached.');
|
||||
|
||||
if ($this->request->is('ajax')) {
|
||||
return $this->RestResponse->viewData(['saved' => true, 'check_publish' => true, 'success' => $message], 'json');
|
||||
}
|
||||
|
||||
$this->Flash->success($message);
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $id ID or UUID of the cluster
|
||||
*/
|
||||
public function delete($id, $hard = false)
|
||||
{
|
||||
$cluster = $this->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $id, 'delete', $throwErrors = true, $full = false);
|
||||
if ($this->request->is('post')) {
|
||||
if (!empty($this->request->getData()['hard'])) {
|
||||
$hard = true;
|
||||
}
|
||||
$result = $this->GalaxyCluster->deleteCluster($cluster['GalaxyCluster']['id'], $hard = $hard);
|
||||
$galaxyId = $cluster['GalaxyCluster']['galaxy_id'];
|
||||
if ($result) {
|
||||
$message = __(
|
||||
'Galaxy cluster successfuly %s deleted%s.',
|
||||
$hard ? __('hard') : __('soft'),
|
||||
$hard ? __(' and added to the block list') : ''
|
||||
);
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('GalaxyCluster', 'delete', $cluster['GalaxyCluster']['id'], $this->response->getType(), $message);
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(array('controller' => 'galaxies', 'action' => 'view', $galaxyId));
|
||||
}
|
||||
} else {
|
||||
$message = __('Galaxy cluster could not be %s deleted.', $hard ? __('hard') : __('soft'));
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('GalaxyCluster', 'delete', $cluster['GalaxyCluster']['id'], $message, $this->response->getType(), $message);
|
||||
} else {
|
||||
$this->Flash->error($message);
|
||||
$this->redirect(array('controller' => 'galaxies', 'action' => 'view', $galaxyId));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($this->request->is('ajax')) {
|
||||
$this->set('id', $cluster['GalaxyCluster']['id']);
|
||||
$this->set('cluster', $cluster['GalaxyCluster']);
|
||||
$this->render('ajax/galaxy_cluster_delete_confirmation');
|
||||
} else {
|
||||
throw new MethodNotAllowedException(__('This function can only be reached via AJAX.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function restore($id)
|
||||
{
|
||||
$cluster = $this->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $id, 'delete', $throwErrors = true, $full = false);
|
||||
if ($this->request->is('post')) {
|
||||
$result = $this->GalaxyCluster->restoreCluster($cluster['GalaxyCluster']['id']);
|
||||
$galaxyId = $cluster['GalaxyCluster']['galaxy_id'];
|
||||
if ($result) {
|
||||
$message = __('Galaxy cluster successfuly restored.');
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('GalaxyCluster', 'restore', $cluster['GalaxyCluster']['id'], $this->response->getType());
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(array('controller' => 'galaxies', 'action' => 'view', $galaxyId));
|
||||
}
|
||||
} else {
|
||||
$message = __('Galaxy cluster could not be %s restored.');
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('GalaxyCluster', 'restore', $cluster['GalaxyCluster']['id'], $message, $this->response->getType());
|
||||
} else {
|
||||
$this->Flash->error($message);
|
||||
$this->redirect(array('controller' => 'galaxies', 'action' => 'view', $galaxyId));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new MethodNotAllowedException(__('This function can only be reached via POST.'));
|
||||
}
|
||||
}
|
||||
|
||||
public function viewCyCatRelations($id)
|
||||
{
|
||||
$cluster = $this->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $id, 'view', true, false);
|
||||
$CyCatRelations = $this->GalaxyCluster->getCyCatRelations($cluster);
|
||||
$this->set('cluster', $cluster);
|
||||
$this->set('CyCatRelations', $CyCatRelations);
|
||||
$this->render('cluster_cycatrelations');
|
||||
}
|
||||
|
||||
public function viewGalaxyMatrix($id)
|
||||
{
|
||||
if (!$this->request->is('ajax')) {
|
||||
throw new MethodNotAllowedException('This function can only be reached via AJAX.');
|
||||
}
|
||||
|
||||
$cluster = $this->GalaxyCluster->fetchGalaxyClusters($this->Auth->user(), array(
|
||||
'conditions' => array('id' => $id)
|
||||
), $full = false);
|
||||
if (empty($cluster)) {
|
||||
throw new MethodNotAllowedException("Invalid Galaxy Cluster.");
|
||||
}
|
||||
$cluster = $cluster[0];
|
||||
$EventsTable = $this->fetchTable('Events');
|
||||
$mitreAttackGalaxyId = $this->GalaxyCluster->Galaxy->getMitreAttackGalaxyId();
|
||||
if ($mitreAttackGalaxyId == 0) { // Mitre Att&ck galaxy not found
|
||||
return new Response(array('body' => '', 'status' => 200, 'type' => 'text'));
|
||||
}
|
||||
$attackPatternTagNames = $this->GalaxyCluster->find('list', array(
|
||||
'conditions' => array('galaxy_id' => $mitreAttackGalaxyId),
|
||||
'fields' => array('tag_name')
|
||||
));
|
||||
|
||||
$cluster = $cluster['GalaxyCluster'];
|
||||
$tag_name = $cluster['tag_name'];
|
||||
|
||||
// fetch all event ids having the requested cluster
|
||||
$eventIds = $EventsTable->EventTag->find('list', array(
|
||||
'contain' => array('Tag'),
|
||||
'conditions' => array(
|
||||
'Tag.name' => $tag_name
|
||||
),
|
||||
'fields' => array('event_id'),
|
||||
'recursive' => -1
|
||||
));
|
||||
|
||||
// fetch all attribute ids having the requested cluster
|
||||
$attributes = $EventsTable->Attribute->AttributeTag->find('all', array(
|
||||
'contain' => array('Tag'),
|
||||
'conditions' => array(
|
||||
'Tag.name' => $tag_name
|
||||
),
|
||||
'fields' => array('attribute_id', 'event_id'),
|
||||
'recursive' => -1
|
||||
));
|
||||
$attributeIds = array();
|
||||
$additional_event_ids = array();
|
||||
foreach ($attributes as $attribute) {
|
||||
$attributeIds[] = $attribute['AttributeTag']['attribute_id'];
|
||||
$additional_event_ids[$attribute['AttributeTag']['event_id']] = $attribute['AttributeTag']['event_id'];
|
||||
}
|
||||
$additional_event_ids = array_keys($additional_event_ids);
|
||||
$eventIds = array_merge($eventIds, $additional_event_ids);
|
||||
unset($attributes);
|
||||
unset($additional_event_ids);
|
||||
|
||||
// fetch all related tags belonging to attack pattern
|
||||
$eventTags = $EventsTable->EventTag->find('all', array(
|
||||
'contain' => array('Tag'),
|
||||
'conditions' => array(
|
||||
'event_id' => $eventIds,
|
||||
'Tag.name' => $attackPatternTagNames
|
||||
),
|
||||
'fields' => array('Tag.name, COUNT(DISTINCT event_id) as tag_count'),
|
||||
'recursive' => -1,
|
||||
'group' => array('Tag.name', 'Tag.id')
|
||||
));
|
||||
|
||||
// fetch all related tags belonging to attack pattern or belonging to an event having this cluster
|
||||
$attributeTags = $EventsTable->Attribute->AttributeTag->find('all', array(
|
||||
'contain' => array('Tag'),
|
||||
'conditions' => array(
|
||||
'OR' => array(
|
||||
'event_id' => $eventIds,
|
||||
'attribute_id' => $attributeIds
|
||||
),
|
||||
'Tag.name' => $attackPatternTagNames
|
||||
),
|
||||
'fields' => array('Tag.name, COUNT(DISTINCT event_id) as tag_count'),
|
||||
'recursive' => -1,
|
||||
'group' => array('Tag.name', 'Tag.id')
|
||||
));
|
||||
|
||||
$scores = array();
|
||||
foreach ($attributeTags as $tag) {
|
||||
$tagName = $tag['Tag']['name'];
|
||||
$scores[$tagName] = intval($tag[0]['tag_count']);
|
||||
}
|
||||
foreach ($eventTags as $tag) {
|
||||
$tagName = $tag['Tag']['name'];
|
||||
if (isset($scores[$tagName])) {
|
||||
$scores[$tagName] = $scores[$tagName] + intval($tag[0]['tag_count']);
|
||||
} else {
|
||||
$scores[$tagName] = intval($tag[0]['tag_count']);
|
||||
}
|
||||
}
|
||||
|
||||
$maxScore = count($scores) > 0 ? max(array_values($scores)) : 0;
|
||||
$matrixData = $this->GalaxyCluster->Galaxy->getMatrix($mitreAttackGalaxyId, $scores);
|
||||
$tabs = $matrixData['tabs'];
|
||||
$matrixTags = $matrixData['matrixTags'];
|
||||
$killChainOrders = $matrixData['killChain'];
|
||||
$instanceUUID = $matrixData['instance-uuid'];
|
||||
|
||||
$gradientTool = new ColourGradientTool();
|
||||
$colours = $gradientTool->createGradientFromValues($scores);
|
||||
$this->set('target_type', 'attribute');
|
||||
$this->set('columnOrders', $killChainOrders);
|
||||
$this->set('tabs', $tabs);
|
||||
$this->set('scores', $scores);
|
||||
$this->set('maxScore', $maxScore);
|
||||
if (!empty($colours)) {
|
||||
$this->set('colours', $colours['mapping']);
|
||||
$this->set('interpolation', $colours['interpolation']);
|
||||
}
|
||||
$this->set('pickingMode', false);
|
||||
$this->set('defaultTabName', 'mitre-attack');
|
||||
$this->set('removeTrailling', 2);
|
||||
|
||||
$this->render('cluster_matrix');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $id ID or UUID of the cluster
|
||||
*/
|
||||
public function updateCluster($id)
|
||||
{
|
||||
$cluster = $this->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $id, 'edit', $throwErrors = true, $full = true);
|
||||
if ($cluster['GalaxyCluster']['default']) {
|
||||
throw new MethodNotAllowedException(__('Default galaxy cluster cannot be updated'));
|
||||
}
|
||||
if (empty($cluster['GalaxyCluster']['extends_uuid'])) {
|
||||
throw new NotFoundException(__('Galaxy cluster is not a fork'));
|
||||
}
|
||||
$conditions = array('conditions' => array('GalaxyCluster.uuid' => $cluster['GalaxyCluster']['extends_uuid']));
|
||||
$parentCluster = $this->GalaxyCluster->fetchGalaxyClusters($this->Auth->user(), $conditions, true);
|
||||
if (empty($parentCluster)) {
|
||||
throw new NotFoundException('Invalid parent galaxy cluster');
|
||||
}
|
||||
$parentCluster = $parentCluster[0];
|
||||
$forkVersion = $cluster['GalaxyCluster']['extends_version'];
|
||||
$parentVersion = $parentCluster['GalaxyCluster']['version'];
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$elements = array();
|
||||
if (!empty($this->request->getData()['GalaxyCluster'])) {
|
||||
foreach ($this->request->getData()['GalaxyCluster'] as $k => $jElement) {
|
||||
$element = json_decode($jElement, true);
|
||||
if (!is_null($element) && $element != 0) {
|
||||
$elements[] = array(
|
||||
'key' => $element['key'],
|
||||
'value' => $element['value'],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
$cluster['GalaxyCluster']['GalaxyElement'] = $elements;
|
||||
$cluster['GalaxyCluster']['extends_version'] = $parentVersion;
|
||||
$cluster['GalaxyCluster']['published'] = false;
|
||||
$errors = $this->GalaxyCluster->editCluster($this->Auth->user(), $cluster, $fieldList = array('extends_version', 'published'), $deleteOldElements = false);
|
||||
if (!empty($errors)) {
|
||||
$flashErrorMessage = implode(', ', $errors);
|
||||
$this->Flash->error($flashErrorMessage);
|
||||
} else {
|
||||
$this->Flash->success(__('Cluster updated to the newer version'));
|
||||
$this->redirect(array('controller' => 'galaxy_clusters', 'action' => 'view', $id));
|
||||
}
|
||||
}
|
||||
$missingElements = array();
|
||||
foreach ($parentCluster['GalaxyCluster']['GalaxyElement'] as $k => $parentElement) {
|
||||
$found = false;
|
||||
foreach ($cluster['GalaxyCluster']['GalaxyElement'] as $k => $clusterElement) {
|
||||
if (
|
||||
$parentElement['key'] == $clusterElement['key'] &&
|
||||
$parentElement['value'] == $clusterElement['value']
|
||||
) {
|
||||
$found = true;
|
||||
break; // element exists in parent
|
||||
}
|
||||
}
|
||||
if (!$found) {
|
||||
$missingElements[] = $parentElement;
|
||||
}
|
||||
}
|
||||
$this->set('missingElements', $missingElements);
|
||||
$this->set('parentElements', $parentCluster['GalaxyCluster']['GalaxyElement']);
|
||||
$this->set('clusterElements', $cluster['GalaxyCluster']['GalaxyElement']);
|
||||
$this->set('forkVersion', $forkVersion);
|
||||
$this->set('parentVersion', $parentVersion);
|
||||
$this->set('newVersionAvailable', $parentVersion > $forkVersion);
|
||||
$this->set('id', $cluster['GalaxyCluster']['id']);
|
||||
$this->set('galaxy_id', $cluster['GalaxyCluster']['galaxy_id']);
|
||||
$this->set('defaultCluster', $cluster['GalaxyCluster']['default']);
|
||||
$this->set('cluster', $cluster);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $id ID or UUID of the cluster
|
||||
*/
|
||||
public function viewRelations($id, $includeInbound = 1)
|
||||
{
|
||||
if (!$this->request->is('ajax')) {
|
||||
throw new MethodNotAllowedException('This function can only be reached via AJAX.');
|
||||
}
|
||||
$cluster = $this->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $id, 'view', true, true);
|
||||
$existingRelations = $this->GalaxyCluster->GalaxyClusterRelation->getExistingRelationships();
|
||||
$cluster = $this->GalaxyCluster->attachClusterToRelations($this->Auth->user(), $cluster, $includeInbound);
|
||||
|
||||
$grapher = new ClusterRelationsTreeTool();
|
||||
$grapher->construct($this->Auth->user(), $this->GalaxyCluster);
|
||||
$tree = $grapher->getTree($cluster);
|
||||
|
||||
$this->set('existingRelations', $existingRelations);
|
||||
$this->set('cluster', $cluster);
|
||||
$relations = $cluster['GalaxyCluster']['GalaxyClusterRelation'];
|
||||
if ($includeInbound && !empty($cluster['GalaxyCluster']['TargetingClusterRelation'])) {
|
||||
foreach ($cluster['GalaxyCluster']['TargetingClusterRelation'] as $targetingCluster) {
|
||||
$targetingCluster['isInbound'] = true;
|
||||
$relations[] = $targetingCluster;
|
||||
}
|
||||
}
|
||||
$this->set('passedArgs', json_encode([]));
|
||||
$this->set('relations', $relations);
|
||||
$this->set('tree', $tree);
|
||||
$this->set('includeInbound', $includeInbound);
|
||||
$AttributesTable = $this->fetchTable('Attributes');
|
||||
$distributionLevels = $AttributesTable->distributionLevels;
|
||||
unset($distributionLevels[4]);
|
||||
unset($distributionLevels[5]);
|
||||
$this->set('distributionLevels', $distributionLevels);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $id ID or UUID of the cluster
|
||||
*/
|
||||
public function viewRelationTree($id, $includeInbound = 1)
|
||||
{
|
||||
$cluster = $this->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $id, 'view', $throwErrors = true, $full = true);
|
||||
$cluster = $this->GalaxyCluster->attachClusterToRelations($this->Auth->user(), $cluster, $includeInbound);
|
||||
$grapher = new ClusterRelationsTreeTool();
|
||||
$grapher->construct($this->Auth->user(), $this->GalaxyCluster);
|
||||
$tree = $grapher->getTree($cluster);
|
||||
$this->set('tree', $tree);
|
||||
$this->set('cluster', $cluster);
|
||||
$this->set('includeInbound', $includeInbound);
|
||||
$this->set('testtest', 'testtest');
|
||||
$this->render('/Elements/GalaxyClusters/view_relation_tree');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Cake\Http\Exception\MethodNotAllowedException;
|
||||
use Cake\Utility\Hash;
|
||||
use Exception;
|
||||
|
||||
class GalaxyElementsController extends AppController
|
||||
{
|
||||
public $components = array('Session', 'RequestHandler');
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 20,
|
||||
'recursive' => -1,
|
||||
'order' => array(
|
||||
'GalaxyElement.key' => 'ASC'
|
||||
)
|
||||
);
|
||||
|
||||
public function index($clusterId)
|
||||
{
|
||||
$user = $this->closeSession();
|
||||
$filters = $this->IndexFilter->harvestParameters(array('context', 'searchall'));
|
||||
$aclConditions = $this->GalaxyElement->buildClusterConditions($user, $clusterId);
|
||||
if (empty($filters['context'])) {
|
||||
$filters['context'] = 'all';
|
||||
}
|
||||
$searchConditions = array();
|
||||
if (empty($filters['searchall'])) {
|
||||
$filters['searchall'] = '';
|
||||
}
|
||||
if (strlen($filters['searchall']) > 0) {
|
||||
$searchall = '%' . strtolower($filters['searchall']) . '%';
|
||||
$searchConditions = array(
|
||||
'OR' => array(
|
||||
'LOWER(GalaxyElement.key) LIKE' => $searchall,
|
||||
'LOWER(GalaxyElement.value) LIKE' => $searchall,
|
||||
),
|
||||
);
|
||||
}
|
||||
$this->paginate['conditions'] = ['AND' => [$aclConditions, $searchConditions]];
|
||||
$this->paginate['contain'] = ['GalaxyCluster' => ['fields' => ['id', 'distribution', 'org_id']]];
|
||||
$elements = $this->paginate();
|
||||
$this->set('elements', $elements);
|
||||
$this->set('clusterId', $clusterId);
|
||||
$this->set('context', $filters['context']);
|
||||
$this->set('passedArgs', json_encode([
|
||||
'context' => $filters['context'],
|
||||
'searchall' => isset($filters['searchall']) ? $filters['searchall'] : ''
|
||||
]));
|
||||
$cluster = $this->GalaxyElement->GalaxyCluster->fetchIfAuthorized($user, $clusterId, array('edit', 'delete'), false, false);
|
||||
$canModify = !empty($cluster['authorized']);
|
||||
$this->set('canModify', $canModify);
|
||||
if ($filters['context'] === 'JSONView') {
|
||||
$expanded = $this->GalaxyElement->getExpandedJSONFromElements($elements);
|
||||
$this->set('JSONElements', $expanded);
|
||||
}
|
||||
$this->layout = false;
|
||||
$this->render('ajax/index');
|
||||
}
|
||||
|
||||
public function delete($elementId)
|
||||
{
|
||||
$element = $this->GalaxyElement->find('first', array('conditions' => array('GalaxyElement.id' => $elementId)));
|
||||
if (empty($element)) {
|
||||
throw new Exception(__('Element not found'));
|
||||
}
|
||||
$this->set('element', $element);
|
||||
$clusterId = $element['GalaxyElement']['galaxy_cluster_id'];
|
||||
$cluster = $this->GalaxyElement->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $clusterId, array('edit'), true, false);
|
||||
if ($this->request->is('post')) {
|
||||
$deleteResult = $this->GalaxyElement->delete($elementId);
|
||||
if ($deleteResult) {
|
||||
$this->GalaxyElement->GalaxyCluster->editCluster($this->Auth->user(), $cluster, [], false);
|
||||
$message = __('Galaxy element %s deleted', $elementId);
|
||||
$this->Flash->success($message);
|
||||
} else {
|
||||
$message = __('Could not delete galaxy element');
|
||||
$this->Flash->error($message);
|
||||
}
|
||||
$this->redirect($this->referer());
|
||||
} else {
|
||||
if (!$this->request->is('ajax')) {
|
||||
throw new MethodNotAllowedException(__('This function can only be reached via AJAX.'));
|
||||
} else {
|
||||
$this->layout = false;
|
||||
$this->set('elementId', $elementId);
|
||||
$this->render('ajax/delete');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function flattenJson($clusterId)
|
||||
{
|
||||
$cluster = $this->GalaxyElement->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $clusterId, array('edit'), true, false);
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$json = $this->_jsonDecode($this->request->getData()['GalaxyElement']['jsonData']);
|
||||
$flattened = Hash::flatten($json);
|
||||
$newElements = [];
|
||||
foreach ($flattened as $k => $v) {
|
||||
$newElements[] = ['key' => $k, 'value' => $v];
|
||||
}
|
||||
$cluster['GalaxyCluster']['GalaxyElement'] = $newElements;
|
||||
$errors = $this->GalaxyElement->GalaxyCluster->editCluster($this->Auth->user(), $cluster, [], false);
|
||||
if (empty($errors)) {
|
||||
return $this->RestResponse->saveSuccessResponse('GalaxyElement', 'flattenJson', $clusterId, false);
|
||||
} else {
|
||||
$message = implode(', ', $errors);
|
||||
return $this->RestResponse->saveFailResponse('GalaxyElement', 'flattenJson', $clusterId, $message, false);
|
||||
}
|
||||
}
|
||||
$this->set('clusterId', $clusterId);
|
||||
if ($this->request->is('ajax')) {
|
||||
$this->layout = false;
|
||||
$this->render('ajax/flattenJson');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace App\Lib\Tools;
|
||||
|
||||
class ClusterRelationsTreeTool
|
||||
{
|
||||
private $GalaxyCluster = false;
|
||||
private $user = false;
|
||||
private $lookup = array();
|
||||
|
||||
public function construct($user, $GalaxyCluster)
|
||||
{
|
||||
$this->GalaxyCluster = $GalaxyCluster;
|
||||
$this->user = $user;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* getTree Creates the relation tree with referencing clusters (left) and referenced clusters (right) for the given cluster
|
||||
*
|
||||
* @param array $cluster
|
||||
* @return array
|
||||
*/
|
||||
public function getTree(array $cluster)
|
||||
{
|
||||
$relationCache = []; // Needed as some default clusters have the same UUID
|
||||
$treeRight = array(array(
|
||||
'GalaxyCluster' => $cluster['GalaxyCluster'],
|
||||
'children' => array()
|
||||
));
|
||||
// add relation info between the two clusters
|
||||
foreach ($cluster['GalaxyCluster']['GalaxyClusterRelation'] as $relation) {
|
||||
if (empty($relation['GalaxyCluster'])) { // unkown cluster, create placeholder
|
||||
$relation['GalaxyCluster'] = array(
|
||||
'uuid' => $relation['referenced_galaxy_cluster_uuid'],
|
||||
'type' => 'unkown galaxy',
|
||||
'value' => $relation['referenced_galaxy_cluster_uuid'],
|
||||
);
|
||||
}
|
||||
$tmp = array(
|
||||
'Relation' => array_diff_key($relation, array_flip(array('GalaxyCluster'))),
|
||||
'children' => array(
|
||||
array('GalaxyCluster' => $relation['GalaxyCluster']),
|
||||
)
|
||||
);
|
||||
$treeRight[0]['children'][] = $tmp;
|
||||
}
|
||||
|
||||
$treeLeft = array(array(
|
||||
'GalaxyCluster' => $cluster['GalaxyCluster'],
|
||||
'children' => array()
|
||||
));
|
||||
if (!empty($cluster['GalaxyCluster']['TargetingClusterRelation'])) {
|
||||
foreach ($cluster['GalaxyCluster']['TargetingClusterRelation'] as $relation) {
|
||||
if (isset($relation['GalaxyCluster']) && !isset($relationCache[$relation['GalaxyCluster']['uuid']])) { // not set if cluster is unkown
|
||||
$relationCache[$relation['GalaxyCluster']['uuid']] = true;
|
||||
$tmp = array(
|
||||
'Relation' => array_diff_key($relation, array_flip(array('GalaxyCluster'))),
|
||||
'children' => array(
|
||||
array('GalaxyCluster' => $relation['GalaxyCluster']),
|
||||
)
|
||||
);
|
||||
$treeLeft[0]['children'][] = $tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tree = array(
|
||||
'right' => $treeRight,
|
||||
'left' => $treeLeft,
|
||||
);
|
||||
return $tree;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
<?php
|
||||
|
||||
namespace App\Lib\Tools;
|
||||
|
||||
class ColourGradientTool
|
||||
{
|
||||
|
||||
// source: https://graphicdesign.stackexchange.com/a/83867
|
||||
// $values of the form array(item1: val1, item2: val2, ...)
|
||||
public function createGradientFromValues($items)
|
||||
{
|
||||
$starColor = '#0000FF';
|
||||
$endColor = '#FF0000';
|
||||
|
||||
if (count($items) == 0) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$vals = array_values($items);
|
||||
$maxDec = max($vals);
|
||||
$minDec = min($vals);
|
||||
|
||||
$interpolation = $this->interpolateColors($starColor, $endColor, $maxDec + 1, true);
|
||||
$coloursMapping = array();
|
||||
foreach ($items as $name => $val) {
|
||||
$color = $interpolation[$val];
|
||||
$coloursMapping[$name] = '#' . str_pad(dechex($color[0]), 2, '0', STR_PAD_LEFT) . str_pad(dechex($color[1]), 2, '0', STR_PAD_LEFT) . str_pad(dechex($color[2]), 2, '0', STR_PAD_LEFT);
|
||||
}
|
||||
return array('mapping' => $coloursMapping, 'interpolation' => $interpolation);
|
||||
}
|
||||
|
||||
private function hue2rgb($p, $q, $t)
|
||||
{
|
||||
if ($t < 0) $t += 1;
|
||||
if ($t > 1) $t -= 1;
|
||||
if ($t < 1 / 6) return $p + ($q - $p) * 6 * $t;
|
||||
if ($t < 1 / 2) return $q;
|
||||
if ($t < 2 / 3) return $p + ($q - $p) * (2 / 3 - $t) * 6;
|
||||
return $p;
|
||||
}
|
||||
|
||||
private function hsl2rgb($color)
|
||||
{
|
||||
$l = $color[2];
|
||||
if ($color[1] == 0) {
|
||||
$l = round($l * 255);
|
||||
return array($l, $l, $l);
|
||||
} else {
|
||||
$s = $color[1];
|
||||
$q = ($l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s);
|
||||
$p = 2 * $l - $q;
|
||||
$r = $this->hue2rgb($p, $q, $color[0] + 1 / 3);
|
||||
$g = $this->hue2rgb($p, $q, $color[0]);
|
||||
$b = $this->hue2rgb($p, $q, $color[0] - 1 / 3);
|
||||
return array(round($r * 255), round($g * 255), round($b * 255));
|
||||
}
|
||||
}
|
||||
|
||||
private function rgb2hsl($color)
|
||||
{
|
||||
$r = $color[0] / 255;
|
||||
$g = $color[1] / 255;
|
||||
$b = $color[2] / 255;
|
||||
$arrRGB = array($r, $g, $b);
|
||||
|
||||
$max = max($arrRGB);
|
||||
$min = min($arrRGB);
|
||||
$h = ($max - $min) / 2;
|
||||
$s = $h;
|
||||
$l = $h;
|
||||
|
||||
if ($max == $min) {
|
||||
$s = 0; // achromatic
|
||||
$h = 0;
|
||||
} else {
|
||||
$d = $max - $min;
|
||||
$s = ($l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min));
|
||||
if ($max == $r) {
|
||||
$h = ($g - $b) / $d + ($g < $b ? 6 : 0);
|
||||
} elseif ($max == $g) {
|
||||
$h = ($b - $r) / $d + 2;
|
||||
} elseif ($max == $b) {
|
||||
$h = ($r - $g) / $d + 4;
|
||||
}
|
||||
$h = $h / 6;
|
||||
return array($h, $s, $l);
|
||||
}
|
||||
}
|
||||
|
||||
private function interpolateColor($color1, $color2, $factor, $useHSL = false)
|
||||
{
|
||||
if ($useHSL) {
|
||||
$hsl1 = $this->rgb2hsl($color1);
|
||||
$hsl2 = $this->rgb2hsl($color2);
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$hsl1[$i] += $factor * ($hsl2[$i] - $hsl1[$i]);
|
||||
}
|
||||
$result = $this->hsl2rgb($hsl1);
|
||||
} else {
|
||||
$result = $color1;
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$result[$i] = round($result[$i] + $factor * ($color2[$i] - $color1[$i]));
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function interpolateColors($hexColor1, $hexColor2, $steps, $useHSL = false)
|
||||
{
|
||||
$stepFactor = 1 / ($steps - 1);
|
||||
$interpolatedColorArray = array();
|
||||
$color1 = sscanf($hexColor1, "#%02x%02x%02x");
|
||||
$color2 = sscanf($hexColor2, "#%02x%02x%02x");
|
||||
|
||||
for ($i = 0; $i < $steps; $i++) {
|
||||
$interpolatedColorArray[$i] = $this->interpolateColor($color1, $color2, $stepFactor * $i, $useHSL);
|
||||
}
|
||||
|
||||
return $interpolatedColorArray;
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ class NamedParamsParserMiddleware implements MiddlewareInterface
|
|||
|
||||
public const NAMED_PARAMS = [
|
||||
'events.index' => ['limit', 'order', 'page', 'sort', 'direction', 'fields', 'search'],
|
||||
'galaxies.index' => ['limit', 'order', 'page', 'sort', 'direction', 'value'],
|
||||
];
|
||||
|
||||
public function process(
|
||||
|
@ -28,7 +29,7 @@ class NamedParamsParserMiddleware implements MiddlewareInterface
|
|||
|
||||
$namedConfig = array_merge(Configure::read('NamedParams', []), self::NAMED_PARAMS);
|
||||
|
||||
$action = $request->getParam('controller') . '.' . $request->getParam('action');
|
||||
$action = strtolower($request->getParam('controller') . '.' . $request->getParam('action'));
|
||||
|
||||
if (!array_key_exists($action, $namedConfig)) {
|
||||
return $handler->handle($request);
|
||||
|
|
|
@ -1,18 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Behavior;
|
||||
|
||||
use App\Model\Entity\AppModel;
|
||||
use ArrayObject;
|
||||
use Cake\Datasource\EntityInterface;
|
||||
use Cake\Event\EventInterface;
|
||||
use Cake\ORM\Behavior;
|
||||
use Cake\ORM\Entity;
|
||||
use Cake\ORM\Query;
|
||||
use Cake\Utility\Text;
|
||||
use Cake\Utility\Security;
|
||||
use \Cake\Http\Session;
|
||||
use Cake\Core\Configure;
|
||||
use Cake\ORM\TableRegistry;
|
||||
use App\Model\Table\AuditLogTable;
|
||||
|
||||
class AuditLogBehavior extends Behavior
|
||||
{
|
||||
|
@ -48,9 +43,13 @@ class AuditLogBehavior extends Behavior
|
|||
{
|
||||
$fields = $entity->extract($entity->getVisible(), true);
|
||||
$skipFields = $this->skipFields;
|
||||
$fieldsToFetch = array_filter($fields, function($key) use ($skipFields) {
|
||||
$fieldsToFetch = array_filter(
|
||||
$fields,
|
||||
function ($key) use ($skipFields) {
|
||||
return strpos($key, '_') !== 0 && !isset($skipFields[$key]);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
},
|
||||
ARRAY_FILTER_USE_KEY
|
||||
);
|
||||
// Do not fetch old version when just few fields will be fetched
|
||||
$fieldToFetch = [];
|
||||
if (!empty($options['fieldList'])) {
|
||||
|
@ -59,7 +58,7 @@ class AuditLogBehavior extends Behavior
|
|||
$fieldToFetch[] = $field;
|
||||
}
|
||||
}
|
||||
if (empty($fieldToFetch)) {
|
||||
if (empty($fieldToFetch)) {
|
||||
$this->old = null;
|
||||
return true;
|
||||
}
|
||||
|
@ -81,14 +80,14 @@ class AuditLogBehavior extends Behavior
|
|||
}
|
||||
|
||||
if ($entity->isNew()) {
|
||||
$action = $entity->getConstant('ACTION_ADD');
|
||||
$action = AppModel::ACTION_ADD;
|
||||
} else {
|
||||
$action = $entity->getConstant('ACTION_EDIT');
|
||||
$action = AppModel::ACTION_EDIT;
|
||||
if (isset($entity['deleted'])) {
|
||||
if ($entity['deleted']) {
|
||||
$action = $entity->getConstant('ACTION_SOFT_DELETE');
|
||||
$action = AppModel::ACTION_SOFT_DELETE;
|
||||
} else if (!$entity['deleted'] && $this->old['deleted']) {
|
||||
$action = $entity->getConstant('ACTION_UNDELETE');
|
||||
$action = AppModel::ACTION_UNDELETE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -105,13 +104,15 @@ class AuditLogBehavior extends Behavior
|
|||
} else if ($this->old[$modelTitleField]) {
|
||||
$modelTitle = $this->old[$modelTitleField];
|
||||
}
|
||||
$this->auditLogs()->insert([
|
||||
'request_action' => $action,
|
||||
'model' => $entity->getSource(),
|
||||
'model_id' => $id,
|
||||
'model_title' => $modelTitle,
|
||||
'changed' => $changedFields
|
||||
]);
|
||||
$this->auditLogs()->insert(
|
||||
[
|
||||
'request_action' => $action,
|
||||
'model' => $entity->getSource(),
|
||||
'model_id' => $id,
|
||||
'model_title' => $modelTitle,
|
||||
'changed' => $changedFields
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function beforeDelete(EventInterface $event, EntityInterface $entity, ArrayObject $options)
|
||||
|
@ -129,13 +130,15 @@ class AuditLogBehavior extends Behavior
|
|||
$modelTitle = $entity[$modelTitleField];
|
||||
}
|
||||
|
||||
$this->auditLogs()->insert([
|
||||
'request_action' => $entity->getConstant('ACTION_DELETE'),
|
||||
'model' => $entity->getSource(),
|
||||
'model_id' => $this->old->id,
|
||||
'model_title' => $modelTitle,
|
||||
'changed' => $this->changedFields($entity)
|
||||
]);
|
||||
$this->auditLogs()->insert(
|
||||
[
|
||||
'request_action' => AppModel::ACTION_DELETE,
|
||||
'model' => $entity->getSource(),
|
||||
'model_id' => $this->old->id,
|
||||
'model_title' => $modelTitle,
|
||||
'changed' => $this->changedFields($entity)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -208,6 +211,5 @@ class AuditLogBehavior extends Behavior
|
|||
|
||||
public function log()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ class AppModel extends Entity
|
|||
const BROTLI_HEADER = "\xce\xb2\xcf\x81";
|
||||
const BROTLI_MIN_LENGTH = 200;
|
||||
|
||||
const ACTION_ADD = 'add',
|
||||
public const ACTION_ADD = 'add',
|
||||
ACTION_EDIT = 'edit',
|
||||
ACTION_SOFT_DELETE = 'soft_delete',
|
||||
ACTION_DELETE = 'delete',
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Entity;
|
||||
|
||||
use App\Model\Entity\AppModel;
|
||||
|
||||
class Distribution extends AppModel
|
||||
{
|
||||
|
||||
public const ORGANISATION_ONLY = '0';
|
||||
public const COMMUNITY_ONLY = '1';
|
||||
public const CONNECTED_COMMUNITIES = '2';
|
||||
public const ALL_COMMUNITIES = '3';
|
||||
public const SHARING_GROUP = '4';
|
||||
|
||||
public const DESCRIPTION = [
|
||||
self::ORGANISATION_ONLY => 'Your organisation only',
|
||||
self::COMMUNITY_ONLY => 'This community only',
|
||||
self::CONNECTED_COMMUNITIES => 'Connected communities',
|
||||
self::ALL_COMMUNITIES => 'All communities',
|
||||
self::SHARING_GROUP => 'Sharing group',
|
||||
];
|
||||
|
||||
public const ALL = [
|
||||
self::ORGANISATION_ONLY,
|
||||
self::COMMUNITY_ONLY,
|
||||
self::CONNECTED_COMMUNITIES,
|
||||
self::ALL_COMMUNITIES,
|
||||
self::SHARING_GROUP,
|
||||
];
|
||||
}
|
|
@ -0,0 +1,919 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Table;
|
||||
|
||||
use App\Model\Table\AppTable;
|
||||
use Cake\ORM\Locator\LocatorAwareTrait;
|
||||
use ArrayObject;
|
||||
use Cake\Datasource\EntityInterface;
|
||||
use Cake\Event\EventInterface;
|
||||
use Cake\Core\Configure;
|
||||
use App\Lib\Tools\FileAccessTool;
|
||||
use Cake\Http\Exception\MethodNotAllowedException;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use Cake\Http\Exception\NotFoundException;
|
||||
use Exception;
|
||||
use GlobIterator;
|
||||
use Cake\Utility\Inflector;
|
||||
use Cake\ORM\Query;
|
||||
|
||||
/**
|
||||
* @property GalaxyClusters $GalaxyCluster
|
||||
* @property Galaxy $Galaxy
|
||||
*/
|
||||
class GalaxiesTable extends AppTable
|
||||
{
|
||||
use LocatorAwareTrait;
|
||||
|
||||
public $useTable = 'galaxies';
|
||||
|
||||
public $recursive = -1;
|
||||
|
||||
public function initialize(array $config): void
|
||||
{
|
||||
parent::initialize($config);
|
||||
$this->addBehavior('AuditLog');
|
||||
$this->addBehavior(
|
||||
'JsonFields',
|
||||
[
|
||||
'fields' => ['kill_chain_order'],
|
||||
]
|
||||
);
|
||||
|
||||
$this->hasMany(
|
||||
'GalaxyClusters',
|
||||
[
|
||||
'dependent' => true,
|
||||
'propertyName' => 'GalaxyCluster'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
function beforeDelete(EventInterface $event, EntityInterface $entity, ArrayObject $options)
|
||||
{
|
||||
$this->GalaxyClusters->deleteAll(array('GalaxyCluster.galaxy_id' => $entity->id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
* @return array Galaxy type => Galaxy ID
|
||||
* @throws Exception
|
||||
*/
|
||||
private function __load_galaxies($force = false)
|
||||
{
|
||||
$files = new GlobIterator(APP . '../libraries' . DS . 'misp-galaxy' . DS . 'galaxies' . DS . '*.json');
|
||||
$galaxies = array();
|
||||
foreach ($files as $file) {
|
||||
$galaxies[] = FileAccessTool::readJsonFromFile($file->getPathname());
|
||||
}
|
||||
$existingGalaxies = $this->find('all', array(
|
||||
'fields' => array('uuid', 'version', 'id', 'icon'),
|
||||
'recursive' => -1
|
||||
))->disableHydration()->toArray();
|
||||
$existingGalaxies = array_column(array_column($existingGalaxies, 'Galaxy'), null, 'uuid');
|
||||
foreach ($galaxies as $galaxy) {
|
||||
if (isset($existingGalaxies[$galaxy['uuid']])) {
|
||||
if (
|
||||
$force ||
|
||||
$existingGalaxies[$galaxy['uuid']]['version'] < $galaxy['version'] ||
|
||||
(!empty($galaxy['icon']) && ($existingGalaxies[$galaxy['uuid']]['icon'] != $galaxy['icon']))
|
||||
) {
|
||||
$galaxy['id'] = $existingGalaxies[$galaxy['uuid']]['id'];
|
||||
$galaxyEntity = $this->newEntity($galaxy);
|
||||
$galaxyEntity->kill_chain_order = $galaxy['kill_chain_order'];
|
||||
$this->save($galaxyEntity);
|
||||
}
|
||||
} else {
|
||||
$galaxyEntity = $this->newEntity($galaxy);
|
||||
$galaxyEntity->kill_chain_order = $galaxy['kill_chain_order'] ?? [];
|
||||
$this->save($galaxyEntity);
|
||||
}
|
||||
}
|
||||
return $this->find('list', array('recursive' => -1, 'fields' => array('type', 'id')))->disableHydration()->toArray();
|
||||
}
|
||||
|
||||
private function __update_prepare_template(array $cluster_package, array $galaxies)
|
||||
{
|
||||
return [
|
||||
'source' => isset($cluster_package['source']) ? $cluster_package['source'] : '',
|
||||
'authors' => json_encode(isset($cluster_package['authors']) ? $cluster_package['authors'] : array()),
|
||||
'collection_uuid' => isset($cluster_package['uuid']) ? $cluster_package['uuid'] : '',
|
||||
'galaxy_id' => $galaxies[$cluster_package['type']],
|
||||
'type' => $cluster_package['type'],
|
||||
'tag_name' => 'misp-galaxy:' . $cluster_package['type'] . '="'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $galaxies
|
||||
* @param array $cluster_package
|
||||
* @return array
|
||||
*/
|
||||
private function __getPreExistingClusters(array $galaxies, array $cluster_package)
|
||||
{
|
||||
$temp = $this->GalaxyClusters->find('all', array(
|
||||
'conditions' => array(
|
||||
'GalaxyCluster.galaxy_id' => $galaxies[$cluster_package['type']]
|
||||
),
|
||||
'recursive' => -1,
|
||||
'fields' => array('version', 'id', 'value', 'uuid')
|
||||
));
|
||||
return array_column(array_column($temp, 'GalaxyCluster'), null, 'value');
|
||||
}
|
||||
|
||||
private function __deleteOutdated(bool $force, array $cluster_package, array $existingClusters)
|
||||
{
|
||||
// Delete all existing outdated clusters
|
||||
$cluster_ids_to_delete = array();
|
||||
$cluster_uuids_to_delete = array();
|
||||
foreach ($cluster_package['values'] as $k => $cluster) {
|
||||
if (empty($cluster['value'])) {
|
||||
continue;
|
||||
}
|
||||
if (isset($cluster['version'])) {
|
||||
} elseif (!empty($cluster_package['version'])) {
|
||||
$cluster_package['values'][$k]['version'] = $cluster_package['version'];
|
||||
} else {
|
||||
$cluster_package['values'][$k]['version'] = 0;
|
||||
}
|
||||
if (isset($existingClusters[$cluster['value']])) {
|
||||
$existing = $existingClusters[$cluster['value']];
|
||||
if ($force || $existing['version'] < $cluster_package['values'][$k]['version']) {
|
||||
$cluster_ids_to_delete[] = $existing['id'];
|
||||
$cluster_uuids_to_delete[] = $existing['uuid'];
|
||||
} else {
|
||||
unset($cluster_package['values'][$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($cluster_ids_to_delete)) {
|
||||
$this->GalaxyClusters->GalaxyElement->deleteAll(array('GalaxyElement.galaxy_cluster_id' => $cluster_ids_to_delete), false);
|
||||
$this->GalaxyClusters->GalaxyClusterRelation->deleteAll(array('GalaxyClusterRelation.galaxy_cluster_uuid' => $cluster_uuids_to_delete));
|
||||
$this->GalaxyClusters->deleteAll(array('GalaxyCluster.id' => $cluster_ids_to_delete), false);
|
||||
}
|
||||
return $cluster_package;
|
||||
}
|
||||
|
||||
private function __createClusters($cluster_package, $template)
|
||||
{
|
||||
$relations = [];
|
||||
$elements = [];
|
||||
$this->GalaxyClusters->bulkEntry = true;
|
||||
|
||||
// Start transaction
|
||||
$this->getDataSource()->begin();
|
||||
|
||||
foreach ($cluster_package['values'] as $cluster) {
|
||||
if (empty($cluster['version'])) {
|
||||
$cluster['version'] = 1;
|
||||
}
|
||||
$template['version'] = $cluster['version'];
|
||||
$this->GalaxyClusters->create();
|
||||
$cluster_to_save = $template;
|
||||
if (isset($cluster['description'])) {
|
||||
$cluster_to_save['description'] = $cluster['description'];
|
||||
unset($cluster['description']);
|
||||
}
|
||||
$cluster_to_save['value'] = $cluster['value'];
|
||||
$cluster_to_save['tag_name'] = $cluster_to_save['tag_name'] . $cluster['value'] . '"';
|
||||
if (!empty($cluster['uuid'])) {
|
||||
$cluster_to_save['uuid'] = $cluster['uuid'];
|
||||
}
|
||||
unset($cluster['value']);
|
||||
if (empty($cluster_to_save['description'])) {
|
||||
$cluster_to_save['description'] = '';
|
||||
}
|
||||
$cluster_to_save['distribution'] = 3;
|
||||
$cluster_to_save['default'] = true;
|
||||
$cluster_to_save['published'] = false;
|
||||
$cluster_to_save['org_id'] = 0;
|
||||
$cluster_to_save['orgc_id'] = 0;
|
||||
// We are already in transaction
|
||||
$result = $this->GalaxyClusters->save($cluster_to_save, ['atomic' => false, 'validate' => false]);
|
||||
if (!$result) {
|
||||
$this->log("Could not save galaxy cluster with UUID {$cluster_to_save['uuid']}.");
|
||||
continue;
|
||||
}
|
||||
$galaxyClusterId = $this->GalaxyClusters->id;
|
||||
if (isset($cluster['meta'])) {
|
||||
foreach ($cluster['meta'] as $key => $value) {
|
||||
if (!is_array($value)) {
|
||||
$value = [$value];
|
||||
}
|
||||
foreach ($value as $v) {
|
||||
if (is_array($v)) {
|
||||
$LogTable = $this->fetchTable('Logs');
|
||||
$logEntry = $LogTable->newEntity([
|
||||
'org' => 'SYSTEM',
|
||||
'model' => 'Galaxy',
|
||||
'model_id' => 0,
|
||||
'email' => 0,
|
||||
'action' => 'error',
|
||||
'title' => sprintf('Found a malformed galaxy cluster (%s) during the update, skipping. Reason: Malformed meta field, embedded array found.', $cluster['uuid']),
|
||||
'change' => ''
|
||||
]);
|
||||
$LogTable->save($logEntry);
|
||||
} else {
|
||||
$elements[] = array(
|
||||
$galaxyClusterId,
|
||||
$key,
|
||||
(string)$v
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($cluster['related'])) {
|
||||
foreach ($cluster['related'] as $relation) {
|
||||
$relations[] = [
|
||||
'galaxy_cluster_id' => $galaxyClusterId,
|
||||
'galaxy_cluster_uuid' => $cluster['uuid'],
|
||||
'referenced_galaxy_cluster_uuid' => $relation['dest-uuid'],
|
||||
'referenced_galaxy_cluster_type' => $relation['type'],
|
||||
'default' => true,
|
||||
'distribution' => 3,
|
||||
'tags' => $relation['tags'] ?? []
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Commit transaction
|
||||
$this->getDataSource()->commit();
|
||||
|
||||
return [$elements, $relations];
|
||||
}
|
||||
|
||||
public function update($force = false)
|
||||
{
|
||||
$galaxies = $this->__load_galaxies($force);
|
||||
$files = new GlobIterator(APP . '../libraries' . DS . 'misp-galaxy' . DS . 'clusters' . DS . '*.json');
|
||||
$force = (bool)$force;
|
||||
$allRelations = [];
|
||||
foreach ($files as $file) {
|
||||
$cluster_package = FileAccessTool::readJsonFromFile($file->getPathname());
|
||||
if (!isset($galaxies[$cluster_package['type']])) {
|
||||
continue;
|
||||
}
|
||||
$template = $this->__update_prepare_template($cluster_package, $galaxies);
|
||||
$existingClusters = $this->__getPreExistingClusters($galaxies, $cluster_package);
|
||||
$cluster_package = $this->__deleteOutdated($force, $cluster_package, $existingClusters);
|
||||
|
||||
// create all clusters
|
||||
list($elements, $relations) = $this->__createClusters($cluster_package, $template);
|
||||
if (!empty($elements)) {
|
||||
$db = $this->getDataSource();
|
||||
$fields = array('galaxy_cluster_id', 'key', 'value');
|
||||
$db->insertMulti('galaxy_elements', $fields, $elements);
|
||||
}
|
||||
$allRelations = array_merge($allRelations, $relations);
|
||||
}
|
||||
// Save relation as last part when all clusters are created
|
||||
if (!empty($allRelations)) {
|
||||
$this->GalaxyClusters->GalaxyClusterRelation->bulkSaveRelations($allRelations);
|
||||
}
|
||||
// Probably unnecessary anymore
|
||||
$this->GalaxyClusters->generateMissingRelations();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture the Galaxy
|
||||
*
|
||||
* @param array $user
|
||||
* @param array $galaxy The galaxy to be captured
|
||||
* @return array|false the captured galaxy or false on error
|
||||
*/
|
||||
public function captureGalaxy(array $user, array $galaxy)
|
||||
{
|
||||
if (empty($galaxy['uuid'])) {
|
||||
return false;
|
||||
}
|
||||
$existingGalaxy = $this->find('all', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Galaxy.uuid' => $galaxy['uuid'])
|
||||
))->first()->toArray();
|
||||
if (empty($existingGalaxy)) {
|
||||
if ($user['Role']['perm_site_admin'] || $user['Role']['perm_galaxy_editor']) {
|
||||
$this->create();
|
||||
unset($galaxy['id']);
|
||||
$galaxyEntity = $this->newEntity($galaxy);
|
||||
$this->save($galaxyEntity);
|
||||
$existingGalaxy = $this->find('all', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Galaxy.id' => $this->id)
|
||||
))->first()->toArray();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return $existingGalaxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import all clusters into the Galaxy they are shipped with, creating the galaxy if not existant.
|
||||
*
|
||||
* This function is meant to be used with manual import or push from remote instance
|
||||
* @param array $user
|
||||
* @param array $clusters clusters to import
|
||||
* @return array The import result with errors if any
|
||||
*/
|
||||
public function importGalaxyAndClusters(array $user, array $clusters)
|
||||
{
|
||||
$results = array('success' => false, 'imported' => 0, 'ignored' => 0, 'failed' => 0, 'errors' => array());
|
||||
foreach ($clusters as $cluster) {
|
||||
if (!empty($cluster['GalaxyCluster']['Galaxy'])) {
|
||||
$existingGalaxy = $this->captureGalaxy($user, $cluster['GalaxyCluster']['Galaxy']);
|
||||
} elseif (!empty($cluster['GalaxyCluster']['type'])) {
|
||||
$existingGalaxy = $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
'fields' => array('id'),
|
||||
'conditions' => array('Galaxy.type' => $cluster['GalaxyCluster']['type']),
|
||||
));
|
||||
if (empty($existingGalaxy)) { // We don't have enough info to create the galaxy
|
||||
$results['failed']++;
|
||||
$results['errors'][] = __('Galaxy not found');
|
||||
continue;
|
||||
}
|
||||
} else { // We don't have the galaxy nor can create it
|
||||
$results['failed']++;
|
||||
$results['errors'][] = __('Galaxy not found');
|
||||
continue;
|
||||
}
|
||||
$cluster['GalaxyCluster']['galaxy_id'] = $existingGalaxy['Galaxy']['id'];
|
||||
$cluster['GalaxyCluster']['locked'] = true;
|
||||
$saveResult = $this->GalaxyClusters->captureCluster($user, $cluster, $fromPull = false);
|
||||
if (empty($saveResult['errors'])) {
|
||||
$results['imported'] += $saveResult['imported'];
|
||||
} else {
|
||||
$results['ignored'] += $saveResult['ignored'];
|
||||
$results['failed'] += $saveResult['failed'];
|
||||
$results['errors'] = array_merge($results['errors'], $saveResult['errors']);
|
||||
}
|
||||
}
|
||||
$results['success'] = !($results['failed'] > 0 && $results['imported'] == 0);
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $user
|
||||
* @param string $targetType
|
||||
* @param int $targetId
|
||||
* @return array
|
||||
*/
|
||||
public function fetchTarget(array $user, $targetType, $targetId)
|
||||
{
|
||||
$TagsTable = $this->fetchTable('Tags');
|
||||
if ($targetType === 'event') {
|
||||
return $TagsTable->EventTag->Event->fetchSimpleEvent($user, $targetId);
|
||||
} elseif ($targetType === 'attribute') {
|
||||
return $TagsTable->AttributeTag->Attribute->fetchAttributeSimple($user, array('conditions' => array('Attribute.id' => $targetId)));
|
||||
} elseif ($targetType === 'tag_collection') {
|
||||
$target = $TagsTable->TagCollectionTag->TagCollection->fetchTagCollection($user, array('conditions' => array('TagCollection.id' => $targetId)));
|
||||
if (!empty($target)) {
|
||||
$target = $target[0];
|
||||
}
|
||||
return $target;
|
||||
} else {
|
||||
throw new InvalidArgumentException("Invalid target type $targetType");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $user
|
||||
* @param string $targetType Can be 'event', 'attribute' or 'tag_collection'
|
||||
* @param array $target
|
||||
* @param int $cluster_id
|
||||
* @param bool $local
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function attachCluster(array $user, $targetType, array $target, $cluster_id, $local = false)
|
||||
{
|
||||
$connectorModel = Inflector::camelize($targetType) . 'Tag';
|
||||
$local = $local == 1 || $local === true ? 1 : 0;
|
||||
$cluster_alias = $this->GalaxyClusters->alias;
|
||||
$galaxy_alias = $this->alias;
|
||||
$cluster = $this->GalaxyClusters->fetchGalaxyClusters($user, array(
|
||||
'first' => true,
|
||||
'conditions' => array("$cluster_alias.id" => $cluster_id),
|
||||
'contain' => array('Galaxy'),
|
||||
'fields' => array('tag_name', 'id', 'value', "$galaxy_alias.local_only"),
|
||||
));
|
||||
|
||||
if (empty($cluster)) {
|
||||
throw new NotFoundException(__('Invalid Galaxy cluster'));
|
||||
}
|
||||
$local_only = $cluster['GalaxyCluster']['Galaxy']['local_only'];
|
||||
if ($local_only && !$local) {
|
||||
throw new MethodNotAllowedException(__("This Cluster can only be attached in a local scope"));
|
||||
}
|
||||
$TagsTable = $this->fetchTable('Tags');
|
||||
$tag_id = $TagsTable->captureTag(array('name' => $cluster['GalaxyCluster']['tag_name'], 'colour' => '#0088cc', 'exportable' => 1, 'local_only' => $local_only), $user, true);
|
||||
if ($targetType === 'event') {
|
||||
$target_id = $target['Event']['id'];
|
||||
} elseif ($targetType === 'attribute') {
|
||||
$target_id = $target['Attribute']['id'];
|
||||
} else {
|
||||
$target_id = $target['TagCollection']['id'];
|
||||
}
|
||||
$existingTag = $TagsTable->$connectorModel->hasAny(array($targetType . '_id' => $target_id, 'tag_id' => $tag_id));
|
||||
if ($existingTag) {
|
||||
return 'Cluster already attached.';
|
||||
}
|
||||
$TagsTable->$connectorModel->create();
|
||||
$toSave = array($targetType . '_id' => $target_id, 'tag_id' => $tag_id, 'local' => $local);
|
||||
if ($targetType === 'attribute') {
|
||||
$toSave['event_id'] = $target['Attribute']['event_id'];
|
||||
}
|
||||
$result = $TagsTable->$connectorModel->save($toSave);
|
||||
if ($result) {
|
||||
if (!$local) {
|
||||
if ($targetType === 'attribute') {
|
||||
$TagsTable->AttributeTag->Attribute->touch($target);
|
||||
} elseif ($targetType === 'event') {
|
||||
$TagsTable->EventTag->Event->unpublishEvent($target);
|
||||
}
|
||||
}
|
||||
if ($targetType === 'attribute' || $targetType === 'event') {
|
||||
$TagsTable->EventTag->Event->insertLock($user, $target['Event']['id']);
|
||||
}
|
||||
$logTitle = 'Attached ' . $cluster['GalaxyCluster']['value'] . ' (' . $cluster['GalaxyCluster']['id'] . ') to ' . $targetType . ' (' . $target_id . ')';
|
||||
$this->loadLog()->createLogEntry($user, 'galaxy', ucfirst($targetType), $target_id, $logTitle);
|
||||
return 'Cluster attached.';
|
||||
}
|
||||
return 'Could not attach the cluster';
|
||||
}
|
||||
|
||||
public function detachCluster($user, $target_type, $target_id, $cluster_id)
|
||||
{
|
||||
$cluster = $this->GalaxyClusters->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('id' => $cluster_id),
|
||||
'fields' => array('tag_name', 'id', 'value')
|
||||
));
|
||||
$TagsTable = $this->fetchTable('Tags');
|
||||
if ($target_type === 'event') {
|
||||
$target = $TagsTable->EventTag->Event->fetchEvent($user, array('eventid' => $target_id, 'metadata' => 1));
|
||||
if (empty($target)) {
|
||||
throw new NotFoundException(__('Invalid %s.', $target_type));
|
||||
}
|
||||
$target = $target[0];
|
||||
$event = $target;
|
||||
$org_id = $event['Event']['org_id'];
|
||||
$orgc_id = $event['Event']['orgc_id'];
|
||||
} elseif ($target_type === 'attribute') {
|
||||
$target = $TagsTable->AttributeTag->Attribute->fetchAttributes($user, array('conditions' => array('Attribute.id' => $target_id), 'flatten' => 1));
|
||||
if (empty($target)) {
|
||||
throw new NotFoundException(__('Invalid %s.', $target_type));
|
||||
}
|
||||
$target = $target[0];
|
||||
$event_id = $target['Attribute']['event_id'];
|
||||
$event = $TagsTable->EventTag->Event->fetchEvent($user, array('eventid' => $event_id, 'metadata' => 1));
|
||||
if (empty($event)) {
|
||||
throw new NotFoundException(__('Invalid event'));
|
||||
}
|
||||
$event = $event[0];
|
||||
$org_id = $event['Event']['org_id'];
|
||||
$orgc_id = $event['Event']['org_id'];
|
||||
} elseif ($target_type === 'tag_collection') {
|
||||
$target = $TagsTable->TagCollectionTag->TagCollection->fetchTagCollection($user, array('conditions' => array('TagCollection.id' => $target_id)));
|
||||
if (empty($target)) {
|
||||
throw new NotFoundException(__('Invalid %s.', $target_type));
|
||||
}
|
||||
$target = $target[0];
|
||||
$org_id = $target['org_id'];
|
||||
$orgc_id = $org_id;
|
||||
}
|
||||
|
||||
if (!$user['Role']['perm_site_admin'] && !$user['Role']['perm_sync']) {
|
||||
if (
|
||||
($target_type === 'tag_collection' && !$user['Role']['perm_tag_editor']) ||
|
||||
($target_type !== 'tag_collection' && !$user['Role']['perm_tagger']) ||
|
||||
($user['org_id'] !== $org_id && $user['org_id'] !== $orgc_id)
|
||||
) {
|
||||
throw new MethodNotAllowedException('Invalid ' . Inflector::humanize($target_type) . '.');
|
||||
}
|
||||
}
|
||||
|
||||
$tag_id = $TagsTable->captureTag(array('name' => $cluster['GalaxyCluster']['tag_name'], 'colour' => '#0088cc', 'exportable' => 1), $user);
|
||||
|
||||
if ($target_type === 'attribute') {
|
||||
$existingTargetTag = $TagsTable->AttributeTag->find('first', array(
|
||||
'conditions' => array('AttributeTag.tag_id' => $tag_id, 'AttributeTag.attribute_id' => $target_id),
|
||||
'recursive' => -1,
|
||||
'contain' => array('Tag')
|
||||
));
|
||||
} elseif ($target_type === 'event') {
|
||||
$existingTargetTag = $TagsTable->EventTag->find('first', array(
|
||||
'conditions' => array('EventTag.tag_id' => $tag_id, 'EventTag.event_id' => $target_id),
|
||||
'recursive' => -1,
|
||||
'contain' => array('Tag')
|
||||
));
|
||||
} elseif ($target_type === 'tag_collection') {
|
||||
$existingTargetTag = $TagsTable->TagCollectionTag->TagCollection->find('first', array(
|
||||
'conditions' => array('tag_id' => $tag_id, 'tag_collection_id' => $target_id),
|
||||
'recursive' => -1,
|
||||
'contain' => array('Tag')
|
||||
));
|
||||
}
|
||||
|
||||
if (empty($existingTargetTag)) {
|
||||
return 'Cluster not attached.';
|
||||
}
|
||||
|
||||
if ($target_type === 'event') {
|
||||
$result = $TagsTable->EventTag->delete($existingTargetTag['EventTag']['id']);
|
||||
} elseif ($target_type === 'attribute') {
|
||||
$result = $TagsTable->AttributeTag->delete($existingTargetTag['AttributeTag']['id']);
|
||||
} elseif ($target_type === 'tag_collection') {
|
||||
$result = $TagsTable->TagCollectionTag->delete($existingTargetTag['TagCollectionTag']['id']);
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
if ($target_type !== 'tag_collection') {
|
||||
$TagsTable->EventTag->Event->insertLock($user, $event['Event']['id']);
|
||||
$TagsTable->EventTag->Event->unpublishEvent($event);
|
||||
}
|
||||
|
||||
$logTitle = 'Detached ' . $cluster['GalaxyCluster']['value'] . ' (' . $cluster['GalaxyCluster']['id'] . ') to ' . $target_type . ' (' . $target_id . ')';
|
||||
$this->loadLog()->createLogEntry($user, 'galaxy', ucfirst($target_type), $target_id, $logTitle);
|
||||
return 'Cluster detached';
|
||||
} else {
|
||||
return 'Could not detach cluster';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $user
|
||||
* @param int $targetId
|
||||
* @param string $targetType Can be 'attribute', 'event' or 'tag_collection'
|
||||
* @param int $tagId
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function detachClusterByTagId(array $user, $targetId, $targetType, $tagId)
|
||||
{
|
||||
if ($targetType === 'attribute') {
|
||||
$attribute = $this->GalaxyClusters->Tag->EventTag->Event->Attribute->find('first', array(
|
||||
'recursive' => -1,
|
||||
'fields' => array('id', 'event_id'),
|
||||
'conditions' => array('Attribute.id' => $targetId)
|
||||
));
|
||||
if (empty($attribute)) {
|
||||
throw new NotFoundException('Invalid Attribute.');
|
||||
}
|
||||
$event_id = $attribute['Attribute']['event_id'];
|
||||
} elseif ($targetType === 'event') {
|
||||
$event_id = $targetId;
|
||||
} elseif ($targetType !== 'tag_collection') {
|
||||
throw new InvalidArgumentException('Invalid target type');
|
||||
}
|
||||
|
||||
if ($targetType === 'tag_collection') {
|
||||
$tag_collection = $this->GalaxyClusters->Tag->TagCollectionTag->TagCollection->fetchTagCollection($user, array(
|
||||
'conditions' => array('TagCollection.id' => $targetId),
|
||||
'recursive' => -1,
|
||||
));
|
||||
if (empty($tag_collection)) {
|
||||
throw new NotFoundException('Invalid Tag Collection');
|
||||
}
|
||||
$tag_collection = $tag_collection[0];
|
||||
if (!$user['Role']['perm_site_admin']) {
|
||||
if (!$user['Role']['perm_tag_editor'] || $user['org_id'] !== $tag_collection['TagCollection']['org_id']) {
|
||||
throw new NotFoundException('Invalid Tag Collection');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$event = $this->GalaxyClusters->Tag->EventTag->Event->fetchSimpleEvent($user, $event_id);
|
||||
if (empty($event)) {
|
||||
throw new NotFoundException('Invalid Event.');
|
||||
}
|
||||
if (!$user['Role']['perm_site_admin'] && !$user['Role']['perm_sync']) {
|
||||
if (!$user['Role']['perm_tagger'] || ($user['org_id'] !== $event['Event']['org_id'] && $user['org_id'] !== $event['Event']['orgc_id'])) {
|
||||
throw new NotFoundException('Invalid Event.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($targetType === 'attribute') {
|
||||
$existingTargetTag = $this->GalaxyClusters->Tag->AttributeTag->find('first', array(
|
||||
'conditions' => array('AttributeTag.tag_id' => $tagId, 'AttributeTag.attribute_id' => $targetId),
|
||||
'recursive' => -1,
|
||||
'contain' => array('Tag')
|
||||
));
|
||||
} elseif ($targetType === 'event') {
|
||||
$existingTargetTag = $this->GalaxyClusters->Tag->EventTag->find('first', array(
|
||||
'conditions' => array('EventTag.tag_id' => $tagId, 'EventTag.event_id' => $targetId),
|
||||
'recursive' => -1,
|
||||
'contain' => array('Tag')
|
||||
));
|
||||
} elseif ($targetType === 'tag_collection') {
|
||||
$existingTargetTag = $this->GalaxyClusters->Tag->TagCollectionTag->find('first', array(
|
||||
'conditions' => array('TagCollectionTag.tag_id' => $tagId, 'TagCollectionTag.tag_collection_id' => $targetId),
|
||||
'recursive' => -1,
|
||||
'contain' => array('Tag')
|
||||
));
|
||||
}
|
||||
|
||||
if (empty($existingTargetTag)) {
|
||||
throw new NotFoundException('Galaxy not attached.');
|
||||
}
|
||||
|
||||
$cluster = $this->GalaxyClusters->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('GalaxyCluster.tag_name' => $existingTargetTag['Tag']['name'])
|
||||
));
|
||||
if (empty($cluster)) {
|
||||
throw new NotFoundException('Tag is not cluster');
|
||||
}
|
||||
|
||||
if ($targetType === 'event') {
|
||||
$result = $this->GalaxyClusters->Tag->EventTag->delete($existingTargetTag['EventTag']['id']);
|
||||
} elseif ($targetType === 'attribute') {
|
||||
$result = $this->GalaxyClusters->Tag->AttributeTag->delete($existingTargetTag['AttributeTag']['id']);
|
||||
} elseif ($targetType === 'tag_collection') {
|
||||
$result = $this->GalaxyClusters->Tag->TagCollectionTag->delete($existingTargetTag['TagCollectionTag']['id']);
|
||||
}
|
||||
if (!$result) {
|
||||
throw new RuntimeException('Could not detach galaxy from event.');
|
||||
}
|
||||
|
||||
if ($targetType !== 'tag_collection') {
|
||||
$this->GalaxyClusters->Tag->EventTag->Event->unpublishEvent($event);
|
||||
}
|
||||
|
||||
$logTitle = 'Detached ' . $cluster['GalaxyCluster']['value'] . ' (' . $cluster['GalaxyCluster']['id'] . ') from ' . $targetType . ' (' . $targetId . ')';
|
||||
$this->loadLog()->createLogEntry($user, 'galaxy', ucfirst($targetType), $targetId, $logTitle);
|
||||
}
|
||||
|
||||
public function getMitreAttackGalaxyId($type = "mitre-attack-pattern", $namespace = "mitre-attack")
|
||||
{
|
||||
$galaxy = $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
'fields' => array('MAX(Galaxy.version) as latest_version', 'id'),
|
||||
'conditions' => array(
|
||||
'Galaxy.type' => $type,
|
||||
'Galaxy.namespace' => $namespace
|
||||
),
|
||||
'group' => array('name', 'id')
|
||||
));
|
||||
return empty($galaxy) ? 0 : $galaxy['Galaxy']['id'];
|
||||
}
|
||||
|
||||
public function getAllowedMatrixGalaxies()
|
||||
{
|
||||
$conditions = array(
|
||||
'NOT' => array(
|
||||
'kill_chain_order' => ''
|
||||
)
|
||||
);
|
||||
$galaxies = $this->find('all', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => $conditions,
|
||||
));
|
||||
return $galaxies;
|
||||
}
|
||||
|
||||
public function getMatrix($galaxy_id, $scores = array())
|
||||
{
|
||||
$conditions = array('Galaxy.id' => $galaxy_id);
|
||||
$contains = array(
|
||||
'GalaxyCluster' => array('GalaxyElement'),
|
||||
);
|
||||
|
||||
$galaxy = $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
'contain' => $contains,
|
||||
'conditions' => $conditions,
|
||||
));
|
||||
|
||||
$mispUUID = Configure::read('MISP')['uuid'];
|
||||
|
||||
if (!isset($galaxy['Galaxy']['kill_chain_order'])) {
|
||||
throw new MethodNotAllowedException(__("Galaxy cannot be represented as a matrix"));
|
||||
}
|
||||
$matrixData = array(
|
||||
'killChain' => $galaxy['Galaxy']['kill_chain_order'],
|
||||
'tabs' => array(),
|
||||
'matrixTags' => array(),
|
||||
'instance-uuid' => $mispUUID,
|
||||
'galaxy' => $galaxy['Galaxy']
|
||||
);
|
||||
|
||||
$clusters = $galaxy['GalaxyCluster'];
|
||||
$cols = array();
|
||||
|
||||
foreach ($clusters as $cluster) {
|
||||
if (empty($cluster['GalaxyElement'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$toBeAdded = false;
|
||||
$clusterType = $cluster['type'];
|
||||
$galaxyElements = $cluster['GalaxyElement'];
|
||||
foreach ($galaxyElements as $element) {
|
||||
// add cluster if kill_chain is present
|
||||
if ($element['key'] == 'kill_chain') {
|
||||
$kc = explode(":", $element['value']);
|
||||
$galaxyType = $kc[0];
|
||||
$kc = $kc[1];
|
||||
$cols[$galaxyType][$kc][] = $cluster;
|
||||
$toBeAdded = true;
|
||||
}
|
||||
if ($element['key'] == 'external_id') {
|
||||
$cluster['external_id'] = $element['value'];
|
||||
}
|
||||
if ($toBeAdded) {
|
||||
$matrixData['matrixTags'][$cluster['tag_name']] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
$matrixData['tabs'] = $cols;
|
||||
|
||||
$this->sortMatrixByScore($matrixData['tabs'], $scores);
|
||||
// #FIXME temporary fix: retrieve tag name of deprecated mitre galaxies (for the stats)
|
||||
if ($galaxy['Galaxy']['id'] == $this->getMitreAttackGalaxyId()) {
|
||||
$names = array('Enterprise Attack - Attack Pattern', 'Pre Attack - Attack Pattern', 'Mobile Attack - Attack Pattern');
|
||||
$tag_names = array();
|
||||
$gals = $this->find('all', array(
|
||||
'recursive' => -1,
|
||||
'contain' => array('GalaxyCluster.tag_name'),
|
||||
'conditions' => array('Galaxy.name' => $names)
|
||||
));
|
||||
foreach ($gals as $gal => $temp) {
|
||||
foreach ($temp['GalaxyCluster'] as $value) {
|
||||
$matrixData['matrixTags'][$value['tag_name']] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// end FIXME
|
||||
|
||||
$matrixData['matrixTags'] = array_keys($matrixData['matrixTags']);
|
||||
return $matrixData;
|
||||
}
|
||||
|
||||
public function sortMatrixByScore(&$tabs, $scores)
|
||||
{
|
||||
foreach (array_keys($tabs) as $i) {
|
||||
foreach (array_keys($tabs[$i]) as $j) {
|
||||
// major ordering based on score, minor based on alphabetical
|
||||
usort($tabs[$i][$j], function ($a, $b) use ($scores) {
|
||||
if ($a['tag_name'] == $b['tag_name']) {
|
||||
return 0;
|
||||
}
|
||||
if (isset($scores[$a['tag_name']]) && isset($scores[$b['tag_name']])) {
|
||||
if ($scores[$a['tag_name']] < $scores[$b['tag_name']]) {
|
||||
$ret = 1;
|
||||
} elseif ($scores[$a['tag_name']] == $scores[$b['tag_name']]) {
|
||||
$ret = strcmp($a['value'], $b['value']);
|
||||
} else {
|
||||
$ret = -1;
|
||||
}
|
||||
} elseif (isset($scores[$a['tag_name']])) {
|
||||
$ret = -1;
|
||||
} elseif (isset($scores[$b['tag_name']])) {
|
||||
$ret = 1;
|
||||
} else { // none are set
|
||||
$ret = strcmp($a['value'], $b['value']);
|
||||
}
|
||||
return $ret;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* generateForkTree
|
||||
*
|
||||
* @param mixed $clusters The accessible cluster for the user to be arranged into a fork tree
|
||||
* @param mixed $galaxy The galaxy for which the fork tree is generated
|
||||
* @param bool $pruneRootLeaves Should the nonforked clusters be removed from the tree
|
||||
* @return array The generated fork tree where the children of a node are contained in the `children` key
|
||||
*/
|
||||
public function generateForkTree(array $clusters, array $galaxy, $pruneRootLeaves = true)
|
||||
{
|
||||
$tree = array();
|
||||
$lookup = array();
|
||||
$lastNodeAdded = array();
|
||||
// generate the lookup table used to immediatly get the correct cluster
|
||||
foreach ($clusters as $i => $cluster) {
|
||||
$clusters[$i]['children'] = array();
|
||||
$lookup[$cluster['GalaxyCluster']['id']] = &$clusters[$i];
|
||||
}
|
||||
foreach ($clusters as $i => $cluster) {
|
||||
if (!empty($cluster['GalaxyCluster']['extended_from'])) {
|
||||
$parent = $cluster['GalaxyCluster']['extended_from'];
|
||||
$clusterVersion = $cluster['GalaxyCluster']['extends_version'];
|
||||
$parentVersion = $lookup[$parent['GalaxyCluster']['id']]['GalaxyCluster']['version'];
|
||||
if ($clusterVersion == $parentVersion) {
|
||||
$lookup[$parent['GalaxyCluster']['id']]['children'][] = &$clusters[$i];
|
||||
} else {
|
||||
// version differs, insert version node between child and parent
|
||||
$lastVersionNode = array(
|
||||
'isVersion' => true,
|
||||
'isLast' => true,
|
||||
'version' => $parentVersion,
|
||||
'parentUuid' => $parent['GalaxyCluster']['uuid'],
|
||||
'children' => array()
|
||||
);
|
||||
$versionNode = array(
|
||||
'isVersion' => true,
|
||||
'isLast' => false,
|
||||
'version' => $clusterVersion,
|
||||
'parentUuid' => $parent['GalaxyCluster']['uuid'],
|
||||
'children' => array(&$clusters[$i])
|
||||
);
|
||||
$lookup[$parent['GalaxyCluster']['id']]['children'][] = $versionNode;
|
||||
if (!isset($lastNodeAdded[$parent['GalaxyCluster']['id']])) {
|
||||
$lookup[$parent['GalaxyCluster']['id']]['children'][] = $lastVersionNode;
|
||||
$lastNodeAdded[$parent['GalaxyCluster']['id']] = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$tree[] = &$clusters[$i];
|
||||
}
|
||||
}
|
||||
|
||||
if ($pruneRootLeaves) {
|
||||
foreach ($tree as $i => $node) {
|
||||
if (empty($node['children'])) {
|
||||
unset($tree[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tree = array(array(
|
||||
'Galaxy' => $galaxy['Galaxy'],
|
||||
'children' => array_values($tree)
|
||||
));
|
||||
return $tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* convertToMISPGalaxyFormat
|
||||
*
|
||||
* @param array $galaxy
|
||||
* @param array $clusters
|
||||
* @return array the converted clusters into the misp-galaxy format
|
||||
*
|
||||
* Special cases:
|
||||
* - authors: (since all clusters have their own, takes all of them)
|
||||
* - version: Takes the higher version number of all clusters
|
||||
* - uuid: Is actually the collection_uuid. Takes the last one
|
||||
* - source (since all clusters have their own, takes the last one)
|
||||
* - category (not saved in MISP nor used)
|
||||
* - description (not used as the description in the galaxy.json is used instead)
|
||||
*/
|
||||
public function convertToMISPGalaxyFormat($galaxy, $clusters)
|
||||
{
|
||||
$converted = [];
|
||||
$converted['name'] = $galaxy['Galaxy']['name'];
|
||||
$converted['type'] = $galaxy['Galaxy']['type'];
|
||||
$converted['authors'] = [];
|
||||
$converted['version'] = 0;
|
||||
$values = [];
|
||||
$fieldsToSave = ['description', 'uuid', 'value', 'extends_uuid', 'extends_version'];
|
||||
foreach ($clusters as $i => $cluster) {
|
||||
foreach ($fieldsToSave as $field) {
|
||||
$values[$i][$field] = $cluster['GalaxyCluster'][$field];
|
||||
}
|
||||
$converted['uuid'] = $cluster['GalaxyCluster']['collection_uuid'];
|
||||
$converted['source'] = $cluster['GalaxyCluster']['source'];
|
||||
if (!empty($cluster['GalaxyCluster']['authors'])) {
|
||||
foreach ($cluster['GalaxyCluster']['authors'] as $author) {
|
||||
if (!is_null($author) && $author != 'null') {
|
||||
$converted['authors'][$author] = $author;
|
||||
}
|
||||
}
|
||||
}
|
||||
$converted['version'] = $converted['version'] > $cluster['GalaxyCluster']['version'];
|
||||
foreach ($cluster['GalaxyCluster']['GalaxyElement'] as $element) {
|
||||
if (isset($values[$i]['meta'][$element['key']])) {
|
||||
if (is_array($values[$i]['meta'][$element['key']])) {
|
||||
$values[$i]['meta'][$element['key']][] = $element['value'];
|
||||
} else {
|
||||
$values[$i]['meta'][$element['key']] = [$values[$i]['meta'][$element['key']], $element['value']];
|
||||
}
|
||||
} else {
|
||||
$values[$i]['meta'][$element['key']] = $element['value'];
|
||||
}
|
||||
}
|
||||
foreach ($cluster['GalaxyCluster']['GalaxyClusterRelation'] as $j => $relation) {
|
||||
$values[$i]['related'][$j] = [
|
||||
'dest-uuid' => $relation['referenced_galaxy_cluster_uuid'],
|
||||
'type' => $relation['referenced_galaxy_cluster_type'],
|
||||
];
|
||||
if (!empty($relation['Tag'])) {
|
||||
foreach ($relation['Tag'] as $tag) {
|
||||
$values[$i]['related'][$j]['tags'][] = $tag['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$converted['authors'] = array_values($converted['authors']);
|
||||
$converted['values'] = $values;
|
||||
return $converted;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,569 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Table;
|
||||
|
||||
use App\Model\Table\AppTable;
|
||||
use Cake\ORM\Locator\LocatorAwareTrait;
|
||||
use Cake\Utility\Hash;
|
||||
use Cake\Validation\Validator;
|
||||
use App\Model\Entity\Distribution;
|
||||
|
||||
/**
|
||||
* @property GalaxyClusterRelationTag $GalaxyClusterRelationTag
|
||||
* @property GalaxyCluster $TargetCluster
|
||||
* @property SharingGroup $SharingGroup
|
||||
*/
|
||||
class GalaxyClusterRelations extends AppTable
|
||||
{
|
||||
use LocatorAwareTrait;
|
||||
|
||||
public $useTable = 'galaxy_cluster_relations';
|
||||
|
||||
public $recursive = -1;
|
||||
|
||||
public function validationDefault(Validator $validator): Validator
|
||||
{
|
||||
$validator
|
||||
->requirePresence(['referenced_galaxy_cluster_type'])
|
||||
->add(
|
||||
'galaxy_cluster_uuid',
|
||||
'uuid',
|
||||
[
|
||||
'rule' => 'uuid',
|
||||
'message' => 'Please provide a valid RFC 4122 UUID'
|
||||
]
|
||||
)
|
||||
->add(
|
||||
'referenced_galaxy_cluster_uuid',
|
||||
'uuid',
|
||||
[
|
||||
'rule' => 'uuid',
|
||||
'message' => 'Please provide a valid RFC 4122 UUID'
|
||||
]
|
||||
)
|
||||
->add(
|
||||
'distribution',
|
||||
'inList',
|
||||
[
|
||||
'rule' => ['inList', Distribution::ALL],
|
||||
'message' => 'Options: ' . implode(', ', Distribution::DESCRIPTION)
|
||||
]
|
||||
);
|
||||
|
||||
return $validator;
|
||||
}
|
||||
|
||||
public function initialize(array $config): void
|
||||
{
|
||||
parent::initialize($config);
|
||||
$this->addBehavior('AuditLog');
|
||||
|
||||
$this->belongsTo(
|
||||
'SourceCluster',
|
||||
[
|
||||
'className' => 'GalaxyCluster',
|
||||
'foreignKey' => 'galaxy_cluster_id',
|
||||
]
|
||||
);
|
||||
|
||||
$this->belongsTo(
|
||||
'TargetCluster',
|
||||
[
|
||||
'className' => 'GalaxyCluster',
|
||||
'foreignKey' => 'galaxy_cluster_id',
|
||||
]
|
||||
);
|
||||
|
||||
$this->belongsTo(
|
||||
'SharingGroup',
|
||||
[
|
||||
'className' => 'SharingGroup',
|
||||
'foreignKey' => 'sharing_group_id'
|
||||
]
|
||||
);
|
||||
|
||||
$this->hasMany(
|
||||
'GalaxyClusterRelationTag',
|
||||
[
|
||||
'dependent' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function afterFind($results, $primary = false)
|
||||
{
|
||||
foreach ($results as $k => $result) {
|
||||
if (isset($result['TargetCluster']) && key_exists('id', $result['TargetCluster']) && is_null($result['TargetCluster']['id'])) {
|
||||
$results[$k]['TargetCluster'] = array();
|
||||
}
|
||||
if (isset($result['GalaxyClusterRelation']['distribution']) && $result['GalaxyClusterRelation']['distribution'] != 4) {
|
||||
unset($results[$k]['SharingGroup']);
|
||||
}
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function buildConditions($user, $clusterConditions = true)
|
||||
{
|
||||
$conditions = [];
|
||||
if (!$user['Role']['perm_site_admin']) {
|
||||
$alias = $this->alias;
|
||||
$SharingGroupsTable = $this->fetchTable('SharingGroups');
|
||||
$sgids = $SharingGroupsTable->authorizedIds($user);
|
||||
$gcOwnerIds = $this->SourceCluster->cacheGalaxyClusterOwnerIDs($user);
|
||||
$conditionsRelations['AND']['OR'] = [
|
||||
"$alias.galaxy_cluster_id" => $gcOwnerIds,
|
||||
[
|
||||
'AND' => [
|
||||
"$alias.distribution >" => 0,
|
||||
"$alias.distribution <" => 4
|
||||
],
|
||||
],
|
||||
[
|
||||
'AND' => [
|
||||
"$alias.sharing_group_id" => $sgids,
|
||||
"$alias.distribution" => 4
|
||||
]
|
||||
]
|
||||
];
|
||||
$conditionsSourceCluster = $clusterConditions ? $this->SourceCluster->buildConditions($user) : [];
|
||||
$conditions = [
|
||||
'AND' => [
|
||||
$conditionsRelations,
|
||||
$conditionsSourceCluster
|
||||
]
|
||||
];
|
||||
}
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
public function fetchRelations($user, $options, $full = false)
|
||||
{
|
||||
$params = array(
|
||||
'conditions' => $this->buildConditions($user),
|
||||
'recursive' => -1
|
||||
);
|
||||
if (!empty($options['contain'])) {
|
||||
$params['contain'] = $options['contain'];
|
||||
} elseif ($full) {
|
||||
$params['contain'] = array('SharingGroup', 'SourceCluster', 'TargetCluster');
|
||||
}
|
||||
if (empty($params['contain'])) {
|
||||
$params['contain'] = ['SourceCluster'];
|
||||
}
|
||||
if (!in_array('SourceCluster', $params['contain'])) {
|
||||
$params['contain'][] = 'SourceCluster';
|
||||
}
|
||||
if (isset($options['fields'])) {
|
||||
$params['fields'] = $options['fields'];
|
||||
}
|
||||
if (isset($options['conditions'])) {
|
||||
$params['conditions']['AND'][] = $options['conditions'];
|
||||
}
|
||||
if (isset($options['group'])) {
|
||||
$params['group'] = empty($options['group']) ? $options['group'] : false;
|
||||
}
|
||||
$relations = $this->find('all', $params);
|
||||
return $relations;
|
||||
}
|
||||
|
||||
public function getExistingRelationships()
|
||||
{
|
||||
$existingRelationships = $this->find('column', array(
|
||||
'recursive' => -1,
|
||||
'fields' => array('referenced_galaxy_cluster_type'),
|
||||
'unique' => true,
|
||||
))->disableHydration()->toArray();
|
||||
$ObjectRelationshipsTable = $this->fetchTable('ObjectRelationships');
|
||||
$objectRelationships = $ObjectRelationshipsTable->find('column', array(
|
||||
'recursive' => -1,
|
||||
'fields' => array('name'),
|
||||
'unique' => true,
|
||||
))->disableHydration()->toArray();
|
||||
return array_unique(array_merge($existingRelationships, $objectRelationships));
|
||||
}
|
||||
|
||||
/**
|
||||
* saveRelations
|
||||
*
|
||||
* @see saveRelation
|
||||
* @return array List of errors if any
|
||||
*/
|
||||
public function saveRelations(array $user, array $cluster, array $relations, $captureTag = false, $force = false)
|
||||
{
|
||||
$errors = array();
|
||||
foreach ($relations as $k => $relation) {
|
||||
$saveResult = $this->saveRelation($user, $cluster, $relation, $captureTag = $captureTag, $force = $force);
|
||||
$errors = array_merge($errors, $saveResult);
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* saveRelation Respecting ACL saves a relation and set correct fields where applicable.
|
||||
* Contrary to its capture equivalent, trying to save a relation for a unknown target cluster will fail.
|
||||
*
|
||||
* @param array $user
|
||||
* @param array $cluster The cluster from which the relation is originating
|
||||
* @param array $relation The relation to save
|
||||
* @param bool $captureTag Should the tag be captured if it doesn't exists
|
||||
* @param bool $force Should the relation be edited if it exists
|
||||
* @return array List errors if any
|
||||
*/
|
||||
public function saveRelation(array $user, array $cluster, array $relation, $captureTag = false, $force = false)
|
||||
{
|
||||
$errors = array();
|
||||
if (!isset($relation['GalaxyClusterRelation']) && !empty($relation)) {
|
||||
$relation = array('GalaxyClusterRelation' => $relation);
|
||||
}
|
||||
$authorizationCheck = $this->SourceCluster->fetchIfAuthorized($user, $cluster, array('edit'), $throwErrors = false, $full = false);
|
||||
if (isset($authorizationCheck['authorized']) && !$authorizationCheck['authorized']) {
|
||||
$errors[] = $authorizationCheck['error'];
|
||||
return $errors;
|
||||
}
|
||||
$relation['GalaxyClusterRelation']['galaxy_cluster_uuid'] = $cluster['uuid'];
|
||||
|
||||
$existingRelation = $this->find('first', [
|
||||
'conditions' => [
|
||||
'galaxy_cluster_uuid' => $relation['GalaxyClusterRelation']['galaxy_cluster_uuid'],
|
||||
'referenced_galaxy_cluster_uuid' => $relation['GalaxyClusterRelation']['referenced_galaxy_cluster_uuid'],
|
||||
'referenced_galaxy_cluster_type' => $relation['GalaxyClusterRelation']['referenced_galaxy_cluster_type'],
|
||||
],
|
||||
'fields' => ['id'],
|
||||
'recursive' => -1,
|
||||
]);
|
||||
if (!empty($existingRelation)) {
|
||||
if (!$force) {
|
||||
$errors[] = __('Relation already exists');
|
||||
return $errors;
|
||||
} else {
|
||||
$relation['GalaxyClusterRelation']['id'] = $existingRelation['GalaxyClusterRelation']['id'];
|
||||
}
|
||||
} else {
|
||||
$this->create();
|
||||
}
|
||||
if (empty($errors)) {
|
||||
if (!isset($relation['GalaxyClusterRelation']['referenced_galaxy_cluster_uuid'])) {
|
||||
$errors[] = __('referenced_galaxy_cluster_uuid not provided');
|
||||
return $errors;
|
||||
}
|
||||
if (!$force) {
|
||||
$targetCluster = $this->TargetCluster->fetchIfAuthorized($user, $relation['GalaxyClusterRelation']['referenced_galaxy_cluster_uuid'], 'view', $throwErrors = false, $full = false);
|
||||
if (isset($targetCluster['authorized']) && !$targetCluster['authorized']) { // do not save the relation if referenced cluster is not accessible by the user (or does not exist)
|
||||
$errors[] = array(__('Invalid referenced galaxy cluster'));
|
||||
return $errors;
|
||||
}
|
||||
}
|
||||
$relation = $this->syncUUIDsAndIDs($user, $relation);
|
||||
$relationEntity = $this->newEntity($relation);
|
||||
$saveSuccess = $this->save($relationEntity);
|
||||
if ($saveSuccess) {
|
||||
$savedRelation = $this->find('first', array(
|
||||
'conditions' => array('id' => $this->id),
|
||||
'recursive' => -1
|
||||
));
|
||||
$tags = array();
|
||||
if (!empty($relation['GalaxyClusterRelation']['tags'])) {
|
||||
$tags = $relation['GalaxyClusterRelation']['tags'];
|
||||
} elseif (!empty($relation['GalaxyClusterRelation']['GalaxyClusterRelationTag'])) {
|
||||
$tags = $relation['GalaxyClusterRelation']['GalaxyClusterRelationTag'];
|
||||
$tags = Hash::extract($tags, '{n}.name');
|
||||
} elseif (!empty($relation['GalaxyClusterRelation']['Tag'])) {
|
||||
$tags = $relation['GalaxyClusterRelation']['Tag'];
|
||||
$tags = Hash::extract($tags, '{n}.name');
|
||||
}
|
||||
|
||||
if (!empty($tags)) {
|
||||
$tagSaveResults = $this->GalaxyClusterRelationTag->attachTags($user, $this->id, $tags, $capture = $captureTag);
|
||||
if (!$tagSaveResults) {
|
||||
$errors[] = __('Tags could not be saved for relation (%s)', $this->id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($this->validationErrors as $validationError) {
|
||||
$errors[] = $validationError[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* editRelation Respecting ACL edits a relation and set correct fields where applicable.
|
||||
* Contrary to its capture equivalent, trying to save a relation for a unknown target cluster will fail.
|
||||
*
|
||||
* @param array $user
|
||||
* @param array $relation The relation to be saved
|
||||
* @param array $fieldList Only edit the fields provided
|
||||
* @param bool $captureTag Should the tag be captured if it doesn't exists
|
||||
* @return array List of errors if any
|
||||
*/
|
||||
public function editRelation(array $user, array $relation, array $fieldList = array(), $captureTag = false)
|
||||
{
|
||||
$SharingGroupsTable = $this->fetchTable('SharingGroups');
|
||||
$errors = array();
|
||||
if (!isset($relation['GalaxyClusterRelation']['galaxy_cluster_id'])) {
|
||||
$errors[] = __('galaxy_cluster_id not provided');
|
||||
return $errors;
|
||||
}
|
||||
$authorizationCheck = $this->SourceCluster->fetchIfAuthorized($user, $relation['GalaxyClusterRelation']['galaxy_cluster_id'], array('edit'), $throwErrors = false, $full = false);
|
||||
if (isset($authorizationCheck['authorized']) && !$authorizationCheck['authorized']) {
|
||||
$errors[] = $authorizationCheck['error'];
|
||||
return $errors;
|
||||
}
|
||||
|
||||
if (isset($relation['GalaxyClusterRelation']['id'])) {
|
||||
$existingRelation = $this->find('first', array('conditions' => array('GalaxyClusterRelation.id' => $relation['GalaxyClusterRelation']['id'])));
|
||||
} else {
|
||||
$errors[] = __('UUID not provided');
|
||||
}
|
||||
if (empty($existingRelation)) {
|
||||
$errors[] = __('Unkown ID');
|
||||
} else {
|
||||
$options = array('conditions' => array(
|
||||
'uuid' => $relation['GalaxyClusterRelation']['galaxy_cluster_uuid']
|
||||
));
|
||||
$cluster = $this->SourceCluster->fetchGalaxyClusters($user, $options);
|
||||
if (empty($cluster)) {
|
||||
$errors[] = __('Invalid source galaxy cluster');
|
||||
}
|
||||
$cluster = $cluster[0];
|
||||
$relation['GalaxyClusterRelation']['id'] = $existingRelation['GalaxyClusterRelation']['id'];
|
||||
$relation['GalaxyClusterRelation']['galaxy_cluster_id'] = $cluster['SourceCluster']['id'];
|
||||
$relation['GalaxyClusterRelation']['galaxy_cluster_uuid'] = $cluster['SourceCluster']['uuid'];
|
||||
|
||||
if (isset($relation['GalaxyClusterRelation']['distribution']) && $relation['GalaxyClusterRelation']['distribution'] == 4 && !$SharingGroupsTable->checkIfAuthorised($user, $relation['GalaxyClusterRelation']['sharing_group_id'])) {
|
||||
$errors[] = array(__('Galaxy Cluster Relation could not be saved: The user has to have access to the sharing group in order to be able to edit it.'));
|
||||
}
|
||||
|
||||
if (empty($errors)) {
|
||||
if (!isset($relation['GalaxyClusterRelation']['referenced_galaxy_cluster_uuid'])) {
|
||||
$errors[] = __('referenced_galaxy_cluster_uuid not provided');
|
||||
return $errors;
|
||||
}
|
||||
$targetCluster = $this->TargetCluster->fetchIfAuthorized($user, $relation['GalaxyClusterRelation']['referenced_galaxy_cluster_uuid'], 'view', $throwErrors = false, $full = false);
|
||||
if (isset($targetCluster['authorized']) && !$targetCluster['authorized']) { // do not save the relation if referenced cluster is not accessible by the user (or does not exist)
|
||||
$errors[] = array(__('Invalid referenced galaxy cluster'));
|
||||
return $errors;
|
||||
}
|
||||
$relation['GalaxyClusterRelation']['referenced_galaxy_cluster_id'] = $targetCluster['TargetCluster']['id'];
|
||||
$relation['GalaxyClusterRelation']['referenced_galaxy_cluster_uuid'] = $targetCluster['TargetCluster']['uuid'];
|
||||
$relation['GalaxyClusterRelation']['default'] = false;
|
||||
if (empty($fieldList)) {
|
||||
$fieldList = array('galaxy_cluster_id', 'galaxy_cluster_uuid', 'referenced_galaxy_cluster_id', 'referenced_galaxy_cluster_uuid', 'referenced_galaxy_cluster_type', 'distribution', 'sharing_group_id', 'default');
|
||||
}
|
||||
$relationEntity = $this->newEntity($relation);
|
||||
$saveSuccess = $this->save($relationEntity, array('fieldList' => $fieldList));
|
||||
if (!$saveSuccess) {
|
||||
foreach ($this->validationErrors as $validationError) {
|
||||
$errors[] = $validationError[0];
|
||||
}
|
||||
} else {
|
||||
$this->GalaxyClusterRelationTag->deleteAll(array('GalaxyClusterRelationTag.galaxy_cluster_relation_id' => $relation['GalaxyClusterRelation']['id']));
|
||||
$this->GalaxyClusterRelationTag->attachTags($user, $relation['GalaxyClusterRelation']['id'], $relation['GalaxyClusterRelation']['tags'], $capture = $captureTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
public function bulkSaveRelations(array $relations)
|
||||
{
|
||||
// Fetch existing tags Name => ID mapping
|
||||
$tagNameToId = $this->GalaxyClusterRelationTag->Tag->find('list', [
|
||||
'fields' => ['Tag.name', 'Tag.id'],
|
||||
'callbacks' => false,
|
||||
]);
|
||||
|
||||
// Fetch all cluster UUID => ID mapping
|
||||
$galaxyClusterUuidToId = $this->TargetCluster->find('list', [
|
||||
'fields' => ['uuid', 'id'],
|
||||
'callbacks' => false,
|
||||
]);
|
||||
|
||||
$lookupSavedIds = [];
|
||||
$relationTagsToSave = [];
|
||||
foreach ($relations as &$relation) {
|
||||
if (isset($galaxyClusterUuidToId[$relation['referenced_galaxy_cluster_uuid']])) {
|
||||
$relation['referenced_galaxy_cluster_id'] = $galaxyClusterUuidToId[$relation['referenced_galaxy_cluster_uuid']];
|
||||
} else {
|
||||
$relation['referenced_galaxy_cluster_id'] = 0; // referenced cluster doesn't exists
|
||||
}
|
||||
if (!empty($relation['tags'])) {
|
||||
$lookupSavedIds[$relation['galaxy_cluster_id']] = true;
|
||||
foreach ($relation['tags'] as $tag) {
|
||||
if (!isset($tagNameToId[$tag])) {
|
||||
$tagNameToId[$tag] = $this->GalaxyClusterRelationTag->Tag->quickAdd($tag);
|
||||
}
|
||||
$relationTagsToSave[$relation['galaxy_cluster_uuid']][$relation['referenced_galaxy_cluster_uuid']][] = $tagNameToId[$tag];
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($galaxyClusterUuidToId, $tagNameToId);
|
||||
|
||||
$this->saveMany($relations, ['validate' => false]); // Some clusters uses invalid UUID :/
|
||||
|
||||
// Insert tags
|
||||
$savedRelations = $this->find('all', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['galaxy_cluster_id' => array_keys($lookupSavedIds)],
|
||||
'fields' => ['id', 'galaxy_cluster_uuid', 'referenced_galaxy_cluster_uuid']
|
||||
]);
|
||||
$relation_tags = [];
|
||||
foreach ($savedRelations as $savedRelation) {
|
||||
$uuid1 = $savedRelation['GalaxyClusterRelation']['galaxy_cluster_uuid'];
|
||||
$uuid2 = $savedRelation['GalaxyClusterRelation']['referenced_galaxy_cluster_uuid'];
|
||||
if (isset($relationTagsToSave[$uuid1][$uuid2])) {
|
||||
foreach ($relationTagsToSave[$uuid1][$uuid2] as $tagId) {
|
||||
$relation_tags[] = [$savedRelation['GalaxyClusterRelation']['id'], $tagId];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($relation_tags)) {
|
||||
$db = $this->getDataSource();
|
||||
$fields = array('galaxy_cluster_relation_id', 'tag_id');
|
||||
$db->insertMulti('galaxy_cluster_relation_tags', $fields, $relation_tags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a relation then save it.
|
||||
*
|
||||
* @param array $user
|
||||
* @param array $cluster The cluster for which the relation is being saved
|
||||
* @param array $relation The relation to be saved
|
||||
* @param bool $fromPull If the current capture is performed from a PULL sync. If set, it allows edition of existing relations
|
||||
* @return array The capture success results
|
||||
*/
|
||||
public function captureRelations(array $user, array $cluster, array $relations, $fromPull = false)
|
||||
{
|
||||
$results = array('success' => false, 'imported' => 0, 'failed' => 0);
|
||||
$LogsTable = $this->fetchTable('Logs');
|
||||
$clusterUuid = $cluster['GalaxyCluster']['uuid'];
|
||||
|
||||
foreach ($relations as $k => $relation) {
|
||||
if (!isset($relation['GalaxyClusterRelation'])) {
|
||||
$relation = array('GalaxyClusterRelation' => $relation);
|
||||
}
|
||||
$relation['GalaxyClusterRelation']['galaxy_cluster_uuid'] = $clusterUuid;
|
||||
$relation['GalaxyClusterRelation']['galaxy_cluster_id'] = $cluster['GalaxyCluster']['id'];
|
||||
|
||||
if (empty($relation['GalaxyClusterRelation']['referenced_galaxy_cluster_uuid'])) {
|
||||
$LogsTable->createLogEntry($user, 'captureRelations', 'GalaxyClusterRelation', 0, __('No referenced cluster UUID provided'), __('relation for cluster (%s)', $clusterUuid));
|
||||
$results['failed']++;
|
||||
continue;
|
||||
} else {
|
||||
$options = array(
|
||||
'conditions' => array(
|
||||
'uuid' => $relation['GalaxyClusterRelation']['referenced_galaxy_cluster_uuid'],
|
||||
),
|
||||
'fields' => array(
|
||||
'id', 'uuid',
|
||||
)
|
||||
);
|
||||
$referencedCluster = $this->SourceCluster->fetchGalaxyClusters($user, $options);
|
||||
if (empty($referencedCluster)) {
|
||||
if (!$fromPull) {
|
||||
$LogsTable->createLogEntry($user, 'captureRelations', 'GalaxyClusterRelation', 0, __('Referenced cluster not found'), __('relation to (%s) for cluster (%s)', $relation['GalaxyClusterRelation']['referenced_galaxy_cluster_uuid'], $clusterUuid));
|
||||
}
|
||||
$relation['GalaxyClusterRelation']['referenced_galaxy_cluster_id'] = 0;
|
||||
} else {
|
||||
$referencedCluster = $referencedCluster[0];
|
||||
$relation['GalaxyClusterRelation']['referenced_galaxy_cluster_id'] = $referencedCluster['SourceCluster']['id'];
|
||||
}
|
||||
}
|
||||
|
||||
$existingRelation = $this->find('first', array('conditions' => array(
|
||||
'GalaxyClusterRelation.galaxy_cluster_uuid' => $relation['GalaxyClusterRelation']['galaxy_cluster_uuid'],
|
||||
'GalaxyClusterRelation.referenced_galaxy_cluster_uuid' => $relation['GalaxyClusterRelation']['referenced_galaxy_cluster_uuid'],
|
||||
'GalaxyClusterRelation.referenced_galaxy_cluster_type' => $relation['GalaxyClusterRelation']['referenced_galaxy_cluster_type'],
|
||||
)));
|
||||
if (!empty($existingRelation)) {
|
||||
if (!$fromPull) {
|
||||
$LogsTable->createLogEntry($user, 'captureRelations', 'GalaxyClusterRelation', 0, __('Relation already exists'), __('relation to (%s) for cluster (%s)', $relation['GalaxyClusterRelation']['referenced_galaxy_cluster_uuid'], $clusterUuid));
|
||||
$results['failed']++;
|
||||
continue;
|
||||
} else {
|
||||
$relation['GalaxyClusterRelation']['id'] = $existingRelation['GalaxyClusterRelation']['id'];
|
||||
}
|
||||
} else {
|
||||
unset($relation['GalaxyClusterRelation']['id']);
|
||||
$this->create();
|
||||
}
|
||||
|
||||
$EventsTable = $this->fetchTable('Events');
|
||||
if (isset($relation['GalaxyClusterRelation']['distribution']) && $relation['GalaxyClusterRelation']['distribution'] == 4) {
|
||||
$relation['GalaxyClusterRelation'] = $EventsTable->captureSGForElement($relation['GalaxyClusterRelation'], $user);
|
||||
}
|
||||
|
||||
$saveSuccess = $this->save($relation);
|
||||
if ($saveSuccess) {
|
||||
$results['imported']++;
|
||||
$modelKey = false;
|
||||
if (!empty($relation['GalaxyClusterRelation']['GalaxyClusterRelationTag'])) {
|
||||
$modelKey = 'GalaxyClusterRelationTag';
|
||||
} elseif (!empty($relation['GalaxyClusterRelation']['Tag'])) {
|
||||
$modelKey = 'Tag';
|
||||
}
|
||||
if ($modelKey !== false) {
|
||||
$tagNames = Hash::extract($relation['GalaxyClusterRelation'][$modelKey], '{n}.name');
|
||||
// Similar behavior as for AttributeTags: Here we only attach tags. If they were removed at some point it's not taken into account.
|
||||
// Since we don't have tag soft-deletion, tags added by users will be kept.
|
||||
$this->GalaxyClusterRelationTag->attachTags($user, $this->id, $tagNames, $capture = true);
|
||||
}
|
||||
} else {
|
||||
$results['failed']++;
|
||||
}
|
||||
}
|
||||
|
||||
$results['success'] = $results['imported'] > 0;
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function removeNonAccessibleTargetCluster($user, $relations)
|
||||
{
|
||||
$availableTargetClusterIDs = $this->TargetCluster->cacheGalaxyClusterIDs($user);
|
||||
$availableTargetClusterIDsKeyed = array_flip($availableTargetClusterIDs);
|
||||
foreach ($relations as $i => $relation) {
|
||||
if (
|
||||
isset($relation['TargetCluster']['id']) &&
|
||||
!isset($availableTargetClusterIDsKeyed[$relation['TargetCluster']['id']])
|
||||
) {
|
||||
$relations[$i]['TargetCluster'] = null;
|
||||
}
|
||||
}
|
||||
return $relations;
|
||||
}
|
||||
|
||||
/**
|
||||
* syncUUIDsAndIDs Adapt IDs of source and target cluster inside the relation based on the provided two UUIDs
|
||||
*
|
||||
* @param array $user
|
||||
* @param array $relation
|
||||
* @return array The adpated relation
|
||||
*/
|
||||
private function syncUUIDsAndIDs(array $user, array $relation)
|
||||
{
|
||||
$options = array('conditions' => array(
|
||||
'uuid' => $relation['GalaxyClusterRelation']['galaxy_cluster_uuid']
|
||||
));
|
||||
$sourceCluster = $this->SourceCluster->fetchGalaxyClusters($user, $options);
|
||||
if (!empty($sourceCluster)) {
|
||||
$sourceCluster = $sourceCluster[0];
|
||||
$relation['GalaxyClusterRelation']['galaxy_cluster_id'] = $sourceCluster['SourceCluster']['id'];
|
||||
$relation['GalaxyClusterRelation']['galaxy_cluster_uuid'] = $sourceCluster['SourceCluster']['uuid'];
|
||||
}
|
||||
$options = array('conditions' => array(
|
||||
'uuid' => $relation['GalaxyClusterRelation']['referenced_galaxy_cluster_uuid']
|
||||
));
|
||||
$targetCluster = $this->TargetCluster->fetchGalaxyClusters($user, $options);
|
||||
if (!empty($targetCluster)) {
|
||||
$targetCluster = $targetCluster[0];
|
||||
$relation['GalaxyClusterRelation']['referenced_galaxy_cluster_id'] = $targetCluster['TargetCluster']['id'];
|
||||
$relation['GalaxyClusterRelation']['referenced_galaxy_cluster_uuid'] = $targetCluster['TargetCluster']['uuid'];
|
||||
} else {
|
||||
$relation['GalaxyClusterRelation']['referenced_galaxy_cluster_id'] = 0;
|
||||
}
|
||||
return $relation;
|
||||
}
|
||||
}
|
|
@ -25,6 +25,9 @@ use Cake\Http\Exception\NotFoundException;
|
|||
use Cake\Validation\Validation;
|
||||
use Exception;
|
||||
use App\Http\Exception\HttpSocketHttpException;
|
||||
use Cake\Validation\Validator;
|
||||
use App\Model\Entity\Distribution;
|
||||
use Cake\ORM\RulesChecker;
|
||||
|
||||
/**
|
||||
* @property Tag $Tag
|
||||
|
@ -33,7 +36,7 @@ use App\Http\Exception\HttpSocketHttpException;
|
|||
* @property GalaxyElement $GalaxyElement
|
||||
* @property SharingGroup $SharingGroup
|
||||
*/
|
||||
class GalaxyClusterTable extends AppTable
|
||||
class GalaxyClustersTable extends AppTable
|
||||
{
|
||||
use LocatorAwareTrait;
|
||||
|
||||
|
@ -46,10 +49,113 @@ class GalaxyClusterTable extends AppTable
|
|||
'json' => array('json', 'JsonExport', 'json'),
|
||||
);
|
||||
|
||||
public function validationDefault(Validator $validator): Validator
|
||||
{
|
||||
$validator
|
||||
->requirePresence(['value'])
|
||||
->add(
|
||||
'value',
|
||||
'stringNotEmpty',
|
||||
[
|
||||
'rule' => 'stringNotEmpty',
|
||||
'message' => 'Please provide a value'
|
||||
]
|
||||
)
|
||||
->add(
|
||||
'uuid',
|
||||
'uuid',
|
||||
[
|
||||
'rule' => 'uuid',
|
||||
'message' => 'Please provide a valid RFC 4122 UUID'
|
||||
]
|
||||
)
|
||||
->add(
|
||||
'distribution',
|
||||
'inList',
|
||||
[
|
||||
'rule' => ['inList', Distribution::ALL],
|
||||
'message' => 'Options: ' . implode(', ', Distribution::DESCRIPTION)
|
||||
]
|
||||
)
|
||||
->add(
|
||||
'published',
|
||||
'boolean',
|
||||
[
|
||||
'rule' => 'boolean'
|
||||
]
|
||||
);
|
||||
|
||||
return $validator;
|
||||
}
|
||||
|
||||
public function buildRules(RulesChecker $rules): RulesChecker
|
||||
{
|
||||
$rules->add($rules->isUnique(['uuid']));
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function initialize(array $config): void
|
||||
{
|
||||
parent::initialize($config);
|
||||
$this->addBehavior('AuditLog');
|
||||
|
||||
$this->belongsTo(
|
||||
'Galaxy',
|
||||
[
|
||||
'className' => 'Galaxy',
|
||||
'foreignKey' => 'galaxy_id',
|
||||
]
|
||||
);
|
||||
$this->belongsTo(
|
||||
'Tag',
|
||||
[
|
||||
'foreignKey' => false,
|
||||
'conditions' => array('GalaxyCluster.tag_name = Tag.name')
|
||||
]
|
||||
);
|
||||
$this->belongsTo(
|
||||
'Org',
|
||||
[
|
||||
'className' => 'Organisation',
|
||||
'foreignKey' => 'org_id'
|
||||
]
|
||||
);
|
||||
$this->belongsTo(
|
||||
'Orgc',
|
||||
[
|
||||
'className' => 'Organisation',
|
||||
'foreignKey' => 'orgc_id'
|
||||
]
|
||||
);
|
||||
$this->belongsTo(
|
||||
'SharingGroup',
|
||||
[
|
||||
'foreignKey' => 'sharing_group_id',
|
||||
'propertyName' => 'SharingGroup'
|
||||
]
|
||||
);
|
||||
|
||||
$this->hasMany(
|
||||
'GalaxyElement',
|
||||
[
|
||||
'dependent' => true,
|
||||
]
|
||||
);
|
||||
$this->hasMany(
|
||||
'GalaxyClusterRelations',
|
||||
[
|
||||
'foreignKey' => 'galaxy_cluster_id',
|
||||
'dependent' => true,
|
||||
'propertyName' => 'GalaxyClusterRelation'
|
||||
]
|
||||
);
|
||||
$this->hasMany(
|
||||
'TargetingClusterRelation',
|
||||
[
|
||||
'foreignKey' => 'referenced_galaxy_cluster_id',
|
||||
'propertyName' => 'TargetingClusterRelation'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function beforeMarshal(EventInterface $event, ArrayObject $data, ArrayObject $options)
|
||||
|
@ -74,7 +180,7 @@ class GalaxyClusterTable extends AppTable
|
|||
public function find(string $type = 'all', array $options = []): Query
|
||||
{
|
||||
$mapper = function ($row) {
|
||||
// TODO: use JsonFieldBehavior
|
||||
// TODO: [3.x-MIGRATION] use JsonFieldBehavior
|
||||
if (isset($row['authors'])) {
|
||||
$row['authors'] = json_decode($row['authors'], true);
|
||||
}
|
||||
|
@ -109,9 +215,9 @@ class GalaxyClusterTable extends AppTable
|
|||
// Update all relations IDs that are unknown but saved
|
||||
if (!$this->bulkEntry) {
|
||||
$cluster = $this->fetchAndSetUUID($entity);
|
||||
$this->GalaxyClusterRelation->updateAll(
|
||||
array('GalaxyClusterRelation.referenced_galaxy_cluster_id' => $cluster['id']),
|
||||
array('GalaxyClusterRelation.referenced_galaxy_cluster_uuid' => $cluster['uuid'])
|
||||
$this->GalaxyClusterRelations->updateAll(
|
||||
array('GalaxyClusterRelations.referenced_galaxy_cluster_id' => $cluster['id']),
|
||||
array('GalaxyClusterRelations.referenced_galaxy_cluster_uuid' => $cluster['uuid'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -134,12 +240,12 @@ class GalaxyClusterTable extends AppTable
|
|||
{
|
||||
// Remove all relations IDs now that the cluster is unknown
|
||||
if (!empty($this->deletedClusterUUID)) {
|
||||
$this->GalaxyClusterRelation->updateAll(
|
||||
array('GalaxyClusterRelation.referenced_galaxy_cluster_id' => 0),
|
||||
array('GalaxyClusterRelation.referenced_galaxy_cluster_uuid' => $this->deletedClusterUUID)
|
||||
$this->GalaxyClusterRelations->updateAll(
|
||||
array('GalaxyClusterRelations.referenced_galaxy_cluster_id' => 0),
|
||||
array('GalaxyClusterRelations.referenced_galaxy_cluster_uuid' => $this->deletedClusterUUID)
|
||||
);
|
||||
$this->GalaxyElement->deleteAll(array('GalaxyElement.galaxy_cluster_id' => $entity->id));
|
||||
$this->GalaxyClusterRelation->deleteAll(array('GalaxyClusterRelation.galaxy_cluster_uuid' => $this->deletedClusterUUID));
|
||||
$this->GalaxyClusterRelations->deleteAll(array('GalaxyClusterRelations.galaxy_cluster_uuid' => $this->deletedClusterUUID));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,7 +268,7 @@ class GalaxyClusterTable extends AppTable
|
|||
|
||||
public function generateMissingRelations()
|
||||
{
|
||||
$missingRelations = $this->GalaxyClusterRelation->find('column', [
|
||||
$missingRelations = $this->GalaxyClusterRelations->find('all', [
|
||||
'conditions' => ['referenced_galaxy_cluster_id' => 0],
|
||||
'fields' => ['referenced_galaxy_cluster_uuid'],
|
||||
'unique' => true,
|
||||
|
@ -175,7 +281,7 @@ class GalaxyClusterTable extends AppTable
|
|||
'fields' => ['uuid', 'id']
|
||||
]);
|
||||
foreach ($ids as $uuid => $id) {
|
||||
$this->GalaxyClusterRelation->updateAll(
|
||||
$this->GalaxyClusterRelations->updateAll(
|
||||
['referenced_galaxy_cluster_id' => $id],
|
||||
['referenced_galaxy_cluster_uuid' => $uuid]
|
||||
);
|
||||
|
@ -310,7 +416,7 @@ class GalaxyClusterTable extends AppTable
|
|||
$this->GalaxyElement->updateElements(-1, $savedCluster['GalaxyCluster']['id'], $elementsToSave);
|
||||
}
|
||||
if (!empty($cluster['GalaxyCluster']['GalaxyClusterRelation'])) {
|
||||
$this->GalaxyClusterRelation->saveRelations($user, $cluster['GalaxyCluster'], $cluster['GalaxyCluster']['GalaxyClusterRelation'], $captureTag = true);
|
||||
$this->GalaxyClusterRelations->saveRelations($user, $cluster['GalaxyCluster'], $cluster['GalaxyCluster']['GalaxyClusterRelation'], $captureTag = true);
|
||||
}
|
||||
} else {
|
||||
foreach ($this->validationErrors as $validationError) {
|
||||
|
@ -386,7 +492,7 @@ class GalaxyClusterTable extends AppTable
|
|||
$this->GalaxyElement->updateElements($cluster['GalaxyCluster']['id'], $cluster['GalaxyCluster']['id'], $elementsToSave, $delete = $deleteOldElements);
|
||||
}
|
||||
if (!empty($cluster['GalaxyCluster']['GalaxyClusterRelation'])) {
|
||||
$this->GalaxyClusterRelation->saveRelations($user, $cluster['GalaxyCluster'], $cluster['GalaxyCluster']['GalaxyClusterRelation'], $captureTag = true, $force = true);
|
||||
$this->GalaxyClusterRelations->saveRelations($user, $cluster['GalaxyCluster'], $cluster['GalaxyCluster']['GalaxyClusterRelation'], $captureTag = true, $force = true);
|
||||
}
|
||||
} else {
|
||||
foreach ($this->validationErrors as $validationError) {
|
||||
|
@ -555,18 +661,18 @@ class GalaxyClusterTable extends AppTable
|
|||
]);
|
||||
$cluster_ids = Hash::extract($clusters, '{n}.GalaxyCluster.id');
|
||||
$cluster_uuids = Hash::extract($clusters, '{n}.GalaxyCluster.uuid');
|
||||
$relation_ids = $this->GalaxyClusterRelation->find('list', [
|
||||
$relation_ids = $this->GalaxyClusterRelations->find('list', [
|
||||
'conditions' => ['galaxy_cluster_id' => $cluster_ids],
|
||||
'fields' => ['id']
|
||||
]);
|
||||
$this->deleteAll(['GalaxyCluster.default' => true], false, false);
|
||||
$this->GalaxyElement->deleteAll(['GalaxyElement.galaxy_cluster_id' => $cluster_ids], false, false);
|
||||
$this->GalaxyClusterRelation->deleteAll(['GalaxyClusterRelation.galaxy_cluster_id' => $cluster_ids], false, false);
|
||||
$this->GalaxyClusterRelation->updateAll(
|
||||
['GalaxyClusterRelation.referenced_galaxy_cluster_id' => 0],
|
||||
['GalaxyClusterRelation.referenced_galaxy_cluster_uuid' => $cluster_uuids] // For all default clusters being referenced
|
||||
$this->GalaxyClusterRelations->deleteAll(['GalaxyClusterRelations.galaxy_cluster_id' => $cluster_ids], false, false);
|
||||
$this->GalaxyClusterRelations->updateAll(
|
||||
['GalaxyClusterRelations.referenced_galaxy_cluster_id' => 0],
|
||||
['GalaxyClusterRelations.referenced_galaxy_cluster_uuid' => $cluster_uuids] // For all default clusters being referenced
|
||||
);
|
||||
$this->GalaxyClusterRelation->GalaxyClusterRelationTag->deleteAll(['GalaxyClusterRelationTag.galaxy_cluster_relation_id' => $relation_ids], false, false);
|
||||
$this->GalaxyClusterRelations->GalaxyClusterRelationTag->deleteAll(['GalaxyClusterRelationTag.galaxy_cluster_relation_id' => $relation_ids], false, false);
|
||||
$LogTable = $this->fetchTable('Logs');
|
||||
$LogTable->createLogEntry('SYSTEM', 'wipe_default', 'GalaxyCluster', 0, "Wiping default galaxy clusters");
|
||||
}
|
||||
|
@ -788,8 +894,8 @@ class GalaxyClusterTable extends AppTable
|
|||
$this->GalaxyElement->captureElements($user, $cluster['GalaxyCluster']['GalaxyElement'], $savedCluster['GalaxyCluster']['id']);
|
||||
}
|
||||
if (!empty($cluster['GalaxyCluster']['GalaxyClusterRelation'])) {
|
||||
$this->GalaxyClusterRelation->deleteAll(array('GalaxyClusterRelation.galaxy_cluster_id' => $savedCluster['GalaxyCluster']['id']));
|
||||
$saveResult = $this->GalaxyClusterRelation->captureRelations($user, $savedCluster, $cluster['GalaxyCluster']['GalaxyClusterRelation'], $fromPull = $fromPull);
|
||||
$this->GalaxyClusterRelations->deleteAll(array('GalaxyClusterRelations.galaxy_cluster_id' => $savedCluster['GalaxyCluster']['id']));
|
||||
$saveResult = $this->GalaxyClusterRelations->captureRelations($user, $savedCluster, $cluster['GalaxyCluster']['GalaxyClusterRelation'], $fromPull = $fromPull);
|
||||
if ($saveResult['failed'] > 0) {
|
||||
$results['errors'][] = __('Issues while capturing relations have been logged.');
|
||||
}
|
||||
|
@ -868,8 +974,9 @@ class GalaxyClusterTable extends AppTable
|
|||
/* Return a list of all tags associated with the cluster specific cluster within the galaxy (or all clusters if $clusterValue is false)
|
||||
* The counts are restricted to the event IDs that the user is allowed to see.
|
||||
*/
|
||||
public function getTags($galaxyType, $clusterValue = false, $user)
|
||||
public function getTags($galaxyType, $clusterValue, $user)
|
||||
{
|
||||
$clusterValue = $clusterValue ? $clusterValue : false;
|
||||
$EventsTable = $this->fetchTable('Events');
|
||||
$event_ids = $EventsTable->fetchEventIds($user, [
|
||||
'list' => true
|
||||
|
@ -1000,7 +1107,7 @@ class GalaxyClusterTable extends AppTable
|
|||
'Galaxy',
|
||||
'GalaxyElement',
|
||||
'GalaxyClusterRelation' => array(
|
||||
'conditions' => $this->GalaxyClusterRelation->buildConditions($user, false),
|
||||
'conditions' => $this->GalaxyClusterRelations->buildConditions($user, false),
|
||||
'GalaxyClusterRelationTag',
|
||||
'SharingGroup',
|
||||
),
|
||||
|
@ -1069,11 +1176,11 @@ class GalaxyClusterTable extends AppTable
|
|||
)
|
||||
));
|
||||
|
||||
$tagsToFetch = Hash::extract($clusters, "{n}.GalaxyClusterRelation.{n}.GalaxyClusterRelationTag.{n}.tag_id");
|
||||
$tagsToFetch = Hash::extract($clusters, "{n}.GalaxyClusterRelations.{n}.GalaxyClusterRelationTag.{n}.tag_id");
|
||||
$tagsToFetch = array_merge($tagsToFetch, Hash::extract($targetingClusterRelations, "GalaxyClusterRelationTag.{n}.tag_id"));
|
||||
|
||||
if (!empty($tagsToFetch)) {
|
||||
$tags = $this->GalaxyClusterRelation->GalaxyClusterRelationTag->Tag->find('all', [
|
||||
$tags = $this->GalaxyClusterRelations->GalaxyClusterRelationTag->Tag->find('all', [
|
||||
'conditions' => ['id' => array_unique($tagsToFetch, SORT_REGULAR)],
|
||||
'recursive' => -1,
|
||||
]);
|
|
@ -0,0 +1,181 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Table;
|
||||
|
||||
use App\Model\Table\AppTable;
|
||||
use Cake\Utility\Hash;
|
||||
|
||||
/**
|
||||
* @property GalaxyCluster $GalaxyCluster
|
||||
*/
|
||||
class GalaxyElements extends AppTable
|
||||
{
|
||||
public $useTable = 'galaxy_elements';
|
||||
|
||||
public $recursive = -1;
|
||||
|
||||
public function initialize(array $config): void
|
||||
{
|
||||
parent::initialize($config);
|
||||
$this->addBehavior('AuditLog');
|
||||
|
||||
$this->belongsTo(
|
||||
'GalaxyClusters',
|
||||
[
|
||||
'className' => 'GalaxyCluster',
|
||||
'foreignKey' => 'galaxy_cluster_id',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function updateElements($oldClusterId, $newClusterId, $elements, $delete = true)
|
||||
{
|
||||
if ($delete) {
|
||||
$this->deleteAll(array('GalaxyElement.galaxy_cluster_id' => $oldClusterId));
|
||||
}
|
||||
$tempElements = array();
|
||||
foreach ($elements as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $arrayElement) {
|
||||
$tempElements[] = array(
|
||||
'key' => $key,
|
||||
'value' => $arrayElement,
|
||||
'galaxy_cluster_id' => $newClusterId
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$tempElements[] = array(
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
'galaxy_cluster_id' => $newClusterId
|
||||
);
|
||||
}
|
||||
}
|
||||
$this->saveMany($tempElements);
|
||||
}
|
||||
|
||||
public function update($galaxy_id, $oldClusters, $newClusters)
|
||||
{
|
||||
$elementsToSave = array();
|
||||
// Since we are dealing with flat files as the end all be all content, we are safe to just drop all of the old clusters and recreate them.
|
||||
foreach ($oldClusters as $oldCluster) {
|
||||
$this->deleteAll(array('GalaxyElement.galaxy_cluster_id' => $oldCluster['GalaxyCluster']['id']));
|
||||
}
|
||||
foreach ($newClusters as $newCluster) {
|
||||
$tempCluster = array();
|
||||
foreach ($newCluster as $key => $value) {
|
||||
// Don't store the reserved fields as elements
|
||||
if ($key == 'description' || $key == 'value') {
|
||||
continue;
|
||||
}
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $arrayElement) {
|
||||
$tempCluster[] = array('key' => $key, 'value' => $arrayElement);
|
||||
}
|
||||
} else {
|
||||
$tempCluster[] = array('key' => $key, 'value' => $value);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($tempCluster as $key => $value) {
|
||||
$tempCluster[$key]['galaxy_cluster_id'] = $oldCluster['GalaxyCluster']['id'];
|
||||
}
|
||||
$elementsToSave = array_merge($elementsToSave, $tempCluster);
|
||||
}
|
||||
$this->saveMany($elementsToSave);
|
||||
}
|
||||
|
||||
public function captureElements($user, $elements, $clusterId)
|
||||
{
|
||||
$tempElements = array();
|
||||
foreach ($elements as $k => $element) {
|
||||
$tempElements[] = array(
|
||||
'key' => $element['key'],
|
||||
'value' => $element['value'],
|
||||
'galaxy_cluster_id' => $clusterId,
|
||||
);
|
||||
}
|
||||
$this->saveMany($tempElements);
|
||||
}
|
||||
|
||||
public function buildACLConditions($user)
|
||||
{
|
||||
$conditions = [];
|
||||
if (!$user['Role']['perm_site_admin']) {
|
||||
$conditions = $this->GalaxyCluster->buildConditions($user);
|
||||
}
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
public function buildClusterConditions($user, $clusterId)
|
||||
{
|
||||
return [
|
||||
$this->buildACLConditions($user),
|
||||
'GalaxyCluster.id' => $clusterId
|
||||
];
|
||||
}
|
||||
|
||||
public function fetchElements(array $user, $clusterId)
|
||||
{
|
||||
$params = array(
|
||||
'conditions' => $this->buildClusterConditions($user, $clusterId),
|
||||
'contain' => ['GalaxyCluster' => ['fields' => ['id', 'distribution', 'org_id']]],
|
||||
'recursive' => -1
|
||||
);
|
||||
$elements = $this->find('all', $params);
|
||||
foreach ($elements as $i => $element) {
|
||||
$elements[$i] = $elements[$i]['GalaxyElement'];
|
||||
unset($elements[$i]['GalaxyCluster']);
|
||||
unset($elements[$i]['GalaxyElement']);
|
||||
}
|
||||
return $elements;
|
||||
}
|
||||
|
||||
public function getExpandedJSONFromElements($elements)
|
||||
{
|
||||
$keyedValue = [];
|
||||
foreach ($elements as $i => $element) {
|
||||
$keyedValue[$element['GalaxyElement']['key']][] = $element['GalaxyElement']['value'];
|
||||
}
|
||||
$expanded = Hash::expand($keyedValue);
|
||||
return $expanded;
|
||||
}
|
||||
|
||||
/**
|
||||
* getClusterIDsFromMatchingElements
|
||||
*
|
||||
* @param array $user
|
||||
* @param array $elements an associative array containg the elements to search for
|
||||
* Example: {"synonyms": "apt42"}
|
||||
* @return array
|
||||
*/
|
||||
public function getClusterIDsFromMatchingElements(array $user, array $elements): array
|
||||
{
|
||||
$conditionCount = 0;
|
||||
$elementConditions = [];
|
||||
foreach ($elements as $key => $value) {
|
||||
$elementConditions['OR'][] = [
|
||||
'GalaxyElement.key' => $key,
|
||||
'GalaxyElement.value' => $value,
|
||||
];
|
||||
$conditionCount += is_array($value) ? count($value) : 1;
|
||||
}
|
||||
$conditions = [
|
||||
$this->buildACLConditions($user),
|
||||
$elementConditions,
|
||||
];
|
||||
$elements = $this->find('all', [
|
||||
'fields' => ['GalaxyElement.galaxy_cluster_id'],
|
||||
'conditions' => $conditions,
|
||||
'contain' => ['GalaxyCluster' => ['fields' => ['id', 'distribution', 'org_id']]],
|
||||
'group' => ['GalaxyElement.galaxy_cluster_id'],
|
||||
'having' => ['COUNT(GalaxyElement.id) =' => $conditionCount],
|
||||
'recursive' => -1
|
||||
]);
|
||||
$clusterIDs = [];
|
||||
foreach ($elements as $element) {
|
||||
$clusterIDs[] = $element['GalaxyElement']['galaxy_cluster_id'];
|
||||
}
|
||||
return $clusterIDs;
|
||||
}
|
||||
}
|
|
@ -2,36 +2,34 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Test\TestCase\Api\ObjectTemplates;
|
||||
namespace App\Test\TestCase\Api\Noticelists;
|
||||
|
||||
use Cake\TestSuite\TestCase;
|
||||
use App\Test\Fixture\AuthKeysFixture;
|
||||
use App\Test\Fixture\ObjectTemplatesFixture;
|
||||
use App\Test\Helper\ApiTestTrait;
|
||||
use Cake\TestSuite\TestCase;
|
||||
|
||||
class IndexObjectTemplatesApiTest extends TestCase
|
||||
class UpdateNoticelistsApiTest extends TestCase
|
||||
{
|
||||
use ApiTestTrait;
|
||||
|
||||
protected const ENDPOINT = '/object-templates/index';
|
||||
protected const ENDPOINT = '/noticelists/update';
|
||||
|
||||
protected $fixtures = [
|
||||
'app.Organisations',
|
||||
'app.Users',
|
||||
'app.AuthKeys',
|
||||
'app.ObjectTemplates',
|
||||
'app.ObjectTemplateElements',
|
||||
'app.Galaxies'
|
||||
];
|
||||
|
||||
public function testIndexObjectTemplates(): void
|
||||
public function testUpdateNoticelists(): void
|
||||
{
|
||||
$this->skipOpenApiValidations();
|
||||
|
||||
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
|
||||
|
||||
$this->get(self::ENDPOINT);
|
||||
$this->post(self::ENDPOINT);
|
||||
|
||||
$this->assertResponseOk();
|
||||
$this->assertResponseContains(sprintf('"name": "%s"', ObjectTemplatesFixture::OBJECT_TEMPLATE_1_NAME));
|
||||
$this->assertDbRecordExists('Noticelists', ['name' => 'gdpr']);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue