Merge pull request #9143 from righel/3.x-sharing-groups

3.x sharing groups
pull/9215/head
Luciano Righetti 2023-06-20 11:54:45 +02:00 committed by GitHub
commit 53cbbc0ec6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 4121 additions and 244 deletions

1
.gitignore vendored
View File

@ -10,6 +10,7 @@ webroot/scss/*.css
.vscode
docker/run/
.phpunit.result.cache
.phpunit.cache
config.json
phpunit.xml
docker-compose.override.yml

View File

@ -9,7 +9,7 @@
"admad/cakephp-social-auth": "^1.1",
"cakephp/authentication": "^2.0",
"cakephp/authorization": "^2.0",
"cakephp/cakephp": "^4.3",
"cakephp/cakephp": "^4.4",
"cakephp/migrations": "^3.0",
"cakephp/plugin-installer": "^1.2",
"erusev/parsedown": "^1.7",
@ -25,7 +25,7 @@
"josegonzalez/dotenv": "^3.2",
"league/openapi-psr7-validator": "^0.17",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^8.5",
"phpunit/phpunit": "^9.5",
"psy/psysh": "@stable",
"wiremock-php/wiremock-php": "^2.33"
},
@ -78,6 +78,5 @@
"dealerdirect/phpcodesniffer-composer-installer": true,
"cakephp/plugin-installer": true
}
},
"minimum-stability": "dev"
}
}

View File

@ -65,8 +65,8 @@ RUN usermod --uid $UID --gid $GID www-data
COPY . /var/www/html/
# Install development dependencies
RUN pecl install -f xdebug \
&& docker-php-ext-enable xdebug
RUN pecl install -f xdebug pcov \
&& docker-php-ext-enable xdebug pcov
# Install additional packages
RUN apt-get update \

View File

@ -29,6 +29,8 @@
<rule ref="PSR2.Files.EndFileNewline" />
<rule ref="PSR12.Files.OpenTag" />
<rule ref="Zend.Files.ClosingTag" />
<rule ref="SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses" />
<rule ref="SlevomatCodingStandard.Namespaces.UnusedUses" />
<!-- Only one argument per line in multi-line function calls -->
<rule ref="PEAR.Functions.FunctionCallSignature">
@ -53,11 +55,6 @@
</properties>
</rule>
<!-- Private methods MUST not be prefixed with an underscore -->
<rule ref="PSR2.Methods.MethodDeclaration.Underscore">
<type>error</type>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.DeclareStrictTypes">
<severity>0</severity>
</rule>

View File

@ -1,16 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true" processIsolation="false" stopOnFailure="false" bootstrap="tests/bootstrap.php">
<php>
<ini name="memory_limit" value="-1" />
<ini name="apc.enable_cli" value="1" />
<env name="WIREMOCK_HOST" value="localhost" />
<env name="WIREMOCK_PORT" value="8080" />
<env name="OPENAPI_SPEC" value="webroot/docs/openapi.yaml" />
<env name="SKIP_DB_MIGRATIONS" value="0" />
</php>
<!-- Add any additional test suites you want to run here -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.6/phpunit.xsd"
bootstrap="tests/bootstrap.php"
cacheResultFile=".phpunit.cache/test-results"
executionOrder="depends,defects"
forceCoversAnnotation="false"
beStrictAboutCoversAnnotation="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
convertDeprecationsToExceptions="true"
failOnRisky="true"
failOnWarning="false"
colors="true"
verbose="true">
<testsuites>
<testsuite name="default">
<directory>tests</directory>
</testsuite>
<testsuite name="app">
<directory>tests/TestCase/</directory>
</testsuite>
@ -21,19 +27,22 @@
<directory>./tests/TestCase/Api</directory>
</testsuite>
</testsuites>
<extensions>
<extension class="\Cake\TestSuite\Fixture\PHPUnitExtension" />
</extensions>
<!-- Ignore vendor tests in code coverage reports -->
<filter>
<whitelist>
<php>
<ini name="memory_limit" value="-1" />
<ini name="apc.enable_cli" value="1" />
<env name="WIREMOCK_HOST" value="localhost" />
<env name="WIREMOCK_PORT" value="8080" />
<env name="OPENAPI_SPEC" value="webroot/docs/openapi.yaml" />
<env name="SKIP_DB_MIGRATIONS" value="0" />
</php>
<coverage cacheDirectory=".phpunit.cache/code-coverage"
processUncoveredFiles="true">
<include>
<directory suffix=".php">src/</directory>
<directory suffix=".php">plugins/*/src/</directory>
<exclude>
<file>src/Console/Installer.php</file>
</exclude>
</whitelist>
</filter>
</include>
<exclude>
<file>src/Console/Installer.php</file>
</exclude>
</coverage>
</phpunit>

View File

@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
/**
@ -14,17 +15,14 @@ declare(strict_types=1);
* @since 0.2.9
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\Controller;
use Cake\Controller\Controller;
use Cake\Core\Configure;
use Cake\Core\Configure\Engine\PhpConfig;
use Cake\Event\EventInterface;
use Cake\Utility\Text;
use Cake\Http\Exception\NotFoundException;
use Cake\Http\Exception\MethodNotAllowedException;
use Cake\Http\Exception\ForbiddenException;
use Cake\Error\Debugger;
use Cake\Utility\Text;
/**
* Application Controller
@ -59,29 +57,44 @@ class AppController extends Controller
$this->loadComponent('Flash');
$this->loadComponent('RestResponse');
$this->loadComponent('Security');
$this->loadComponent('ParamHandler', [
$this->loadComponent(
'ParamHandler',
[
'request' => $this->request
]);
]
);
$this->loadModel('MetaFields');
$this->loadModel('MetaTemplates');
$table = $this->getTableLocator()->get($this->modelClass);
$this->loadComponent('CRUD', [
$this->loadComponent(
'CRUD',
[
'request' => $this->request,
'table' => $table,
'MetaFields' => $this->MetaFields,
'MetaTemplates' => $this->MetaTemplates
]);
]
);
$this->loadComponent('Authentication.Authentication');
$this->loadComponent('ACL', [
$this->loadComponent(
'ACL',
[
'request' => $this->request,
'Authentication' => $this->Authentication
]);
$this->loadComponent('Navigation', [
]
);
$this->loadComponent(
'Navigation',
[
'request' => $this->request,
]);
$this->loadComponent('Notification', [
]
);
$this->loadComponent(
'Notification',
[
'request' => $this->request,
]);
]
);
if (Configure::read('debug')) {
Configure::write('DebugKit.panels', ['DebugKit.Packages' => true]);
Configure::write('DebugKit.forceEnable', true);
@ -105,9 +118,12 @@ class AppController extends Controller
}
$this->ACL->setPublicInterfaces();
if (!empty($this->request->getAttribute('identity'))) {
$user = $this->Users->get($this->request->getAttribute('identity')->getIdentifier(), [
$user = $this->Users->get(
$this->request->getAttribute('identity')->getIdentifier(),
[
'contain' => ['Roles', /*'UserSettings',*/ 'Organisations']
]);
]
);
if (!empty($user['disabled'])) {
$this->Authentication->logout();
$this->Flash->error(__('The user account is disabled.'));
@ -140,7 +156,7 @@ class AppController extends Controller
$this->ACL->checkAccess();
$this->set('default_memory_limit', ini_get('memory_limit'));
if (isset($user['Role']['memory_limit']) && $user['Role']['memory_limit'] !== '') {
ini_set('memory_limit', $user['Role']['memory_limit']);
ini_set('memory_limit', $user['Role']['memory_limit']);
}
$this->set('default_max_execution_time', ini_get('max_execution_time'));
if (isset($user['Role']['max_execution_time']) && $user['Role']['max_execution_time'] !== '') {
@ -186,25 +202,29 @@ class AppController extends Controller
if (!empty($authKey)) {
$this->loadModel('Users');
$user = $this->Users->get($authKey['user_id']);
$logModel->insert([
$logModel->insert(
[
'request_action' => 'login',
'model' => 'Users',
'model_id' => $user['id'],
'model_title' => $user['username'],
'changed' => []
]);
]
);
if (!empty($user)) {
$this->Authentication->setIdentity($user);
}
} else {
$user = $logModel->userInfo();
$logModel->insert([
$logModel->insert(
[
'request_action' => 'login',
'model' => 'Users',
'model_id' => $user['id'],
'model_title' => $user['name'],
'changed' => []
]);
]
);
}
}
}
@ -228,12 +248,18 @@ class AppController extends Controller
/**
* Convert an array to the same array but with the values also as index instead of an interface_exists
*/
protected function _arrayToValuesIndexArray(array $oldArray): array
protected function arrayToValuesIndexArray(array $oldArray): array
{
$newArray = array();
$newArray = [];
foreach ($oldArray as $value) {
$newArray[$value] = $value;
}
return $newArray;
}
// checks if the currently logged user is a site administrator (an admin that can manage any user or event on the instance and create / edit the roles).
protected function isSiteAdmin()
{
return $this->ACL->getUser()->Role->perm_site_admin;
}
}

View File

