Merge branch 'main' into develop

dependabot/npm_and_yarn/webroot/theme/braces-3.0.3
iglocska 2024-05-15 14:25:07 +02:00
commit 93612950ed
No known key found for this signature in database
GPG Key ID: BEA224F1FEF113AC
15 changed files with 141 additions and 33 deletions

View File

@ -89,13 +89,18 @@ try {
if (file_exists(CONFIG . 'app_local.php')) { if (file_exists(CONFIG . 'app_local.php')) {
Configure::load('app_local', 'default'); Configure::load('app_local', 'default');
//Configure::load('cerebrate', 'default', true); //Configure::load('cerebrate', 'default', true);
$settingsFile = new File(CONFIG . 'config.json'); }
if ($settingsFile->exists()) {
$settings = file_get_contents(CONFIG . 'config.json'); $settingsFile = new File(CONFIG . 'config.json');
$settings = json_decode($settings, true); if ($settingsFile->exists()) {
foreach ($settings as $path => $setting) { $settings = file_get_contents(CONFIG . 'config.json');
Configure::write($path, $setting); $settings = json_decode($settings, true);
foreach ($settings as $path => $setting) {
if ($path == 'debug') {
Configure::write($path, (bool) $setting);
continue;
} }
Configure::write($path, $setting);
} }
} }

View File

@ -101,12 +101,15 @@ class AppController extends Controller
public function beforeFilter(EventInterface $event) public function beforeFilter(EventInterface $event)
{ {
$this->loadModel('Settings');
$this->Settings->loadSettings();
$this->loadModel('Users'); $this->loadModel('Users');
$this->Users->checkForNewInstance(); $this->Users->checkForNewInstance();
if ($this->ParamHandler->isRest()) { if ($this->ParamHandler->isRest()) {
$this->authApiUser(); $this->authApiUser();
$this->Security->setConfig('unlockedActions', [$this->request->getParam('action')]); $this->Security->setConfig('unlockedActions', [$this->request->getParam('action')]);
} }
$this->ACL->setPublicInterfaces(); $this->ACL->setPublicInterfaces();
if (!empty($this->request->getAttribute('identity'))) { if (!empty($this->request->getAttribute('identity'))) {
$user = $this->Users->get($this->request->getAttribute('identity')->getIdentifier(), [ $user = $this->Users->get($this->request->getAttribute('identity')->getIdentifier(), [

View File

@ -112,6 +112,7 @@ class CRUDComponent extends Component
if (!$this->Controller->ParamHandler->isRest()) { if (!$this->Controller->ParamHandler->isRest()) {
$this->setRequestedEntryAmount(); $this->setRequestedEntryAmount();
} else if (empty($this->request->getQuery('limit'))) { } 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 $this->Controller->paginate['limit'] = PHP_INT_MAX; // Make sure to download the entire filtered table
} }
$data = $this->Controller->paginate($query, $this->Controller->paginate ?? []); $data = $this->Controller->paginate($query, $this->Controller->paginate ?? []);
@ -924,9 +925,10 @@ class CRUDComponent extends Component
$tableSettings = IndexSetting::getTableSetting($user, $this->Table); $tableSettings = IndexSetting::getTableSetting($user, $this->Table);
if (!empty($tableSettings['number_of_element'])) { if (!empty($tableSettings['number_of_element'])) {
if ($tableSettings['number_of_element'] === 'all') { 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['limit'] = intval($tableSettings['number_of_element']);
$this->Controller->paginate['maxLimit'] = $this->Controller->paginate['limit'];
} }
} }

View File

@ -14,8 +14,8 @@ use Cake\Error\Debugger;
class EncryptionKeysController extends AppController class EncryptionKeysController extends AppController
{ {
public $filterFields = ['owner_model', 'owner_id', 'encryption_key']; public $filterFields = ['owner_model', 'owner_id', 'encryption_key', 'Individuals.email', 'Organisations.name'];
public $quickFilterFields = [['encryption_key' => true]]; public $quickFilterFields = [['encryption_key' => true], ['Individuals.email' => true], ['Organisations.name' => true]];
public $containFields = ['Individuals', 'Organisations']; public $containFields = ['Individuals', 'Organisations'];
public $statisticsFields = ['type']; public $statisticsFields = ['type'];

View File

@ -70,7 +70,7 @@ class OrganisationsController extends AppController
$additionalContainFields[] = 'MetaFields'; $additionalContainFields[] = 'MetaFields';
} }
$containFields = array_merge($this->containFields, $additionalContainFields); $containFields = array_merge($this->containFields, $additionalContainFields);
$this->set('validOrgs', $this->Users->getValidOrgsForUser($this->ACL->getUser()));
$this->CRUD->index([ $this->CRUD->index([
'filters' => $this->filterFields, 'filters' => $this->filterFields,
'quickFilters' => $this->quickFilterFields, 'quickFilters' => $this->quickFilterFields,
@ -184,9 +184,14 @@ class OrganisationsController extends AppController
if ($currentUser['role']['perm_admin']) { if ($currentUser['role']['perm_admin']) {
return true; return true;
} }
if ($currentUser['role']['perm_org_admin'] && $currentUser['organisation']['id'] == $orgId) { if ($currentUser['role']['perm_org_admin'] && $currentUser['organisation']['id'] == $orgId) {
return true; return true;
} }
if ($currentUser['role']['perm_group_admin'] && in_array($orgId, $this->Users->getValidOrgsForUser($currentUser))) {
return true;
}
return false; return false;
} }
} }

View File

@ -117,7 +117,14 @@ class UsersController extends AppController
$data['role_id'] = $defaultRole['id']; $data['role_id'] = $defaultRole['id'];
} }
if (!$currentUser['role']['perm_admin']) { 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))) { if (!in_array($data['role_id'], array_keys($validRoles))) {
throw new MethodNotAllowedException(__('You do not have permission to assign that role.')); throw new MethodNotAllowedException(__('You do not have permission to assign that role.'));
} }
@ -171,7 +178,8 @@ class UsersController extends AppController
*/ */
$org_conditions = []; $org_conditions = [];
if (empty($currentUser['role']['perm_admin'])) { if (empty($currentUser['role']['perm_admin'])) {
$org_conditions = ['id' => $currentUser['organisation_id']]; $validOrgs = $this->Users->getValidOrgsForUser($currentUser);
$org_conditions = ['id IN' => $validOrgs];
} }
$dropdownData = [ $dropdownData = [
'role' => $validRoles, 'role' => $validRoles,

View File

@ -907,6 +907,12 @@ class MispConnector extends CommonConnectorTools
'reload_url' => '/localTools/action/' . h($params['connection']['id']) . '/organisationsAction', 'reload_url' => '/localTools/action/' . h($params['connection']['id']) . '/organisationsAction',
'popover_url' => '/localTools/action/' . h($params['connection']['id']) . '/fetchSelectedOrganisationsAction' 'popover_url' => '/localTools/action/' . h($params['connection']['id']) . '/fetchSelectedOrganisationsAction'
], ],
[
'text' => __('Fetch all organisations'),
'html' => '<i class="fas fa-download"></i> ',
'reload_url' => '/localTools/action/' . h($params['connection']['id']) . '/organisationsAction',
'popover_url' => '/localTools/action/' . h($params['connection']['id']) . '/fetchSelectedOrganisationsAction?ids=all'
],
[ [
'text' => __('Push organisations'), 'text' => __('Push organisations'),
'html' => '<i class="fas fa-upload"></i> ', 'html' => '<i class="fas fa-upload"></i> ',
@ -1026,6 +1032,7 @@ class MispConnector extends CommonConnectorTools
$data = $response->getJson(); $data = $response->getJson();
$temp = $this->getSharingGroups(); $temp = $this->getSharingGroups();
$existingOrgs = []; $existingOrgs = [];
$existingSGs = [];
foreach ($temp as $k => $v) { foreach ($temp as $k => $v) {
$existingSGs[$v['uuid']] = $v; $existingSGs[$v['uuid']] = $v;
unset($temp[$k]); unset($temp[$k]);
@ -1190,23 +1197,42 @@ class MispConnector extends CommonConnectorTools
return [ return [
'data' => [ 'data' => [
'title' => __('Fetch organisations'), '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' => [ 'submit' => [
'action' => $params['request']->getParam('action') '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'])) { } elseif ($params['request']->is(['post'])) {
$successes = 0; $successes = 0;
$errors = 0; $errors = 0;
foreach ($ids as $id) { if (!is_array($ids) && $ids === 'all') {
$response = $this->getData('/organisations/view/' . $id, $params); $response = $this->getData('/organisations/index/scope:all', $params);
$result = $this->captureOrganisation($response->getJson()['Organisation']);
if ($response->getStatusCode() == 200) { if ($response->getStatusCode() == 200) {
$successes++; $orgs = $response->getJson();
} else { foreach ($orgs as $org) {
$errors++; $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) { if ($successes) {

View File

@ -199,6 +199,9 @@ class AuthKeycloakBehavior extends Behavior
'model_title' => __('Successful Keycloak enrollment for user {0}', $user['username']), 'model_title' => __('Successful Keycloak enrollment for user {0}', $user['username']),
'changed' => $logChange 'changed' => $logChange
]); ]);
$saved_user = $this->getCerebrateUsers($user['id']);
$clientId = $this->getClientId();
$this->syncUsers($saved_user, $clientId);
$response = $this->restApiRequest( $response = $this->restApiRequest(
'%s/admin/realms/%s/users/' . urlencode($newUserId) . '/execute-actions-email', '%s/admin/realms/%s/users/' . urlencode($newUserId) . '/execute-actions-email',
['UPDATE_PASSWORD'], ['UPDATE_PASSWORD'],
@ -335,7 +338,7 @@ class AuthKeycloakBehavior extends Behavior
public function getParsedKeycloakUser(): array 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); $keycloakUsers = json_decode($response->getStringBody(), true);
$keycloakUsersParsed = []; $keycloakUsersParsed = [];
$mappers = array_merge(['role_name', 'role_uuid', 'org_uuid', 'org_name'], $this->getMappedFieldList()); $mappers = array_merge(['role_name', 'role_uuid', 'org_uuid', 'org_name'], $this->getMappedFieldList());
@ -357,10 +360,10 @@ class AuthKeycloakBehavior extends Behavior
return $keycloakUsersParsed; return $keycloakUsersParsed;
} }
private function getCerebrateUsers(): array private function getCerebrateUsers($id = null): array
{ {
$metaFieldsSelector = ['fields' => ['MetaFields.field', 'MetaFields.parent_id', 'MetaFields.value']]; $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', 'id',
'uuid', 'uuid',
'username', 'username',
@ -373,7 +376,11 @@ class AuthKeycloakBehavior extends Behavior
'Roles.uuid', 'Roles.uuid',
'Organisations.name', 'Organisations.name',
'Organisations.uuid' 'Organisations.uuid'
])->disableHydration()->toArray(); ]);
if ($id) {
$query->where(['User.id' => $id]);
}
$results = $query->disableHydration()->toArray();
foreach ($results as &$result) { foreach ($results as &$result) {
if (!empty($result['meta_fields'])) { if (!empty($result['meta_fields'])) {
$temp = []; $temp = [];

View File

@ -29,7 +29,7 @@ class AuthKeysTable extends AppTable
public function beforeMarshal(EventInterface $event, ArrayObject $data, ArrayObject $options) public function beforeMarshal(EventInterface $event, ArrayObject $data, ArrayObject $options)
{ {
$data['created'] = time(); $data['created'] = time();
if (empty($data['expiration'])) { if (!isset($data['expiration'])) {
$data['expiration'] = 0; $data['expiration'] = 0;
} else { } else {
$data['expiration'] = strtotime($data['expiration']); $data['expiration'] = strtotime($data['expiration']);
@ -56,7 +56,16 @@ class AuthKeysTable extends AppTable
{ {
$validator $validator
->notEmptyString('user_id') ->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; return $validator;
} }

View File

@ -62,6 +62,36 @@ class CerebrateSettingsProvider extends BaseSettingsProvider
'test' => 'testUuid', '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' => [ 'Miscellaneous' => [
'sc2.hero' => [ 'sc2.hero' => [
@ -316,9 +346,8 @@ class CerebrateSettingsProvider extends BaseSettingsProvider
'description' => __('The debug level of the instance'), 'description' => __('The debug level of the instance'),
'default' => 0, 'default' => 0,
'options' => [ 'options' => [
0 => __('Debug Off'), false => __('Debug Off'),
1 => __('Debug On'), true => __('Debug On'),
2 => __('Debug On + SQL Dump'),
], ],
'test' => function ($value, $setting, $validator) { 'test' => function ($value, $setting, $validator) {
$validator->range('value', [0, 2]); $validator->range('value', [0, 2]);

View File

@ -126,7 +126,7 @@ class SettingsTable extends AppTable
return $settings; return $settings;
} }
private function loadSettings(): void public function loadSettings(): void
{ {
$settings = file_get_contents(CONFIG . 'config.json'); $settings = file_get_contents(CONFIG . 'config.json');
$settings = json_decode($settings, true); $settings = json_decode($settings, true);

View File

@ -293,8 +293,18 @@ class UsersTable extends AppTable
return true; return true;
} }
public function getAllOrganisations($currentUser) { public function getAllOrganisations(\App\Model\Entity\User $currentUser)
{
$this->Individuals = TableRegistry::get('Individuals'); $this->Individuals = TableRegistry::get('Individuals');
return $this->Individuals->getAllOrganisations($currentUser); 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']];
}
}
} }

View File

@ -86,7 +86,7 @@ echo $this->element('genericElements/IndexTable/index_table', [
'data_path' => 'origin', 'data_path' => 'origin',
], ],
[ [
'name' => 'user', 'name' => __('to'),
'sort' => 'Inbox.user_id', 'sort' => 'Inbox.user_id',
'data_path' => 'user', 'data_path' => 'user',
'element' => 'user' 'element' => 'user'

View File

@ -109,10 +109,13 @@ echo $this->element('genericElements/IndexTable/index_table', [
'modal_params_data_path' => 'id', 'modal_params_data_path' => 'id',
'icon' => 'edit', 'icon' => 'edit',
'complex_requirement' => [ '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'])) { if ($loggedUser['role']['perm_admin'] || ($loggedUser['role']['perm_org_admin'] && $row['id'] == $loggedUser['organisation']['id'])) {
return true; return true;
} }
if ($loggedUser['role']['perm_group_admin'] && in_array($row['id'], $validOrgs)) {
return true;
}
return false; return false;
} }
] ]

View File

@ -53,6 +53,7 @@
'type' => 'number', 'type' => 'number',
'min' => '0', 'min' => '0',
'step' => 1, 'step' => 1,
'value' => isset($setting['value']) ? h($setting['value']) : '',
'id' => h($settingId), 'id' => h($settingId),
'data-setting-name' => h($settingName), 'data-setting-name' => h($settingName),
'aria-describedby' => h("{$settingId}Help") 'aria-describedby' => h("{$settingId}Help")