diff --git a/config/Migrations/20240516000003_MonsterKeySupport.php b/config/Migrations/20240516000003_MonsterKeySupport.php
new file mode 100644
index 0000000..af8cba0
--- /dev/null
+++ b/config/Migrations/20240516000003_MonsterKeySupport.php
@@ -0,0 +1,30 @@
+table('encryption_keys')
+ ->changeColumn('encryption_key', 'text', [
+ 'limit' => MysqlAdapter::TEXT_MEDIUM
+ ])
+ ->update();
+ }
+}
diff --git a/config/bootstrap.php b/config/bootstrap.php
index e241b1c..397659f 100644
--- a/config/bootstrap.php
+++ b/config/bootstrap.php
@@ -89,13 +89,18 @@ try {
if (file_exists(CONFIG . 'app_local.php')) {
Configure::load('app_local', 'default');
//Configure::load('cerebrate', 'default', true);
- $settingsFile = new File(CONFIG . 'config.json');
- if ($settingsFile->exists()) {
- $settings = file_get_contents(CONFIG . 'config.json');
- $settings = json_decode($settings, true);
- foreach ($settings as $path => $setting) {
- Configure::write($path, $setting);
+}
+
+$settingsFile = new File(CONFIG . 'config.json');
+if ($settingsFile->exists()) {
+ $settings = file_get_contents(CONFIG . 'config.json');
+ $settings = json_decode($settings, true);
+ foreach ($settings as $path => $setting) {
+ if ($path == 'debug') {
+ Configure::write($path, (bool) $setting);
+ continue;
}
+ Configure::write($path, $setting);
}
}
diff --git a/src/Controller/AppController.php b/src/Controller/AppController.php
index e095561..0b6aaa3 100644
--- a/src/Controller/AppController.php
+++ b/src/Controller/AppController.php
@@ -101,12 +101,15 @@ class AppController extends Controller
public function beforeFilter(EventInterface $event)
{
+ $this->loadModel('Settings');
+ $this->Settings->loadSettings();
$this->loadModel('Users');
$this->Users->checkForNewInstance();
if ($this->ParamHandler->isRest()) {
$this->authApiUser();
$this->Security->setConfig('unlockedActions', [$this->request->getParam('action')]);
}
+
$this->ACL->setPublicInterfaces();
if (!empty($this->request->getAttribute('identity'))) {
$user = $this->Users->get($this->request->getAttribute('identity')->getIdentifier(), [
diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php
index da00194..e6d6f6c 100644
--- a/src/Controller/Component/CRUDComponent.php
+++ b/src/Controller/Component/CRUDComponent.php
@@ -112,6 +112,7 @@ class CRUDComponent extends Component
if (!$this->Controller->ParamHandler->isRest()) {
$this->setRequestedEntryAmount();
} else if (empty($this->request->getQuery('limit'))) {
+ $this->Controller->paginate['maxLimit'] = PHP_INT_MAX;
$this->Controller->paginate['limit'] = PHP_INT_MAX; // Make sure to download the entire filtered table
}
$data = $this->Controller->paginate($query, $this->Controller->paginate ?? []);
@@ -924,9 +925,10 @@ class CRUDComponent extends Component
$tableSettings = IndexSetting::getTableSetting($user, $this->Table);
if (!empty($tableSettings['number_of_element'])) {
if ($tableSettings['number_of_element'] === 'all') {
- $tableSettings['number_of_element'] = 10000; // Even with all selecect, make sure not to return too much data for the browser
+ $tableSettings['number_of_element'] = 10000; // Even with all selected, make sure not to return too much data for the browser
}
$this->Controller->paginate['limit'] = intval($tableSettings['number_of_element']);
+ $this->Controller->paginate['maxLimit'] = $this->Controller->paginate['limit'];
}
}
diff --git a/src/Controller/EncryptionKeysController.php b/src/Controller/EncryptionKeysController.php
index 0bbc74e..3637095 100644
--- a/src/Controller/EncryptionKeysController.php
+++ b/src/Controller/EncryptionKeysController.php
@@ -14,8 +14,8 @@ use Cake\Error\Debugger;
class EncryptionKeysController extends AppController
{
- public $filterFields = ['owner_model', 'owner_id', 'encryption_key'];
- public $quickFilterFields = [['encryption_key' => true]];
+ public $filterFields = ['owner_model', 'owner_id', 'encryption_key', 'Individuals.email', 'Organisations.name'];
+ public $quickFilterFields = [['encryption_key' => true], ['Individuals.email' => true], ['Organisations.name' => true]];
public $containFields = ['Individuals', 'Organisations'];
public $statisticsFields = ['type'];
diff --git a/src/Controller/OrganisationsController.php b/src/Controller/OrganisationsController.php
index aca7f36..4274443 100644
--- a/src/Controller/OrganisationsController.php
+++ b/src/Controller/OrganisationsController.php
@@ -70,7 +70,7 @@ class OrganisationsController extends AppController
$additionalContainFields[] = 'MetaFields';
}
$containFields = array_merge($this->containFields, $additionalContainFields);
-
+ $this->set('validOrgs', $this->Users->getValidOrgsForUser($this->ACL->getUser()));
$this->CRUD->index([
'filters' => $this->filterFields,
'quickFilters' => $this->quickFilterFields,
@@ -184,9 +184,14 @@ class OrganisationsController extends AppController
if ($currentUser['role']['perm_admin']) {
return true;
}
+
if ($currentUser['role']['perm_org_admin'] && $currentUser['organisation']['id'] == $orgId) {
return true;
}
+
+ if ($currentUser['role']['perm_group_admin'] && in_array($orgId, $this->Users->getValidOrgsForUser($currentUser))) {
+ return true;
+ }
return false;
}
}
diff --git a/src/Controller/UsersController.php b/src/Controller/UsersController.php
index 863af88..d86f314 100644
--- a/src/Controller/UsersController.php
+++ b/src/Controller/UsersController.php
@@ -117,7 +117,14 @@ class UsersController extends AppController
$data['role_id'] = $defaultRole['id'];
}
if (!$currentUser['role']['perm_admin']) {
- $data['organisation_id'] = $currentUser['organisation_id'];
+ $validOrgs = $this->Users->getValidOrgsForUser($currentUser);
+ if ($currentUser['role']['perm_group_admin']) {
+ if (!empty($data['organisation_id']) && !in_array($currentUser['organisation_id'], $validOrgs)) {
+ throw new MethodNotAllowedException(__('You do not have permission to assign that organisation.'));
+ }
+ } else {
+ $data['organisation_id'] = $currentUser['organisation_id'];
+ }
if (!in_array($data['role_id'], array_keys($validRoles))) {
throw new MethodNotAllowedException(__('You do not have permission to assign that role.'));
}
@@ -171,7 +178,8 @@ class UsersController extends AppController
*/
$org_conditions = [];
if (empty($currentUser['role']['perm_admin'])) {
- $org_conditions = ['id' => $currentUser['organisation_id']];
+ $validOrgs = $this->Users->getValidOrgsForUser($currentUser);
+ $org_conditions = ['id IN' => $validOrgs];
}
$dropdownData = [
'role' => $validRoles,
diff --git a/src/Lib/default/local_tool_connectors/MispConnector.php b/src/Lib/default/local_tool_connectors/MispConnector.php
index d7d4b30..97734da 100644
--- a/src/Lib/default/local_tool_connectors/MispConnector.php
+++ b/src/Lib/default/local_tool_connectors/MispConnector.php
@@ -907,6 +907,12 @@ class MispConnector extends CommonConnectorTools
'reload_url' => '/localTools/action/' . h($params['connection']['id']) . '/organisationsAction',
'popover_url' => '/localTools/action/' . h($params['connection']['id']) . '/fetchSelectedOrganisationsAction'
],
+ [
+ 'text' => __('Fetch all organisations'),
+ 'html' => ' ',
+ 'reload_url' => '/localTools/action/' . h($params['connection']['id']) . '/organisationsAction',
+ 'popover_url' => '/localTools/action/' . h($params['connection']['id']) . '/fetchSelectedOrganisationsAction?ids=all'
+ ],
[
'text' => __('Push organisations'),
'html' => ' ',
@@ -1026,6 +1032,7 @@ class MispConnector extends CommonConnectorTools
$data = $response->getJson();
$temp = $this->getSharingGroups();
$existingOrgs = [];
+ $existingSGs = [];
foreach ($temp as $k => $v) {
$existingSGs[$v['uuid']] = $v;
unset($temp[$k]);
@@ -1190,23 +1197,42 @@ class MispConnector extends CommonConnectorTools
return [
'data' => [
'title' => __('Fetch organisations'),
- 'description' => __('Fetch and create/update the selected {0} organisations from MISP?', count($ids)),
+ 'description' => is_array($ids) ?
+ __('Fetch and create/update the selected {0} organisations from MISP?', count($ids)) :
+ __('Fetch and create/update ALL organisations from MISP?'),
'submit' => [
'action' => $params['request']->getParam('action')
],
- 'url' => ['controller' => 'localTools', 'action' => 'action', $params['connection']['id'], 'fetchSelectedOrganisationsAction']
+ 'url' => is_array($ids) ?
+ ['controller' => 'localTools', 'action' => 'action', $params['connection']['id'], 'fetchSelectedOrganisationsAction'] :
+ ['controller' => 'localTools', 'action' => 'action', $params['connection']['id'], 'fetchSelectedOrganisationsAction?ids=all']
]
];
} elseif ($params['request']->is(['post'])) {
$successes = 0;
$errors = 0;
- foreach ($ids as $id) {
- $response = $this->getData('/organisations/view/' . $id, $params);
- $result = $this->captureOrganisation($response->getJson()['Organisation']);
+ if (!is_array($ids) && $ids === 'all') {
+ $response = $this->getData('/organisations/index/scope:all', $params);
if ($response->getStatusCode() == 200) {
- $successes++;
- } else {
- $errors++;
+ $orgs = $response->getJson();
+ foreach ($orgs as $org) {
+ $result = $this->captureOrganisation($org['Organisation']);
+ if ($result) {
+ $successes++;
+ } else {
+ $errors++;
+ }
+ }
+ }
+ } else {
+ foreach ($ids as $id) {
+ $response = $this->getData('/organisations/view/' . $id, $params);
+ $result = $this->captureOrganisation($response->getJson()['Organisation']);
+ if ($response->getStatusCode() == 200) {
+ $successes++;
+ } else {
+ $errors++;
+ }
}
}
if ($successes) {
diff --git a/src/Model/Behavior/AuthKeycloakBehavior.php b/src/Model/Behavior/AuthKeycloakBehavior.php
index 34ad57d..1ad6362 100644
--- a/src/Model/Behavior/AuthKeycloakBehavior.php
+++ b/src/Model/Behavior/AuthKeycloakBehavior.php
@@ -199,6 +199,9 @@ class AuthKeycloakBehavior extends Behavior
'model_title' => __('Successful Keycloak enrollment for user {0}', $user['username']),
'changed' => $logChange
]);
+ $saved_user = $this->getCerebrateUsers($user['id']);
+ $clientId = $this->getClientId();
+ $this->syncUsers($saved_user, $clientId);
$response = $this->restApiRequest(
'%s/admin/realms/%s/users/' . urlencode($newUserId) . '/execute-actions-email',
['UPDATE_PASSWORD'],
@@ -335,7 +338,7 @@ class AuthKeycloakBehavior extends Behavior
public function getParsedKeycloakUser(): array
{
- $response = $this->restApiRequest('%s/admin/realms/%s/users', [], 'get');
+ $response = $this->restApiRequest('%s/admin/realms/%s/users/?max=999999', [], 'get');
$keycloakUsers = json_decode($response->getStringBody(), true);
$keycloakUsersParsed = [];
$mappers = array_merge(['role_name', 'role_uuid', 'org_uuid', 'org_name'], $this->getMappedFieldList());
@@ -357,10 +360,10 @@ class AuthKeycloakBehavior extends Behavior
return $keycloakUsersParsed;
}
- private function getCerebrateUsers(): array
+ private function getCerebrateUsers($id = null): array
{
$metaFieldsSelector = ['fields' => ['MetaFields.field', 'MetaFields.parent_id', 'MetaFields.value']];
- $results = $this->_table->find()->contain(['Individuals', 'Organisations', 'Roles', 'MetaFields' => $metaFieldsSelector])->select([
+ $query = $this->_table->find()->contain(['Individuals', 'Organisations', 'Roles', 'MetaFields' => $metaFieldsSelector])->select([
'id',
'uuid',
'username',
@@ -373,7 +376,11 @@ class AuthKeycloakBehavior extends Behavior
'Roles.uuid',
'Organisations.name',
'Organisations.uuid'
- ])->disableHydration()->toArray();
+ ]);
+ if ($id) {
+ $query->where(['User.id' => $id]);
+ }
+ $results = $query->disableHydration()->toArray();
foreach ($results as &$result) {
if (!empty($result['meta_fields'])) {
$temp = [];
diff --git a/src/Model/Table/AuthKeysTable.php b/src/Model/Table/AuthKeysTable.php
index f5336e6..f20299e 100644
--- a/src/Model/Table/AuthKeysTable.php
+++ b/src/Model/Table/AuthKeysTable.php
@@ -29,7 +29,7 @@ class AuthKeysTable extends AppTable
public function beforeMarshal(EventInterface $event, ArrayObject $data, ArrayObject $options)
{
$data['created'] = time();
- if (empty($data['expiration'])) {
+ if (!isset($data['expiration'])) {
$data['expiration'] = 0;
} else {
$data['expiration'] = strtotime($data['expiration']);
@@ -56,7 +56,16 @@ class AuthKeysTable extends AppTable
{
$validator
->notEmptyString('user_id')
- ->requirePresence(['user_id'], 'create');
+ ->requirePresence(['user_id'], 'create')
+ ->add('expiration', 'custom', [
+ 'rule' => function ($value, $context) {
+ if ($value && $value < time()) {
+ return false;
+ }
+ return true;
+ },
+ 'message' => __('Expiration date/time has to be in the future.')
+ ]);
return $validator;
}
diff --git a/src/Model/Table/SettingProviders/CerebrateSettingsProvider.php b/src/Model/Table/SettingProviders/CerebrateSettingsProvider.php
index 19f9a8b..256b3a9 100644
--- a/src/Model/Table/SettingProviders/CerebrateSettingsProvider.php
+++ b/src/Model/Table/SettingProviders/CerebrateSettingsProvider.php
@@ -62,6 +62,36 @@ class CerebrateSettingsProvider extends BaseSettingsProvider
'test' => 'testUuid',
],
],
+ 'Session handling' => [
+ 'Session.defaults' => [
+ 'description' => 'The session handler used. We strongly recommend "php" and configuring the redis handler in your php.ini. Be advised, that database sessions require the creation of a session table first.',
+ 'default' => 'php',
+ 'name' => 'Session handler',
+ 'options' => function ($settingsProviders) {
+ return [
+ 'php' => 'php',
+ 'cake' => 'cake',
+ 'database' => 'database'
+ ];
+ },
+ 'severity' => 'info',
+ 'type' => 'select'
+ ],
+ 'Session.timeout' => [
+ 'description' => 'The session timeout (in minutes)',
+ 'default' => 30,
+ 'name' => 'Session Timeout',
+ 'severity' => 'info',
+ 'type' => 'integer',
+ ],
+ 'Session.ini.session.cookie_lifetime' => [
+ 'description' => 'The cookie timeout (in seconds). Whilst the session itself gets refreshed on each interaction, the cookie\'s timeout cannot be prolonged, so make sure that you set something at least as high as the session timeout, preferrably longer.',
+ 'default' => 604800,
+ 'name' => 'Cookie Timeout',
+ 'severity' => 'info',
+ 'type' => 'integer',
+ ],
+ ],
/*
'Miscellaneous' => [
'sc2.hero' => [
diff --git a/src/Model/Table/SettingsTable.php b/src/Model/Table/SettingsTable.php
index 2eb941c..ad35c05 100644
--- a/src/Model/Table/SettingsTable.php
+++ b/src/Model/Table/SettingsTable.php
@@ -126,7 +126,7 @@ class SettingsTable extends AppTable
return $settings;
}
- private function loadSettings(): void
+ public function loadSettings(): void
{
$settings = file_get_contents(CONFIG . 'config.json');
$settings = json_decode($settings, true);
diff --git a/src/Model/Table/UsersTable.php b/src/Model/Table/UsersTable.php
index 4cb2dde..f82d54d 100644
--- a/src/Model/Table/UsersTable.php
+++ b/src/Model/Table/UsersTable.php
@@ -293,8 +293,18 @@ class UsersTable extends AppTable
return true;
}
- public function getAllOrganisations($currentUser) {
+ public function getAllOrganisations(\App\Model\Entity\User $currentUser)
+ {
$this->Individuals = TableRegistry::get('Individuals');
return $this->Individuals->getAllOrganisations($currentUser);
}
+
+ public function getValidOrgsForUser(\App\Model\Entity\User $user): array
+ {
+ if (!empty($user['role']['perm_group_admin'])) {
+ return $this->Organisations->OrgGroups->getGroupOrgIdsForUser($user);
+ } else {
+ return [$user['organisation_id']];
+ }
+ }
}
diff --git a/src/VERSION.json b/src/VERSION.json
index d03174e..8610ab4 100644
--- a/src/VERSION.json
+++ b/src/VERSION.json
@@ -1,4 +1,4 @@
{
- "version": "1.18",
+ "version": "1.19",
"application": "Cerebrate"
}
diff --git a/templates/Inbox/index.php b/templates/Inbox/index.php
index a22e106..acdcb7c 100644
--- a/templates/Inbox/index.php
+++ b/templates/Inbox/index.php
@@ -86,7 +86,7 @@ echo $this->element('genericElements/IndexTable/index_table', [
'data_path' => 'origin',
],
[
- 'name' => 'user',
+ 'name' => __('to'),
'sort' => 'Inbox.user_id',
'data_path' => 'user',
'element' => 'user'
diff --git a/templates/Individuals/view.php b/templates/Individuals/view.php
index 423c7e6..ed7ce00 100644
--- a/templates/Individuals/view.php
+++ b/templates/Individuals/view.php
@@ -40,6 +40,12 @@ echo $this->element(
'scope' => 'individuals'
]
],
- 'children' => []
+ 'children' => [
+ [
+ 'url' => '/EncryptionKeys/index?owner_id={{0}}&owner_model=individual',
+ 'url_params' => ['id'],
+ 'title' => __('Encryption keys')
+ ]
+ ]
]
);
diff --git a/templates/Instance/home.php b/templates/Instance/home.php
index f3c6604..814e294 100644
--- a/templates/Instance/home.php
+++ b/templates/Instance/home.php
@@ -1,6 +1,7 @@
user_settings_by_name['ui.bookmarks']['value']) ? json_decode($loggedUser->user_settings_by_name['ui.bookmarks']['value'], true) : [];
$this->userSettingsTable = TableRegistry::getTableLocator()->get('UserSettings');
@@ -49,9 +50,10 @@ $this->userSettingsTable = TableRegistry::getTableLocator()->get('UserSettings')
$modelForDisplay = $exploded[count($exploded) - 1];
$panelTitle = $this->Html->link(
h($modelForDisplay),
- $this->Url->build([
+ Router::url([
'controller' => $modelForDisplay,
'action' => 'index',
+ '?' => ['sort' => 'modified', 'direction' => 'desc']
]),
['class' => 'text-white text-decoration-none fw-light stretched-link']
);
diff --git a/templates/Organisations/index.php b/templates/Organisations/index.php
index c3fb2ae..b081b88 100644
--- a/templates/Organisations/index.php
+++ b/templates/Organisations/index.php
@@ -109,10 +109,13 @@ echo $this->element('genericElements/IndexTable/index_table', [
'modal_params_data_path' => 'id',
'icon' => 'edit',
'complex_requirement' => [
- 'function' => function ($row, $options) use ($loggedUser) {
+ 'function' => function ($row, $options) use ($loggedUser, $validOrgs) {
if ($loggedUser['role']['perm_admin'] || ($loggedUser['role']['perm_org_admin'] && $row['id'] == $loggedUser['organisation']['id'])) {
return true;
}
+ if ($loggedUser['role']['perm_group_admin'] && in_array($row['id'], $validOrgs)) {
+ return true;
+ }
return false;
}
]
diff --git a/templates/Organisations/view.php b/templates/Organisations/view.php
index a27339b..218003d 100644
--- a/templates/Organisations/view.php
+++ b/templates/Organisations/view.php
@@ -63,6 +63,12 @@ echo $this->element(
'data' => $entity,
'fields' => $fields,
'combinedFieldsView' => false,
- 'children' => []
+ 'children' => [
+ [
+ 'url' => '/EncryptionKeys/index?owner_id={{0}}&owner_model=organisation',
+ 'url_params' => ['id'],
+ 'title' => __('Encryption keys')
+ ]
+ ]
]
);
diff --git a/templates/element/Settings/field.php b/templates/element/Settings/field.php
index ed297f0..eb9a214 100644
--- a/templates/element/Settings/field.php
+++ b/templates/element/Settings/field.php
@@ -53,6 +53,7 @@
'type' => 'number',
'min' => '0',
'step' => 1,
+ 'value' => isset($setting['value']) ? h($setting['value']) : '',
'id' => h($settingId),
'data-setting-name' => h($settingName),
'aria-describedby' => h("{$settingId}Help")