@ -3,11 +3,6 @@
namespace App\Controller;
use App\Controller\AppController;
use Cake\Utility\Hash;
use Cake\Utility\Text;
use Cake\Database\Expression\QueryExpression;
use Cake\Http\Exception\NotFoundException;
use Cake\Http\Exception\ForbiddenException;
class CommunitiesController extends AppController
{
@ -16,7 +11,8 @@ class CommunitiesController extends AppController
public function index()
{
$this->CRUD->index([
$this->CRUD->index(
[
'filters' => $this->filterFields,
'quickFilters' => $this->quickFilterFields,
'quickFilterForMetaField' => ['enabled' => true, 'wildcard_search' => true],
@ -41,7 +37,9 @@ class CommunitiesController extends AppController
[
'label' => __('ENISA CSIRT Network (GOV)'),
'filterConditionFunction' => function($query) {
return $this->CRUD->setParentConditionsForMetaFields($query, [
return $this->CRUD->setParentConditionsForMetaFields(
$query,
[
'ENISA CSIRT Network' => [
[
'field' => 'constituency',
@ -52,14 +50,16 @@ class CommunitiesController extends AppController
'value' => 'Member',
],
]
]);
]
);
}
]
],
],
'contain' => $this->containFields,
'statisticsFields' => $this->statisticsFields,
]);
]
);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
@ -67,29 +67,29 @@ class CommunitiesController extends AppController
$this->set('metaGroup', 'Communities');
}
public function index()
{
$filters = $this->IndexFilter->harvestParameters(array('context', 'value'));
if (empty($filters['context'])) {
$filters['context'] = 'vetted';
}
if (!empty($filters['value'])) {
$filters['value'] = strtolower($filters['value']);
} else {
$filters['value'] = false;
}
$community_list = $this->Community->getCommunityList($filters['context'], $filters['value']);
// public function index()
// {
// $filters = $this->IndexFilter->harvestParameters(array('context', 'value'));
// if (empty($filters['context'])) {
// $filters['context'] = 'vetted';
// }
// if (!empty($filters['value'])) {
// $filters['value'] = strtolower($filters['value']);
// } else {
// $filters['value'] = false;
// }
// $community_list = $this->Community->getCommunityList($filters['context'], $filters['value']);
//foreach ($community)
if ($this->_isRest()) {
return $this->RestResponse->viewData($community_list, $this->response->type());
}
App::uses('CustomPaginationTool', 'Tools');
$customPagination = new CustomPaginationTool();
$customPagination->truncateAndPaginate($community_list, $this->params, $this->modelClass, true);
$this->set('community_list', $community_list);
$this->set('context', $filters['context']);
}
// //foreach ($community)
// if ($this->_isRest()) {
// return $this->RestResponse->viewData($community_list, $this->response->type());
// }
// App::uses('CustomPaginationTool', 'Tools');
// $customPagination = new CustomPaginationTool();
// $customPagination->truncateAndPaginate($community_list, $this->params, $this->modelClass, true);
// $this->set('community_list', $community_list);
// $this->set('context', $filters['context']);
// }
public function view($id)
{
@ -106,11 +106,14 @@ class CommunitiesController extends AppController
{
$community = $this->Community->getCommunity($id);
$this->loadModel('User');
$gpgkey = $this->User->find('first', array(
'conditions' => array('User.id' => $this->Auth->user('id')),
$gpgkey = $this->User->find(
'first',
[
'conditions' => ['User.id' => $this->Auth->user('id')],
'recursive' => -1,
'fields' => array('User.gpgkey')
));
'fields' => ['User.gpgkey']
]
);
if (!empty($gpgkey['User']['gpgkey'])) {
$gpgkey = $gpgkey['User']['gpgkey'];
} else {
@ -126,7 +129,7 @@ class CommunitiesController extends AppController
$this->request->data['Server']['gpgkey'] = $gpgkey;
} else {
if (empty($this->request->data['Server'])) {
$this->request->data = array('Server' => $this->request->data);
$this->request->data = ['Server' => $this->request->data];
}
$body = sprintf(
'To whom it may concern,
@ -166,7 +169,7 @@ Thank you in advance!',
)
);
$imgPath = APP . WEBROOT_DIR . DS . 'img' . DS . 'orgs' . DS;
$possibleFields = array('id', 'name');
$possibleFields = ['id', 'name'];
$image = false;
App::uses('File', 'Utility');
foreach ($possibleFields as $field) {
@ -187,7 +190,7 @@ Thank you in advance!',
if (!empty($this->request->data['Server']['gpgkey'])) {
$params['attachments']['requestor.asc'] = $this->request->data['Server']['gpgkey'];
}
$params = array();
$params = [];
$params['to'] = $community['email'];
$params['reply-to'] = empty($this->request->data['Server']['email']) ? $this->Auth->user('email') : $this->request->data['Server']['email'];
$params['requestor_gpgkey'] = empty($this->request->data['Server']['gpgkey']) ? $gpgkey : $this->request->data['Server']['gpgkey'];
@ -211,7 +214,7 @@ Thank you in advance!',
} else {
if ($result === true) {
$this->Flash->success($message);
$this->redirect(array('controller' => 'communities', 'action' => 'view', $id));
$this->redirect(['controller' => 'communities', 'action' => 'view', $id]);
} elseif ($result) {
$this->set('result', $result);
if (empty($this->request->data['Server']['mock'])) {
@ -220,7 +223,7 @@ Thank you in advance!',
$this->render('request_access_email');
} else {
$this->Flash->error($message);
$this->redirect(array('controller' => 'communities', 'action' => 'view', $id));
$this->redirect(['controller' => 'communities', 'action' => 'view', $id]);
}
}
if (!empty($this->request->data['Server']['mock'])) {

View File

@ -0,0 +1,688 @@
<?php
namespace App\Controller;
use App\Controller\AppController;
use App\Model\Entity\SharingGroup;
use App\Model\Entity\SharingGroupOrg;
use App\Model\Entity\SharingGroupServer;
use Cake\Core\Configure;
use Cake\Event\EventInterface;
use Cake\Http\Exception\MethodNotAllowedException;
use Cake\Http\Exception\NotFoundException;
use Cake\Http\Response;
use Cake\ORM\Locator\LocatorAwareTrait;
use Cake\Validation\Validation;
class SharingGroupsController extends AppController
{
use LocatorAwareTrait;
public function beforeFilter(EventInterface $event)
{
parent::beforeFilter($event);
if (!empty($this->request->getParam('admin')) && !$this->isSiteAdmin()) {
$this->redirect('/');
}
}
public $paginate = [
'limit' => 60,
'maxLimit' => 9999,
'order' => [
'SharingGroup.name' => 'ASC'
],
'fields' => ['id', 'uuid', 'name', 'description', 'releasability', 'local', 'active', 'roaming'],
'contain' => [
'SharingGroupOrgs' => [
'Organisations' => ['fields' => ['name', 'id', 'uuid']]
],
'Organisations' => [
'fields' => ['id', 'name', 'uuid'],
],
'SharingGroupServers' => [
'fields' => ['sharing_group_id', 'all_orgs'],
'Servers' => [
'fields' => ['name', 'id']
]
]
],
];
public function add()
{
$canModifyUuid = $this->ACL->getUser()->Role->perm_site_admin;
if ($this->request->is('post')) {
if ($this->ParamHandler->isRest()) {
if (!empty($this->request->getData('SharingGroup'))) {
$data = $this->request->getData('SharingGroup');
} else {
$data = $this->request->getData();
}
$sg = $data;
$id = $this->SharingGroups->captureSG($sg, $this->ACL->getUser()->toArray());
if ($id) {
if (empty($sg['roaming']) && empty($sg['SharingGroupServer'])) {
$sharingGroupServerEntity = new SharingGroupServer(
[
'sharing_group_id' => $id,
'server_id' => 0,
'all_orgs' => 0
]
);
$this->SharingGroups->SharingGroupServers->save($sharingGroupServerEntity);
}
$sg = $this->SharingGroups->fetchAllAuthorised($this->ACL->getUser()->toArray(), 'simplified', false, $id);
if (!empty($sg)) {
$sg = empty($sg) ? [] : $sg[0];
}
return $this->RestResponse->viewData($sg);
} else {
return $this->RestResponse->saveFailResponse('SharingGroup', 'add', false, 'Could not save sharing group.');
}
} else {
$json = json_decode($this->request->getData('json'), true);
$sg = $json['sharingGroup'];
if (!empty($json['organisations'])) {
$sg['Organisation'] = $json['organisations'];
}
if (!empty($json['servers'])) {
$sg['Server'] = $json['servers'];
}
}
if (!$canModifyUuid) {
unset($sg['uuid']);
}
$sg['active'] = $sg['active'] ? 1 : 0;
$sg['roaming'] = $sg['roaming'] ? 1 : 0;
$sg['organisation_uuid'] = $this->ACL->getUser()->Organisation->uuid;
$sg['local'] = 1;
$sg['org_id'] = $this->ACL->getUser()->org_id;
$sharingGroupEntity = new SharingGroup($sg);
if ($this->SharingGroups->save($sharingGroupEntity)) {
if (!empty($sg['Organisation'])) {
foreach ($sg['Organisation'] as $org) {
$sharingGroupOrgEntity = new SharingGroupOrg(
[
'sharing_group_id' => $sharingGroupEntity->id,
'org_id' => $org['id'],
'extend' => $org['extend']
]
);
$this->SharingGroups->SharingGroupOrgs->save($sharingGroupOrgEntity);
}
}
if (empty($sg['roaming']) && !empty($sg['Server'])) {
foreach ($sg['Server'] as $server) {
$sharingGroupServerEntity = new SharingGroupServer(
[
'sharing_group_id' => $sharingGroupEntity->id,
'server_id' => $server['id'],
'all_orgs' => $server['all_orgs']
]
);
$this->SharingGroups->SharingGroupServers->save($sharingGroupServerEntity);
}
}
$this->redirect('/sharing-groups/view/' . $sharingGroupEntity->id);
} else {
$validationReplacements = [
'notempty' => 'This field cannot be left empty.',
];
$validationErrors = $this->SharingGroups->validationErrors;
$failedField = array_keys($validationErrors)[0];
$reason = reset($this->SharingGroups->validationErrors)[0];
foreach ($validationReplacements as $k => $vR) {
if ($reason == $k) {
$reason = $vR;
}
}
$this->Flash->error('The sharing group could not be added. ' . ucfirst($failedField) . ': ' . $reason);
}
} elseif ($this->ParamHandler->isRest()) {
return $this->RestResponse->describe('SharingGroup', 'add');
}
$this->set('localInstance', empty(Configure::read('MISP.external_baseurl')) ? Configure::read('MISP.baseurl') : Configure::read('MISP.external_baseurl'));
// We just pass true and allow the user to edit, since he/she is just about to create the SG. This is needed to reuse the view for the edit
$this->set('user', $this->ACL->getUser()->toArray());
$this->set('canModifyUuid', $canModifyUuid);
}
public function edit($id = false)
{
if (empty($id)) {
throw new NotFoundException('Invalid sharing group.');
}
// check if the user is eligible to edit the SG (original creator or extend)
$sharingGroup = $this->SharingGroups->find(
'all',
[
'conditions' => Validation::uuid($id) ? ['SharingGroups.uuid' => $id] : ['SharingGroups.id' => $id],
'recursive' => -1,
'contain' => [
'SharingGroupOrgs' => [
'Organisations' => [
'fields' => ['name', 'local', 'id']
]
],
'SharingGroupServers' => [
'Servers' => [
'fields' => ['name', 'url', 'id']
]
],
'Organisations' => [
'fields' => ['name', 'local', 'id']
],
],
]
)->disableHydration()->first();
if (empty($sharingGroup)) {
throw new NotFoundException('Invalid sharing group.');
}
if (!$this->SharingGroups->checkIfAuthorisedExtend($this->ACL->getUser()->toArray(), $sharingGroup['id'])) {
throw new MethodNotAllowedException('Action not allowed.');
}
if ($this->request->is('post')) {
if ($this->ParamHandler->isRest()) {
if (!empty($this->request->getData('SharingGroup'))) {
$data = $this->request->getData('SharingGroup');
} else {
$data = $this->request->getData();
}
$data['uuid'] = $sharingGroup['uuid'];
$id = $this->SharingGroups->captureSG($data, $this->ACL->getUser()->toArray());
if ($id) {
$sg = $this->SharingGroups->fetchAllAuthorised($this->ACL->getUser()->toArray(), 'simplified', false, $id);
return $this->RestResponse->viewData($sg[0]);
} else {
return $this->RestResponse->saveFailResponse('SharingGroup', 'add', false, 'Could not save sharing group.');
}
} else {
$json = json_decode($this->request->getData('json'), true);
$sg = $json['sharingGroup'];
$sg['id'] = $sharingGroup['id'];
$fields = ['name', 'releasability', 'description', 'active', 'roaming'];
$existingSG = $this->SharingGroups->find('all', ['recursive' => -1, 'conditions' => ['SharingGroup.id' => $sharingGroup['id']]])->disableHydration()->first();
foreach ($fields as $field) {
$existingSG[$field] = $sg[$field];
}
unset($existingSG['modified']);
if ($this->SharingGroups->save($existingSG)) {
$this->SharingGroups->SharingGroupOrgs->updateOrgsForSG($sharingGroup['id'], $json['organisations'], $sharingGroup['SharingGroupOrg'], $this->ACL->getUser()->toArray());
$this->SharingGroups->SharingGroupServers->updateServersForSG($sharingGroup['id'], $json['servers'], $sharingGroup['SharingGroupServer'], $json['sharingGroup']['roaming'], $this->ACL->getUser()->toArray());
$this->redirect('/sharing-groups/view/' . $sharingGroup['id']);
} else {
$validationReplacements = [
'notempty' => 'This field cannot be left empty.',
];
$validationErrors = $this->SharingGroups->validationErrors;
$failedField = array_keys($validationErrors)[0];
$reason = reset($this->SharingGroups->validationErrors)[0];
foreach ($validationReplacements as $k => $vR) {
if ($reason == $k) {
$reason = $vR;
}
}
$this->Flash->error('The sharing group could not be edited. ' . ucfirst($failedField) . ': ' . $reason);
}
}
} elseif ($this->ParamHandler->isRest()) {
return $this->RestResponse->describe('SharingGroup', 'edit', false);
}
$orgs = $this->SharingGroups->Organisations->find(
'all',
[
'conditions' => ['local' => 1],
'recursive' => -1,
'fields' => ['id', 'name']
]
)->disableHydration()->toArray();
$this->set('entity', $sharingGroup);
$this->set('id', $sharingGroup['id']);
$this->set('orgs', $orgs);
$this->set('localInstance', empty(Configure::read('MISP.external_baseurl')) ? Configure::read('MISP.baseurl') : Configure::read('MISP.external_baseurl'));
// We just pass true and allow the user to edit, since he/she is just about to create the SG. This is needed to reuse the view for the edit
$this->set('user', $this->ACL->getUser()->toArray());
}
public function delete($id)
{
$this->request->allowMethod(['post', 'delete']);
$deletedSg = $this->SharingGroups->find(
'all',
[
'conditions' => Validation::uuid($id) ? ['uuid' => $id] : ['id' => $id],
'recursive' => -1,
'fields' => ['id', 'active', 'name'],
]
)->first();
if (empty($deletedSg) || !$this->SharingGroups->checkIfOwner($this->ACL->getUser()->toArray(), $deletedSg->id)) {
throw new MethodNotAllowedException('Action not allowed.');
}
if ($this->SharingGroups->delete($deletedSg)) {
if ($this->ParamHandler->isRest()) {
return $this->RestResponse->saveSuccessResponse('SharingGroups', 'delete', $id);
}
$this->Flash->success(__('Sharing Group deleted'));
} else {
if ($this->ParamHandler->isRest()) {
return $this->RestResponse->saveFailResponse('SharingGroups', 'delete', $id, 'The sharing group could not be deleted.');
}
$this->Flash->error(__('Sharing Group could not be deleted. Make sure that there are no events, attributes or threads belonging to this sharing group.'));
}
if ($deletedSg->active) {
$this->redirect('/sharing-groups/index');
} else {
$this->redirect('/sharing-groups/index/true');
}
}
public function index($passive = false)
{
$passive = $passive === 'true';
$authorizedSgIds = $this->SharingGroups->authorizedIds($this->ACL->getUser()->toArray());
// TODO: [3.x-MIGRATION] fix this array conversion
// $this->paginate['conditions'][] = ['id' => $authorizedSgIds];
// $this->paginate['conditions'][] = ['active' => $passive === true ? 0 : 1];
if (!empty($this->request->getParam('value'))) {
$term = '%' . strtolower($this->request->getParam('value')) . '%';
if ($this->__showOrgs()) {
$sgIds = $this->SharingGroups->SharingGroupOrgs->find(
'column',
[
'conditions' => [
'OR' => [
'Organisations.uuid LIKE' => $term,
'LOWER(Organisations.name) LIKE' => $term,
],
'SharingGroupOrg.sharing_group_id' => $authorizedSgIds,
],
'contain' => ['Organisations'],
'fields' => ['SharingGroupOrgs.sharing_group_id'],
]
);
} else {
$sgIds = [];
}
$this->paginate['conditions'][]['OR'] = [
'id' => $sgIds,
'uuid LIKE' => $term,
'LOWER(name) LIKE' => $term,
'LOWER(description) LIKE' => $term,
'LOWER(releasability) LIKE' => $term,
'LOWER(Organisations.name) LIKE' => $term,
];
}
if ($this->__showOrgs() && !empty($this->request->getParam('searchorg'))) {
$orgs = explode('|', $this->request->getParam('searchorg'));
$conditions = [];
foreach ($orgs as $org) {
$exclude = $org[0] === '!';
if ($exclude) {
$org = substr($org, 1);
}
$org = $this->SharingGroups->Organisations->fetchOrg($org);
if ($org) {
if ($exclude) {
$conditions['AND'][] = ['org_id !=' => $org['id']];
} else {
$conditions['OR'][] = ['org_id' => $org['id']];
}
}
}
$sgIds = $this->SharingGroups->SharingGroupOrgs->find(
'column',
[
'conditions' => $conditions,
'fields' => ['SharingGroupOrgs.sharing_group_id'],
]
);
if (empty($sgIds)) {
$sgIds = -1;
}
$this->paginate['conditions'][] = ['id' => $sgIds];
}
// To allow sort sharing group by number of organisation and also show org count when user don't have permission ot see them
// TODO: [3.x-MIGRATION] fixme, cannot paginate on virtual fields
// $this->paginate['fields'][] = 'SharingGroup.org_count';
if (!$this->__showOrgs()) {
unset($this->paginate['contain']['SharingGroupOrgs']);
unset($this->paginate['contain']['SharingGroupServers']);
}
$result = $this->paginate()->toArray();
// check if the current user can modify or delete the SG
$userOrganisationUuid = $this->ACL->getUser()->Organisation->uuid;
$response = [];
foreach ($result as $k => $sg) {
$sg = $sg->toArray();
$editable = false;
$deletable = false;
if ($this->ACL->getUser()->Role->perm_site_admin || ($this->ACL->getUser()->Role->perm_sharing_group && $sg['Organisation']['uuid'] === $userOrganisationUuid)) {
$editable = true;
$deletable = true;
} else if ($this->ACL->getUser()->Role->perm_sharing_group) {
if (!empty($sg['SharingGroupOrgs'])) {
foreach ($sg['SharingGroupOrgs'] as $sgo) {
if ($sgo['extend'] && $sgo['org_id'] == $this->Auth->user('org_id')) {
$editable = true;
break;
}
}
}
}
$response[$k] = $sg;
$response[$k]['editable'] = $editable;
$response[$k]['deletable'] = $deletable;
}
if ($this->ParamHandler->isRest()) {
return $this->RestResponse->viewData(['response' => $response]); // 'response' to keep BC
}
$this->set('passive', $passive);
$this->set('sharingGroups', $response);
$this->set('passedArgs', $passive ? 'true' : '[]');
$this->set('title_for_layout', __('Sharing Groups'));
}
public function view($id)
{
if ($this->request->is('head')) { // Just check if sharing group exists and user can access it
$exists = $this->SharingGroups->checkIfAuthorised($this->ACL->getUser()->toArray(), $id);
return new Response(['status' => $exists ? 200 : 404]);
}
if (!$this->SharingGroups->checkIfAuthorised($this->ACL->getUser()->toArray(), $id)) {
throw new MethodNotAllowedException('Sharing group doesn\'t exist or you do not have permission to access it.');
}
$contain = [
'Organisations',
'SharingGroupOrgs' => [
'Organisations' => [
'fields' => ['id', 'name', 'uuid', 'local']
]
],
'SharingGroupServers' => [
'Servers' => [
'fields' => ['id', 'name', 'url']
]
]
];
if (!$this->__showOrgs()) {
unset($contain['SharingGroupOrgs']);
unset($contain['SharingGroupServers']);
}
$sg = $this->SharingGroups->find(
'all',
[
'conditions' => Validation::uuid($id) ? ['SharingGroups.uuid' => $id] : ['SharingGroups.id' => $id],
'contain' => $contain,
]
)->disableHydration()->first();
if (empty($sg)) {
throw new NotFoundException('Sharing group doesn\'t exist or you do not have permission to access it.');
}
if (isset($sg['SharingGroupServer'])) {
foreach ($sg['SharingGroupServer'] as $key => $sgs) {
if ($sgs['server_id'] == 0) {
$sg['SharingGroupServer'][$key]['Server'] = [
'id' => "0",
'name' => 'Local instance',
'url' => empty(Configure::read('MISP.external_baseurl')) ? Configure::read('MISP.baseurl') : Configure::read('MISP.external_baseurl')
];
}
}
}
if (isset($sg['sync_user_id'])) {
$UserTable = $this->fetchTable('Users');
$syncUser = $UserTable->find(
'all',
[
'conditions' => ['Users.id' => $sg['sync_user_id']],
'recursive' => -1,
'fields' => ['Users.id'],
'contain' => ['Organisations' => [
'fields' => ['Organisations.id', 'Organisations.name', 'Organisations.uuid'],
]]
]
)->disableHydration()->first();
if (empty($syncUser)) {
$sg['sync_org_name'] = 'N/A';
} else {
$sg['sync_org_name'] = $syncUser['Organisation']['name'];
$sg['sync_org'] = $syncUser['Organisation'];
}
}
if ($this->ParamHandler->isRest()) {
return $this->RestResponse->viewData($sg);
}
$EventsTable = $this->fetchTable('Events');
$conditions = $EventsTable->createEventConditions($this->ACL->getUser()->toArray());
$conditions['AND']['sharing_group_id'] = $sg['id'];
$sg['event_count'] = $EventsTable->find(
'all',
[
'conditions' => $conditions,
'recursive' => -1,
'callbacks' => false,
]
)->count();
$this->set('mayModify', $this->SharingGroups->checkIfAuthorisedExtend($this->ACL->getUser()->toArray(), $sg['id']));
$this->set('id', $sg['id']);
$this->set('entity', $sg);
$this->set('menuData', ['menuList' => 'globalActions', 'menuItem' => 'viewSG']);
}
private function __initialiseSGQuickEdit($id, $request)
{
if (!$this->request->is('post') || !$this->ParamHandler->isRest()) {
//throw new MethodNotAllowedException('This action only accepts POST requests coming from the API.');
}
// allow passing the sg_id via a JSON object
if (!$id) {
$validParams = ['sg_id', 'sg_uuid', 'id', 'uuid'];
foreach ($validParams as $param) {
if (!empty($request[$param])) {
$id = $request[$param];
break;
}
}
if (empty($id)) {
throw new MethodNotAllowedException('No valid sharing group ID provided.');
}
}
$sg = $this->SharingGroups->fetchSG($id, $this->ACL->getUser()->toArray(), false);
if (empty($sg)) {
throw new MethodNotAllowedException('Invalid sharing group or no editing rights.');
}
return $sg;
}
private function __initialiseSGQuickEditObject($id, $request, $type = 'org')
{
$params = [
'org' => [
'org_id', 'org_uuid', 'org_name'
],
'server' => [
'server_id', 'server_url', 'server_baseurl', 'server_name'
]
];
if (!empty($id)) {
if ($type == 'org') {
return $this->SharingGroups->SharingGroupOrgs->Organisations->fetchOrg($id);
} else {
return $this->SharingGroups->SharingGroupServers->Servers->fetchServer($id);
}
}
if ($type !== 'org' && $type !== 'server') {
return false;
}
foreach ($params[$type] as $param) {
if (!empty($request[$param])) {
if ($type == 'org') {
return $this->SharingGroups->SharingGroupOrgs->Organisations->fetchOrg($request[$param]);
} else {
return $this->SharingGroups->SharingGroupServers->Servers->fetchServer($request[$param]);
}
}
}
}
public function addOrg($sg_id = false, $object_id = false, $extend = false)
{
$sg = $this->__initialiseSGQuickEdit($sg_id, $this->request->getData());
$org = $this->__initialiseSGQuickEditObject($object_id, $this->request->getData(), $type = 'org');
if (empty($org)) {
throw new MethodNotAllowedException('Invalid organisation.');
}
if (!empty($this->request->getData('extend'))) {
$extend = $this->request->getData('extend');
}
$addOrg = true;
if (!empty($sg['SharingGroupOrg'])) {
foreach ($sg['SharingGroupOrg'] as $sgo) {
if ($sgo['org_id'] == $org['id']) {
$addOrg = false;
}
}
}
if (!$addOrg) {
return $this->RestResponse->saveFailResponse('SharingGroup', 'addOrg', false, 'Organisation is already in the sharing group.');
}
$sharingGroupOrgEntity = new SharingGroupOrg(
[
'org_id' => $org['id'],
'sharing_group_id' => $sg['id'],
'extend' => $extend ? 1 : 0
]
);
$result = $this->SharingGroups->SharingGroupOrgs->save($sharingGroupOrgEntity);
return $this->__sendQuickSaveResponse('addOrg', $result, 'Organisation');
}
public function removeOrg($sg_id = false, $object_id = false)
{
$sg = $this->__initialiseSGQuickEdit($sg_id, $this->request->getData());
$org = $this->__initialiseSGQuickEditObject($object_id, $this->request->getData(), $type = 'org');
if (empty($org)) {
throw new MethodNotAllowedException('Invalid organisation.');
}
$removeOrg = false;
if (!empty($sg['SharingGroupOrg'])) {
foreach ($sg['SharingGroupOrg'] as $sgo) {
if ($sgo['org_id'] == $org['id']) {
$removeOrg = $sgo['id'];
break;
}
}
}
if (false === $removeOrg) {
return $this->RestResponse->saveFailResponse('SharingGroup', 'removeOrg', false, 'Organisation is not in the sharing group.');
}
$orgEntity = $this->SharingGroups->SharingGroupOrgs->get($removeOrg);
$result = $this->SharingGroups->SharingGroupOrgs->delete($orgEntity);
return $this->__sendQuickSaveResponse('removeOrg', $result, 'Organisation');
}
public function addServer($sg_id = false, $object_id = false, $all = false)
{
$sg = $this->__initialiseSGQuickEdit($sg_id, $this->request->getData());
$server = $this->__initialiseSGQuickEditObject($object_id, $this->request->getData(), $type = 'server');
if (empty($server)) {
throw new MethodNotAllowedException('Invalid Server.');
}
if (!empty($this->request->getData('all'))) {
$all = $this->request->getData('all');
}
if (!empty($this->request->getData('all_orgs'))) {
$all = $this->request->getData('all_orgs');
}
$addServer = true;
if (!empty($sg['SharingGroupServer'])) {
foreach ($sg['SharingGroupServer'] as $sgs) {
if ($sgs['server_id'] == $server['id']) {
$addServer = false;
}
}
}
if (!$addServer) {
return $this->RestResponse->saveFailResponse('SharingGroup', 'addServer', false, 'Server is already in the sharing group.');
}
$sharingGroupServerEntity = new SharingGroupServer(
[
'server_id' => $server['id'],
'sharing_group_id' => $sg['id'],
'all_orgs' => $all ? 1 : 0
]
);
$result = $this->SharingGroups->SharingGroupServers->save($sharingGroupServerEntity);
return $this->__sendQuickSaveResponse('addServer', $result);
}
public function removeServer($sg_id = false, $object_id = false)
{
$sg = $this->__initialiseSGQuickEdit($sg_id, $this->request->getData());
$server = $this->__initialiseSGQuickEditObject($object_id, $this->request->getData(), $type = 'server');
if (empty($server)) {
throw new MethodNotAllowedException('Invalid Server.');
}
$removeServer = false;
if (!empty($sg['SharingGroupServer'])) {
foreach ($sg['SharingGroupServer'] as $sgs) {
if ($sgs['server_id'] == $server['id']) {
$removeServer = $sgs['id'];
break;
}
}
}
if (false === $removeServer) {
return $this->RestResponse->saveFailResponse('SharingGroup', 'removeServer', false, 'Server is not in the sharing group.');
}
$serverEntity = $this->SharingGroups->SharingGroupServers->get($removeServer);
$result = $this->SharingGroups->SharingGroupServers->delete($serverEntity);
return $this->__sendQuickSaveResponse('removeServer', $result);
}
private function __sendQuickSaveResponse($action, $result, $object_type = 'Server')
{
$actionType = 'added to';
if (strpos($action, 'remove') !== false) {
$actionType = 'removed from';
}
if ($result) {
return $this->RestResponse->saveSuccessResponse('SharingGroup', $action, false, 'json', $object_type . ' ' . $actionType . ' the sharing group.');
} else {
return $this->RestResponse->saveFailResponse('SharingGroup', $action, false, $object_type . ' could not be ' . $actionType . ' the sharing group.');
}
}
/**
* @return bool
*/
private function __showOrgs()
{
return $this->ACL->getUser()->Role->perm_sharing_group || !Configure::read('Security.hide_organisations_in_sharing_groups');
}
}

View File

@ -1,14 +1,13 @@
<?php
namespace MetaFieldsTypes;
namespace App\Lib\default\meta_fields_types;
use Cake\Database\Expression\QueryExpression;
use Cake\ORM\TableRegistry;
use Cake\ORM\Query;
use MetaFieldsTypes\TextType;
use TypeError;
use App\Lib\default\meta_fields_types\TextType;
use App\Lib\Tools\CidrTool;
use App\Model\Entity\MetaTemplateField;
use Cake\Database\Expression\QueryExpression;
use Cake\ORM\Query;
use Cake\ORM\TableRegistry;
class IPv4Type extends TextType
{
@ -31,7 +30,7 @@ class IPv4Type extends TextType
return $this->_isValidIP($value) || $this->_isValidIP(explode('/', $value)[0]);
}
public function setQueryExpression(QueryExpression $exp, string $searchValue, \App\Model\Entity\MetaTemplateField $metaTemplateField): QueryExpression
public function setQueryExpression(QueryExpression $exp, string $searchValue, MetaTemplateField $metaTemplateField): QueryExpression
{
if (strpos($searchValue, '%') !== false) {
$textHandler = new TextType(); // we are wildcard filtering, use text filter instead
@ -85,10 +84,13 @@ class IPv4Type extends TextType
return [];
}
$conditions = array_merge($conditions, ['meta_template_field_id IN' => $metaTemplateFieldsIDs]);
$allMetaValues = $this->MetaFields->find('list', [
$allMetaValues = $this->MetaFields->find(
'list',
[
'keyField' => 'id',
'valueField' => 'value'
])->where($conditions)->toArray();
]
)->where($conditions)->toArray();
return $allMetaValues;
}

View File

@ -1,8 +1,8 @@
<?php
namespace MetaFieldsTypes;
namespace App\Lib\default\meta_fields_types;
use MetaFieldsTypes\IPv4Type;
use App\Lib\default\meta_fields_types\IPv4Type;
class IPv6Type extends IPv4Type
{

View File

@ -1,6 +1,7 @@
<?php
namespace MetaFieldsTypes;
namespace App\Lib\default\meta_fields_types;
use App\Model\Entity\MetaTemplateField;
use Cake\Database\Expression\QueryExpression;
use Cake\ORM\TableRegistry;
@ -9,6 +10,9 @@ class TextType
public const OPERATORS = ['=', '!='];
public const TYPE = 'text';
protected $MetaFields;
protected $MetaTemplateFields;
public function __construct()
{
$this->MetaFields = TableRegistry::getTableLocator()->get('MetaFields');
@ -19,7 +23,7 @@ class TextType
return is_string($value);
}
public function setQueryExpression(QueryExpression $exp, string $searchValue, \App\Model\Entity\MetaTemplateField $metaTemplateField): QueryExpression
public function setQueryExpression(QueryExpression $exp, string $searchValue, MetaTemplateField $metaTemplateField): QueryExpression
{
$field = 'MetaFields.value';
if (substr($searchValue, 0, 1) == '!') {

View File

@ -0,0 +1,9 @@
<?php
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
class Event extends AppModel
{
}

View File

@ -0,0 +1,9 @@
<?php
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
class MetaField extends AppModel
{
}

View File

@ -0,0 +1,9 @@
<?php
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
class MetaTemplate extends AppModel
{
}

View File

@ -0,0 +1,49 @@
<?php
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
class MetaTemplateField extends AppModel
{
protected $_virtual = ['index_type', 'form_type', 'form_options',];
protected function _getIndexType()
{
$indexType = 'text';
if ($this->type === 'boolean') {
$indexType = 'boolean';
} else if ($this->type === 'date') {
$indexType = 'datetime';
} else if ($this->type === 'ipv4' || $this->type === 'ipv6') {
$indexType = 'text';
}
return $indexType;
}
protected function _getFormType()
{
$formType = 'text';
if (!empty($this->sane_default) || !empty($this->values_list)) {
$formType = 'dropdown';
} else if ($this->type === 'boolean') {
$formType = 'checkbox';
}
return $formType;
}
protected function _getFormOptions()
{
$formOptions = [];
if ($this->formType === 'dropdown') {
$selectOptions = !empty($this->sane_default) ? $this->sane_default : $this->values_list;
$selectOptions = array_combine($selectOptions, $selectOptions);
if (!empty($this->sane_default)) {
$selectOptions[] = ['value' => '_custom', 'text' => __('-- custom value --'), 'class' => 'custom-value'];
}
$selectOptions[''] = __('-- no value --');
$formOptions['options'] = $selectOptions;
}
return $formOptions;
}
}

View File

@ -0,0 +1,9 @@
<?php
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
class Server extends AppModel
{
}

View File

@ -0,0 +1,25 @@
<?php
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
use Cake\ORM\Locator\LocatorAwareTrait;
class SharingGroup extends AppModel
{
use LocatorAwareTrait;
protected $_virtual = ['org_count'];
protected function _getOrgCount()
{
$SharingGroupOrgsTable = $this->fetchTable('SharingGroupOrgs');
return $SharingGroupOrgsTable->find(
'all',
[
'conditions' => ['SharingGroupOrgs.sharing_group_id = id']
]
)->count();
}
}

View File

@ -0,0 +1,9 @@
<?php
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
class SharingGroupOrg extends AppModel
{
}

View File

@ -0,0 +1,9 @@
<?php
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
class SharingGroupServer extends AppModel
{
}

View File

@ -2,15 +2,15 @@
namespace App\Model\Table;
use Cake\Collection\CollectionInterface;
use Cake\Database\Expression\QueryExpression;
use Cake\Http\Exception\MethodNotAllowedException;
use Cake\I18n\FrozenTime;
use Cake\ORM\Query;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\Core\Configure;
use Cake\Core\Configure\Engine\PhpConfig;
use Cake\ORM\TableRegistry;
use Cake\Utility\Hash;
use Cake\Database\Expression\QueryExpression;
use Cake\ORM\Query;
use Cake\I18n\FrozenTime;
use InvalidArgumentException;
class AppTable extends Table
{
@ -18,7 +18,7 @@ class AppTable extends Table
{
}
public function getStatisticsUsageForModel(Object $table, array $scopes, array $options=[]): array
public function getStatisticsUsageForModel(Object $table, array $scopes, array $options = []): array
{
$defaultOptions = [
'limit' => 5,
@ -30,16 +30,20 @@ class AppTable extends Table
foreach ($scopes as $scope) {
$queryTopUsage = $table->find();
$queryTopUsage
->select([
->select(
[
$scope,
'count' => $queryTopUsage->func()->count('id'),
]);
]
);
if ($queryTopUsage->getDefaultTypes()[$scope] != 'boolean') {
$queryTopUsage->where(function (QueryExpression $exp) use ($scope) {
$queryTopUsage->where(
function (QueryExpression $exp) use ($scope) {
return $exp
->isNotNull($scope)
->notEq($scope, '');
});
}
);
}
$queryTopUsage
->group($scope)
@ -55,23 +59,29 @@ class AppTable extends Table
) {
$queryOthersUsage = $table->find();
$queryOthersUsage
->select([
->select(
[
'count' => $queryOthersUsage->func()->count('id'),
])
->where(function (QueryExpression $exp, Query $query) use ($topUsage, $scope, $options) {
]
)
->where(
function (QueryExpression $exp, Query $query) use ($topUsage, $scope, $options) {
if (!empty($options['ignoreNull'])) {
return $exp
->isNotNull($scope)
->notEq($scope, '')
->notIn($scope, Hash::extract($topUsage, "{n}.{$scope}"));
} else {
return $exp->or([
return $exp->or(
[
$query->newExpr()->isNull($scope),
$query->newExpr()->eq($scope, ''),
$query->newExpr()->notIn($scope, Hash::extract($topUsage, "{n}.{$scope}")),
]);
]
);
}
})
}
)
->enableHydration(false);
$othersUsage = $queryOthersUsage->all()->toList();
if (!empty($othersUsage)) {
@ -85,7 +95,7 @@ class AppTable extends Table
return $stats;
}
private function getOptions($defaults=[], $options=[]): array
private function getOptions($defaults = [], $options = []): array
{
return array_merge($defaults, $options);
}
@ -130,10 +140,12 @@ class AppTable extends Table
}
$days = $days - 1;
$query = $table->find();
$query->select([
$query->select(
[
'count' => $query->func()->count('id'),
'date' => "DATE({$field})",
])
]
)
->where(["{$field} >" => FrozenTime::now()->subDays($days)])
->group(['date'])
->order(['date']);
@ -159,10 +171,12 @@ class AppTable extends Table
$this->MetaFields = TableRegistry::getTableLocator()->get('MetaFields');
$this->MetaTemplates = TableRegistry::getTableLocator()->get('MetaTemplates');
foreach ($input['metaFields'] as $templateID => $metaFields) {
$metaTemplates = $this->MetaTemplates->find()->where([
$metaTemplates = $this->MetaTemplates->find()->where(
[
'id' => $templateID,
'enabled' => 1
])->contain(['MetaTemplateFields'])->first();
]
)->contain(['MetaTemplateFields'])->first();
$fieldNameToId = [];
foreach ($metaTemplates->meta_template_fields as $i => $metaTemplateField) {
$fieldNameToId[$metaTemplateField->field] = $metaTemplateField->id;
@ -199,16 +213,58 @@ class AppTable extends Table
*/
public function addCountField(string $field, Table $model, array $conditions)
{
$db = $this->getDataSource();
$subQuery = $db->buildStatement(
array(
$subQuery = $this->buildStatement(
[
'fields' => ['COUNT(*)'],
'table' => $db->fullTableName($model),
'table' => $model->table(),
'alias' => $model->alias,
'conditions' => $conditions,
),
],
$model
);
$subQuery->select(['count' => $subQuery->func()->count('*')]);
$this->virtualFields[$field] = $subQuery;
}
/**
* Find method that allows to fetch just one column from database.
* @param $state
* @param $query
* @param array $results
* @return \Cake\ORM\Query The query builder
* @throws InvalidArgumentException
*/
protected function findColumn(Query $query, array $options): Query
{
$fields = $query->clause('select');
if (!isset($fields)) {
throw new InvalidArgumentException("This method requires `fields` option defined.");
}
if (!is_array($fields) || count($fields) != 1) {
throw new InvalidArgumentException("Not a valid array or invalid number of columns, expected one, " . count($fields) . " given");
}
if (isset($options['unique']) && $options['unique']) {
$query->distinct();
}
$query->enableHydration(false);
$query->formatResults(
function (CollectionInterface $results) use ($fields) {
return $results->map(
function ($row) use ($fields) {
return $row[$fields[0]];
}
);
},
$query::APPEND
);
return $query;
}
}

View File

@ -4,11 +4,11 @@
namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\Validation\Validator;
use Cake\ORM\RulesChecker;
use Cake\Event\EventInterface;
use Cake\Datasource\EntityInterface;
use ArrayObject;
use Cake\Datasource\EntityInterface;
use Cake\Event\EventInterface;
use Cake\ORM\RulesChecker;
use Cake\Validation\Validator;
class EventBlocklistsTable extends AppTable
{
@ -18,7 +18,7 @@ class EventBlocklistsTable extends AppTable
$this->addBehavior('AuditLog');
}
public $blocklistFields = array('event_uuid', 'comment', 'event_info', 'event_orgc');
public $blocklistFields = ['event_uuid', 'comment', 'event_info', 'event_orgc'];
public $blocklistTarget = 'event';
@ -56,10 +56,13 @@ class EventBlocklistsTable extends AppTable
{
// When event array contains a lot events, it is more efficient to fetch all blocked events
$conditions = (count($eventArray) > 10000) ? [] : ['EventBlocklist.event_uuid' => array_column($eventArray, 'uuid')];
$blocklistHits = $this->find('column', [
$blocklistHits = $this->find(
'column',
[
'conditions' => $conditions,
'fields' => ['EventBlocklist.event_uuid'],
]);
]
);
if (empty($blocklistHits)) {
return;
}
@ -77,6 +80,6 @@ class EventBlocklistsTable extends AppTable
*/
public function isBlocked($eventUuid)
{
return $this->hasAny(['event_uuid' => $eventUuid]);
return $this->exists(['event_uuid' => $eventUuid]);
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\Core\Configure;
class EventsTable extends AppTable
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->addBehavior('AuditLog');
}
public function createEventConditions($user)
{
$conditions = [];
if (!$user['Role']['perm_site_admin']) {
$sgids = $this->SharingGroup->authorizedIds($user);
$unpublishedPrivate = Configure::read('MISP.unpublishedprivate');
$conditions['AND']['OR'] = [
'Event.org_id' => $user['org_id'],
[
'AND' => [
'Event.distribution >' => 0,
'Event.distribution <' => 4,
$unpublishedPrivate ? ['Event.published' => 1] : [],
],
],
[
'AND' => [
'Event.sharing_group_id' => $sgids,
'Event.distribution' => 4,
$unpublishedPrivate ? ['Event.published' => 1] : [],
]
]
];
}
return $conditions;
}
}

View File

@ -2,12 +2,12 @@
namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\Validation\Validator;
use App\Lib\Tools\FileAccessTool;
use App\Model\Entity\Noticelist;
use App\Model\Entity\NoticelistEntry;
use App\Model\Table\AppTable;
use Cake\ORM\Locator\LocatorAwareTrait;
use Cake\Validation\Validator;
class NoticelistsTable extends AppTable
{
@ -17,14 +17,18 @@ class NoticelistsTable extends AppTable
{
parent::initialize($config);
$this->addBehavior('AuditLog');
$this->addBehavior('JsonFields', [
$this->addBehavior(
'JsonFields',
[
'fields' => ['ref', 'geographical_area'],
]);
]
);
$this->hasMany(
'NoticelistEntries',
[
'dependent' => true,
'propertyName' => 'NoticelistEntry',
]
);
}
@ -41,25 +45,28 @@ class NoticelistsTable extends AppTable
public function update()
{
$directories = glob(APP . '..' . DS . 'libraries' . DS . 'noticelists' . DS . 'lists' . DS . '*', GLOB_ONLYDIR);
$updated = array();
$updated = [];
foreach ($directories as $dir) {
$list = FileAccessTool::readJsonFromFile($dir . DS . 'list.json');
if (!isset($list['version'])) {
$list['version'] = 1;
}
$current = $this->find('all', array(
'conditions' => array('name' => $list['name']),
$current = $this->find(
'all',
[
'conditions' => ['name' => $list['name']],
'recursive' => -1
))->first();
]
)->first();
if (empty($current) || $list['version'] > $current['version']) {
$result = $this->__updateList($list, $current);
if (is_numeric($result)) {
$updated['success'][$result] = array('name' => $list['name'], 'new' => $list['version']);
$updated['success'][$result] = ['name' => $list['name'], 'new' => $list['version']];
if (!empty($current)) {
$updated['success'][$result]['old'] = $current['version'];
}
} else {
$updated['fails'][] = array('name' => $list['name'], 'fail' => json_encode($result));
$updated['fails'][] = ['name' => $list['name'], 'fail' => json_encode($result)];
}
}
}
@ -72,24 +79,24 @@ class NoticelistsTable extends AppTable
private function __updateList($list, $current)
{
$list['enabled'] = 0;
$noticelist = array();
$noticelist = [];
if (!empty($current)) {
if ($current['enabled']) {
$list['enabled'] = 1;
}
$this->quickDelete($current['id']);
}
$fieldsToSave = array('name', 'expanded_name', 'ref', 'geographical_area', 'version', 'enabled');
$fieldsToSave = ['name', 'expanded_name', 'ref', 'geographical_area', 'version', 'enabled'];
foreach ($fieldsToSave as $fieldToSave) {
$noticelist[$fieldToSave] = $list[$fieldToSave];
}
$noticelist = new Noticelist($noticelist);
$result = $this->save($noticelist);
if ($result) {
$values = array();
$values = [];
foreach ($list['notice'] as $value) {
if (!empty($value)) {
$values[] = array('data' => $value, 'noticelist_id' => $result->id);
$values[] = ['data' => $value, 'noticelist_id' => $result->id];
}
}
unset($list['notice']);
@ -106,29 +113,32 @@ class NoticelistsTable extends AppTable
public function getTriggerData($scope = 'attribute')
{
$noticelists = $this->find('all', array(
'conditions' => array('enabled' => 1),
$noticelists = $this->find(
'all',
[
'conditions' => ['enabled' => 1],
'recursive' => -1,
'contain' => 'NoticelistEntry'
));
$noticelist_triggers = array();
$validTriggers = array(
'attribute' => array(
]
);
$noticelist_triggers = [];
$validTriggers = [
'attribute' => [
'category',
'type'
)
);
]
];
foreach ($noticelists as $noticelist) {
foreach ($noticelist['NoticelistEntry'] as $entry) {
if (in_array('attribute', $entry['data']['scope'])) {
foreach ($entry['data']['field'] as $data_field) {
if (in_array($data_field, $validTriggers[$scope])) {
foreach ($entry['data']['value'] as $value) {
$noticelist_triggers[$data_field][$value][] = array(
$noticelist_triggers[$data_field][$value][] = [
'message' => $entry['data']['message'],
'list_id' => $noticelist['id'],
'list_name' => $noticelist['name']
);
];
}
}
}

View File

@ -3,12 +3,11 @@
namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\Error\Debugger;
use Cake\Event\EventInterface;
use Cake\Datasource\EntityInterface;
use ArrayObject;
use Cake\Datasource\EntityInterface;
use Cake\Event\EventInterface;
use Cake\Validation\Validation;
use Cake\Validation\Validator;
class OrganisationsTable extends AppTable
{
@ -45,17 +44,23 @@ class OrganisationsTable extends AppTable
public function captureOrg($org): ?int
{
if (!empty($org['uuid'])) {
$existingOrg = $this->find()->where([
$existingOrg = $this->find()->where(
[
'uuid' => $org['uuid']
])->first();
]
)->first();
} else {
return null;
}
if (empty($existingOrg)) {
$entityToSave = $this->newEmptyEntity();
$this->patchEntity($entityToSave, $org, [
$this->patchEntity(
$entityToSave,
$org,
[
'accessibleFields' => $entityToSave->getAccessibleFieldForNew()
]);
]
);
} else {
$this->patchEntity($existingOrg, $org);
$entityToSave = $existingOrg;
@ -67,4 +72,25 @@ class OrganisationsTable extends AppTable
}
return $savedEntity->id;
}
public function fetchOrg($id)
{
if (empty($id)) {
return false;
}
$conditions = ['Organisations.id' => $id];
if (Validation::uuid($id)) {
$conditions = ['Organisations.uuid' => $id];
} elseif (!is_numeric($id)) {
$conditions = ['LOWER(Organisations.name)' => strtolower($id)];
}
$org = $this->find(
'all',
[
'conditions' => $conditions,
'recursive' => -1
]
)->disableHydration()->first();
return (empty($org)) ? false : $org;
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\Core\Configure;
class ServersTable extends AppTable
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->addBehavior('AuditLog');
}
public function captureServer($server, $user)
{
if (isset($server[0])) {
$server = $server[0];
}
if ($server['url'] == Configure::read('MISP.baseurl')) {
return 0;
}
$existingServer = $this->find(
'all',
[
'recursive' => -1,
'conditions' => ['url' => $server['url']]
]
)->disableHydration()->first();
// unlike with other capture methods, if we find a server that we don't know
// we don't want to save it.
if (empty($existingServer)) {
return false;
}
return $existingServer['id'];
}
public function fetchServer($id)
{
if (empty($id)) {
return false;
}
$conditions = ['Servers.id' => $id];
if (!is_numeric($id)) {
$conditions = ['OR' => [
'LOWER(Servers.name)' => strtolower($id),
'LOWER(Servers.url)' => strtolower($id)
]];
}
$server = $this->find(
'all',
[
'conditions' => $conditions,
'recursive' => -1
]
)->disableHydration()->first();
return (empty($server)) ? false : $server;
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Model\Table;
use App\Model\Table\AppTable;
class SharingGroupOrgsTable extends AppTable
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->addBehavior('AuditLog');
$this->belongsTo(
'Organisations',
[
'foreignKey' => 'org_id',
'propertyName' => 'Organisation',
]
);
$this->belongsTo(
'SharingGroups',
[
'foreignKey' => 'sharing_group_id',
]
);
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Model\Table;
use App\Model\Table\AppTable;
class SharingGroupServersTable extends AppTable
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->addBehavior('AuditLog');
$this->belongsTo(
'Servers',
[
'foreignKey' => 'server_id',
'propertyName' => 'Server',
]
);
$this->belongsTo(
'SharingGroups',
[
'foreignKey' => 'sharing_group_id',
]
);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3,25 +3,26 @@
namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\ORM\RulesChecker;
use Cake\ORM\TableRegistry;
use Cake\Event\EventInterface;
use Cake\Datasource\EntityInterface;
use Cake\Http\Session;
use Cake\Http\Client;
use Cake\Utility\Security;
use Cake\Core\Configure;
use Cake\Utility\Text;
use ArrayObject;
use Cake\Core\Configure;
use Cake\Datasource\EntityInterface;
use Cake\Event\EventInterface;
use Cake\Http\Exception\BadRequestException;
use Cake\Http\Exception\NotFoundException;
use Cake\ORM\Locator\LocatorAwareTrait;
use Cake\ORM\RulesChecker;
use Cake\Validation\Validator;
use InvalidArgumentException;
class UsersTable extends AppTable
{
use LocatorAwareTrait;
private const PERIODIC_USER_SETTING_KEY = 'periodic_notification_filters';
public const PERIODIC_NOTIFICATIONS = ['notification_daily', 'notification_weekly', 'notification_monthly'];
private $PermissionLimitations;
public function initialize(array $config): void
{
parent::initialize($config);
@ -96,7 +97,7 @@ class UsersTable extends AppTable
private function checkPermissionRestrictions(EntityInterface $entity)
{
if (!isset($this->PermissionLimitations)) {
$this->PermissionLimitations = TableRegistry::get('PermissionLimitations');
$this->PermissionLimitations = $this->fetchTable('PermissionLimitations');
}
$permissions = $this->PermissionLimitations->getListOfLimitations($entity);
foreach ($permissions as $permission_name => $permission) {
@ -127,7 +128,7 @@ class UsersTable extends AppTable
if ($valueToCompareTo > $permission_data['limit']) {
return [
$permission_name =>
$permission_name =>
__(
'{0} limit exceeded.',
$scope
@ -151,9 +152,11 @@ class UsersTable extends AppTable
$validator
->setStopOnFailure()
->requirePresence(['password'], 'create')
->add('password', [
->add(
'password',
[
'password_complexity' => [
'rule' => function($value, $context) {
'rule' => function ($value, $context) {
if (!preg_match('/^((?=.*\d)|(?=.*\W+))(?![\n])(?=.*[A-Z])(?=.*[a-z]).*$|.{16,}/s', $value) || strlen($value) < 12) {
return false;
}
@ -162,7 +165,7 @@ class UsersTable extends AppTable
'message' => __('Invalid password. Passwords have to be either 16 character long or 12 character long with 3/4 special groups.')
],
'password_confirmation' => [
'rule' => function($value, $context) {
'rule' => function ($value, $context) {
if (isset($context['data']['confirm_password'])) {
if ($context['data']['confirm_password'] !== $value) {
return false;
@ -172,24 +175,32 @@ class UsersTable extends AppTable
},
'message' => __('Password confirmation missing or not matching the password.')
]
])
->add('username', [
]
)
->add(
'username',
[
'username_policy' => [
'rule' => function($value, $context) {
'rule' => function ($value, $context) {
if (mb_strlen(trim($value)) < 5 || mb_strlen(trim($value)) > 50) {
return __('Invalid username length. Make sure that you provide a username of at least 5 and up to 50 characters in length.');
}
return true;
}
]
])
]
)
->requirePresence(['username'], 'create')
->notEmptyString('username', __('Please fill this field'), 'create');
if (Configure::read('user.username-must-be-email')) {
$validator->add('username', 'valid_email', [
$validator->add(
'username',
'valid_email',
[
'rule' => 'email',
'message' => 'Username has to be a valid e-mail address.'
]);
]
);
}
return $validator;
}
@ -244,4 +255,19 @@ class UsersTable extends AppTable
}
return true;
}
/**
* Get the current user and rearrange it to be in the same format as in the auth component.
* @param int $id
* @param bool $full
* @return array|null
*/
public function getAuthUser($id, $full = false)
{
if (empty($id)) {
throw new InvalidArgumentException('Invalid user ID.');
}
$conditions = ['User.id' => $id];
return $this->getAuthUserByConditions($conditions, $full);
}
}

View File

@ -30,8 +30,11 @@ class BootstrapIcon extends BootstrapGeneric
'attrs' => [],
];
function __construct($icon, array $options = [], $bsHelper)
function __construct($icon, array $options, $bsHelper)
{
if(empty($options)){
$options = [];
}
$this->icon = $icon;
$this->processOptions($options);
$this->bsHelper = $bsHelper;
@ -65,4 +68,4 @@ class BootstrapIcon extends BootstrapGeneric
$html = $this->bsHelper->Icon->icon($options);
return $html;
}
}
}

View File

@ -0,0 +1,158 @@
<div class="users form">
<fieldset>
<legend><?php echo __('New Sharing Group'); ?></legend>
<?php
$data = array(
'children' => array(
array(
'children' => array(
array(
'text' => __('General'),
'title' => __('General tab'),
'class' => 'progress_tab',
'id' => 'page1_tab',
'active' => true,
'onClick' => 'simpleTabPage',
'onClickParams' => array(1)
),
array(
'text' => __('Organisations'),
'title' => __('Organisations tab'),
'class' => 'progress_tab',
'id' => 'page2_tab',
'onClick' => 'simpleTabPage',
'onClickParams' => array(2)
),
array(
'text' => __('MISP Instances'),
'title' => __('MISP instances tab'),
'class' => 'progress_tab',
'id' => 'page3_tab',
'onClick' => 'simpleTabPage',
'onClickParams' => array(3)
),
array(
'text' => __('Summary and Save'),
'title' => __('Sharing group summary'),
'class' => 'progress_tab',
'id' => 'page4_tab',
'onClick' => 'simpleTabPage',
'onClickParams' => array(4)
)
)
)
)
);
if (!$ajax) {
echo $this->element('/genericElements/ListTopBar/scaffold', array('data' => $data));
}
?>
<div id="page1_content" class="multi-page-form-div tabContent" style="width:544px;">
<?php if ($canModifyUuid): ?>
<label for="SharingGroupUuid"><?php echo __('UUID');?></label>
<input type="text" class="input-xxlarge" placeholder="<?= __('If not provided, random UUID will be generated') ?>" id="SharingGroupUuid">
<?php endif; ?>
<label for="SharingGroupName"><?php echo __('Name');?></label>
<input type="text" class="input-xxlarge" placeholder="<?php echo __('Example: Multinational sharing group');?>" id="SharingGroupName">
<label for="SharingGroupReleasability"><?php echo __('Releasable to');?></label>
<input type="text" class="input-xxlarge" placeholder="<?php echo __('Example: Community1, Organisation1, Organisation2');?>" id="SharingGroupReleasability">
<label for="SharingGroupDescription"><?php echo __('Description');?></label>
<textarea class="input-xxlarge" placeholder="<?php echo __('A description of the sharing group.');?>" cols="30" rows="6" id="SharingGroupDescription"></textarea>
<div style="display:block;">
<input type="checkbox" style="float:left;" title="<?php echo __('Active sharing groups can be selected by users of the local instance when creating events. Generally, sharing groups received through synchronisation will have this disabled until manually enabled.');?>" value="1" id="SharingGroupActive" checked>
<label for="SharingGroupActive" style="padding-left:20px;"><?php echo __('Make the sharing group selectable (active)');?></label>
</div>
<span role="button" tabindex="0" aria-label="<?php echo __('Next page');?>" title="<?php echo __('Next page');?>" class="btn btn-inverse" onClick="simpleTabPage(2);"><?php echo __('Next page');?></span>
</div>
<div id="page2_content" class="multi-page-form-div tabContent" style="display:none;width:544px;">
<div class="tabMenuFixedContainer">
<span role="button" tabindex="0" aria-label="<?php echo __('Add local organisation(s) to the sharing group');?>" title="<?php echo __('Add local organisation(s) to the sharing group');?>" class="tabMenuFixed tabMenuFixedCenter tabMenuSides useCursorPointer" onClick="sharingGroupAdd('organisation', 'local');"><?php echo __('Add local organisation');?></span>
<span role="button" tabindex="0" aria-label="<?php echo __('Add remote organisations to the sharing group');?>" title="<?php echo __('Add remote organisations to the sharing group');?>" class="tabMenuFixed tabMenuFixedCenter tabMenuSides useCursorPointer" onClick="sharingGroupAdd('organisation', 'remote');"><?php echo __('Add remote organisation');?></span>
</div>
<table id="organisations_table" class="table table-striped table-hover table-condensed">
<tr id="organisations_table_header">
<th><?php echo __('Type');?></th>
<th><?php echo __('Name');?></th>
<th><?php echo __('UUID');?></th>
<th><?php echo __('Extend');?></th>
<th><?php echo __('Actions');?></th>
</tr>
</table>
<span role="button" tabindex="0" aria-label="<?php echo __('Previous page');?>" title="<?php echo __('Previous page');?>" class="btn btn-inverse" onClick="simpleTabPage(1);"><?php echo __('Previous page');?></span>
<span role="button" tabindex="0" aria-label="<?php echo __('Next page');?>" title="<?php echo __('Next page');?>" class="btn btn-inverse" onClick="simpleTabPage(3);"><?php echo __('Next page');?></span>
</div>
<div id="page3_content" class="multi-page-form-div tabContent" style="display:none;width:544px;">
<div style="display:block;">
<input type="checkbox" style="float:left;" title="<?php echo __('Enable roaming mode for this sharing group. Roaming mode will allow the sharing group to be passed to any instance where the remote recipient is contained in the organisation list. It is preferred to list the recipient instances instead.');?>" value="1" id="SharingGroupRoaming">
<label for="SharingGroupRoaming" style="padding-left:20px;"><?php echo __('<b>Enable roaming mode</b> for this sharing group (pass the event to any connected instance where the sync connection is tied to an organisation contained in the SG organisation list).');?></label>
</div>
<div id="serverList">
<div class="tabMenuFixedContainer">
<span role="button" tabindex="0" aria-label="<?php echo __('Add instance');?>" title="<?php echo __('Add instance');?>" class="tabMenuFixed tabMenuFixedCenter tabMenuSides useCursorPointer" onClick="sharingGroupAdd('server');"><?php echo __('Add instance');?></span>
</div>
<table id="servers_table" class="table table-striped table-hover table-condensed">
<tr>
<th><?php echo __('Name');?></th>
<th><?php echo __('URL');?></th>
<th><?php echo __('All orgs');?></th>
<th><?php echo __('Actions');?></th>
</tr>
</table>
</div>
<span role="button" tabindex="0" aria-label="<?php echo __('Previous page');?>" title="<?php echo __('Previous page');?>" class="btn btn-inverse" onClick="simpleTabPage(2);"><?php echo __('Previous page');?></span>
<span role="button" tabindex="0" aria-label="<?php echo __('Next page');?>" title="<?php echo __('Next page');?>" class="btn btn-inverse" onClick="simpleTabPage(4);"><?php echo __('Next page');?></span>
</div>
</fieldset>
<div id="page4_content" class="multi-page-form-div tabContent" style="display:none;width:544px;">
<p><?php echo __('<span class="bold">General: </span>You are about to create the <span id="summarytitle" class="red bold"></span> sharing group, which is intended to be releasable to <span id="summaryreleasable" class="red bold"></span>.');?> </p>
<p id="localText"><span class="bold"><?php echo __('Local organisations: </span>It will be visible to <span id="summarylocal" class="red bold"></span>, from which <span id="summarylocalextend" class="red bold"></span> can extend the sharing group.');?> </p>
<p id="externalText"><span class="bold"><?php echo __('External organisations: </span>It will also be visible to <span id="summaryexternal" class="red bold"></span>, out of which <span id="summaryexternalextend" class="red bold"></span> can extend the sharing group.');?></p>
<p id="synchronisationText"><?php echo __('<span class="bold">Synchronisation: </span>Furthermore, events are automatically pushed to: <span id="summaryservers" class="red bold"></span>');?></p>
<p><?php echo __('You can edit this information by going back to one of the previous pages, or if you agree with the above mentioned information, click Submit to create the Sharing group.');?></p>
<?php
echo $this->Form->create('SharingGroup');
echo $this->Form->input('json', array('style' => 'display:none;', 'label' => false, 'div' => false));
//echo $this->Form->button(__('Submit'), array('class' => 'btn btn-primary'));
echo $this->Form->end();
?>
<span role="button" tabindex="0" aria-label="<?php echo __('Previous page');?>" title="<?php echo __('Previous page');?>" class="btn btn-inverse" onClick="simpleTabPage(3);"><?php echo __('Previous page');?></span>
<span role="button" tabindex="0" aria-label="<?php echo __('Submit and create sharing group');?>" title="<?php echo __('Submit and create sharing group');?>" class="btn btn-primary" onClick="sgSubmitForm('Add');"><?php echo __('Submit');?></span>
</div>
</div>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'globalActions', 'menuItem' => 'addSG'));
?>
<script type="text/javascript">
var lastPage = 4;
var roaming = false;
var organisations = [{
id: '<?php echo h($user['Organisation']['id'])?>',
type: 'local',
name: '<?php echo h($user['Organisation']['name'])?>',
extend: true,
uuid: '',
removable: 0
}];
var orgids = ['<?php echo h($user['Organisation']['id'])?>'];
var servers = [{
id: '0',
name: '<?php echo __('Local instance');?>',
url: '<?php echo h($localInstance); ?>',
all_orgs: false,
removable: 0
}];
var serverids = [0];
$(function() {
if ($('#SharingGroupJson').val()) sharingGroupPopulateFromJson();
sharingGroupPopulateOrganisations();
sharingGroupPopulateServers();
});
$('#SharingGroupRoaming').change(function() {
if ($(this).is(":checked")) {
$('#serverList').hide();
} else {
$('#serverList').show();
}
});
</script>

View File

@ -0,0 +1,225 @@
<?php
use App\Model\Entity\SharingGroup;
use Cake\Core\Configure;
?>
<div class="users form">
<fieldset>
<legend><?php echo __('Edit Sharing Group'); ?></legend>
<?php
$data = [
'children' => [
[
'children' => [
[
'text' => __('General'),
'title' => __('General tab'),
'class' => 'progress_tab',
'id' => 'page1_tab',
'active' => true,
'onClick' => 'simpleTabPage',
'onClickParams' => [1]
],
[
'text' => __('Organisations'),
'title' => __('Organisations tab'),
'class' => 'progress_tab',
'id' => 'page2_tab',
'onClick' => 'simpleTabPage',
'onClickParams' => [2]
],
[
'text' => __('MISP Instances'),
'title' => __('MISP instances tab'),
'class' => 'progress_tab',
'id' => 'page3_tab',
'onClick' => 'simpleTabPage',
'onClickParams' => [3]
],
[
'text' => __('Summary and Save'),
'title' => __('Sharing group summary'),
'class' => 'progress_tab',
'id' => 'page4_tab',
'onClick' => 'simpleTabPage',
'onClickParams' => [4]
]
]
]
],
];
if (!$ajax) {
echo $this->element(
'/genericElements/ListTopBar/scaffold',
[
'data' => $data,
'table_data' => [],
'tableRandomValue' => Cake\Utility\Security::randomString(8)
]
);
}
?>
<div id="page1_content" class="multi-page-form-div tabContent" style="width:544px;">
<label for="SharingGroupName"><?php echo __('Name'); ?></label>
<input type="text" class="input-xxlarge" placeholder="<?php echo __('Example: Multinational sharing group'); ?>" id="SharingGroupName" value="<?php echo h($entity['name']); ?>">
<label for="SharingGroupReleasability"><?php echo __('Releasable to'); ?></label>
<input type="text" class="input-xxlarge" placeholder="<?php echo __('Example: Community1, Organisation1, Organisation2'); ?>" id="SharingGroupReleasability" value="<?php echo h($entity['releasability']); ?>">
<label for="SharingGroupDescription"><?php echo __('Description'); ?></label>
<textarea class="input-xxlarge" placeholder="<?php echo __('A description of the sharing group.'); ?>" cols="30" rows="6" id="SharingGroupDescription"><?php echo h($entity['description']); ?></textarea>
<div style="display:block;">
<input type="checkbox" style="float:left;" title="<?php echo __('Active sharing groups can be selected by users of the local instance when creating events. Generally, sharing groups received through synchronisation will have this disabled until manually enabled.'); ?>" <?php if ($entity['active']) echo "checked"; ?> id="SharingGroupActive">
<label for="SharingGroupActive" style="padding-left:20px;"><?php echo __('Make the sharing group selectable (active)'); ?></label>
</div>
<span role="button" tabindex="0" aria-label="<?php echo __('Next page'); ?>" title="<?php echo __('Next page'); ?>" class="btn btn-inverse" onClick="simpleTabPage(2);"><?php echo __('Next page'); ?></span>
</div>
<div id="page2_content" class="multi-page-form-div tabContent" style="display:none;width:544px;">
<div class="tabMenuFixedContainer">
<span role="button" tabindex="0" aria-label="<?php echo __('Add local organisation(s) to the sharing group'); ?>" title="<?php echo __('Add local organisation(s) to the sharing group'); ?>" class="tabMenuFixed tabMenuFixedCenter tabMenuSides useCursorPointer" onClick="sharingGroupAdd('organisation', 'local');"><?php echo __('Add local organisation'); ?></span>
<span role="button" tabindex="0" aria-label="<?php echo __('Add remote organisations to the sharing group'); ?>" title="<?php echo __('Add remote organisations to the sharing group'); ?>" class="tabMenuFixed tabMenuFixedCenter tabMenuSides useCursorPointer" onClick="sharingGroupAdd('organisation', 'remote');"><?php echo __('Add remote organisation'); ?></span>
</div>
<table id="organisations_table" class="table table-striped table-hover table-condensed">
<tr id="organisations_table_header">
<th><?php echo __('Type'); ?></th>
<th><?php echo __('Name'); ?></th>
<th><?php echo __('UUID'); ?></th>
<th><?php echo __('Extend'); ?></th>
<th><?php echo __('Actions'); ?></th>
</tr>
</table>
<span role="button" tabindex="0" aria-label="<?php echo __('Previous page'); ?>" title="<?php echo __('Previous page'); ?>" class="btn btn-inverse" onClick="simpleTabPage(1);"><?php echo __('Previous page'); ?></span>
<span role="button" tabindex="0" aria-label="<?php echo __('Next page'); ?>" title="<?php echo __('Next page'); ?>" class="btn btn-inverse" onClick="simpleTabPage(3);"><?php echo __('Next page'); ?></span>
</div>
<div id="page3_content" class="multi-page-form-div tabContent" style="display:none;width:544px;">
<?php
$serverDivVisibility = "";
$checked = "";
if ($entity['roaming']) {
$serverDivVisibility = 'style="display:none;"';
$checked = "checked";
}
?>
<div style="display:block;">
<input type="checkbox" style="float:left;" title="<?php echo __('Enable roaming mode for this sharing group. Roaming mode will allow the sharing group to be passed to any instance where the remote recipient is contained in the organisation list. It is preferred to list the recipient instances instead.'); ?>" <?php echo $checked; ?> id="SharingGroupRoaming">
<label for="SharingGroupRoaming" style="padding-left:20px;"><?php echo __('<b>Enable roaming mode</b> for this sharing group (pass the event to any connected instance where the sync connection is tied to an organisation contained in the SG organisation list).'); ?></label>
</div>
<div id="serverList" <?php echo $serverDivVisibility; ?>>
<div class="tabMenuFixedContainer">
<span role="button" tabindex="0" aria-label="<?php echo __('Add instance'); ?>" title="<?php echo __('Add instance'); ?>" class="tabMenuFixed tabMenuFixedCenter tabMenuSides useCursorPointer" onClick="sharingGroupAdd('server');"><?php echo __('Add instance'); ?></span>
</div>
<table id="servers_table" class="table table-striped table-hover table-condensed">
<tr>
<th><?php echo __('Name'); ?></th>
<th><?php echo __('URL'); ?></th>
<th><?php echo __('All orgs'); ?></th>
<th><?php echo __('Actions'); ?></th>
</tr>
</table>
</div>
<span role="button" tabindex="0" aria-label="<?php echo __('Previous page'); ?>" title="<?php echo __('Previous page'); ?>" class="btn btn-inverse" onClick="simpleTabPage(2);"><?php echo __('Previous page'); ?></span>
<span role="button" tabindex="0" aria-label="<?php echo __('Next page'); ?>" title="<?php echo __('Next page'); ?>" class="btn btn-inverse" onClick="simpleTabPage(4);"><?php echo __('Next page'); ?></span>
</div>
</fieldset>
<div id="page4_content" class="multi-page-form-div tabContent" style="display:none;width:544px;">
<p><?php echo __(
'<span class="bold">General: </span>You are about to create the <span id="summarytitle" class="red bold"></span> sharing group, which is intended to be releasable to <span id="summaryreleasable" class="red bold"></span>. </p>
<p id="localText"><span class="bold">Local organisations: </span>It will be visible to <span id="summarylocal" class="red bold"></span>, from which <span id="summarylocalextend" class="red bold"></span> can extend the sharing group. </p>
<p id="externalText"><span class="bold">External organisations: </span>It will also be visible to <span id="summaryexternal" class="red bold"></span>, out of which <span id="summaryexternalextend" class="red bold"></span> can extend the sharing group.'
); ?></p>
<p id="synchronisationText"><span class="bold"><?php echo __('Synchronisation: </span>Furthermore, events are automatically pushed to: <span id="summaryservers" class="red bold"></span>'); ?></p>
<p><?php echo __('You can edit this information by going back to one of the previous pages, or if you agree with the above mentioned information, click Submit to create the Sharing group.'); ?></p>
<?php
$sharingGroup = new SharingGroup();
echo $this->Form->create($sharingGroup);
echo $this->Form->input('json', ['style' => 'display:none;', 'label' => false, 'div' => false]);
//echo $this->Form->button(__('Submit'), array('class' => 'btn btn-primary'));
echo $this->Form->end();
?>
<span role="button" tabindex="0" aria-label="<?php echo __('Previous page'); ?>" title="<?php echo __('Previous page'); ?>" class="btn btn-inverse" onClick="simpleTabPage(3);"><?php echo __('Previous page'); ?></span>
<span role="button" tabindex="0" aria-label="<?php echo __('Submit and create sharing group'); ?>" title="<?php echo __('Submit and create sharing group'); ?>" class="btn btn-primary" onClick="sgSubmitForm('Edit');">Submit</span>
</div>
</div>
<?php
// TODO: [3.x-MIGRATION]
// echo $this->element('/genericElements/SideMenu/side_menu', ['menuList' => 'globalActions', 'menuItem' => 'editSG']);
?>
<script type="text/javascript">
var lastPage = 4;
var organisations = [];
var orgids = ['<?php echo h($user['Organisation']['id']) ?>'];
var servers = [];
var serverids = [0];
<?php
if (empty($entity['SharingGroupServer'])) :
?>
var servers = [{
id: '0',
name: 'Local instance',
url: '<?php echo h($localInstance); ?>',
all_orgs: true,
removable: 0
}];
var serverids = [0];
<?php
else :
foreach ($entity['SharingGroupServer'] as $s) :
?>
serverids.push(<?php echo h($s['server_id']); ?>);
<?php
if ($s['server_id'] == 0) :
?>
servers.push({
id: '<?php echo h($s['server_id']); ?>',
name: 'Local instance',
url: '<?php echo empty(Configure::read('MISP.external_baseurl')) ? Configure::read('MISP.baseurl') : Configure::read('MISP.external_baseurl'); ?>',
all_orgs: '<?php echo h($s['all_orgs']); ?>',
removable: 0,
});
<?php
else :
?>
servers.push({
id: '<?php echo h($s['server_id']); ?>',
name: '<?php echo h($s['Server']['name']); ?>',
url: '<?php echo h($s['Server']['url']); ?>',
all_orgs: '<?php echo h($s['all_orgs']); ?>',
removable: 1,
});
<?php
endif;
endforeach;
endif;
?>
<?php
foreach ($entity['SharingGroupOrg'] as $s) :
?>
orgids.push(<?php echo h($s['org_id']); ?>);
var removable = 1;
if (<?php echo h($entity['Organisation']['id']); ?> == <?php echo h($s['org_id']) ?>) removable = 0;
organisations.push({
id: '<?php echo h($s['org_id']); ?>',
type: '<?php echo ($s['Organisation']['local'] == 1 ? 'local' : 'remote'); ?>',
name: '<?php echo h($s['Organisation']['name']) ?>',
extend: '<?php echo h($s['extend']); ?>',
uuid: '',
removable: removable
});
<?php
endforeach;
?>
$(function() {
if ($('#SharingGroupJson').val()) sharingGroupPopulateFromJson();
sharingGroupPopulateOrganisations();
sharingGroupPopulateServers();
});
$('#SharingGroupRoaming').change(function() {
if ($(this).is(":checked")) {
$('#serverList').hide();
} else {
$('#serverList').show();
}
});
</script>

View File

@ -0,0 +1,176 @@
<div class="sharingGroups<?php if (!$ajax) echo ' index' ?>">
<?= $this->element(
'/genericElements/IndexTable/index_table',
[
'data' => [
'title' => __('Sharing Groups'),
'data' => $sharingGroups,
'top_bar' => $ajax ? [] : [
'children' => [
[
'type' => 'simple',
'children' => [
[
'text' => __('Add'),
'fa-icon' => 'plus',
'url' => '/sharing-groups/add',
'requirement' => $this->Acl->checkAccess('sharingGroups', 'add'),
]
]
],
[
'type' => 'simple',
'children' => [
[
'url' => '/sharing-groups/index',
'text' => __('Active Sharing Groups'),
'active' => !$passive,
],
[
'url' => '/sharing-groups/index/true',
'text' => __('Passive Sharing Groups'),
'active' => $passive,
]
]
],
[
'type' => 'search',
'button' => __('Filter'),
'placeholder' => __('Enter value to search'),
'searchKey' => 'value',
'cancel' => [
'fa-icon' => 'times',
'title' => __('Remove filters'),
'onClick' => 'cancelSearch',
]
]
]
],
'fields' => [
[
'name' => __('ID'),
'sort' => 'id',
'class' => 'short',
'data_path' => 'id',
'url' => '/sharing-groups/view/{{id}}',
'url_vars' => ['id' => 'id']
],
[
'name' => __('UUID'),
'data_path' => 'uuid',
'sort' => 'uuid',
'class' => 'short quickSelect',
],
[
'name' => __('Name'),
'data_path' => 'name',
'sort' => 'name',
'class' => 'short',
],
[
'name' => __('Creator'),
'sort' => 'Organisation.name',
'element' => 'org',
'data_path' => 'Organisation',
'class' => 'short',
],
[
'name' => __('Description'),
'data_path' => 'description',
],
[
'name' => __('Org count'),
'class' => 'short',
'sort' => 'org_count',
'data_path' => 'org_count',
],
[
'name' => __('Releasable to'),
'element' => 'custom',
'function' => function (array $sharingGroup) {
$combined = __("Organisations:");
if (empty($sharingGroup['SharingGroupOrg'])) {
$combined .= "<br>N/A";
} else {
foreach ($sharingGroup['SharingGroupOrg'] as $sge) {
if (!empty($sge['Organisation'])) {
$combined .= "<br><a href='/organisation/view/" . h($sge['Organisation']['id']) . "'>" . h($sge['Organisation']['name']) . "</a>";
if ($sge['extend']) {
$combined .= ' (can extend)';
}
}
}
}
$combined .= '<hr style="margin:5px 0;"><br>Instances:';
if (empty($sharingGroup['SharingGroupServer'])) {
$combined .= "<br>N/A";
} else {
foreach ($sharingGroup['SharingGroupServer'] as $sgs) {
if ($sgs['server_id'] != 0) {
$combined .= "<br><a href='/server/view/" . h($sgs['Server']['id']) . "'>" . h($sgs['Server']['name']) . "</a>";
} else {
$combined .= "<br>This instance";
}
if ($sgs['all_orgs']) {
$combined .= ' (all organisations)';
} else {
$combined .= ' (as defined above)';
}
}
} ?>
<span data-toggle="popover" data-trigger="hover" title="<?= __('Distribution List') ?>" data-content="<?= h($combined) ?>">
<?= empty($sharingGroup['releasability']) ?
'<span style="color: gray">' . __('Not defined') . '</span>' :
h($sharingGroup['releasability'])
?>
</span>
<?php
},
]
],
'actions' => [
[
'url' => '/sharing-groups/view',
'url_params_data_paths' => ['id'],
'icon' => 'eye',
'title' => __('View Sharing Group'),
],
[
'url' => '/sharing-groups/edit',
'url_params_data_paths' => ['id'],
'icon' => 'edit',
'complex_requirement' => [
'function' => function (array $sharingGroup) {
return $sharingGroup['editable'];
}
],
'title' => __('Edit Sharing Group'),
],
[
'url' => '/sharing-groups/delete',
'url_params_data_paths' => ['id'],
'postLinkConfirm' => __('Are you sure you want to delete the sharing group?'),
'icon' => 'trash',
'complex_requirement' => [
'function' => function (array $sharingGroup) {
return $sharingGroup['deletable'];
}
],
'title' => __('Delete Sharing Group'),
],
]
]
]
);
?>
</div>
<script type="text/javascript">
$(function(){
popoverStartup();
});
</script>
<?php
// TODO: [3.x-MIGRATION]
// if (!$ajax) {
// echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'globalActions', 'menuItem' => 'indexSG'));
// }

View File

@ -0,0 +1,110 @@
<?php
echo $this->element(
'genericElements/SingleViews/single_view',
[
'title' => __('Sharing Group %s', $entity['name']),
'data' => $entity,
'fields' => [
[
'key' => __('ID'),
'path' => 'id'
],
[
'key' => __('UUID'),
'path' => 'uuid'
],
[
'key' => __('Name'),
'path' => 'name'
],
[
'key' => __('Releasability'),
'path' => 'releasability'
],
[
'key' => __('Description'),
'path' => 'description'
],
[
'key' => __('Selectable'),
'path' => 'active',
'type' => 'boolean'
],
[
'key' => __('Created by'),
'element' => 'org',
'path' => 'Organisation.name',
'data_path' => 'Organisation'
],
[
'key' => __('Synced by'),
'element' => 'org',
'path' => 'sync_org.name',
'data_path' => 'sync_org',
'requirement' => isset($entity['sync_org'])
],
[
'key' => __('Events'),
'raw' => __n('{0} event', '{0} events', $entity['event_count'], $entity['event_count']),
'url' => sprintf('/events/index/searchsharinggroup:%s', h($entity['id']))
],
[
'key' => __('Organisations'),
'type' => 'custom',
'requirement' => isset($entity['SharingGroupOrg']),
'function' => function (array $sharingGroup) {
echo sprintf(
'<div class="span6">
<table class="table table-striped table-hover table-condensed">
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>',
__('Name'),
__('Is local'),
__('Can extend')
);
foreach ($sharingGroup['SharingGroupOrg'] as $sgo) {
echo '<tr>';
// TODO: [3.x-MIGRATION]
// echo sprintf('<td>%s</td>', $this->OrgImg->getNameWithImg($sgo));
echo sprintf('<td><span class="%s"></span></td>', $sgo['Organisation']['local'] ? 'fas fa-check' : 'fas fa-times');
echo sprintf('<td><span class="%s"></span></td>', $sgo['extend'] ? 'fas fa-check' : 'fas fa-times');
echo '</tr>';
}
echo '</table>
</div>';
}
],
[
'key' => __('Instances'),
'type' => 'custom',
'requirement' => isset($entity['SharingGroupServer']),
'function' => function (array $sharingGroup) {
echo sprintf(
'<div class="span6">
<table class="table table-striped table-hover table-condensed">
<tr>
<th>%s</th>
<th>%s</th>
<th>%s</th>
</tr>',
__('Name'),
__('URL'),
__('All orgs')
);
foreach ($sharingGroup['SharingGroupServer'] as $entitys) {
echo '<tr>';
echo sprintf('<td>%s</td>', h($entitys['Server']['name']));
echo sprintf('<td>%s</td>', h($entitys['Server']['url']));
echo sprintf('<td><span class="%s"></span></td>', $entitys['all_orgs'] ? 'fas fa-check' : 'fas fa-times');
echo '</tr>';
}
echo '</table>
</div>';
}
]
]
]
);

View File

@ -16,6 +16,9 @@ class OrganisationsFixture extends TestFixture
public const ORGANISATION_B_ID = 2000;
public const ORGANISATION_B_UUID = '36d22d9a-851e-4838-a655-9999c1d19497';
public const ORGANISATION_C_ID = 3000;
public const ORGANISATION_C_UUID = '73adc87b-be68-43d4-be8a-be5bdfcb59d2';
public function init(): void
{
$faker = \Faker\Factory::create();
@ -36,13 +39,27 @@ class OrganisationsFixture extends TestFixture
],
[
'id' => self::ORGANISATION_B_ID,
'uuid' => $faker->uuid(),
'name' => self::ORGANISATION_B_UUID,
'uuid' => self::ORGANISATION_B_UUID,
'name' => 'Organisation B',
'url' => $faker->url,
'nationality' => $faker->countryCode,
'sector' => 'IT',
'type' => '',
'contacts' => '',
'description' => 'ORGANISATION B',
'date_created' => $faker->dateTime()->getTimestamp(),
'date_modified' => $faker->dateTime()->getTimestamp()
],
[
'id' => self::ORGANISATION_C_ID,
'uuid' => self::ORGANISATION_C_UUID,
'name' => 'Organisation C',
'url' => $faker->url,
'nationality' => $faker->countryCode,
'sector' => 'IT',
'type' => '',
'contacts' => '',
'description' => 'ORGANISATION C',
'date_created' => $faker->dateTime()->getTimestamp(),
'date_modified' => $faker->dateTime()->getTimestamp()
]

View File

@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
namespace App\Test\Fixture;
use Cake\TestSuite\Fixture\TestFixture;
class ServersFixture extends TestFixture
{
public $connection = 'test';
public const SERVER_A_ID = 1000;
public const SERVER_A_NAME = 'Server A';
public const SERVER_B_ID = 2000;
public const SERVER_B_NAME = 'Server B';
public const SERVER_C_ID = 3000;
public const SERVER_C_NAME = 'Server C';
public function init(): void
{
$faker = \Faker\Factory::create();
$this->records = [
[
'id' => self::SERVER_A_ID,
'org_id' => OrganisationsFixture::ORGANISATION_A_ID,
'name' => self::SERVER_A_NAME,
'url' => $faker->url,
'authkey' => $faker->sha1(),
'push' => true,
'pull' => true,
'push_sightings' => true,
'push_galaxy_clusters' => true,
'pull_galaxy_clusters' => true,
'organization' => 'Org A',
'remote_org_id' => $faker->numberBetween(1000, 2000),
'publish_without_email' => true,
'unpublish_event' => true,
'self_signed' => true,
'pull_rules' => json_encode([]),
'push_rules' => json_encode([]),
'internal' => false,
'skip_proxy' => false,
'remove_missing_tags' => false,
'caching_enabled' => false,
'priority' => 1,
],
[
'id' => self::SERVER_B_ID,
'org_id' => OrganisationsFixture::ORGANISATION_B_ID,
'name' => self::SERVER_B_NAME,
'url' => $faker->url,
'authkey' => $faker->sha1(),
'push' => true,
'pull' => true,
'push_sightings' => true,
'push_galaxy_clusters' => true,
'pull_galaxy_clusters' => true,
'organization' => 'Org B',
'remote_org_id' => $faker->numberBetween(1000, 2000),
'publish_without_email' => true,
'unpublish_event' => true,
'self_signed' => true,
'pull_rules' => json_encode([]),
'push_rules' => json_encode([]),
'internal' => false,
'skip_proxy' => false,
'remove_missing_tags' => false,
'caching_enabled' => false,
'priority' => 1,
],
[
'id' => self::SERVER_C_ID,
'org_id' => OrganisationsFixture::ORGANISATION_C_ID,
'name' => self::SERVER_C_NAME,
'url' => $faker->url,
'authkey' => $faker->sha1(),
'push' => true,
'pull' => true,
'push_sightings' => true,
'push_galaxy_clusters' => true,
'pull_galaxy_clusters' => true,
'organization' => 'Org B',
'remote_org_id' => $faker->numberBetween(1000, 2000),
'publish_without_email' => true,
'unpublish_event' => true,
'self_signed' => true,
'pull_rules' => json_encode([]),
'push_rules' => json_encode([]),
'internal' => false,
'skip_proxy' => false,
'remove_missing_tags' => false,
'caching_enabled' => false,
'priority' => 1,
],
];
parent::init();
}
}

View File

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace App\Test\Fixture;
use Cake\TestSuite\Fixture\TestFixture;
class SharingGroupOrgsFixture extends TestFixture
{
public $connection = 'test';
public function init(): void
{
$faker = \Faker\Factory::create();
$this->records = [
[
'sharing_group_id' => SharingGroupsFixture::SHARING_GROUP_A_ID,
'org_id' => OrganisationsFixture::ORGANISATION_A_ID,
'extend' => false,
],
[
'sharing_group_id' => SharingGroupsFixture::SHARING_GROUP_A_ID,
'org_id' => OrganisationsFixture::ORGANISATION_B_ID,
'extend' => false,
],
[
'sharing_group_id' => SharingGroupsFixture::SHARING_GROUP_B_ID,
'org_id' => OrganisationsFixture::ORGANISATION_A_ID,
'extend' => false,
],
[
'sharing_group_id' => SharingGroupsFixture::SHARING_GROUP_B_ID,
'org_id' => OrganisationsFixture::ORGANISATION_B_ID,
'extend' => false,
],
];
parent::init();
}
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace App\Test\Fixture;
use Cake\TestSuite\Fixture\TestFixture;
class SharingGroupServersFixture extends TestFixture
{
public $connection = 'test';
public function init(): void
{
$faker = \Faker\Factory::create();
$this->records = [
[
'sharing_group_id' => SharingGroupsFixture::SHARING_GROUP_A_ID,
'server_id' => ServersFixture::SERVER_A_ID,
'all_orgs' => false,
],
[
'sharing_group_id' => SharingGroupsFixture::SHARING_GROUP_A_ID,
'server_id' => ServersFixture::SERVER_C_ID,
'all_orgs' => false,
]
];
parent::init();
}
}

View File

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace App\Test\Fixture;
use Cake\TestSuite\Fixture\TestFixture;
class SharingGroupsFixture extends TestFixture
{
public $connection = 'test';
public const SHARING_GROUP_A_ID = 1000;
public const SHARING_GROUP_A_UUID = '6e4706da-a808-4db6-8f0b-3ec7d53f48c7';
public const SHARING_GROUP_B_ID = 2000;
public const SHARING_GROUP_B_UUID = '9cb340c1-aebe-4d57-a9ac-915632e3eadf';
public function init(): void
{
$faker = \Faker\Factory::create();
$this->records = [
[
'id' => self::SHARING_GROUP_A_ID,
'uuid' => self::SHARING_GROUP_A_UUID,
'name' => 'Sharing Group A',
'description' => 'Sharing Group A',
'releasability' => 'UNCLASSIFIED',
'organisation_uuid' => OrganisationsFixture::ORGANISATION_A_UUID,
'org_id' => OrganisationsFixture::ORGANISATION_A_ID,
'active' => true,
'local' => true,
'roaming' => false,
'created' => $faker->dateTime()->getTimestamp(),
'modified' => $faker->dateTime()->getTimestamp()
],
[
'id' => self::SHARING_GROUP_B_ID,
'uuid' => self::SHARING_GROUP_B_UUID,
'name' => 'Sharing Group B',
'description' => 'Sharing Group B',
'releasability' => 'UNCLASSIFIED',
'organisation_uuid' => OrganisationsFixture::ORGANISATION_B_UUID,
'org_id' => OrganisationsFixture::ORGANISATION_B_ID,
'active' => true,
'local' => true,
'roaming' => false,
'created' => $faker->dateTime()->getTimestamp(),
'modified' => $faker->dateTime()->getTimestamp()
]
];
parent::init();
}
}

View File

@ -4,8 +4,8 @@ declare(strict_types=1);
namespace App\Test\Fixture;
use Cake\TestSuite\Fixture\TestFixture;
use Authentication\PasswordHasher\DefaultPasswordHasher;
use Cake\TestSuite\Fixture\TestFixture;
class UsersFixture extends TestFixture
{
@ -13,22 +13,22 @@ class UsersFixture extends TestFixture
// Admin user
public const USER_ADMIN_ID = 1000;
public const USER_ADMIN_EMAIL = 'admin@admin.test';
public const USER_ADMIN_EMAIL = 'admin@test.test';
public const USER_ADMIN_PASSWORD = 'AdminPassword';
// Sync user
public const USER_SYNC_ID = 2000;
public const USER_SYNC_EMAIL = 'sync@admin.test';
public const USER_SYNC_EMAIL = 'sync@test.test';
public const USER_SYNC_PASSWORD = 'SyncPassword';
// Org Admin user
public const USER_ORG_ADMIN_ID = 3000;
public const USER_ORG_ADMIN_EMAIL = 'org_admin@admin.test';
public const USER_ORG_ADMIN_EMAIL = 'org_admin@test.test';
public const USER_ORG_ADMIN_PASSWORD = 'OrgAdminPassword';
// Regular User user
public const USER_REGULAR_USER_ID = 4000;
public const USER_REGULAR_USER_EMAIL = 'user@admin.test';
public const USER_REGULAR_USER_EMAIL = 'user@test.test';
public const USER_REGULAR_USER_PASSWORD = 'UserPassword';
// Default Roles IDs

View File

@ -4,11 +4,11 @@ declare(strict_types=1);
namespace App\Test\TestCase\Api\Noticelists;
use Cake\TestSuite\TestCase;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Helper\ApiTestTrait;
use Cake\TestSuite\TestCase;
class AddNoticelistApiTest extends TestCase
class UpdateNoticelistsApiTest extends TestCase
{
use ApiTestTrait;

View File

@ -4,11 +4,11 @@ declare(strict_types=1);
namespace App\Test\TestCase\Api\Noticelists;
use Cake\TestSuite\TestCase;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\NoticelistsFixture;
use App\Test\Fixture\NoticelistEntriesFixture;
use App\Test\Fixture\NoticelistsFixture;
use App\Test\Helper\ApiTestTrait;
use Cake\TestSuite\TestCase;
class ViewNoticelistApiTest extends TestCase
{
@ -38,8 +38,8 @@ class ViewNoticelistApiTest extends TestCase
$this->assertEquals(NoticelistsFixture::NOTICELIST_1_ID, $noticelist['id']);
$this->assertEquals(NoticelistsFixture::NOTICELIST_1_NAME, $noticelist['name']);
$this->assertArrayHasKey('noticelist_entries', $noticelist);
$this->assertCount(1, $noticelist['noticelist_entries']);
$this->assertEquals(NoticelistEntriesFixture::NOTICELIST_ENTRY_1_ID, $noticelist['noticelist_entries'][0]['id']);
$this->assertArrayHasKey('NoticelistEntry', $noticelist);
$this->assertCount(1, $noticelist['NoticelistEntry']);
$this->assertEquals(NoticelistEntriesFixture::NOTICELIST_ENTRY_1_ID, $noticelist['NoticelistEntry'][0]['id']);
}
}

View File

@ -0,0 +1,143 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\SharingGroups;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\OrganisationsFixture;
use App\Test\Helper\ApiTestTrait;
use Cake\TestSuite\TestCase;
class AddSharingGroupApiTest extends TestCase
{
use ApiTestTrait;
protected const ENDPOINT = '/sharing-groups/add';
protected $fixtures = [
'app.Organisations',
'app.Users',
'app.AuthKeys',
'app.Servers',
'app.SharingGroups',
'app.SharingGroupOrgs',
'app.SharingGroupServers',
];
public function testAddSharingGroup(): void
{
$this->skipOpenApiValidations();
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$faker = \Faker\Factory::create();
$uuid = $faker->uuid();
$this->post(
self::ENDPOINT,
[
'uuid' => $uuid,
'name' => 'Test Sharing Group',
'org_id' => OrganisationsFixture::ORGANISATION_A_ID
]
);
$this->assertResponseOk();
$this->assertDbRecordExists(
'SharingGroups',
[
'uuid' => $uuid,
'name' => 'Test Sharing Group',
'org_id' => OrganisationsFixture::ORGANISATION_A_ID
]
);
}
public function testAddSharingGroupOrganisation(): void
{
$this->skipOpenApiValidations();
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$faker = \Faker\Factory::create();
$uuid = $faker->uuid();
$this->post(
self::ENDPOINT,
[
'uuid' => $uuid,
'name' => 'Test Sharing Group with Organisation',
'org_id' => OrganisationsFixture::ORGANISATION_A_ID,
'SharingGroupOrg' => [
[
'uuid' => OrganisationsFixture::ORGANISATION_B_UUID,
'extend' => false
]
]
]
);
$this->assertResponseOk();
$this->assertDbRecordExists(
'SharingGroups',
[
'uuid' => $uuid,
'org_id' => OrganisationsFixture::ORGANISATION_A_ID
]
);
$this->assertDbRecordExists(
'SharingGroupOrgs',
[
'org_id' => OrganisationsFixture::ORGANISATION_B_ID
]
);
}
public function testAddSharingGroupServer(): void
{
$this->skipOpenApiValidations();
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$faker = \Faker\Factory::create();
$uuid = $faker->uuid();
$server = $this->getRecordFromDb('Servers', ['id' => OrganisationsFixture::ORGANISATION_B_ID]);
$this->post(
self::ENDPOINT,
[
'uuid' => $uuid,
'name' => 'Test Sharing Group with Server',
'org_id' => OrganisationsFixture::ORGANISATION_A_ID,
'SharingGroupServer' => [
[
'server_id' => $server['id'],
'url' => $server['url'],
'all_orgs' => false,
]
]
]
);
$this->assertResponseOk();
$sharingGroup = $this->getJsonResponseAsArray();
$this->assertDbRecordExists(
'SharingGroups',
[
'uuid' => $uuid,
'org_id' => OrganisationsFixture::ORGANISATION_A_ID
]
);
$this->assertDbRecordExists(
'SharingGroupServers',
[
'server_id' => $server['id'],
'sharing_group_id' => $sharingGroup['id']
]
);
}
}

View File

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\SharingGroups;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\OrganisationsFixture;
use App\Test\Fixture\SharingGroupsFixture;
use App\Test\Helper\ApiTestTrait;
use Cake\TestSuite\TestCase;
class AddSharingGroupOrgApiTest extends TestCase
{
use ApiTestTrait;
protected const ENDPOINT = '/sharing-groups/add-org';
protected $fixtures = [
'app.Organisations',
'app.Users',
'app.AuthKeys',
'app.Servers',
'app.SharingGroups',
'app.SharingGroupOrgs',
'app.SharingGroupServers',
];
public function testAddSharingGroupOrganisation(): void
{
$this->skipOpenApiValidations();
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$this->post(
sprintf("%s/%s/%s", self::ENDPOINT, SharingGroupsFixture::SHARING_GROUP_A_ID, OrganisationsFixture::ORGANISATION_C_ID)
);
$this->assertResponseOk();
$this->assertDbRecordExists(
'SharingGroupOrgs',
[
'sharing_group_id' => SharingGroupsFixture::SHARING_GROUP_A_ID,
'org_id' => OrganisationsFixture::ORGANISATION_C_ID
]
);
}
}

View File

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\SharingGroups;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\ServersFixture;
use App\Test\Fixture\SharingGroupsFixture;
use App\Test\Helper\ApiTestTrait;
use Cake\TestSuite\TestCase;
class AddSharingGroupServerApiTest extends TestCase
{
use ApiTestTrait;
protected const ENDPOINT = '/sharing-groups/add-server';
protected $fixtures = [
'app.Organisations',
'app.Users',
'app.AuthKeys',
'app.Servers',
'app.SharingGroups',
'app.SharingGroupOrgs',
'app.SharingGroupServers',
];
public function testAddSharingGroupServer(): void
{
$this->skipOpenApiValidations();
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$this->post(
sprintf("%s/%s/%s", self::ENDPOINT, SharingGroupsFixture::SHARING_GROUP_A_ID, ServersFixture::SERVER_B_ID)
);
$this->assertResponseOk();
$this->assertDbRecordExists(
'SharingGroupServers',
[
'sharing_group_id' => SharingGroupsFixture::SHARING_GROUP_A_ID,
'server_id' => ServersFixture::SERVER_B_ID
]
);
}
}

View File

@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\SharingGroups;
use Cake\TestSuite\TestCase;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Helper\ApiTestTrait;
use App\Test\Fixture\SharingGroupsFixture;
class DeleteSharingGroupApiTest extends TestCase
{
use ApiTestTrait;
protected const ENDPOINT = '/sharing-groups/delete';
protected $fixtures = [
'app.Organisations',
'app.Users',
'app.AuthKeys',
'app.Servers',
'app.SharingGroups',
'app.SharingGroupOrgs',
'app.SharingGroupServers',
];
public function testDeleteSharingGroupByUUID(): void
{
$this->skipOpenApiValidations();
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$url = sprintf('%s/%s', self::ENDPOINT, SharingGroupsFixture::SHARING_GROUP_A_UUID);
$this->delete($url);
$this->assertResponseOk();
$this->assertDbRecordNotExists('SharingGroups', ['uuid' => SharingGroupsFixture::SHARING_GROUP_A_UUID]);
}
public function testDeleteSharingGroupById(): void
{
$this->skipOpenApiValidations();
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$url = sprintf('%s/%s', self::ENDPOINT, SharingGroupsFixture::SHARING_GROUP_A_ID);
$this->delete($url);
$this->assertResponseOk();
$this->assertDbRecordNotExists('SharingGroups', ['id' => SharingGroupsFixture::SHARING_GROUP_A_ID]);
}
}

View File

@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\SharingGroups;
use Cake\TestSuite\TestCase;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Helper\ApiTestTrait;
use App\Test\Fixture\EventBlocklistsFixture;
use App\Test\Fixture\SharingGroupsFixture;
class EditSharingGroupApiTest extends TestCase
{
use ApiTestTrait;
protected const ENDPOINT = '/sharing-groups/edit';
protected $fixtures = [
'app.Organisations',
'app.Users',
'app.AuthKeys',
'app.Servers',
'app.SharingGroups',
'app.SharingGroupOrgs',
'app.SharingGroupServers',
];
public function testEditSharingGroup(): void
{
$this->skipOpenApiValidations();
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$new_name = "Edited Sharing Group";
$url = sprintf('%s/%s', self::ENDPOINT, SharingGroupsFixture::SHARING_GROUP_A_UUID);
$this->post(
$url,
[
'name' => $new_name,
]
);
$this->assertResponseOk();
$this->assertDbRecordExists('SharingGroups', [
'uuid' => SharingGroupsFixture::SHARING_GROUP_A_UUID,
'name' => $new_name,
]);
}
}

View File

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\SharingGroups;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\SharingGroupsFixture;
use App\Test\Helper\ApiTestTrait;
use Cake\TestSuite\TestCase;
class IndexSharingGroupsApiTest extends TestCase
{
use ApiTestTrait;
protected const ENDPOINT = '/sharing-groups/index';
protected $fixtures = [
'app.Organisations',
'app.Users',
'app.AuthKeys',
'app.Servers',
'app.SharingGroups',
'app.SharingGroupOrgs',
'app.SharingGroupServers',
];
public function testIndexSharingGroups(): void
{
$this->skipOpenApiValidations();
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$this->get(self::ENDPOINT);
$sharingGroups = $this->getJsonResponseAsArray()['response'];
$this->assertResponseOk();
$this->assertCount(2, $sharingGroups);
$this->assertEquals('Sharing Group A', $sharingGroups[0]['name']);
$this->assertEquals(SharingGroupsFixture::SHARING_GROUP_A_UUID, $sharingGroups[0]['uuid']);
$this->assertEquals('Sharing Group B', $sharingGroups[1]['name']);
$this->assertEquals(SharingGroupsFixture::SHARING_GROUP_B_UUID, $sharingGroups[1]['uuid']);
}
}

View File

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\SharingGroups;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\OrganisationsFixture;
use App\Test\Fixture\SharingGroupsFixture;
use App\Test\Helper\ApiTestTrait;
use Cake\TestSuite\TestCase;
class RemoveSharingGroupOrgApiTest extends TestCase
{
use ApiTestTrait;
protected const ENDPOINT = '/sharing-groups/remove-org';
protected $fixtures = [
'app.Organisations',
'app.Users',
'app.AuthKeys',
'app.Servers',
'app.SharingGroups',
'app.SharingGroupOrgs',
'app.SharingGroupServers',
];
public function testRemoveSharingGroupOrganisation(): void
{
$this->skipOpenApiValidations();
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$this->assertDbRecordExists(
'SharingGroupOrgs',
[
'sharing_group_id' => SharingGroupsFixture::SHARING_GROUP_A_ID,
'org_id' => OrganisationsFixture::ORGANISATION_B_ID
]
);
$this->post(
sprintf("%s/%s/%s", self::ENDPOINT, SharingGroupsFixture::SHARING_GROUP_A_ID, OrganisationsFixture::ORGANISATION_B_ID)
);
$this->assertResponseOk();
$this->assertDbRecordNotExists(
'SharingGroupOrgs',
[
'sharing_group_id' => SharingGroupsFixture::SHARING_GROUP_A_ID,
'org_id' => OrganisationsFixture::ORGANISATION_B_ID
]
);
}
}

View File

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\SharingGroups;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\ServersFixture;
use App\Test\Fixture\SharingGroupsFixture;
use App\Test\Helper\ApiTestTrait;
use Cake\TestSuite\TestCase;
class RemoveSharingGroupServerApiTest extends TestCase
{
use ApiTestTrait;
protected const ENDPOINT = '/sharing-groups/remove-server';
protected $fixtures = [
'app.Organisations',
'app.Users',
'app.AuthKeys',
'app.Servers',
'app.SharingGroups',
'app.SharingGroupOrgs',
'app.SharingGroupServers',
];
public function testRemoveSharingGroupServer(): void
{
$this->skipOpenApiValidations();
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$this->assertDbRecordExists(
'SharingGroupServers',
[
'sharing_group_id' => SharingGroupsFixture::SHARING_GROUP_A_ID,
'server_id' => ServersFixture::SERVER_C_ID
]
);
$this->post(
sprintf("%s/%s/%s", self::ENDPOINT, SharingGroupsFixture::SHARING_GROUP_A_ID, ServersFixture::SERVER_C_ID)
);
$this->assertResponseOk();
$this->assertDbRecordNotExists(
'SharingGroupServers',
[
'sharing_group_id' => SharingGroupsFixture::SHARING_GROUP_A_ID,
'server_id' => ServersFixture::SERVER_C_ID
]
);
}
}

View File

@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\SharingGroups;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\OrganisationsFixture;
use App\Test\Fixture\ServersFixture;
use App\Test\Fixture\SharingGroupsFixture;
use App\Test\Helper\ApiTestTrait;
use Cake\TestSuite\TestCase;
class ViewSharingGroupApiTest extends TestCase
{
use ApiTestTrait;
protected const ENDPOINT = '/sharing-groups/view';
protected $fixtures = [
'app.Organisations',
'app.Users',
'app.AuthKeys',
'app.Servers',
'app.SharingGroups',
'app.SharingGroupOrgs',
'app.SharingGroupServers',
];
public function testViewSharingGroupById(): void
{
$this->skipOpenApiValidations();
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$url = sprintf('%s/%d', self::ENDPOINT, SharingGroupsFixture::SHARING_GROUP_A_ID);
$this->get($url);
$this->assertResponseOk();
$sharingGroup = $this->getJsonResponseAsArray();
$this->assertEquals(SharingGroupsFixture::SHARING_GROUP_A_ID, $sharingGroup['id']);
$this->assertEquals(SharingGroupsFixture::SHARING_GROUP_A_UUID, $sharingGroup['uuid']);
// check that the sharing group contains the correct organisations
$this->assertArrayHasKey('SharingGroupOrg', $sharingGroup);
$this->assertCount(2, $sharingGroup['SharingGroupOrg']);
$this->assertEquals(OrganisationsFixture::ORGANISATION_A_ID, $sharingGroup['SharingGroupOrg'][0]['Organisation']['id']);
$this->assertEquals(OrganisationsFixture::ORGANISATION_A_UUID, $sharingGroup['SharingGroupOrg'][0]['Organisation']['uuid']);
$this->assertEquals(OrganisationsFixture::ORGANISATION_B_ID, $sharingGroup['SharingGroupOrg'][1]['Organisation']['id']);
$this->assertEquals(OrganisationsFixture::ORGANISATION_B_UUID, $sharingGroup['SharingGroupOrg'][1]['Organisation']['uuid']);
// check that the sharing group contains the correct servers
$this->assertArrayHasKey('SharingGroupServer', $sharingGroup);
$this->assertCount(2, $sharingGroup['SharingGroupServer']);
$this->assertEquals(ServersFixture::SERVER_A_ID, $sharingGroup['SharingGroupServer'][0]['Server']['id']);
$this->assertEquals(ServersFixture::SERVER_A_NAME, $sharingGroup['SharingGroupServer'][0]['Server']['name']);
$this->assertEquals(ServersFixture::SERVER_C_ID, $sharingGroup['SharingGroupServer'][1]['Server']['id']);
$this->assertEquals(ServersFixture::SERVER_C_NAME, $sharingGroup['SharingGroupServer'][1]['Server']['name']);
}
}

View File

@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
/**
@ -14,6 +15,7 @@ declare(strict_types=1);
* @since 3.3.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\Test\TestCase;
use App\Application;
@ -21,19 +23,23 @@ use Cake\Error\Middleware\ErrorHandlerMiddleware;
use Cake\Http\MiddlewareQueue;
use Cake\Routing\Middleware\AssetMiddleware;
use Cake\Routing\Middleware\RoutingMiddleware;
use Cake\TestSuite\IntegrationTestCase;
use Cake\TestSuite\IntegrationTestTrait;
use Cake\TestSuite\TestCase;
use InvalidArgumentException;
/**
* ApplicationTest class
*/
class ApplicationTest extends IntegrationTestCase
class ApplicationTest extends TestCase
{
/**
* testBootstrap
*
* @return void
*/
use IntegrationTestTrait;
public function testBootstrap()
{
$app = new Application(dirname(dirname(__DIR__)) . '/config');
@ -60,7 +66,7 @@ class ApplicationTest extends IntegrationTestCase
$app = $this->getMockBuilder(Application::class)
->setConstructorArgs([dirname(dirname(__DIR__)) . '/config'])
->setMethods(['addPlugin'])
->onlyMethods(['addPlugin'])
->getMock();
$app->method('addPlugin')

View File

@ -8,7 +8,7 @@ function executeStateDependencyChecks(dependenceSourceSelector) {
} else {
var tempSelector = '*[data-dependence-source="' + dependenceSourceSelector + '"]';
}
$(tempSelector).each(function(index, dependent) {
$(tempSelector).each(function (index, dependent) {
var dependenceSource = $(dependent).data('dependence-source');
if ($(dependent).data('dependence-option') === $(dependenceSource).val()) {
$(dependent).parent().parent().removeClass('d-none');
@ -30,19 +30,19 @@ function testConnection(id) {
UI.overlayUntilResolve(
$container[0],
AJAXApi.quickFetchJSON(`/broods/testConnection/${id}`),
{text: 'Running test'}
{ text: 'Running test' }
).then(result => {
const $testResult = attachTestConnectionResultHtml(result, $container)
$(`#connection_test_${id}`).append($testResult)
})
.catch((error) => {
const $testResult = attachTestConnectionResultHtml(error.message, $container)
$(`#connection_test_${id}`).append($testResult)
})
.catch((error) => {
const $testResult = attachTestConnectionResultHtml(error.message, $container)
$(`#connection_test_${id}`).append($testResult)
})
}
function attachTestConnectionResultHtml(result, $container) {
function getKVHtml(key, value, valueClasses=[], extraValue='') {
function getKVHtml(key, value, valueClasses = [], extraValue = '') {
return $('<div/>').append(
$('<strong/>').text(key + ': '),
$('<span/>').addClass(valueClasses).text(value),
@ -84,15 +84,15 @@ function syntaxHighlightJson(json, indent) {
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
var cls = 'text-info';
if (/^"/.test(match)) {
if (/:$/.test(match)) {
cls = 'text-primary';
} else {
cls = '';
}
if (/:$/.test(match)) {
cls = 'text-primary';
} else {
cls = '';
}
} else if (/true|false/.test(match)) {
cls = 'text-info';
cls = 'text-info';
} else if (/null/.test(match)) {
cls = 'text-danger';
cls = 'text-danger';
}
return '<span class="' + cls + '">' + match + '</span>';
});
@ -103,10 +103,10 @@ function getTextColour(hex) {
return 'black'
}
hex = hex.slice(1)
var r = parseInt(hex.substring(0,2), 16)
var g = parseInt(hex.substring(2,4), 16)
var b = parseInt(hex.substring(4,6), 16)
var avg = ((2 * r) + b + (3 * g))/6
var r = parseInt(hex.substring(0, 2), 16)
var g = parseInt(hex.substring(2, 4), 16)
var b = parseInt(hex.substring(4, 6), 16)
var avg = ((2 * r) + b + (3 * g)) / 6
if (avg < 128) {
return 'white'
} else {
@ -131,7 +131,7 @@ function performGlobalSearch(evt) {
return;
}
const endpoint = '/instance/searchAll'
const searchParams = new URLSearchParams({search: value});
const searchParams = new URLSearchParams({ search: value });
const url = endpoint + '?' + searchParams
const options = {
statusNode: $resultContainer.find('.search-results-wrapper')
@ -171,7 +171,7 @@ function openSaveBookmarkModal(bookmark_url = '') {
})
}
function deleteBookmark(bookmark, forSidebar=false) {
function deleteBookmark(bookmark, forSidebar = false) {
const url = '/user-settings/deleteMyBookmark'
AJAXApi.quickFetchAndPostForm(url, {
bookmark_name: bookmark.name,
@ -315,10 +315,124 @@ $(document).ready(() => {
const url = `/user-settings/setMySetting/${settingName}`
AJAXApi.quickFetchAndPostForm(url, {
value: expanded ? 0 : 1
}, { provideFeedback: false})
}, { provideFeedback: false })
})
$('.sidebar #btn-add-bookmark').click(() => {
openSaveBookmarkModal(window.location.pathname)
})
})
function simpleTabPage(page) {
$(".progress_tab").removeClass("btn-primary").addClass("btn-inverse");
$("#page" + page + "_tab").removeClass("btn-inverse").addClass("btn-primary");
$(".tabContent").hide();
$("#page" + page + "_content").show();
if (page == lastPage) simpleTabPageLast();
}
function simpleTabPageLast() {
var summaryorgs = summaryextendorgs = remotesummaryorgs = remotesummaryextendorgs = summaryservers = "";
var orgcounter = extendcounter = remoteorgcounter = remoteextendcounter = servercounter = 0;
var sgname = "[Sharing group name not set!]";
if ($('#SharingGroupName').val()) sgname = $('#SharingGroupName').val();
var sgreleasability = "[Sharing group releasability not set!]";
if ($('#SharingGroupReleasability').val()) sgreleasability = $('#SharingGroupReleasability').val();
$('#summarytitle').text(sgname);
$('#summaryreleasable').text(sgreleasability);
organisations.forEach(function (organisation) {
if (organisation.type == 'local') {
if (orgcounter > 0) summaryorgs += ", ";
summaryorgs += organisation.name;
if (organisation.extend == true) {
if (extendcounter > 0) summaryextendorgs += ", "
summaryextendorgs += organisation.name;
extendcounter++;
}
orgcounter++;
} else {
if (remoteorgcounter > 0) remotesummaryorgs += ", ";
remotesummaryorgs += organisation.name;
if (organisation.extend == true) {
if (remoteextendcounter > 0) remotesummaryextendorgs += ", "
remotesummaryextendorgs += organisation.name;
remoteextendcounter++;
}
remoteorgcounter++;
}
});
if (orgcounter == 0) $('#localText').hide();
if (remoteorgcounter == 0) $('#externalText').hide();
if (extendcounter == 0) summaryextendorgs = "nobody";
if (remoteextendcounter == 0) remotesummaryextendorgs = "nobody";
servers.forEach(function (server) {
if (servercounter > 0) summaryservers += ", ";
if (server.id != 0) {
summaryservers += server.name;
if (extendcounter == 0) summaryextendorgs = "none";
servercounter++;
}
if (server.id == 0 && server.all_orgs == true) summaryorgs = "all organisations on this instance";
});
if ($('#SharingGroupRoaming').is(":checked")) {
summaryservers = "any interconnected instances linked by an eligible organisation.";
} else {
if (servercounter == 0) {
summaryservers = "data marked with this sharing group will not be pushed.";
}
}
$('#summarylocal').text(summaryorgs);
$('#summarylocalextend').text(summaryextendorgs);
$('#summaryexternal').text(remotesummaryorgs);
$('#summaryexternalextend').text(remotesummaryextendorgs);
$('#summaryservers').text(summaryservers);
}
function sharingGroupPopulateOrganisations() {
$('input[id=SharingGroupOrganisations]').val(JSON.stringify(organisations));
$('.orgRow').remove();
var id = 0;
var html = '';
organisations.forEach(function (org) {
html = '<tr id="orgRow' + id + '" class="orgRow">';
html += '<td class="short">' + org.type + '&nbsp;</td>';
html += '<td>' + $('<div>').text(org.name).html() + '&nbsp;</td>';
html += '<td>' + org.uuid + '&nbsp;</td>';
html += '<td class="short" style="text-align:center;">';
if (org.removable == 1) {
html += '<input id="orgExtend' + id + '" type="checkbox" onClick="sharingGroupExtendOrg(' + id + ')" ';
if (org.extend) html += 'checked';
html += '>';
} else {
html += '<span class="icon-ok"></span>'
}
html += '</td>';
html += '<td class="actions short">';
if (org.removable == 1) html += '<span class="icon-trash" onClick="sharingGroupRemoveOrganisation(' + id + ')"></span>';
html += '&nbsp;</td></tr>';
$('#organisations_table tr:last').after(html);
id++;
});
}
function sharingGroupPopulateServers() {
$('input[id=SharingGroupServers]').val(JSON.stringify(servers));
$('.serverRow').remove();
var id = 0;
var html = '';
servers.forEach(function (server) {
html = '<tr id="serverRow' + id + '" class="serverRow">';
html += '<td>' + server.name + '&nbsp;</td>';
html += '<td>' + server.url + '&nbsp;</td>';
html += '<td>';
html += '<input id="serverAddOrgs' + id + '" type="checkbox" onClick="sharingGroupServerAddOrgs(' + id + ')" ';
if (server.all_orgs) html += 'checked';
html += '>';
html += '</td>';
html += '<td class="actions short">';
if (server.removable == 1) html += '<span class="icon-trash" onClick="sharingGroupRemoveServer(' + id + ')"></span>';
html += '&nbsp;</td></tr>';
$('#servers_table tr:last').after(html);
id++;
});
}