mirror of https://github.com/MISP/MISP
new: [wip] migrate taxonomies to 3.x
parent
dadd0442f5
commit
ca58366491
|
@ -7,3 +7,6 @@
|
|||
[submodule "libraries/misp-galaxy"]
|
||||
path = libraries/misp-galaxy
|
||||
url = https://github.com/MISP/misp-galaxy
|
||||
[submodule "libraries/misp-taxonomies"]
|
||||
path = libraries/misp-taxonomies
|
||||
url = https://github.com/MISP/misp-taxonomies
|
||||
|
|
|
@ -56,6 +56,8 @@ return [
|
|||
'log_paranoid_include_sql_queries' => false,
|
||||
'log_new_audit_compress' => false,
|
||||
'log_paranoid_include_post_body' => false,
|
||||
'redis_host' => 'redis',
|
||||
'redis_port' => 6379,
|
||||
],
|
||||
'BackgroundJobs' => [
|
||||
'enabled' => true,
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 47ca23022be96a30b7bf6cc021132af81420466f
|
|
@ -0,0 +1,661 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Controller\AppController;
|
||||
use Cake\Http\Exception\NotFoundException;
|
||||
use Exception;
|
||||
use Cake\Http\Exception\MethodNotAllowedException;
|
||||
use App\Lib\Tools\CustomPaginationTool;
|
||||
use Cake\ORM\Locator\LocatorAwareTrait;
|
||||
|
||||
class TaxonomiesController extends AppController
|
||||
{
|
||||
use LocatorAwareTrait;
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 60,
|
||||
'contain' => array(
|
||||
'TaxonomyPredicates' => array(
|
||||
'fields' => array('TaxonomyPredicates.id', 'TaxonomyPredicates.taxonomy_id', 'TaxonomyPredicates.value'),
|
||||
'TaxonomyEntries' => array('fields' => array('TaxonomyEntries.id', 'TaxonomyEntries.taxonomy_predicate_id', 'TaxonomyEntries.value'))
|
||||
)
|
||||
),
|
||||
'order' => array(
|
||||
'Taxonomies.id' => 'DESC'
|
||||
),
|
||||
);
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->paginate['recursive'] = -1;
|
||||
|
||||
if (!empty($this->request->getQueryParams()['value'])) {
|
||||
$this->paginate['conditions']['id'] = $this->__search($this->request->getQueryParams()['value']);
|
||||
}
|
||||
|
||||
if (isset($this->request->getQueryParams()['enabled'])) {
|
||||
$this->paginate['conditions']['enabled'] = $this->request->getQueryParams()['enabled'] ? 1 : 0;
|
||||
}
|
||||
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
$keepFields = array('conditions', 'contain', 'recursive', 'sort');
|
||||
$searchParams = array();
|
||||
foreach ($keepFields as $field) {
|
||||
if (!empty($this->paginate[$field])) {
|
||||
$searchParams[$field] = $this->paginate[$field];
|
||||
}
|
||||
}
|
||||
$taxonomies = $this->Taxonomies->find('all', $searchParams);
|
||||
} else {
|
||||
$taxonomies = $this->paginate();
|
||||
}
|
||||
|
||||
$taxonomies = $this->__tagCount($taxonomies->toArray());
|
||||
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->viewData($taxonomies, $this->response->getType());
|
||||
}
|
||||
|
||||
$this->set('taxonomies', $taxonomies);
|
||||
$this->set('passedArgsArray', $this->request->getQueryParams());
|
||||
}
|
||||
|
||||
public function view($id)
|
||||
{
|
||||
$taxonomy = $this->Taxonomies->getTaxonomy($id, $this->ParamHandler->isRest());
|
||||
if (empty($taxonomy)) {
|
||||
throw new NotFoundException(__('Taxonomy not found.'));
|
||||
}
|
||||
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->viewData($taxonomy, $this->response->getType());
|
||||
}
|
||||
|
||||
$this->set('taxonomy', $taxonomy['Taxonomy']);
|
||||
$this->set('id', $taxonomy['Taxonomy']['id']);
|
||||
}
|
||||
|
||||
public function taxonomy_tags($id)
|
||||
{
|
||||
$urlparams = '';
|
||||
$filter = isset($this->request->getQueryParams()['filter']) ? $this->request->getQueryParams()['filter'] : false;
|
||||
$taxonomy = $this->Taxonomies->getTaxonomy($id, true, $filter);
|
||||
if (empty($taxonomy)) {
|
||||
throw new NotFoundException(__('Taxonomy not found.'));
|
||||
}
|
||||
|
||||
$EventTagsTable = $this->fetchTable('EventTags');
|
||||
$AttributeTagsTable = $this->fetchTable('AttributeTags');
|
||||
|
||||
$tagIds = array_column(array_column(array_column($taxonomy['entries'], 'existing_tag'), 'Tag'), 'id');
|
||||
$eventCount = $EventTagsTable->countForTags($tagIds, $this->ACL->getUser()->toArray());
|
||||
$attributeTags = $AttributeTagsTable->countForTags($tagIds, $this->ACL->getUser()->toArray());
|
||||
|
||||
foreach ($taxonomy['entries'] as $key => $value) {
|
||||
$count = 0;
|
||||
$count_a = 0;
|
||||
if (!empty($value['existing_tag'])) {
|
||||
$tagId = $value['existing_tag']['Tag']['id'];
|
||||
$count = isset($eventCount[$tagId]) ? $eventCount[$tagId] : 0;
|
||||
$count_a = isset($attributeTags[$tagId]) ? $attributeTags[$tagId] : 0;
|
||||
}
|
||||
$taxonomy['entries'][$key]['events'] = $count;
|
||||
$taxonomy['entries'][$key]['attributes'] = $count_a;
|
||||
}
|
||||
$customPagination = new CustomPaginationTool();
|
||||
$params = $customPagination->createPaginationRules($taxonomy['entries'], $this->request->getQueryParams(), 'TaxonomyEntry');
|
||||
if ($params['sort'] == 'id') {
|
||||
$params['sort'] = 'tag';
|
||||
}
|
||||
$params['options'] = ['filter' => $filter];
|
||||
$this->params->params['paging'] = array('Taxonomies' => $params);
|
||||
$params = $customPagination->applyRulesOnArray($taxonomy['entries'], $params, 'taxonomies');
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->viewData($taxonomy, $this->response->getType());
|
||||
}
|
||||
|
||||
if (isset($this->request->getQueryParams()['pages'])) {
|
||||
$currentPage = $this->request->getQueryParams()['pages'];
|
||||
} else {
|
||||
$currentPage = 1;
|
||||
}
|
||||
$this->set('page', $currentPage);
|
||||
|
||||
$this->set('entries', $taxonomy['entries']);
|
||||
$this->set('urlparams', $urlparams);
|
||||
$this->set('passedArgs', json_encode($this->request->getQueryParams()));
|
||||
$this->set('passedArgsArray', $this->request->getQueryParams());
|
||||
$this->set('taxonomy', $taxonomy['Taxonomy']);
|
||||
$this->set('id', $taxonomy['Taxonomy']['id']);
|
||||
$this->set('title_for_layout', __('%s Taxonomy Library', h(strtoupper($taxonomy['Taxonomy']['namespace']))));
|
||||
$this->render('ajax/taxonomy_tags');
|
||||
}
|
||||
|
||||
public function export($id)
|
||||
{
|
||||
$taxonomy = $this->Taxonomies->find('all', [
|
||||
'recursive' => -1,
|
||||
'contain' => ['TaxonomyPredicate' => ['TaxonomyEntry']],
|
||||
'conditions' => is_numeric($id) ? ['Taxonomy.id' => $id] : ['LOWER(Taxonomy.namespace)' => mb_strtolower($id)],
|
||||
])->first();
|
||||
if (empty($taxonomy)) {
|
||||
throw new NotFoundException(__('Taxonomy not found.'));
|
||||
}
|
||||
|
||||
$data = [
|
||||
'namespace' => $taxonomy['Taxonomy']['namespace'],
|
||||
'description' => $taxonomy['Taxonomy']['description'],
|
||||
'version' => (int)$taxonomy['Taxonomy']['version'],
|
||||
'exclusive' => $taxonomy['Taxonomy']['exclusive'],
|
||||
'predicates' => [],
|
||||
];
|
||||
|
||||
foreach ($taxonomy['TaxonomyPredicate'] as $predicate) {
|
||||
$predicateOutput = [];
|
||||
foreach (['value', 'expanded', 'colour', 'description', 'exclusive', 'numerical_value'] as $field) {
|
||||
if (isset($predicate[$field]) && !empty($predicate[$field])) {
|
||||
$predicateOutput[$field] = $predicate[$field];
|
||||
}
|
||||
}
|
||||
$data['predicates'][] = $predicateOutput;
|
||||
|
||||
if (!empty($predicate['TaxonomyEntry'])) {
|
||||
$entries = [];
|
||||
foreach ($predicate['TaxonomyEntry'] as $entry) {
|
||||
$entryOutput = [];
|
||||
foreach (['value', 'expanded', 'colour', 'description', 'exclusive', 'numerical_value'] as $field) {
|
||||
if (isset($entry[$field]) && !empty($entry[$field])) {
|
||||
$entryOutput[$field] = $entry[$field];
|
||||
}
|
||||
}
|
||||
$entries[] = $entryOutput;
|
||||
}
|
||||
$data['values'][] = [
|
||||
'predicate' => $predicate['value'],
|
||||
'entry' => $entries,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->RestResponse->viewData($data, 'json');
|
||||
}
|
||||
|
||||
public function enable($id)
|
||||
{
|
||||
$this->request->allowMethod(['post']);
|
||||
|
||||
$taxonomy = $this->Taxonomies->find('all', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Taxonomy.id' => $id),
|
||||
))->first();
|
||||
if (empty($taxonomy)) {
|
||||
$message = __('Invalid taxonomy.');
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('Taxonomy', 'enable', $id, $message);
|
||||
} else {
|
||||
$this->Flash->error($message);
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
} else {
|
||||
$taxonomy['Taxonomy']['enabled'] = true;
|
||||
$this->Taxonomies->save($taxonomy);
|
||||
|
||||
$this->__log('enable', $id, 'Taxonomy enabled', $taxonomy['Taxonomy']['namespace'] . ' - enabled');
|
||||
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('Taxonomy', 'enable', $id, $this->response->getType());
|
||||
} else {
|
||||
$this->Flash->success(__('Taxonomy enabled.'));
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function disable($id)
|
||||
{
|
||||
$this->request->allowMethod(['post']);
|
||||
|
||||
$taxonomy = $this->Taxonomies->find('all', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Taxonomy.id' => $id),
|
||||
))->first();
|
||||
if (empty($taxonomy)) {
|
||||
$message = __('Invalid taxonomy.');
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('Taxonomy', 'disable', $id, $message);
|
||||
} else {
|
||||
$this->Flash->error($message);
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
} else {
|
||||
$this->Taxonomies->disableTags($id);
|
||||
$taxonomy['Taxonomy']['enabled'] = 0;
|
||||
$this->Taxonomies->save($taxonomy);
|
||||
|
||||
$this->__log('disable', $id, 'Taxonomy disabled', $taxonomy['Taxonomy']['namespace'] . ' - disabled');
|
||||
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('Taxonomy', 'disable', $id, $this->response->getType());
|
||||
} else {
|
||||
$this->Flash->success(__('Taxonomy disabled.'));
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function import()
|
||||
{
|
||||
$this->request->allowMethod(['post']);
|
||||
|
||||
try {
|
||||
$id = $this->Taxonomies->import($this->request->getData());
|
||||
return $this->view($id);
|
||||
} catch (Exception $e) {
|
||||
return $this->RestResponse->saveFailResponse('Taxonomy', 'import', false, $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update()
|
||||
{
|
||||
$result = $this->Taxonomies->update();
|
||||
$fails = 0;
|
||||
$successes = 0;
|
||||
if (!empty($result)) {
|
||||
if (isset($result['success'])) {
|
||||
foreach ($result['success'] as $id => $success) {
|
||||
if (isset($success['old'])) {
|
||||
$change = $success['namespace'] . ': updated from v' . $success['old'] . ' to v' . $success['new'];
|
||||
} else {
|
||||
$change = $success['namespace'] . ' v' . $success['new'] . ' installed';
|
||||
}
|
||||
$this->__log('update', $id, 'Taxonomy updated', $change);
|
||||
$successes++;
|
||||
}
|
||||
}
|
||||
if (isset($result['fails'])) {
|
||||
foreach ($result['fails'] as $id => $fail) {
|
||||
$this->__log('update', $id, 'Taxonomy failed to update', $fail['namespace'] . ' could not be installed/updated. Error: ' . $fail['fail']);
|
||||
$fails++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->__log('update', 0, 'Taxonomy update (nothing to update)', 'Executed an update of the taxonomy library, but there was nothing to update.');
|
||||
}
|
||||
if ($successes == 0 && $fails == 0) {
|
||||
$flashType = 'info';
|
||||
$message = __('All taxonomy libraries are up to date already.');
|
||||
} elseif ($successes == 0) {
|
||||
$flashType = 'error';
|
||||
$message = __('Could not update any of the taxonomy libraries');
|
||||
} else {
|
||||
$flashType = 'success';
|
||||
$message = __('Successfully updated {0} taxonomy libraries.', $successes);
|
||||
if ($fails != 0) {
|
||||
$message .= __(' However, could not update %s taxonomy libraries.', $fails);
|
||||
}
|
||||
}
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('Taxonomy', 'update', false, $this->response->getType(), $message);
|
||||
} else {
|
||||
$this->Flash->{$flashType}($message);
|
||||
$this->redirect(array('controller' => 'taxonomies', 'action' => 'index'));
|
||||
}
|
||||
}
|
||||
|
||||
public function addTag($taxonomy_id = false)
|
||||
{
|
||||
$data = $this->request->getData();
|
||||
if ($this->request->is('get')) {
|
||||
if (empty($taxonomy_id) && !empty($this->request->getParam('named')['taxonomy_id'])) {
|
||||
$taxonomy_id = $this->request->getParam('named')['taxonomy_id'];
|
||||
}
|
||||
if (
|
||||
empty($taxonomy_id) ||
|
||||
empty($this->request->getParam('named')['name'])
|
||||
) {
|
||||
throw new MethodNotAllowedException(__('Taxonomy ID or tag name must be provided.'));
|
||||
} else {
|
||||
$data['Taxonomy']['taxonomy_id'] = $taxonomy_id;
|
||||
$data['Taxonomy']['name'] = $this->request->getParam('named')['name'];
|
||||
}
|
||||
} else {
|
||||
if ($taxonomy_id) {
|
||||
$result = $this->Taxonomies->addTags($taxonomy_id);
|
||||
} else {
|
||||
if (isset($data['Taxonomy'])) {
|
||||
$data['Tag'] = $data['Taxonomy'];
|
||||
unset($data['Taxonomy']);
|
||||
}
|
||||
if (isset($data['Tag']['request'])) {
|
||||
$data['Tag'] = $data['Tag']['request'];
|
||||
}
|
||||
if (!isset($data['Tag']['nameList'])) {
|
||||
$data['Tag']['nameList'] = array($data['Tag']['name']);
|
||||
} else {
|
||||
$data['Tag']['nameList'] = json_decode($data['Tag']['nameList'], true);
|
||||
}
|
||||
$result = $this->Taxonomies->addTags($data['Tag']['taxonomy_id'], $data['Tag']['nameList']);
|
||||
}
|
||||
if ($result) {
|
||||
$message = __('The tag(s) has been saved.');
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('Taxonomy', 'addTag', $taxonomy_id, $this->response->getType(), $message);
|
||||
}
|
||||
$this->Flash->success($message);
|
||||
} else {
|
||||
$message = __('The tag(s) could not be saved. Please, try again.');
|
||||
if ($this->ParamHandler->isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('Taxonomy', 'addTag', $taxonomy_id, $message, $this->response->getType());
|
||||
}
|
||||
$this->Flash->error($message);
|
||||
}
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
}
|
||||
|
||||
public function hideTag($taxonomy_id = false)
|
||||
{
|
||||
$this->request->allowMethod(['post']);
|
||||
$data = $this->request->getData();
|
||||
|
||||
if ($taxonomy_id) {
|
||||
$result = $this->Taxonomies->hideTags($taxonomy_id);
|
||||
} else {
|
||||
if (isset($data['Taxonomy'])) {
|
||||
$data['Tag'] = $data['Taxonomy'];
|
||||
unset($data['Taxonomy']);
|
||||
}
|
||||
if (isset($data['Tag']['request'])) {
|
||||
$data['Tag'] = $data['Tag']['request'];
|
||||
}
|
||||
if (!isset($data['Tag']['nameList'])) {
|
||||
$data['Tag']['nameList'] = array($data['Tag']['name']);
|
||||
} else {
|
||||
$data['Tag']['nameList'] = json_decode($data['Tag']['nameList'], true);
|
||||
}
|
||||
$result = $this->Taxonomies->hideTags($data['Tag']['taxonomy_id'], $data['Tag']['nameList']);
|
||||
}
|
||||
if ($result) {
|
||||
$this->Flash->success(__('The tag(s) has been saved.'));
|
||||
} else {
|
||||
$this->Flash->error(__('The tag(s) could not be saved. Please, try again.'));
|
||||
}
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
|
||||
public function unhideTag($taxonomy_id = false)
|
||||
{
|
||||
$this->request->allowMethod(['post']);
|
||||
$data = $this->request->getData();
|
||||
|
||||
if ($taxonomy_id) {
|
||||
$result = $this->Taxonomies->unhideTags($taxonomy_id);
|
||||
} else {
|
||||
if (isset($data['Taxonomy'])) {
|
||||
$data['Tag'] = $data['Taxonomy'];
|
||||
unset($data['Taxonomy']);
|
||||
}
|
||||
if (isset($data['Tag']['request'])) {
|
||||
$data['Tag'] = $data['Tag']['request'];
|
||||
}
|
||||
if (!isset($data['Tag']['nameList'])) {
|
||||
$data['Tag']['nameList'] = array($data['Tag']['name']);
|
||||
} else {
|
||||
$data['Tag']['nameList'] = json_decode($data['Tag']['nameList'], true);
|
||||
}
|
||||
$result = $this->Taxonomies->unhideTags($data['Tag']['taxonomy_id'], $data['Tag']['nameList']);
|
||||
}
|
||||
if ($result) {
|
||||
$this->Flash->success(__('The tag(s) has been saved.'));
|
||||
} else {
|
||||
$this->Flash->error(__('The tag(s) could not be saved. Please, try again.'));
|
||||
}
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
|
||||
public function disableTag($taxonomy_id = false)
|
||||
{
|
||||
$data = $this->request->getData();
|
||||
|
||||
if ($this->request->is('get')) {
|
||||
if (empty($taxonomy_id) && !empty($this->request->getParam('named')['taxonomy_id'])) {
|
||||
$taxonomy_id = $this->request->getParam('named')['taxonomy_id'];
|
||||
}
|
||||
if (
|
||||
empty($taxonomy_id) ||
|
||||
empty($this->request->getParam('named')['name'])
|
||||
) {
|
||||
throw new MethodNotAllowedException(__('Taxonomy ID or tag name must be provided.'));
|
||||
} else {
|
||||
$data['Taxonomy']['taxonomy_id'] = $taxonomy_id;
|
||||
$data['Taxonomy']['name'] = $this->request->getParam('named')['name'];
|
||||
}
|
||||
} else {
|
||||
if ($taxonomy_id) {
|
||||
$result = $this->Taxonomies->disableTags($taxonomy_id);
|
||||
} else {
|
||||
if (isset($data['Taxonomy'])) {
|
||||
$data['Tag'] = $data['Taxonomy'];
|
||||
unset($data['Taxonomy']);
|
||||
}
|
||||
if (isset($data['Tag']['request'])) {
|
||||
$data['Tag'] = $data['Tag']['request'];
|
||||
}
|
||||
if (!isset($data['Tag']['nameList'])) {
|
||||
$data['Tag']['nameList'] = array($data['Tag']['name']);
|
||||
} else {
|
||||
$data['Tag']['nameList'] = json_decode($data['Tag']['nameList'], true);
|
||||
}
|
||||
$result = $this->Taxonomies->disableTags($data['Tag']['taxonomy_id'], $data['Tag']['nameList']);
|
||||
}
|
||||
if ($result) {
|
||||
$this->Flash->success(__('The tag(s) has been hidden.'));
|
||||
} else {
|
||||
$this->Flash->error(__('The tag(s) could not be hidden. Please, try again.'));
|
||||
}
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
}
|
||||
|
||||
public function taxonomyMassConfirmation($id)
|
||||
{
|
||||
$this->set('id', $id);
|
||||
$this->render('ajax/taxonomy_mass_confirmation');
|
||||
}
|
||||
|
||||
public function taxonomyMassHide($id)
|
||||
{
|
||||
$this->set('id', $id);
|
||||
$this->render('ajax/taxonomy_mass_hide');
|
||||
}
|
||||
|
||||
public function taxonomyMassUnhide($id)
|
||||
{
|
||||
$this->set('id', $id);
|
||||
$this->render('ajax/taxonomy_mass_unhide');
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
if ($this->request->is('post')) {
|
||||
$result = $this->Taxonomies->delete($id, true);
|
||||
if ($result) {
|
||||
$this->Flash->success(__('Taxonomy successfully deleted.'));
|
||||
$this->redirect(array('controller' => 'taxonomies', 'action' => 'index'));
|
||||
} else {
|
||||
$this->Flash->error(__('Taxonomy could not be deleted.'));
|
||||
$this->redirect(array('controller' => 'taxonomies', 'action' => 'index'));
|
||||
}
|
||||
} else {
|
||||
if ($this->request->is('ajax')) {
|
||||
$this->set('id', $id);
|
||||
$this->render('ajax/taxonomy_delete_confirmation');
|
||||
} else {
|
||||
throw new MethodNotAllowedException(__('This function can only be reached via AJAX.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function toggleRequired($id)
|
||||
{
|
||||
$taxonomy = $this->Taxonomies->find('all', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Taxonomy.id' => $id)
|
||||
))->first();
|
||||
if (empty($taxonomy)) {
|
||||
return $this->RestResponse->saveFailResponse('Taxonomy', 'toggleRequired', $id, 'Invalid Taxonomy', $this->response->getType());
|
||||
}
|
||||
if ($this->request->is('post')) {
|
||||
$taxonomy['Taxonomy']['required'] = $this->request->gtData()['Taxonomy']['required'];
|
||||
$result = $this->Taxonomies->save($taxonomy);
|
||||
if ($result) {
|
||||
return $this->RestResponse->saveSuccessResponse('Taxonomy', 'toggleRequired', $id, $this->response->getType());
|
||||
} else {
|
||||
return $this->RestResponse->saveFailResponse('Taxonomy', 'toggleRequired', $id, $this->validationError, $this->response->getType());
|
||||
}
|
||||
}
|
||||
|
||||
$this->set('required', !$taxonomy['Taxonomy']['required']);
|
||||
$this->set('id', $id);
|
||||
$this->autoRender = false;
|
||||
$this->layout = false;
|
||||
$this->render('ajax/toggle_required');
|
||||
}
|
||||
|
||||
public function toggleHighlighted($id)
|
||||
{
|
||||
$taxonomy = $this->Taxonomies->find('all', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Taxonomy.id' => $id)
|
||||
))->first();
|
||||
if (empty($taxonomy)) {
|
||||
return $this->RestResponse->saveFailResponse('Taxonomy', 'toggleHighlighted', $id, 'Invalid Taxonomy', $this->response->getType());
|
||||
}
|
||||
if ($this->request->is('post')) {
|
||||
$taxonomy['Taxonomy']['highlighted'] = $this->request->getData()['Taxonomy']['highlighted'];
|
||||
$result = $this->Taxonomies->save($taxonomy);
|
||||
if ($result) {
|
||||
return $this->RestResponse->saveSuccessResponse('Taxonomy', 'toggleHighlighted', $id, $this->response->getType());
|
||||
} else {
|
||||
return $this->RestResponse->saveFailResponse('Taxonomy', 'toggleHighlighted', $id, $this->validationError, $this->response->getType());
|
||||
}
|
||||
}
|
||||
|
||||
$this->set('highlighted', !$taxonomy['Taxonomy']['highlighted']);
|
||||
$this->set('id', $id);
|
||||
$this->autoRender = false;
|
||||
$this->layout = false;
|
||||
$this->render('ajax/toggle_highlighted');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $action
|
||||
* @param int $modelId
|
||||
* @param string $title
|
||||
* @param string $change
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
private function __log($action, $modelId, $title, $change)
|
||||
{
|
||||
/** @var LogsTable $LogsTable */
|
||||
$LogsTable = $this->fetchTable('Logs');
|
||||
$LogsTable->createLogEntry($this->ACL->getUser()->toArray(), $action, 'Taxonomy', $modelId, $title, $change);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach tag counts.
|
||||
* @param array $taxonomies
|
||||
* @return array
|
||||
*/
|
||||
private function __tagCount(array $taxonomies)
|
||||
{
|
||||
$tags = [];
|
||||
foreach ($taxonomies as $taxonomyPos => $taxonomy) {
|
||||
$total = 0;
|
||||
foreach ($taxonomy['TaxonomyPredicate'] as $predicate) {
|
||||
if (isset($predicate['TaxonomyEntry']) && !empty($predicate['TaxonomyEntry'])) {
|
||||
foreach ($predicate['TaxonomyEntry'] as $entry) {
|
||||
$tag = mb_strtolower($taxonomy['namespace'] . ':' . $predicate['value'] . '="' . $entry['value'] . '"');
|
||||
$tags[$tag] = $taxonomyPos;
|
||||
$total++;
|
||||
}
|
||||
} else {
|
||||
$tag = mb_strtolower($taxonomy['namespace'] . ':' . $predicate['value']);
|
||||
$tags[$tag] = $taxonomyPos;
|
||||
$total++;
|
||||
}
|
||||
}
|
||||
$taxonomies[$taxonomyPos]['total_count'] = $total;
|
||||
$taxonomies[$taxonomyPos]['current_count'] = 0;
|
||||
unset($taxonomies[$taxonomyPos]['TaxonomyPredicate']);
|
||||
}
|
||||
|
||||
$TagsTable = $this->fetchTable('Tags');
|
||||
if (!empty($tags)) {
|
||||
$existingTags = $TagsTable->find('column', [
|
||||
'fields' => ['Tags.name'],
|
||||
'conditions' => [
|
||||
'lower(name) IN' => array_keys($tags),
|
||||
'hide_tag' => 0
|
||||
],
|
||||
]);
|
||||
} else {
|
||||
$existingTags = $TagsTable->find('column', ['fields' => ['name']]);
|
||||
}
|
||||
|
||||
foreach ($existingTags as $existingTag) {
|
||||
$existingTag = mb_strtolower($existingTag);
|
||||
if (isset($tags[$existingTag])) {
|
||||
$taxonomies[$tags[$existingTag]]['current_count']++;
|
||||
}
|
||||
}
|
||||
|
||||
return $taxonomies;
|
||||
}
|
||||
|
||||
private function __search($value)
|
||||
{
|
||||
$value = mb_strtolower(trim($value));
|
||||
$searchTerm = "%$value%";
|
||||
$taxonomyPredicateIds = $this->Taxonomies->TaxonomyPredicates->TaxonomyEntry->find('column', [
|
||||
'fields' => ['TaxonomyEntry.taxonomy_predicate_id'],
|
||||
'conditions' => ['OR' => [
|
||||
'LOWER(value) LIKE' => $searchTerm,
|
||||
'LOWER(expanded) LIKE' => $searchTerm,
|
||||
]],
|
||||
'unique' => true,
|
||||
]);
|
||||
|
||||
$taxonomyIds = $this->Taxonomies->TaxonomyPredicates->find('column', [
|
||||
'fields' => ['TaxonomyPredicate.taxonomy_id'],
|
||||
'conditions' => ['OR' => [
|
||||
'id' => $taxonomyPredicateIds,
|
||||
'LOWER(value) LIKE' => $searchTerm,
|
||||
'LOWER(expanded) LIKE' => $searchTerm,
|
||||
]],
|
||||
'unique' => true,
|
||||
]);
|
||||
|
||||
$taxonomyIds = $this->Taxonomies->find('column', [
|
||||
'fields' => ['Taxonomy.id'],
|
||||
'conditions' => ['OR' => [
|
||||
'id' => $taxonomyIds,
|
||||
'LOWER(namespace) LIKE' => $searchTerm,
|
||||
'LOWER(description) LIKE' => $searchTerm,
|
||||
]],
|
||||
]);
|
||||
|
||||
return $taxonomyIds;
|
||||
}
|
||||
|
||||
|
||||
public function normalizeCustomTagsToTaxonomyFormat()
|
||||
{
|
||||
$this->request->allowMethod(['post', 'put']);
|
||||
$conversionResult = $this->Taxonomies->normalizeCustomTagsToTaxonomyFormat();
|
||||
$this->Flash->success(__('%s tags successfully converted. %s row updated.', $conversionResult['tag_converted'], $conversionResult['row_updated']));
|
||||
$this->redirect(array('controller' => 'taxonomies', 'action' => 'index'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
<?php
|
||||
|
||||
namespace App\Lib\Tools;
|
||||
|
||||
use Cake\Validation\Validation;
|
||||
|
||||
class ColourPaletteTool
|
||||
{
|
||||
/**
|
||||
* @param int $count Pass the number of distinct colours to receive an array of colours
|
||||
* @return array
|
||||
*/
|
||||
public function createColourPalette($count)
|
||||
{
|
||||
$interval = 1 / $count;
|
||||
$colours = array();
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$colours[] = $this->HSVtoRGB(array($interval * $i, 1, 1));
|
||||
}
|
||||
return $colours;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $hsv
|
||||
* @return string
|
||||
*/
|
||||
public function HSVtoRGB(array $hsv)
|
||||
{
|
||||
list($H, $S, $V) = $hsv;
|
||||
//1
|
||||
$H *= 6;
|
||||
//2
|
||||
$I = floor($H);
|
||||
$F = $H - $I;
|
||||
//3
|
||||
$M = $V * (1 - $S);
|
||||
$N = $V * (1 - $S * $F);
|
||||
$K = $V * (1 - $S * (1 - $F));
|
||||
//4
|
||||
switch ($I) {
|
||||
case 0:
|
||||
list($R, $G, $B) = array($V, $K, $M);
|
||||
break;
|
||||
case 1:
|
||||
list($R, $G, $B) = array($N, $V, $M);
|
||||
break;
|
||||
case 2:
|
||||
list($R, $G, $B) = array($M, $V, $K);
|
||||
break;
|
||||
case 3:
|
||||
list($R, $G, $B) = array($M, $N, $V);
|
||||
break;
|
||||
case 4:
|
||||
list($R, $G, $B) = array($K, $M, $V);
|
||||
break;
|
||||
case 5:
|
||||
case 6: //for when $H=1 is given
|
||||
list($R, $G, $B) = array($V, $M, $N);
|
||||
break;
|
||||
}
|
||||
return $this->convertToHex(array($R, $G, $B));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $channels
|
||||
* @return string
|
||||
*/
|
||||
public function convertToHex($channels)
|
||||
{
|
||||
$colour = '#';
|
||||
foreach ($channels as $channel) {
|
||||
$channel = dechex(round($channel * 255));
|
||||
if (strlen($channel) === 1) {
|
||||
$channel = '0' . $channel;
|
||||
}
|
||||
$colour .= $channel;
|
||||
}
|
||||
return $colour;
|
||||
}
|
||||
|
||||
// pass the element's id from the list along to get a colour for a single item
|
||||
public function generatePaletteFromString($string, $items, $onlySpecific = false)
|
||||
{
|
||||
if (Validation::uuid($string)) {
|
||||
$hue = $this->__uuidToNumber($string);
|
||||
} else {
|
||||
$hue = $this->__stringToNumber($string);
|
||||
}
|
||||
$saturation = 1;
|
||||
$steps = 80 / $items;
|
||||
$results = array();
|
||||
if ($onlySpecific !== false && is_numeric($onlySpecific)) {
|
||||
$value = (20 + ($steps * ($onlySpecific + 1))) / 100;
|
||||
return $this->HSVtoRGB(array($hue, $saturation, $value));
|
||||
}
|
||||
for ($i = 0; $i < $items; $i++) {
|
||||
$value = (20 + ($steps * ($i + 1))) / 100;
|
||||
$rgb = $this->HSVtoRGB(array($hue, $saturation, $value));
|
||||
$results[] = $rgb;
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
private function __stringToNumber($string)
|
||||
{
|
||||
$string = mb_convert_encoding($string, 'ASCII');
|
||||
$number = 0;
|
||||
for ($i = 0; $i < strlen($string); $i++) {
|
||||
$number += ord($string[$i]);
|
||||
}
|
||||
return $number % 100 / 100;
|
||||
}
|
||||
|
||||
private function __uuidToNumber($string)
|
||||
{
|
||||
$part = explode('-', $string)[4];
|
||||
$number = hexdec($part);
|
||||
$max = hexdec('ffffffffffff');
|
||||
return round($number / $max, 2);
|
||||
}
|
||||
}
|
|
@ -10,7 +10,6 @@ use Psr\Http\Server\RequestHandlerInterface;
|
|||
|
||||
class NamedParamsParserMiddleware implements MiddlewareInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* This middleware allows to configure named params for specific controller/actions to keep CakePHP 2.x backwards compatibility.
|
||||
* Reads Configure::read('NamedParams') and parses the named params from the request->pass array.
|
||||
|
@ -20,6 +19,8 @@ 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'],
|
||||
'taxonomies.addTag' => ['taxonomy_id'],
|
||||
'taxonomies.disableTag' => ['taxonomy_id'],
|
||||
];
|
||||
|
||||
public function process(
|
||||
|
|
|
@ -425,7 +425,7 @@ class GalaxyClusterRelationsTable extends AppTable
|
|||
'all',
|
||||
[
|
||||
'recursive' => -1,
|
||||
'conditions' => ['galaxy_cluster_id in' => array_keys($lookupSavedIds)],
|
||||
'conditions' => ['galaxy_cluster_id IN' => array_keys($lookupSavedIds)],
|
||||
'fields' => ['id', 'galaxy_cluster_uuid', 'referenced_galaxy_cluster_uuid']
|
||||
]
|
||||
);
|
||||
|
|
|
@ -697,19 +697,19 @@ class GalaxyClustersTable extends AppTable
|
|||
$relation_ids = $this->GalaxyClusterRelations->find(
|
||||
'all',
|
||||
[
|
||||
'conditions' => ['galaxy_cluster_id in' => $cluster_ids],
|
||||
'conditions' => ['galaxy_cluster_id IN' => $cluster_ids],
|
||||
'fields' => ['id']
|
||||
]
|
||||
)->toArray();
|
||||
$relation_ids = Hash::extract($relation_ids, '{n}.id');
|
||||
$this->deleteAll(['GalaxyCluster.default' => true], false, false);
|
||||
$this->GalaxyElements->deleteAll(['GalaxyElement.galaxy_cluster_id in' => $cluster_ids], false, false);
|
||||
$this->GalaxyClusterRelations->deleteAll(['GalaxyClusterRelations.galaxy_cluster_id in' => $cluster_ids], false, false);
|
||||
$this->GalaxyElements->deleteAll(['GalaxyElement.galaxy_cluster_id IN' => $cluster_ids], false, false);
|
||||
$this->GalaxyClusterRelations->deleteAll(['GalaxyClusterRelations.galaxy_cluster_id IN' => $cluster_ids], false, false);
|
||||
$this->GalaxyClusterRelations->updateAll(
|
||||
['referenced_galaxy_cluster_id' => 0],
|
||||
['referenced_galaxy_cluster_uuid in' => $cluster_uuids] // For all default clusters being referenced
|
||||
['referenced_galaxy_cluster_uuid IN' => $cluster_uuids] // For all default clusters being referenced
|
||||
);
|
||||
$this->GalaxyClusterRelations->GalaxyClusterRelationTags->deleteAll(['galaxy_cluster_relation_id in' => $relation_ids], false, false);
|
||||
$this->GalaxyClusterRelations->GalaxyClusterRelationTags->deleteAll(['galaxy_cluster_relation_id IN' => $relation_ids], false, false);
|
||||
$LogTable = $this->fetchTable('Logs');
|
||||
$LogTable->createLogEntry('SYSTEM', 'wipe_default', 'GalaxyCluster', 0, "Wiping default galaxy clusters");
|
||||
}
|
||||
|
@ -1248,7 +1248,7 @@ class GalaxyClustersTable extends AppTable
|
|||
'SharingGroup',
|
||||
],
|
||||
'conditions' => [
|
||||
'referenced_galaxy_cluster_id in' => $clusterIds,
|
||||
'referenced_galaxy_cluster_id IN' => $clusterIds,
|
||||
]
|
||||
]
|
||||
)->toArray();
|
||||
|
|
|
@ -9,7 +9,7 @@ class TagsTable extends AppTable
|
|||
{
|
||||
public function initialize(array $config): void
|
||||
{
|
||||
$this->setDisplayField('name'); // Change to name?
|
||||
$this->setDisplayField('name');
|
||||
|
||||
$this->belongsTo(
|
||||
'Organisation',
|
||||
|
@ -64,7 +64,6 @@ class TagsTable extends AppTable
|
|||
$this->hasMany('GalaxyClusterRelationTag');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $tag
|
||||
* @param array $user
|
||||
|
@ -160,4 +159,26 @@ class TagsTable extends AppTable
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @param bool $containTagConnectors
|
||||
* @return array Uppercase tag name in key
|
||||
*/
|
||||
public function getTagsForNamespace($namespace, $containTagConnectors = true)
|
||||
{
|
||||
$tag_params = [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['LOWER(name) LIKE' => strtolower($namespace) . '%'],
|
||||
];
|
||||
if ($containTagConnectors) {
|
||||
$tag_params['contain'] = ['EventTag', 'AttributeTag'];
|
||||
}
|
||||
$tags_temp = $this->find('all', $tag_params);
|
||||
$tags = [];
|
||||
foreach ($tags_temp as $temp) {
|
||||
$tags[strtoupper($temp['Tag']['name'])] = $temp;
|
||||
}
|
||||
return $tags;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,929 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Table;
|
||||
|
||||
use App\Model\Table\AppTable;
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Validation\Validator;
|
||||
use Exception;
|
||||
use App\Lib\Tools\ColourPaletteTool;
|
||||
use App\Lib\Tools\FileAccessTool;
|
||||
use App\Lib\Tools\RedisTool;
|
||||
use Cake\Utility\Hash;
|
||||
|
||||
class TaxonomiesTable extends AppTable
|
||||
{
|
||||
private $__taxonomyConflicts = [];
|
||||
|
||||
public function initialize(array $config): void
|
||||
{
|
||||
$this->setDisplayField('name');
|
||||
|
||||
$this->hasMany(
|
||||
'TaxonomyPredicates',
|
||||
[
|
||||
'className' => 'TaxonomyPredicates',
|
||||
'foreignKey' => 'taxonomy_id',
|
||||
'propertyName' => 'TaxonomyPredicate',
|
||||
'dependent' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function validationDefault(Validator $validator): Validator
|
||||
{
|
||||
$validator
|
||||
->requirePresence(['namespace', 'description'], 'create')
|
||||
->add('version', 'numeric');
|
||||
|
||||
return $validator;
|
||||
}
|
||||
|
||||
public function update()
|
||||
{
|
||||
$existing = $this->find('all', array(
|
||||
'recursive' => -1,
|
||||
'fields' => array('version', 'enabled', 'namespace')
|
||||
))->toArray();
|
||||
$existing = array_column(array_column($existing, 'Taxonomy'), null, 'namespace');
|
||||
|
||||
$directories = glob(APP . '..' . DS . 'libraries' . DS . 'misp-taxonomies' . DS . '*', GLOB_ONLYDIR);
|
||||
$updated = array();
|
||||
foreach ($directories as $dir) {
|
||||
$dir = basename($dir);
|
||||
if ($dir === 'tools' || $dir === 'mapping') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$machineTagPath = APP . '..' . DS . 'libraries' . DS . 'misp-taxonomies' . DS . $dir . DS . 'machinetag.json';
|
||||
|
||||
try {
|
||||
$vocab = FileAccessTool::readJsonFromFile($machineTagPath);
|
||||
} catch (Exception $e) {
|
||||
$updated['fails'][] = ['namespace' => $dir, 'fail' => $e->getMessage()];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($vocab['type'])) {
|
||||
if (is_array($vocab['type'])) {
|
||||
if (!in_array('event', $vocab['type'])) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if ($vocab['type'] !== 'event') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($vocab['version'])) {
|
||||
$vocab['version'] = 1;
|
||||
}
|
||||
if (!isset($existing[$vocab['namespace']]) || $vocab['version'] > $existing[$vocab['namespace']]['version']) {
|
||||
$current = $existing[$vocab['namespace']] ?? [];
|
||||
$result = $this->__updateVocab($vocab, $current);
|
||||
if (is_numeric($result)) {
|
||||
$updated['success'][$result] = array('namespace' => $vocab['namespace'], 'new' => $vocab['version']);
|
||||
if (!empty($current)) {
|
||||
$updated['success'][$result]['old'] = $current['version'];
|
||||
}
|
||||
} else {
|
||||
$updated['fails'][] = array('namespace' => $vocab['namespace'], 'fail' => json_encode($result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($updated['success'])) {
|
||||
$this->cleanupCache();
|
||||
}
|
||||
|
||||
return $updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $vocab
|
||||
* @return int Taxonomy ID
|
||||
* @throws Exception
|
||||
*/
|
||||
public function import(array $vocab)
|
||||
{
|
||||
foreach (['namespace', 'description', 'predicates'] as $requiredField) {
|
||||
if (!isset($vocab[$requiredField])) {
|
||||
throw new Exception("Required field '$requiredField' not provided.");
|
||||
}
|
||||
}
|
||||
if (!is_array($vocab['predicates'])) {
|
||||
throw new Exception("Field 'predicates' must be array.");
|
||||
}
|
||||
if (isset($vocab['values']) && !is_array($vocab['values'])) {
|
||||
throw new Exception("Field 'values' must be array.");
|
||||
}
|
||||
if (!isset($vocab['version'])) {
|
||||
$vocab['version'] = 1;
|
||||
}
|
||||
$current = $this->find('all', array(
|
||||
'conditions' => array('namespace' => $vocab['namespace']),
|
||||
'recursive' => -1,
|
||||
'fields' => array('version', 'enabled', 'namespace', 'highlighted')
|
||||
))->first();
|
||||
$current = empty($current) ? [] : $current['Taxonomy'];
|
||||
$result = $this->__updateVocab($vocab, $current);
|
||||
if (is_array($result)) {
|
||||
throw new Exception('Could not save taxonomy because of validation errors: ' . json_encode($result));
|
||||
}
|
||||
$this->cleanupCache();
|
||||
return (int)$result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private function __updateVocab(array $vocab, array $current)
|
||||
{
|
||||
$enabled = 0;
|
||||
if (!empty($current)) {
|
||||
if ($current['enabled']) {
|
||||
$enabled = 1;
|
||||
}
|
||||
$this->deleteAll(['Taxonomy.namespace' => $current['namespace']]);
|
||||
}
|
||||
$taxonomy = [
|
||||
'namespace' => $vocab['namespace'],
|
||||
'description' => $vocab['description'],
|
||||
'version' => $vocab['version'],
|
||||
'exclusive' => !empty($vocab['exclusive']),
|
||||
'enabled' => $enabled,
|
||||
'highlighted' => !empty($vocab['highlighted']),
|
||||
];
|
||||
$predicateLookup = array();
|
||||
foreach ($vocab['predicates'] as $k => $predicate) {
|
||||
$taxonomy['TaxonomyPredicate'][$k] = $predicate;
|
||||
$predicateLookup[$predicate['value']] = $k;
|
||||
}
|
||||
if (!empty($vocab['values'])) {
|
||||
foreach ($vocab['values'] as $value) {
|
||||
if (!isset($predicateLookup[$value['predicate']])) {
|
||||
throw new Exception("Invalid taxonomy `{$vocab['namespace']}` provided. Predicate `{$value['predicate']}` is missing.");
|
||||
}
|
||||
$predicatePosition = $predicateLookup[$value['predicate']];
|
||||
if (empty($taxonomy['TaxonomyPredicate'][$predicatePosition]['TaxonomyEntry'])) {
|
||||
$taxonomy['TaxonomyPredicate'][$predicatePosition]['TaxonomyEntry'] = $value['entry'];
|
||||
} else {
|
||||
$taxonomy['TaxonomyPredicate'][$predicatePosition]['TaxonomyEntry'] = array_merge($taxonomy['TaxonomyPredicate'][$predicatePosition]['TaxonomyEntry'], $value['entry']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$taxonomyEntity = $this->newEntity($taxonomy, ['associated' => ['TaxonomyPredicates.TaxonomyEntries']]);
|
||||
|
||||
try {
|
||||
$this->saveOrFail($taxonomyEntity, ['associated' => ['TaxonomyPredicates.TaxonomyEntries']]);
|
||||
$this->__updateTags($taxonomyEntity->id);
|
||||
return $taxonomyEntity->id;
|
||||
} catch (Exception $e) {
|
||||
return $taxonomyEntity->getErrors();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $id Taxonomy ID or namespace
|
||||
* @param string|boolean $filter String to filter to apply to the tags
|
||||
* @return array|false
|
||||
*/
|
||||
private function __getTaxonomy($id, $filter = false)
|
||||
{
|
||||
if (!is_numeric($id)) {
|
||||
$conditions = ['Taxonomies.namespace' => trim(mb_strtolower($id))];
|
||||
} else {
|
||||
$conditions = ['Taxonomies.id' => $id];
|
||||
}
|
||||
$taxonomy_params = array(
|
||||
'recursive' => -1,
|
||||
'contain' => array('TaxonomyPredicates' => array('TaxonomyEntries')),
|
||||
'conditions' => $conditions
|
||||
);
|
||||
$taxonomy = $this->find('all', $taxonomy_params)->first();
|
||||
if (empty($taxonomy)) {
|
||||
return false;
|
||||
}
|
||||
$entries = array();
|
||||
foreach ($taxonomy['TaxonomyPredicate'] as $predicate) {
|
||||
if (isset($predicate['TaxonomyEntry']) && !empty($predicate['TaxonomyEntry'])) {
|
||||
foreach ($predicate['TaxonomyEntry'] as $entry) {
|
||||
$temp = [
|
||||
'tag' => $taxonomy['namespace'] . ':' . $predicate['value'] . '="' . $entry['value'] . '"',
|
||||
'expanded' => (!empty($predicate['expanded']) ? $predicate['expanded'] : $predicate['value']) . ': ' . (!empty($entry['expanded']) ? $entry['expanded'] : $entry['value']),
|
||||
'exclusive_predicate' => $predicate['exclusive'],
|
||||
];
|
||||
if (!empty($entry['description'])) {
|
||||
$temp['description'] = $entry['description'];
|
||||
}
|
||||
if (!empty($entry['colour'])) {
|
||||
$temp['colour'] = $entry['colour'];
|
||||
}
|
||||
if (isset($entry['numerical_value'])) {
|
||||
$temp['numerical_value'] = $entry['numerical_value'];
|
||||
}
|
||||
if (empty($filter) || mb_strpos(mb_strtolower($temp['tag']), mb_strtolower($filter)) !== false) {
|
||||
$entries[] = $temp;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$temp = [
|
||||
'tag' => $taxonomy['namespace'] . ':' . $predicate['value'],
|
||||
'expanded' => !empty($predicate['expanded']) ? $predicate['expanded'] : $predicate['value']
|
||||
];
|
||||
if (!empty($predicate['description'])) {
|
||||
$temp['description'] = $predicate['description'];
|
||||
}
|
||||
if (!empty($predicate['colour'])) {
|
||||
$temp['colour'] = $predicate['colour'];
|
||||
}
|
||||
if (isset($predicate['numerical_value'])) {
|
||||
$temp['numerical_value'] = $predicate['numerical_value'];
|
||||
}
|
||||
if (empty($filter) || mb_strpos(mb_strtolower($temp['tag']), mb_strtolower($filter)) !== false) {
|
||||
$entries[] = $temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
$taxonomy = [
|
||||
'Taxonomy' => $taxonomy->toArray(),
|
||||
'entries' => $entries,
|
||||
];
|
||||
return $taxonomy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all tags associated to a taxonomy
|
||||
* Returns all tags not associated to a taxonomy if $inverse is true
|
||||
* @param bool $inverse
|
||||
* @param false|array $user
|
||||
* @param bool $full
|
||||
* @param bool $hideUnselectable
|
||||
* @param bool $local_tag
|
||||
* @return array|int|null
|
||||
*/
|
||||
public function getAllTaxonomyTags($inverse = false, $user = false, $full = false, $hideUnselectable = true, $local_tag = false)
|
||||
{
|
||||
$taxonomies = $this->find('all', [
|
||||
'fields' => ['namespace'],
|
||||
'recursive' => -1,
|
||||
'contain' => [
|
||||
'TaxonomyPredicate' => [
|
||||
'fields' => ['value'],
|
||||
'TaxonomyEntry' => ['fields' => ['value']]
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$allTaxonomyTags = [];
|
||||
foreach ($taxonomies as $taxonomy) {
|
||||
$namespace = $taxonomy['Taxonomy']['namespace'];
|
||||
foreach ($taxonomy['TaxonomyPredicate'] as $predicate) {
|
||||
if (isset($predicate['TaxonomyEntry']) && !empty($predicate['TaxonomyEntry'])) {
|
||||
foreach ($predicate['TaxonomyEntry'] as $entry) {
|
||||
$tag = $namespace . ':' . $predicate['value'] . '="' . $entry['value'] . '"';
|
||||
$allTaxonomyTags[mb_strtolower($tag)] = true;
|
||||
}
|
||||
} else {
|
||||
$tag = $namespace . ':' . $predicate['value'];
|
||||
$allTaxonomyTags[mb_strtolower($tag)] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$TagsTable = $this->fetchTable('Tags');
|
||||
|
||||
$conditions = ['Tag.is_galaxy' => 0];
|
||||
if ($user && !$user['Role']['perm_site_admin']) {
|
||||
$conditions[] = array('Tag.org_id' => array(0, $user['org_id']));
|
||||
$conditions[] = array('Tag.user_id' => array(0, $user['id']));
|
||||
}
|
||||
if (Configure::read('MISP.incoming_tags_disabled_by_default') || $hideUnselectable) {
|
||||
$conditions['Tag.hide_tag'] = 0;
|
||||
}
|
||||
// If the tag is to be added as global, we filter out the local_only tags
|
||||
if (!$local_tag) {
|
||||
$conditions['Tag.local_only'] = 0;
|
||||
}
|
||||
if ($full) {
|
||||
$allTags = $TagsTable->find('all', [
|
||||
'fields' => array('id', 'name', 'colour'),
|
||||
'order' => array('UPPER(Tag.name) ASC'),
|
||||
'conditions' => $conditions,
|
||||
'recursive' => -1
|
||||
]);
|
||||
} else {
|
||||
$allTags = $TagsTable->find('list', [
|
||||
'fields' => array('name'),
|
||||
'order' => array('UPPER(Tag.name) ASC'),
|
||||
'conditions' => $conditions
|
||||
]);
|
||||
}
|
||||
foreach ($allTags as $k => $tag) {
|
||||
$needle = $full ? $tag['Tag']['name'] : $tag;
|
||||
if ($inverse) {
|
||||
if (isset($allTaxonomyTags[mb_strtolower($needle)])) {
|
||||
unset($allTags[$k]);
|
||||
}
|
||||
}
|
||||
if (!$inverse && !isset($allTaxonomyTags[mb_strtolower($needle)])) {
|
||||
unset($allTags[$k]);
|
||||
}
|
||||
}
|
||||
return $allTags;
|
||||
}
|
||||
|
||||
public function getTaxonomyTags($id, $upperCase = false, $existingOnly = false)
|
||||
{
|
||||
$taxonomy = $this->__getTaxonomy($id);
|
||||
if ($existingOnly) {
|
||||
$TagsTable = $this->fetchTable('Tags');
|
||||
$tags = $TagsTable->find('list', array('fields' => array('name'), 'order' => array('UPPER(Tag.name) ASC')));
|
||||
foreach ($tags as $key => $tag) {
|
||||
$tags[$key] = strtoupper($tag);
|
||||
}
|
||||
}
|
||||
$entries = array();
|
||||
if ($taxonomy) {
|
||||
foreach ($taxonomy['entries'] as $entry) {
|
||||
$searchTerm = $upperCase ? strtoupper($entry['tag']) : $entry['tag'];
|
||||
if ($existingOnly) {
|
||||
if (in_array(strtoupper($entry['tag']), $tags)) {
|
||||
$entries[$searchTerm] = $entry['expanded'];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$entries[$searchTerm] = $entry['expanded'];
|
||||
}
|
||||
}
|
||||
return $entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $id Taxonomy ID or namespace
|
||||
* @param bool $full Add tag information to entries
|
||||
* @param string|boolean $filter String filter to apply to the tag names
|
||||
* @return array|false
|
||||
*/
|
||||
public function getTaxonomy($id, $full = true, $filter = false)
|
||||
{
|
||||
$taxonomy = $this->__getTaxonomy($id, $filter);
|
||||
if (empty($taxonomy)) {
|
||||
return false;
|
||||
}
|
||||
if ($full) {
|
||||
$TagsTable = $this->fetchTable('Tags');
|
||||
$tagNames = array_column($taxonomy['entries'], 'tag');
|
||||
$tags = $TagsTable->getTagsByName($tagNames, false);
|
||||
foreach ($taxonomy['entries'] as $key => $temp) {
|
||||
$tagLower = mb_strtolower($temp['tag']);
|
||||
if (isset($tags[$tagLower])) {
|
||||
$existingTag = $tags[$tagLower];
|
||||
$taxonomy['entries'][$key]['existing_tag'] = $existingTag;
|
||||
// numerical_value is overridden at tag level. Propagate the override further up
|
||||
if (isset($existingTag['Tag']['original_numerical_value'])) {
|
||||
$taxonomy['entries'][$key]['original_numerical_value'] = $existingTag['Tag']['original_numerical_value'];
|
||||
$taxonomy['entries'][$key]['numerical_value'] = $existingTag['Tag']['numerical_value'];
|
||||
}
|
||||
} else {
|
||||
$taxonomy['entries'][$key]['existing_tag'] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $taxonomy;
|
||||
}
|
||||
|
||||
private function __updateTags($id, $skipUpdateFields = array())
|
||||
{
|
||||
$paletteTool = new ColourPaletteTool();
|
||||
$taxonomy = $this->__getTaxonomy($id);
|
||||
$colours = $paletteTool->generatePaletteFromString($taxonomy['Taxonomy']['namespace'], count($taxonomy['entries']));
|
||||
$TagsTable = $this->fetchTable('Tags');
|
||||
$tags = $TagsTable->getTagsForNamespace($taxonomy['Taxonomy']['namespace'], false);
|
||||
foreach ($taxonomy['entries'] as $k => $entry) {
|
||||
if (isset($tags[strtoupper($entry['tag'])])) {
|
||||
$temp = $tags[strtoupper($entry['tag'])];
|
||||
if (
|
||||
(!in_array('colour', $skipUpdateFields) && $temp['Tag']['colour'] != $colours[$k]) ||
|
||||
(!in_array('name', $skipUpdateFields) && $temp['Tag']['name'] !== $entry['tag']) ||
|
||||
(
|
||||
!in_array('numerical_value', $skipUpdateFields) &&
|
||||
isset($entry['numerical_value']) && array_key_exists('numerical_value', $temp['Tag']) && // $temp['Tag']['num..'] may be null.
|
||||
$temp['Tag']['numerical_value'] !== $entry['numerical_value']
|
||||
)
|
||||
) {
|
||||
if (!in_array('colour', $skipUpdateFields)) {
|
||||
$temp['Tag']['colour'] = (isset($entry['colour']) && !empty($entry['colour'])) ? $entry['colour'] : $colours[$k];
|
||||
}
|
||||
if (!in_array('name', $skipUpdateFields)) {
|
||||
$temp['Tag']['name'] = $entry['tag'];
|
||||
}
|
||||
if (!in_array('numerical_value', $skipUpdateFields) && (isset($entry['numerical_value']) && $entry['numerical_value'] !== null)) {
|
||||
$temp['Tag']['numerical_value'] = $entry['numerical_value'];
|
||||
}
|
||||
$TagsTable->save($temp['Tag']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function addTags($id, $tagList = false)
|
||||
{
|
||||
if ($tagList && !is_array($tagList)) {
|
||||
$tagList = array($tagList);
|
||||
}
|
||||
$TagsTable = $this->fetchTable('Tags');
|
||||
|
||||
$paletteTool = new ColourPaletteTool();
|
||||
$taxonomy = $this->__getTaxonomy($id);
|
||||
if (empty($taxonomy)) {
|
||||
return false;
|
||||
}
|
||||
$tags = $TagsTable->getTagsForNamespace($taxonomy['Taxonomy']['namespace']);
|
||||
$colours = $paletteTool->generatePaletteFromString($taxonomy['Taxonomy']['namespace'], count($taxonomy['entries']));
|
||||
foreach ($taxonomy['entries'] as $k => $entry) {
|
||||
$colour = $colours[$k];
|
||||
if (isset($entry['colour']) && !empty($entry['colour'])) {
|
||||
$colour = $entry['colour'];
|
||||
}
|
||||
$numerical_value = null;
|
||||
if (isset($entry['numerical_value'])) {
|
||||
$numerical_value = $entry['numerical_value'];
|
||||
}
|
||||
if ($tagList) {
|
||||
foreach ($tagList as $tagName) {
|
||||
if ($tagName === $entry['tag'] || $tagName === h($entry['tag'])) {
|
||||
if (isset($tags[strtoupper($entry['tag'])])) {
|
||||
$TagsTable->quickEdit($tags[strtoupper($entry['tag'])], $entry['tag'], $colour, 0, $numerical_value);
|
||||
} else {
|
||||
$TagsTable->quickAdd($entry['tag'], $colour, $numerical_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isset($tags[strtoupper($entry['tag'])])) {
|
||||
$TagsTable->quickEdit($tags[strtoupper($entry['tag'])], $entry['tag'], $colour, 0, $numerical_value);
|
||||
} else {
|
||||
$TagsTable->quickAdd($entry['tag'], $colour, $numerical_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function disableTags($id, $tagList = false)
|
||||
{
|
||||
if ($tagList && !is_array($tagList)) {
|
||||
$tagList = array($tagList);
|
||||
}
|
||||
$TagsTable = $this->fetchTable('Tags');
|
||||
$tags = array();
|
||||
if ($tagList) {
|
||||
$tags = $tagList;
|
||||
} else {
|
||||
$taxonomy = $this->__getTaxonomy($id);
|
||||
foreach ($taxonomy['entries'] as $entry) {
|
||||
$tags[] = $entry['tag'];
|
||||
}
|
||||
}
|
||||
if (empty($tags)) {
|
||||
return true;
|
||||
}
|
||||
$tags = $TagsTable->find('all', array(
|
||||
'conditions' => array('Tag.name' => $tags, 'Tag.hide_tag' => 0),
|
||||
'recursive' => -1
|
||||
));
|
||||
if (empty($tags)) {
|
||||
return true;
|
||||
}
|
||||
$TagsTable->disableTags($tags);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function hideTags($id, $tagList = false)
|
||||
{
|
||||
if ($tagList && !is_array($tagList)) {
|
||||
$tagList = array($tagList);
|
||||
}
|
||||
$TagsTable = $this->fetchTable('Tags');
|
||||
$paletteTool = new ColourPaletteTool();
|
||||
$taxonomy = $this->__getTaxonomy($id);
|
||||
$tags = $TagsTable->getTagsForNamespace($taxonomy['Taxonomy']['namespace']);
|
||||
$colours = $paletteTool->generatePaletteFromString($taxonomy['Taxonomy']['namespace'], count($taxonomy['entries']));
|
||||
foreach ($taxonomy['entries'] as $k => $entry) {
|
||||
$colour = $colours[$k];
|
||||
if (isset($entry['colour']) && !empty($entry['colour'])) {
|
||||
$colour = $entry['colour'];
|
||||
}
|
||||
if ($tagList) {
|
||||
foreach ($tagList as $tagName) {
|
||||
if ($tagName === $entry['tag']) {
|
||||
if (isset($tags[strtoupper($entry['tag'])])) {
|
||||
$TagsTable->quickEdit($tags[strtoupper($entry['tag'])], $tagName, $colour, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isset($tags[strtoupper($entry['tag'])])) {
|
||||
$TagsTable->quickEdit($tags[strtoupper($entry['tag'])], $entry['tag'], $colour, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function unhideTags($id, $tagList = false)
|
||||
{
|
||||
if ($tagList && !is_array($tagList)) {
|
||||
$tagList = array($tagList);
|
||||
}
|
||||
$TagsTable = $this->fetchTable('Tags');
|
||||
$paletteTool = new ColourPaletteTool();
|
||||
$taxonomy = $this->__getTaxonomy($id);
|
||||
$tags = $TagsTable->getTagsForNamespace($taxonomy['Taxonomy']['namespace']);
|
||||
$colours = $paletteTool->generatePaletteFromString($taxonomy['Taxonomy']['namespace'], count($taxonomy['entries']));
|
||||
foreach ($taxonomy['entries'] as $k => $entry) {
|
||||
$colour = $colours[$k];
|
||||
if (isset($entry['colour']) && !empty($entry['colour'])) {
|
||||
$colour = $entry['colour'];
|
||||
}
|
||||
if ($tagList) {
|
||||
foreach ($tagList as $tagName) {
|
||||
if ($tagName === $entry['tag']) {
|
||||
if (isset($tags[strtoupper($entry['tag'])])) {
|
||||
$TagsTable->quickEdit($tags[strtoupper($entry['tag'])], $tagName, $colour, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isset($tags[strtoupper($entry['tag'])])) {
|
||||
$TagsTable->quickEdit($tags[strtoupper($entry['tag'])], $entry['tag'], $colour, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function listTaxonomies($options = array('full' => false, 'enabled' => false))
|
||||
{
|
||||
$recursive = -1;
|
||||
if (isset($options['full']) && $options['full']) {
|
||||
$recursive = 2;
|
||||
}
|
||||
$conditions = array();
|
||||
if (isset($options['enabled']) && $options['enabled']) {
|
||||
$conditions[] = array('Taxonomy.enabled' => 1);
|
||||
}
|
||||
$temp = $this->find('all', array(
|
||||
'recursive' => $recursive,
|
||||
'conditions' => $conditions
|
||||
));
|
||||
$taxonomies = array();
|
||||
foreach ($temp as $t) {
|
||||
if (isset($options['full']) && $options['full']) {
|
||||
$t['Taxonomy']['TaxonomyPredicate'] = $t['TaxonomyPredicate'];
|
||||
}
|
||||
$taxonomies[$t['Taxonomy']['namespace']] = $t['Taxonomy'];
|
||||
}
|
||||
return $taxonomies;
|
||||
}
|
||||
|
||||
private function cleanupCache()
|
||||
{
|
||||
RedisTool::deleteKeysByPattern(RedisTool::init(), "misp:taxonomies_cache:*");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tagName
|
||||
* @param bool $fullTaxonomy
|
||||
* @return array|false
|
||||
* @throws JsonException
|
||||
* @throws RedisException
|
||||
*/
|
||||
public function getTaxonomyForTag($tagName, $fullTaxonomy = false)
|
||||
{
|
||||
$splits = $this->splitTagToComponents($tagName);
|
||||
if ($splits === null) {
|
||||
return false; // not a taxonomy tag
|
||||
}
|
||||
$key = "misp:taxonomies_cache:tagName=$tagName&fullTaxonomy=$fullTaxonomy";
|
||||
|
||||
try {
|
||||
$redis = RedisTool::init();
|
||||
$taxonomy = RedisTool::deserialize(RedisTool::decompress($redis->get($key)));
|
||||
if (is_array($taxonomy)) {
|
||||
return $taxonomy;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
if (isset($splits['value'])) {
|
||||
$contain = array(
|
||||
'TaxonomyPredicate' => array(
|
||||
'TaxonomyEntry' => array()
|
||||
)
|
||||
);
|
||||
if (!$fullTaxonomy) {
|
||||
$contain['TaxonomyPredicate']['conditions'] = array(
|
||||
'LOWER(TaxonomyPredicate.value)' => mb_strtolower($splits['predicate']),
|
||||
);
|
||||
$contain['TaxonomyPredicate']['TaxonomyEntry']['conditions'] = array(
|
||||
'LOWER(TaxonomyEntry.value)' => mb_strtolower($splits['value']),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$contain = array('TaxonomyPredicate' => array());
|
||||
if (!$fullTaxonomy) {
|
||||
$contain['TaxonomyPredicate']['conditions'] = array(
|
||||
'LOWER(TaxonomyPredicate.value)' => mb_strtolower($splits['predicate'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$taxonomy = $this->find('all', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('LOWER(Taxonomy.namespace)' => mb_strtolower($splits['namespace'])),
|
||||
'contain' => $contain
|
||||
))->first();
|
||||
|
||||
if (isset($redis)) {
|
||||
$redis->setex($key, 1800, RedisTool::compress(RedisTool::serialize($taxonomy)));
|
||||
}
|
||||
|
||||
return $taxonomy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the value for triple component tags or the predicate for double components tags
|
||||
* @param string $tagName
|
||||
* @return string
|
||||
*/
|
||||
public function stripLastTagComponent($tagName)
|
||||
{
|
||||
$splits = $this->splitTagToComponents($tagName);
|
||||
if ($splits === null) {
|
||||
return '';
|
||||
}
|
||||
if (isset($splits['value'])) {
|
||||
return $splits['namespace'] . ':' . $splits['predicate'];
|
||||
}
|
||||
return $splits['namespace'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $newTagName
|
||||
* @param array $tagNameList
|
||||
* @return bool
|
||||
*/
|
||||
public function checkIfNewTagIsAllowedByTaxonomy($newTagName, array $tagNameList = array())
|
||||
{
|
||||
$newTagShortened = $this->stripLastTagComponent($newTagName);
|
||||
$prefixIsFree = true;
|
||||
foreach ($tagNameList as $tagName) {
|
||||
$tagShortened = $this->stripLastTagComponent($tagName);
|
||||
if ($newTagShortened === $tagShortened) {
|
||||
$prefixIsFree = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$prefixIsFree) {
|
||||
// at this point, we have a duplicated namespace(-predicate)
|
||||
$taxonomy = $this->getTaxonomyForTag($newTagName);
|
||||
if (!empty($taxonomy['Taxonomy']['exclusive'])) {
|
||||
if (
|
||||
($newTagName === 'tlp:white' && in_array('tlp:clear', $tagNameList)) ||
|
||||
($newTagName === 'tlp:clear' && in_array('tlp:white', $tagNameList))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false; // only one tag of this taxonomy is allowed
|
||||
} elseif (!empty($taxonomy['TaxonomyPredicate'][0]['exclusive'])) {
|
||||
return false; // only one tag belonging to this predicate is allowed
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $tagList
|
||||
* @return array[]
|
||||
*/
|
||||
public function checkIfTagInconsistencies($tagList)
|
||||
{
|
||||
if (Configure::read('MISP.disable_taxonomy_consistency_checks')) {
|
||||
return [
|
||||
'global' => [],
|
||||
'local' => []
|
||||
];
|
||||
}
|
||||
|
||||
$eventTags = array();
|
||||
$localEventTags = array();
|
||||
foreach ($tagList as $tag) {
|
||||
if ($tag['local'] == 0) {
|
||||
$eventTags[] = $tag['Tag']['name'];
|
||||
} else {
|
||||
$localEventTags[] = $tag['Tag']['name'];
|
||||
}
|
||||
}
|
||||
$tagConflicts = $this->getTagConflicts($eventTags);
|
||||
$localTagConflicts = $this->getTagConflicts($localEventTags);
|
||||
return array(
|
||||
'global' => $tagConflicts,
|
||||
'local' => $localTagConflicts
|
||||
);
|
||||
}
|
||||
|
||||
public function getTagConflicts($tagNameList)
|
||||
{
|
||||
$potentiallyConflictingTaxonomy = array();
|
||||
$conflictingTaxonomy = array();
|
||||
foreach ($tagNameList as $tagName) {
|
||||
$tagShortened = $this->stripLastTagComponent($tagName);
|
||||
// No exclusivity in non taxonomy tags.
|
||||
if ($tagShortened === '') {
|
||||
continue;
|
||||
}
|
||||
if (isset($potentiallyConflictingTaxonomy[$tagShortened])) {
|
||||
if (!isset($this->__taxonomyConflicts[$tagShortened])) {
|
||||
$this->__taxonomyConflicts[$tagShortened] = $this->getTaxonomyForTag($tagName);
|
||||
}
|
||||
$potentiallyConflictingTaxonomy[$tagShortened]['count']++;
|
||||
} else {
|
||||
$potentiallyConflictingTaxonomy[$tagShortened] = [
|
||||
'count' => 1
|
||||
];
|
||||
}
|
||||
$potentiallyConflictingTaxonomy[$tagShortened]['tagNames'][] = $tagName;
|
||||
}
|
||||
if (
|
||||
!empty($potentiallyConflictingTaxonomy['tlp']) &&
|
||||
count($potentiallyConflictingTaxonomy['tlp']['tagNames']) == 2 &&
|
||||
in_array('tlp:white', $potentiallyConflictingTaxonomy['tlp']['tagNames']) &&
|
||||
in_array('tlp:clear', $potentiallyConflictingTaxonomy['tlp']['tagNames'])
|
||||
) {
|
||||
unset($potentiallyConflictingTaxonomy['tlp']);
|
||||
}
|
||||
foreach ($potentiallyConflictingTaxonomy as $taxonomyName => $potTaxonomy) {
|
||||
if ($potTaxonomy['count'] > 1) {
|
||||
$taxonomy = $this->__taxonomyConflicts[$taxonomyName];
|
||||
if (isset($taxonomy['Taxonomy']['exclusive']) && $taxonomy['Taxonomy']['exclusive']) {
|
||||
$conflictingTaxonomy[] = array(
|
||||
'tags' => $potTaxonomy['tagNames'],
|
||||
'taxonomy' => $taxonomy,
|
||||
'conflict' => sprintf(__('Taxonomy `%s` is an exclusive Taxonomy'), $taxonomy['Taxonomy']['namespace'])
|
||||
);
|
||||
} elseif (isset($taxonomy['TaxonomyPredicate'][0]['exclusive']) && $taxonomy['TaxonomyPredicate'][0]['exclusive']) {
|
||||
$conflictingTaxonomy[] = array(
|
||||
'tags' => $potTaxonomy['tagNames'],
|
||||
'taxonomy' => $taxonomy,
|
||||
'conflict' => sprintf(
|
||||
__('Predicate `%s` is exclusive'),
|
||||
$taxonomy['TaxonomyPredicate'][0]['value']
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $conflictingTaxonomy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tag
|
||||
* @return array|null Returns null if tag is not in taxonomy format
|
||||
*/
|
||||
public function splitTagToComponents($tag)
|
||||
{
|
||||
preg_match('/^([^:="]+):([^:="]+)(="([^"]+)")?$/i', $tag, $matches);
|
||||
if (empty($matches)) {
|
||||
return null; // tag is not in taxonomy format
|
||||
}
|
||||
$splits = [
|
||||
'namespace' => $matches[1],
|
||||
'predicate' => $matches[2],
|
||||
];
|
||||
if (isset($matches[4])) {
|
||||
$splits['value'] = $matches[4];
|
||||
}
|
||||
return $splits;
|
||||
}
|
||||
|
||||
private function __craftTaxonomiesTags()
|
||||
{
|
||||
$taxonomies = $this->find('all', [
|
||||
'fields' => ['namespace'],
|
||||
'contain' => ['TaxonomyPredicate' => ['TaxonomyEntry']],
|
||||
]);
|
||||
$allTaxonomyTags = [];
|
||||
foreach ($taxonomies as $taxonomy) {
|
||||
$namespace = $taxonomy['Taxonomy']['namespace'];
|
||||
foreach ($taxonomy['TaxonomyPredicate'] as $predicate) {
|
||||
if (isset($predicate['TaxonomyEntry']) && !empty($predicate['TaxonomyEntry'])) {
|
||||
foreach ($predicate['TaxonomyEntry'] as $entry) {
|
||||
$tag = $namespace . ':' . $predicate['value'] . '="' . $entry['value'] . '"';
|
||||
$allTaxonomyTags[$tag] = true;
|
||||
}
|
||||
} else {
|
||||
$tag = $namespace . ':' . $predicate['value'];
|
||||
$allTaxonomyTags[$tag] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $allTaxonomyTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* normalizeCustomTagsToTaxonomyFormat Transform all custom tags into their taxonomy version.
|
||||
*
|
||||
* @return int The number of converted tag
|
||||
*/
|
||||
public function normalizeCustomTagsToTaxonomyFormat(): array
|
||||
{
|
||||
$tagConverted = 0;
|
||||
$rowUpdated = 0;
|
||||
$craftedTags = $this->__craftTaxonomiesTags();
|
||||
$allTaxonomyTagsByName = Hash::combine($this->getAllTaxonomyTags(false, false, true, false, true), '{n}.Tag.name', '{n}.Tag.id');
|
||||
$tagsToMigrate = array_diff_key($allTaxonomyTagsByName, $craftedTags);
|
||||
foreach ($tagsToMigrate as $tagToMigrate_name => $tagToMigrate_id) {
|
||||
foreach (array_keys($craftedTags) as $craftedTag) {
|
||||
if (strcasecmp($craftedTag, $tagToMigrate_name) == 0) {
|
||||
$result = $this->__updateTagToNormalized(intval($tagToMigrate_id), intval($allTaxonomyTagsByName[$craftedTag]));
|
||||
$tagConverted += 1;
|
||||
$rowUpdated += $result['changed'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return [
|
||||
'tag_converted' => $tagConverted,
|
||||
'row_updated' => $rowUpdated,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* __updateTagToNormalized Change the link of element having $source_id tag attached to them for the $target_id one.
|
||||
* Updated:
|
||||
* - event_tags
|
||||
* - attribute_tags
|
||||
* - galaxy_cluster_relation_tags
|
||||
*
|
||||
* Ignored: As this is defined by users, let them do the migration themselves
|
||||
* - tag_collection_tags
|
||||
* - template_tags
|
||||
* - favorite_tags
|
||||
*
|
||||
* @param int $source_id
|
||||
* @param int $target_id
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
private function __updateTagToNormalized($source_id, $target_id): array
|
||||
{
|
||||
$TagsTable = $this->fetchTable('Tags');
|
||||
return $TagsTable->mergeTag($source_id, $target_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getHighlightedTaxonomies()
|
||||
{
|
||||
return $this->find('all', [
|
||||
'conditions' => [
|
||||
'highlighted' => 1,
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $highlightedTaxonomies
|
||||
* @param array $tags
|
||||
* @return array
|
||||
*/
|
||||
public function getHighlightedTags($highlightedTaxonomies, $tags)
|
||||
{
|
||||
$highlightedTags = [];
|
||||
if (is_array($highlightedTaxonomies) && !empty($highlightedTaxonomies)) {
|
||||
foreach ($highlightedTaxonomies as $k => $taxonomy) {
|
||||
$highlightedTags[$k] = [
|
||||
'taxonomy' => $taxonomy,
|
||||
'tags' => []
|
||||
];
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
$splits = $this->splitTagToComponents($tag['Tag']['name']);
|
||||
if (!empty($splits) && $splits['namespace'] === $taxonomy['Taxonomy']['namespace']) {
|
||||
$highlightedTags[$k]['tags'][] = $tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $highlightedTags;
|
||||
}
|
||||
|
||||
return $highlightedTags;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Table;
|
||||
|
||||
use App\Model\Table\AppTable;
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Validation\Validator;
|
||||
use Exception;
|
||||
use App\Lib\Tools\ColourPaletteTool;
|
||||
use App\Lib\Tools\FileAccessTool;
|
||||
use App\Lib\Tools\RedisTool;
|
||||
use Cake\Utility\Hash;
|
||||
use Cake\Datasource\EntityInterface;
|
||||
use Cake\Event\Event;
|
||||
use Cake\Event\EventInterface;
|
||||
use ArrayObject;
|
||||
|
||||
class TaxonomyEntriesTable extends AppTable
|
||||
{
|
||||
public function initialize(array $config): void
|
||||
{
|
||||
$this->setDisplayField('name');
|
||||
|
||||
$this->belongsTo(
|
||||
'TaxonomyPredicates',
|
||||
[
|
||||
'className' => 'TaxonomyPredicates',
|
||||
'foreignKey' => 'taxonomy_predicate_id',
|
||||
'propertyName' => 'TaxonomyPredicate',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function validationDefault(Validator $validator): Validator
|
||||
{
|
||||
$validator
|
||||
->requirePresence(['value', 'expanded'], 'create');
|
||||
|
||||
return $validator;
|
||||
}
|
||||
|
||||
function beforeMarshal(EventInterface $event, ArrayObject $data, ArrayObject $options)
|
||||
{
|
||||
if (empty($data['expanded'])) {
|
||||
$data['expanded'] = $data['value'];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Table;
|
||||
|
||||
use App\Model\Table\AppTable;
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Validation\Validator;
|
||||
use Exception;
|
||||
use App\Lib\Tools\ColourPaletteTool;
|
||||
use App\Lib\Tools\FileAccessTool;
|
||||
use App\Lib\Tools\RedisTool;
|
||||
use Cake\Utility\Hash;
|
||||
use Cake\Datasource\EntityInterface;
|
||||
use Cake\Event\Event;
|
||||
use Cake\Event\EventInterface;
|
||||
use ArrayObject;
|
||||
|
||||
class TaxonomyPredicatesTable extends AppTable
|
||||
{
|
||||
public function initialize(array $config): void
|
||||
{
|
||||
$this->setDisplayField('name');
|
||||
|
||||
$this->hasMany(
|
||||
'TaxonomyEntries',
|
||||
[
|
||||
'className' => 'TaxonomyEntries',
|
||||
'foreignKey' => 'taxonomy_predicate_id',
|
||||
'propertyName' => 'TaxonomyEntry',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function validationDefault(Validator $validator): Validator
|
||||
{
|
||||
$validator
|
||||
->requirePresence(['value', 'expanded'], 'create');
|
||||
|
||||
return $validator;
|
||||
}
|
||||
|
||||
function beforeMarshal(EventInterface $event, ArrayObject $data, ArrayObject $options)
|
||||
{
|
||||
if (empty($data['expanded'])) {
|
||||
$data['expanded'] = $data['value'];
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue