From 633ae86886ca2c8f53d4c702a8ca0c0143104b25 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 29 Aug 2023 14:53:03 +0200 Subject: [PATCH 1/7] chg: [command:summary] Added support of user MetaFields Allow to show addition and deletion of user metafields such as the ones used for permissions --- src/Command/SummaryCommand.php | 77 ++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/src/Command/SummaryCommand.php b/src/Command/SummaryCommand.php index cf5debf..c922e02 100644 --- a/src/Command/SummaryCommand.php +++ b/src/Command/SummaryCommand.php @@ -80,7 +80,14 @@ class SummaryCommand extends Command fwrite($file_input, $message); $this->io->out($message); $logsUsers = $this->_fetchLogsForUsers($userID, $days); - $modifiedUsers = $this->_formatLogsForTable($logsUsers); + $userByIDs = Hash::combine($userForOrg, '{n}.id', '{n}'); + $logsUserMetaFields = $this->_fetchLogsForUserMetaFields($userID, $days); + $logsUserMetaFields = $this->_formatUserMetafieldLogs($logsUserMetaFields, $userByIDs); + $logsUsersCombined = array_merge($logsUsers, $logsUserMetaFields); + usort($logsUsersCombined, function($a, $b) { + return $a['created'] < $b['created'] ? -1 : 1; + }); + $modifiedUsers = $this->_formatLogsForTable($logsUsersCombined); foreach ($modifiedUsers as $row) { fputcsv($file_input, $row); } @@ -167,6 +174,45 @@ class SummaryCommand extends Command ]); } + protected function _fetchLogsForUserMetaFields(array $userIDs = [], int $days=7): array + { + if (empty($userIDs)) { + return []; + } + $logs = $this->_fetchLogs([ + 'contain' => ['Users'], + 'conditions' => [ + 'model' => 'MetaFields', + 'request_action IN' => ['add', 'edit', 'delete'], + 'AuditLogs.created >=' => FrozenTime::now()->subDays($days), + ] + ]); + $metaFieldLogs = array_filter($logs, function ($log) use ($userIDs) { + return !empty($log['changed']['scope']) && $log['changed']['scope'] === 'user' && in_array($log['changed']['parent_id'], $userIDs); + }); + $metaFieldDeletionLogs = array_filter($logs, function ($log) use ($userIDs) { + return $log['request_action'] === 'delete'; + }); + foreach ($metaFieldDeletionLogs as $i => $log) { + $latestAssociatedLog = $this->_fetchLogs([ + 'contain' => ['Users'], + 'conditions' => [ + 'model' => 'MetaFields', + 'request_action IN' => ['add'], + 'model_id' => $log['model_id'], + ], + 'order' => ['AuditLogs.created' => 'DESC'], + 'limit' => 1, + ]); + if (!empty($latestAssociatedLog)) { + $metaFieldDeletionLogs[$i]['changed']['orig_value'] = $latestAssociatedLog[0]['changed']['value']; + $metaFieldDeletionLogs[$i]['changed']['value'] = ''; + } + } + $allLogs = array_merge($metaFieldLogs, $metaFieldDeletionLogs); + return $allLogs; + } + protected function _fetchLogsForOrgs(array $orgIDs = [], int $days = 7): array { if (empty($orgIDs)) { @@ -201,18 +247,41 @@ class SummaryCommand extends Command protected function _fetchLogs(array $options=[]): array { - $logs = $this->AuditLogs->find() + $query = $this->AuditLogs->find() ->contain($options['contain']) - ->where($options['conditions']) + ->where($options['conditions']); + if (!empty($options['order'])) { + $query = $query->order($options['order']); + } + if (!empty($options['limit'])) { + $query = $query + ->limit($options['limit']) + ->page(1); + } + $logs = $query ->enableHydration(false) ->all()->toList(); return array_map(function ($log) { $log['changed'] = is_resource($log['changed']) ? stream_get_contents($log['changed']) : $log['changed']; - $log['changed'] = json_decode($log['changed']); + $log['changed'] = json_decode($log['changed'], true); return $log; }, $logs); } + protected function _formatUserMetafieldLogs($logEntries, $userByIDs): array + { + return array_map(function($log) use ($userByIDs) { + $log['model'] = 'Users'; + $log['changed'] = [ + $log['model_title'] => [ + $log['changed']['orig_value'] ?? '', + $log['changed']['value'] + ] + ]; + return $log; + }, $logEntries); + } + protected function _formatLogsForTable($logEntries): array { $header = ['Model', 'Action', 'Editor user', 'Log ID', 'Datetime', 'Change']; From 1ea7c796ac85f91e21210357729744049cfa4743 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 29 Aug 2023 14:57:48 +0200 Subject: [PATCH 2/7] chg: [component:CRUD] Include meta-fields in REST queries and clever pagination support for REST queries --- src/Controller/Component/CRUDComponent.php | 8 ++++++-- .../ListTopBar/group_table_action/numberOfElement.php | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php index 9891b20..a1ecba1 100644 --- a/src/Controller/Component/CRUDComponent.php +++ b/src/Controller/Component/CRUDComponent.php @@ -96,17 +96,18 @@ class CRUDComponent extends Component $query->order($sort . ' ' . $direction); } } - if ($this->metaFieldsSupported() && !$this->Controller->ParamHandler->isRest()) { + if ($this->metaFieldsSupported()) { $query = $this->includeRequestedMetaFields($query); } if (!$this->Controller->ParamHandler->isRest()) { $this->setRequestedEntryAmount(); + } else if (!empty($this->request->getQuery('limit'))) { + $this->Controller->paginate['limit'] = PHP_INT_MAX; // Make sure to download the entire filtered table } $data = $this->Controller->paginate($query, $this->Controller->paginate ?? []); $totalCount = $this->Controller->getRequest()->getAttribute('paging')[$this->TableAlias]['count']; if ($this->Controller->ParamHandler->isRest()) { - $data = $this->Controller->paginate($query, $this->Controller->paginate ?? []); if (isset($options['hidden'])) { $data->each(function($value, $key) use ($options) { $hidden = is_array($options['hidden']) ? $options['hidden'] : [$options['hidden']]; @@ -795,6 +796,9 @@ class CRUDComponent extends Component $user = $this->Controller->ACL->getUser(); $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, sure not to return too much data + } $this->Controller->paginate['limit'] = intval($tableSettings['number_of_element']); } } diff --git a/templates/element/genericElements/ListTopBar/group_table_action/numberOfElement.php b/templates/element/genericElements/ListTopBar/group_table_action/numberOfElement.php index 6b69c50..e3913d1 100644 --- a/templates/element/genericElements/ListTopBar/group_table_action/numberOfElement.php +++ b/templates/element/genericElements/ListTopBar/group_table_action/numberOfElement.php @@ -10,6 +10,7 @@ $numberOfElementSelectSeed = 'seed-' . mt_rand(); + From 279c69c5106d677535f73b1f9df8ae8e19675882 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Mon, 4 Sep 2023 09:34:44 +0200 Subject: [PATCH 3/7] fix: [userSettings:edit] Correctly pre-select user to be edited --- src/Controller/UserSettingsController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller/UserSettingsController.php b/src/Controller/UserSettingsController.php index 7f9690a..b6d8ecd 100644 --- a/src/Controller/UserSettingsController.php +++ b/src/Controller/UserSettingsController.php @@ -114,7 +114,7 @@ class UserSettingsController extends AppController $dropdownData = [ 'user' => $this->UserSettings->Users->find('list', [ 'sort' => ['username' => 'asc'] - ]), + ])->toArray(), ]; $this->set(compact('dropdownData')); $this->set('user_id', $this->entity->user_id); From 9be81055651649658243b5aa274b175064bfc6db Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Mon, 4 Sep 2023 10:30:51 +0200 Subject: [PATCH 4/7] chg: [config] Force usage of secure cookie for session and csrf protection --- config/bootstrap.php | 8 ++++++++ config/routes.php | 1 + 2 files changed, 9 insertions(+) diff --git a/config/bootstrap.php b/config/bootstrap.php index 739122d..6da31ba 100644 --- a/config/bootstrap.php +++ b/config/bootstrap.php @@ -173,6 +173,14 @@ Mailer::setConfig(Configure::consume('Email')); Log::setConfig(Configure::consume('Log')); Security::setSalt(Configure::consume('Security.salt')); +Configure::write('Session', [ + 'defaults' => 'php', + 'ini' => [ + 'session.cookie_httponly' => true, + 'session.cookie_secure' => true, + ] +]); + /* * Setup detectors for mobile and tablet. */ diff --git a/config/routes.php b/config/routes.php index ac1ab26..32712cd 100644 --- a/config/routes.php +++ b/config/routes.php @@ -49,6 +49,7 @@ $routes->scope('/', function (RouteBuilder $builder) { // Register scoped middleware for in scopes. $builder->registerMiddleware('csrf', new CsrfProtectionMiddleware([ 'httponly' => true, + 'secure' => true, ])); /* * Apply a middleware to the current route scope. From 26cbaf01296f12ac70f65ffe0a81309dca55fd05 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Mon, 4 Sep 2023 14:43:53 +0200 Subject: [PATCH 5/7] chg: [command:summary] Consider perm meta-fields addition/deletion as uesr edit --- src/Command/SummaryCommand.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Command/SummaryCommand.php b/src/Command/SummaryCommand.php index c922e02..f7622a1 100644 --- a/src/Command/SummaryCommand.php +++ b/src/Command/SummaryCommand.php @@ -272,6 +272,7 @@ class SummaryCommand extends Command { return array_map(function($log) use ($userByIDs) { $log['model'] = 'Users'; + $log['request_action'] = 'edit'; $log['changed'] = [ $log['model_title'] => [ $log['changed']['orig_value'] ?? '', From 3e5ae5271e2a6460407fd70c1cacbdcf0715d33a Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 4 Sep 2023 15:23:21 +0200 Subject: [PATCH 6/7] chg: [misisng] change --- src/Controller/Component/NavigationComponent.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Controller/Component/NavigationComponent.php b/src/Controller/Component/NavigationComponent.php index 3c606e8..452d575 100644 --- a/src/Controller/Component/NavigationComponent.php +++ b/src/Controller/Component/NavigationComponent.php @@ -37,6 +37,7 @@ class NavigationComponent extends Component 'Instance' => 'server', 'Tags' => 'tags', 'API' => 'code', + 'EnumerationCollections' => 'list', ]; public function initialize(array $config): void @@ -163,6 +164,7 @@ class NavigationComponent extends Component 'LocalTools', 'UserSettings', 'MailingLists', + 'EnumerationCollections', ]; foreach ($CRUDControllers as $controller) { $bcf->setDefaultCRUDForModel($controller); From 012e120db218ee178d269cd2b6cc19aa822e3284 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 4 Sep 2023 15:25:04 +0200 Subject: [PATCH 7/7] chg: [version] bump --- src/VERSION.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VERSION.json b/src/VERSION.json index 0b24643..423dd7e 100644 --- a/src/VERSION.json +++ b/src/VERSION.json @@ -1,4 +1,4 @@ { - "version": "1.14", + "version": "1.15", "application": "Cerebrate" }