From 0d7199cb085cac3a60f59e70d9c0346838bec7a2 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 6 Apr 2020 10:14:45 +0200 Subject: [PATCH 01/22] fix: [events:export-csv] Default to_ids to be 1 --- app/Controller/EventsController.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 2757642b5..73e562877 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -3087,7 +3087,8 @@ class EventsController extends AppController 'named_params' => $this->params['named'], 'ordered_url_params' => func_get_args(), 'injectedParams' => array( - 'returnFormat' => 'csv' + 'returnFormat' => 'csv', + 'to_ids' => '1' ) )); return $this->restSearch(); From d5f5552d912e2a6e78eeea350bf9eaee828b771e Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 6 Apr 2020 11:49:39 +0200 Subject: [PATCH 02/22] chg: [events:exports] Migrated majority of export type to use restSearch --- app/Controller/Component/RestSearchComponent.php | 2 +- app/Controller/EventsController.php | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/Controller/Component/RestSearchComponent.php b/app/Controller/Component/RestSearchComponent.php index 62645efc1..420c6a9c3 100644 --- a/app/Controller/Component/RestSearchComponent.php +++ b/app/Controller/Component/RestSearchComponent.php @@ -15,7 +15,7 @@ class RestSearchComponent extends Component 'Event' => array( 'returnFormat', 'value', 'type', 'category', 'org', 'tags', 'searchall', 'from', 'to', 'last', 'eventid', 'withAttachments', 'metadata', 'uuid', 'publish_timestamp', 'timestamp', 'published', 'enforceWarninglist', 'sgReferenceOnly', - 'limit', 'page', 'requested_attributes', 'includeContext', 'headerless', 'includeWarninglistHits', 'attackGalaxy', 'deleted', + 'limit', 'page', 'requested_attributes', 'includeContext', 'headerless', 'includeWarninglistHits', 'attackGalaxy', 'to_ids', 'deleted', 'excludeLocalTags', 'date', 'includeSightingdb', 'tag', 'object_relation' ), 'Object' => array( diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 73e562877..857300987 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -3088,7 +3088,8 @@ class EventsController extends AppController 'ordered_url_params' => func_get_args(), 'injectedParams' => array( 'returnFormat' => 'csv', - 'to_ids' => '1' + 'to_ids' => '1', + 'published' => '1' ) )); return $this->restSearch(); @@ -3996,26 +3997,26 @@ class EventsController extends AppController 'checkbox_default' => true ), 'openIOC' => array( - 'url' => '/events/downloadOpenIOCEvent/download/' . $id, + 'url' => '/events/restSearch/openioc/to_ids:1/published:1/eventid:' . $id . '.json', 'text' => 'OpenIOC (all indicators marked to IDS)', 'requiresPublished' => false, 'checkbox' => false, ), 'csv' => array( - 'url' => '/events/csv/download/' . $id, + 'url' => '/events/restSearch/returnFormat:csv/to_ids:1/published:1/includeContext:0/eventid:' . $id, 'text' => 'CSV', 'requiresPublished' => false, 'checkbox' => true, 'checkbox_text' => 'Include non-IDS marked attributes', - 'checkbox_set' => '/events/csv/download/' . $id . '/1' + 'checkbox_set' => '/events/restSearch/returnFormat:csv/to_ids:1||0/published:1||0/includeContext:0/eventid:' . $id ), 'csv_with_context' => array( - 'url' => '/events/restSearch/returnFormat:csv/eventid:' . $id, + 'url' => '/events/restSearch/returnFormat:csv/to_ids:1/published:1/includeContext:1/eventid:' . $id, 'text' => 'CSV with additional context', 'requiresPublished' => false, 'checkbox' => true, 'checkbox_text' => 'Include non-IDS marked attributes', - 'checkbox_set' => '/events/restSearch/returnFormat:csv/to_ids:1||0/published:1||0/eventid:' . $id + 'checkbox_set' => '/events/restSearch/returnFormat:csv/to_ids:1||0/published:1||0/includeContext:1/eventid:' . $id ), 'stix_xml' => array( 'url' => '/events/restSearch/stix/eventid:' . $id, @@ -4061,6 +4062,7 @@ class EventsController extends AppController ), 'bro' => array( 'url' => '/attributes/bro/download/all/false/' . $id, + // 'url' => '/attributes/restSearch/returnFormat:bro/published:1||0/eventid:' . $id, 'text' => 'Download Bro rules', 'requiresPublished' => false, 'checkbox' => false @@ -4081,7 +4083,7 @@ class EventsController extends AppController } } $exports['csv'] = array( - 'url' => '/events/csv/download/' . $id . '/1', + 'url' => '/events/restSearch/returnFormat:csv/includeContext:0/eventid:' . $id, 'text' => 'CSV (event not published, IDS flag ignored)', 'requiresPublished' => false, 'checkbox' => false From 27ac2aa05f64fc23cc40a4cd15caf2f318243eac Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 6 Apr 2020 13:57:55 +0200 Subject: [PATCH 03/22] fix: [console:admin] getSetting can be used to retrieve all settings --- app/Console/Command/AdminShell.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Console/Command/AdminShell.php b/app/Console/Command/AdminShell.php index 28887411b..a35bdfac3 100644 --- a/app/Console/Command/AdminShell.php +++ b/app/Console/Command/AdminShell.php @@ -291,7 +291,7 @@ class AdminShell extends AppShell $param = empty($this->args[0]) ? 'all' : $this->args[0]; $settings = $this->Server->serverSettingsRead(); $result = $settings; - if (!empty($param)) { + if ($param != 'all') { $result = 'No valid setting found for ' . $param; foreach ($settings as $setting) { if ($setting['setting'] == $param) { From 75d6a4b8296b061dbff9d919ee27ac91733d188d Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 6 Apr 2020 22:28:30 +0200 Subject: [PATCH 04/22] chg: [cakephp] version bump to get TLS 1.3 support, fixes #5764 - #yolo --- app/Lib/cakephp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Lib/cakephp b/app/Lib/cakephp index d2e1681eb..5ccb12354 160000 --- a/app/Lib/cakephp +++ b/app/Lib/cakephp @@ -1 +1 @@ -Subproject commit d2e1681eb8ec75e6c2819fa113834843fed6995a +Subproject commit 5ccb12354dfc08ca1b3e0a430e8668bf1610b5d3 From 1604096a26deec72ddefcd8df29d44b473d463b4 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 7 Apr 2020 06:14:39 +0200 Subject: [PATCH 05/22] chg: [warninglists] bump --- app/files/warninglists | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/files/warninglists b/app/files/warninglists index bad8b17ff..28687d90d 160000 --- a/app/files/warninglists +++ b/app/files/warninglists @@ -1 +1 @@ -Subproject commit bad8b17fffd059f8fd739ce16fc885d79e749022 +Subproject commit 28687d90d575332776480cd5d683361e7485033c From 4ebc0a7988c605e5c69fc3558a63a67727789a58 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 7 Apr 2020 13:21:01 +0200 Subject: [PATCH 06/22] new: [inbox] system added - user self-registration is the first use-case - if the feature is enabled, users can unauthenticated send a registration request to MISP - request includes information on desired org and some privileges (sync / org admin / publisher) - requests land in the inbox, admins can inspect the registration requests - they can accept/discard them individually or en masse - users will be notified of their credentials automatically - quick user creation if the user asks for an org that doesn't exist yet --- app/Controller/AppController.php | 5 +- app/Controller/Component/ACLComponent.php | 4 + app/Controller/OrganisationsController.php | 7 + app/Controller/UsersController.php | 262 +++++++++++++++++- app/Model/AppModel.php | 25 +- app/Model/Inbox.php | 28 ++ app/Model/Log.php | 3 + app/Model/Organisation.php | 25 ++ app/Model/Role.php | 30 ++ app/Model/Server.php | 18 ++ app/Model/User.php | 47 ++++ .../genericElements/Form/genericForm.ctp | 6 +- .../IndexTable/Fields/list.ctp | 12 +- .../genericElements/IndexTable/headers.ctp | 13 +- .../genericElements/SideMenu/side_menu.ctp | 9 + app/View/Elements/global_menu.ctp | 4 + app/View/Inbox/index.ctp | 64 +++++ app/View/Users/accept_registrations.ctp | 106 +++++++ app/View/Users/ajax/discardRegistrations.ctp | 15 + app/View/Users/login.ctp | 20 +- app/View/Users/register.ctp | 82 ++++++ app/View/Users/registrations.ctp | 138 +++++++++ app/webroot/css/main.css | 7 + app/webroot/js/misp.js | 13 +- 24 files changed, 920 insertions(+), 23 deletions(-) create mode 100644 app/Model/Inbox.php create mode 100644 app/View/Inbox/index.ctp create mode 100644 app/View/Users/accept_registrations.ctp create mode 100644 app/View/Users/ajax/discardRegistrations.ctp create mode 100644 app/View/Users/register.ctp create mode 100644 app/View/Users/registrations.ctp diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php index ed302bc3c..15bcecc51 100755 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -46,7 +46,7 @@ class AppController extends Controller public $helpers = array('Utility', 'OrgImg', 'FontAwesome', 'UserName'); - private $__queryVersion = '101'; + private $__queryVersion = '102'; public $pyMispVersion = '2.4.123'; public $phpmin = '7.2'; public $phprec = '7.4'; @@ -120,7 +120,6 @@ class AppController extends Controller } else { $this->Auth->logoutRedirect = Configure::read('MISP.baseurl') . '/users/login'; } - $this->__sessionMassage(); if (Configure::read('Security.allow_cors')) { // Add CORS headers @@ -352,7 +351,7 @@ class AppController extends Controller } } } else { - if (!($this->params['controller'] === 'users' && $this->params['action'] === 'login')) { + if ($this->params['controller'] !== 'users' || !in_array($this->params['action'], array('login', 'register'))) { if (!$this->request->is('ajax')) { $this->Session->write('pre_login_requested_url', $this->here); } diff --git a/app/Controller/Component/ACLComponent.php b/app/Controller/Component/ACLComponent.php index 2fb2be9b3..f761275b3 100644 --- a/app/Controller/Component/ACLComponent.php +++ b/app/Controller/Component/ACLComponent.php @@ -549,6 +549,7 @@ class ACLComponent extends Component 'viewEvent' => array('*'), ), 'users' => array( + 'acceptRegistrations' => array('perm_site_admin'), 'admin_add' => array('perm_admin'), 'admin_delete' => array('perm_admin'), 'admin_edit' => array('perm_admin'), @@ -564,6 +565,7 @@ class ACLComponent extends Component 'checkIfLoggedIn' => array('*'), 'dashboard' => array('*'), 'delete' => array('perm_admin'), + 'discardRegistrations' => array('perm_site_admin'), 'downloadTerms' => array('*'), 'edit' => array('*'), 'searchGpgKey' => array('*'), @@ -572,6 +574,8 @@ class ACLComponent extends Component 'initiatePasswordReset' => array('perm_admin'), 'login' => array('*'), 'logout' => array('*'), + 'register' => array('*'), + 'registrations' => array('perm_site_admin'), 'resetAllSyncAuthKeys' => array(), 'resetauthkey' => array('*'), 'request_API' => array('*'), diff --git a/app/Controller/OrganisationsController.php b/app/Controller/OrganisationsController.php index f26c44f6c..400f3b39f 100644 --- a/app/Controller/OrganisationsController.php +++ b/app/Controller/OrganisationsController.php @@ -143,6 +143,13 @@ class OrganisationsController extends AppController } else { if ($this->_isRest()) { return $this->RestResponse->describe('Organisations', 'admin_add', false, $this->response->type()); + } else { + if (!empty($this->params['named']['name'])) { + $this->request->data['Organisation']['name'] = $this->params['named']['name']; + } + if (!empty($this->params['named']['uuid'])) { + $this->request->data['Organisation']['uuid'] = $this->params['named']['uuid']; + } } } $this->set('countries', $this->_arrayToValuesIndexArray($this->Organisation->countries)); diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php index 9ee5f1051..85987e290 100644 --- a/app/Controller/UsersController.php +++ b/app/Controller/UsersController.php @@ -30,7 +30,11 @@ class UsersController extends AppController parent::beforeFilter(); // what pages are allowed for non-logged-in users - $this->Auth->allow('login', 'logout'); + $allowedActions = array('login', 'logout'); + if (!empty(Configure::read('Security.allow_self_registration'))) { + $allowedActions[] = 'register'; + } + $this->Auth->allow($allowedActions); } public function view($id = null) @@ -438,7 +442,7 @@ class UsersController extends AppController $users[$key]['User']['authkey'] = __('Redacted'); } } else if (!empty(Configure::read('Security.user_monitoring_enabled'))) { - $users[$key]['User']['monitored'] = $redis->sismember('misp:monitored_users', $id); + $users[$key]['User']['monitored'] = $redis->sismember('misp:monitored_users', $value['User']['id']); } unset($users[$key]['User']['password']); } @@ -2240,4 +2244,258 @@ class UsersController extends AppController } } } + + public function register() + { + if (empty(Configure::read('Security.allow_self_registration'))) { + throw new MethodNotAllowedException(__('Self registration is not enabled on this instance.')); + } + if ($this->request->is('post')) { + if (isset($this->request->data['User'])) { + $this->request->data = $this->request->data['User']; + } + $validKeys = array( + 'email', + 'org_name', + 'org_uuid', + 'message', + 'custom_perms', + 'perm_sync', + 'perm_publish', + 'perm_admin' + ); + $requestObject = array(); + foreach ($validKeys as $key) { + if (isset($this->request->data[$key])) { + $requestObject[$key] = trim($this->request->data[$key]); + } + } + if (empty($requestObject['email']) || empty($requestObject['org_name'])) { + throw new InvalidArgumentException(__('We require at least the email and org_name fields to be filled.')); + } + $this->loadModel('Inbox'); + $this->Inbox->create(); + $data = array( + 'Inbox' => array( + 'title' => __('User registration for %s.', $requestObject['email']), + 'type' => 'registration', + 'comment' => $requestObject['message'], + 'data' => json_encode($requestObject) + ) + ); + $result = $this->Inbox->save($data); + if (empty($result)) { + $message = __('Request could not be created. Make sure that the email and org name fields are filled.'); + if ($this->_isRest()) { + return $this->RestResponse->saveFailResponse('Users', 'register', false, $message, $this->response->type()); + } else { + $this->Flash->error($message); + } + } else { + $message = __('Request sent. The administrators of this community have been notified.'); + if ($this->_isRest()) { + return $this->RestResponse->saveSuccessResponse('User', 'register', false, $this->response->type(), $message); + } else { + $this->Flash->success($message); + $this->redirect('/'); + } + } + } else { + $message = Configure::read('Security.self_registration_message'); + if (empty($message)) { + $this->loadModel('Server'); + $message = $this->Server->serverSettings['Security']['self_registration_message']['value']; + } + $this->set('message', $message); + } + } + + public function registrations() + { + $this->loadModel('Inbox'); + $params = array( + 'recursive' => -1, + 'conditions' => array( + 'deleted' => 0, + 'type' => 'registration' + ), + 'order' => array( + 'timestamp desc' + ) + ); + $passedArgs = $this->passedArgs; + if (!empty($passedArgs['value'])) { + $lookup = strtolower($passedArgs['value']); + $allSearchFields = array('data', 'user_agent', 'ip'); + foreach ($allSearchFields as $field) { + $params['conditions']['AND']['OR'][] = array('LOWER(Inbox.' . $field . ') LIKE' => '%' . $lookup . '%'); + } + } + $this->set('passedArgs', json_encode($passedArgs)); + if ($this->_isRest()) { + $data = $this->Inbox->find('all', array( + 'recursive' => -1, + 'conditions' => $params['conditions'] + )); + foreach ($data as $k => $v) { + $data[$k]['Inbox']['data'] = json_decode($data[$k]['Inbox']['data'], true); + } + return $this->RestResponse->viewData($data, $this->response->type()); + } else { + $this->paginate = $params; + $data = $this->paginate('Inbox'); + foreach ($data as $k => $message) { + $data[$k]['Inbox']['data'] = json_decode($data[$k]['Inbox']['data'], true); + $data[$k]['Inbox']['requested_role'] = __('default'); + if (!empty($data[$k]['Inbox']['data']['custom_perms'])) { + $data[$k]['Inbox']['requested_role'] = array( + 'perm_publish' => !empty($data[$k]['Inbox']['data']['perm_publish']) ? __('Yes') : __('No'), + 'perm_sync' => !empty($data[$k]['Inbox']['data']['perm_sync']) ? __('Yes') : __('No'), + 'perm_admin' => !empty($data[$k]['Inbox']['data']['perm_admin']) ? __('Yes') : __('No') + ); + } + } + $this->set('data', $data); + } + } + + public function discardRegistrations($id = false) + { + if (!$this->request->is('post') && !$this->request->is('delete')) { + $this->set('id', $id); + $this->set('type', 'discardRegistrations'); + $this->render('ajax/discardRegistrations'); + } else { + if (empty($id) && !empty($this->params['named']['id'])) { + $id = $this->params['named']['id']; + } + $this->loadModel('Inbox'); + $registrations = $this->Inbox->find('all', array( + 'recursive' => -1, + 'conditions' => array( + 'deleted' => 0, + 'type' => 'registration', + 'id' => $id + ) + )); + foreach ($registrations as $registration) { + $this->Inbox->delete($registration['Inbox']['id']); + } + $message = sprintf( + '%s registration(s) discarded.', + count($registrations) + ); + if ($this->_isRest()) { + return $this->RestResponse->saveSuccessResponse('User', 'discardRegistrations', false, $this->response->type(), $message); + } else { + $this->Flash->success($message); + $this->redirect(array('controller' => 'users', 'action' => 'registrations')); + } + } + } + + public function acceptRegistrations($id = false) + { + if (empty($id) && !empty($this->params['named']['id'])) { + $id = $this->params['named']['id']; + } + $this->loadModel('Inbox'); + $registrations = $this->Inbox->find('all', array( + 'recursive' => -1, + 'conditions' => array( + 'deleted' => 0, + 'type' => 'registration', + 'id' => $id + ) + )); + $suggestedOrg = null; + $suggestedRole = null; + $orgCache = array(); + foreach ($registrations as $k => $v) { + $registrations[$k]['Inbox']['data'] = json_decode($registrations[$k]['Inbox']['data'], true); + $roleRequirements = array(); + if ($this->request->is('get')) { + $suggestedOrg = $this->User->Organisation->checkDesiredOrg($suggestedOrg, $registrations[$k]); + $suggestedRole = $this->User->Role->checkDesiredRole($suggestedRole, $registrations[$k]); + } + } + if ($this->request->is('get')) { + if (!is_array($id)) { + $id = array($id); + } + foreach ($id as $k => $v) { + $id[$k] = 'id[]:' . intval($v); + } + $roles_raw = $this->User->Role->find('all', array( + 'recursive' => -1 + )); + //roles = id => name + $roles = array(); + $role_perms = array(); + foreach ($roles_raw as $role) { + $roles[$role['Role']['id']] = $role['Role']['name']; + $role_perms[$role['Role']['id']] = array( + 'perm_publish' => $role['Role']['perm_publish'], + 'perm_sync' => $role['Role']['perm_sync'], + 'perm_admin' => $role['Role']['perm_admin'] + ); + } + $default_role = $this->User->Role->find('first', array( + 'recursive' => -1, + 'conditions' => array('Role.default_role' => 1), + 'fields' => array('Role.id') + )); + if (!empty($default_role)) { + $this->request->data['User']['role_id'] = $default_role['Role']['id']; + } + $this->set('roles', $roles); + $this->set('role_perms', $role_perms); + $this->set('orgs', $this->User->Organisation->find('list', array( + 'fields' => array('id', 'name'), + 'recursive' => -1, + 'conditions' => array('local' => 1) + ))); + $this->set('registration', $registrations[$k]); + $this->set('suggestedOrg', $suggestedOrg); + $this->set('suggestedRole', $suggestedRole); + $id = implode('/', $id); + $this->set('id', $id); + $this->layout = false; + } else { + $results = array('successes' => 0, 'fails' => 0); + foreach ($registrations as $registration) { + $result = $this->User->registerUser( + $this->Auth->user(), + $registration['Inbox'], + $this->request->data['User']['org_id'], + $this->request->data['User']['role_id'] + ); + $results[($result ? 'successes' : 'fails')] += 1; + } + $message = array(); + if (!empty($results['successes'])) { + $message[] = __('Added %s user(s).', $results['successes']); + } + if (!empty($results['fails'])) { + $message[] = __('Could not add %s user(s), reasons for the failure have been logged.', $results['fails']); + } + if (empty($message)) { + $message[] = __('No new users added - there was nothing to add.'); + } + $message = implode(' ', $message); + if ($this->_isRest()) { + if (empty($results['fails']) && !empty($results['successes'])) { + return $this->RestResponse->saveSuccessResponse('User', 'acceptRegistrations', false, $this->response->type(), $message); + } else { + return $this->RestResponse->saveFailResponse('Users', 'acceptRegistrations', false, $message, $this->response->type()); + } + } else { + if (empty($results['fails']) && !empty($results['successes'])) { + return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'errors' => $message)), 'status'=>200, 'type' => 'json')); + } else { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'success' => $message)), 'status'=>200, 'type' => 'json')); + } + } + } + } } diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index a2fbbd98d..b7c9ba417 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -77,7 +77,7 @@ class AppModel extends Model 27 => false, 28 => false, 29 => false, 30 => false, 31 => false, 32 => false, 33 => false, 34 => false, 35 => false, 36 => false, 37 => false, 38 => false, 39 => false, 40 => false, 41 => false, 42 => false, 43 => false, 44 => false, - 45 => false, 46 => false, 47 => false, 48 => false, 49 => false + 45 => false, 46 => false, 47 => false, 48 => false, 49 => false, 50 => false ); public $advanced_updates_description = array( @@ -1349,6 +1349,29 @@ class AppModel extends Model INDEX `restrict_to_permission_flag` (`restrict_to_permission_flag`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"; break; + case 50: + $sqlArray[] = "CREATE TABLE IF NOT EXISTS inbox ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) COLLATE utf8_bin NOT NULL, + `title` varchar(191) NOT NULL, + `type` varchar(191) NOT NULL, + `ip` varchar(191) NOT NULL, + `user_agent` text, + `user_agent_sha256` varchar(64) NOT NULL, + `comment` text, + `deleted` tinyint(1) NOT NULL DEFAULT 0, + `timestamp` int(11) NOT NULL, + `store_as_file` tinyint(1) NOT NULL DEFAULT 0, + `data` longtext, + PRIMARY KEY (id), + INDEX `title` (`title`), + INDEX `type` (`type`), + INDEX `uuid` (`uuid`), + INDEX `user_agent_sha256` (`user_agent_sha256`), + INDEX `ip` (`ip`), + INDEX `timestamp` (`timestamp`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"; + break; case 'fixNonEmptySharingGroupID': $sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;'; $sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;'; diff --git a/app/Model/Inbox.php b/app/Model/Inbox.php new file mode 100644 index 000000000..c3f1d45f5 --- /dev/null +++ b/app/Model/Inbox.php @@ -0,0 +1,28 @@ +data['Inbox']['uuid'] = CakeText::uuid(); + $this->data['Inbox']['timestamp'] = time(); + $this->data['Inbox']['ip'] = $_SERVER['REMOTE_ADDR']; + $this->data['Inbox']['user_agent'] = $_SERVER['HTTP_USER_AGENT']; + $this->data['Inbox']['user_agent_sha256'] = hash('sha256', $_SERVER['HTTP_USER_AGENT']); + return true; + } + + +} diff --git a/app/Model/Log.php b/app/Model/Log.php index 4df47f92e..05f3e36e2 100644 --- a/app/Model/Log.php +++ b/app/Model/Log.php @@ -21,6 +21,7 @@ class Log extends AppModel array( // ensure that the length of the rules is < 20 in length 'accept', 'accept_delegation', + 'acceptRegistrations', 'add', 'admin_email', 'auth', @@ -30,11 +31,13 @@ class Log extends AppModel 'delete', 'disable', 'discard', + 'discardRegistrations', 'edit', 'email', 'enable', 'error', 'export', + 'failed_registration', 'file_upload', 'galaxy', 'include_formula', diff --git a/app/Model/Organisation.php b/app/Model/Organisation.php index 3ce126dd9..dd08545bd 100644 --- a/app/Model/Organisation.php +++ b/app/Model/Organisation.php @@ -428,4 +428,29 @@ class Organisation extends AppModel } return array_values($orgIds); } + + public function checkDesiredOrg($suggestedOrg, $registration) + { + if ($suggestedOrg !== false) { + $conditions = array(); + if (!empty($registration['Inbox']['data']['org_uuid'])) { + $conditions = array('Organisation.uuid' => $registration['Inbox']['data']['org_uuid']); + } else if (!empty($registration['Inbox']['data']['org_name'])) { + $conditions = array('Organisation.name' => $registration['Inbox']['data']['org_name']); + } + $identifiedOrg = $this->User->Organisation->find('first', array( + 'recursive' => -1, + 'fields' => array('id', 'name', 'local'), + 'conditions' => $conditions + )); + if (!empty($suggestedOrg) && $suggestedOrg[0] !== $identifiedOrg['Organisation']['id']) { + $suggestedOrg = false; + } else if (empty($identifiedOrg)) { + $suggestedOrg = -1; + } else { + $suggestedOrg = array($identifiedOrg['Organisation']['id'], $identifiedOrg['Organisation']['name'], $identifiedOrg['Organisation']['local']); + } + } + return $suggestedOrg; + } } diff --git a/app/Model/Role.php b/app/Model/Role.php index ac3c13152..6d5e37947 100644 --- a/app/Model/Role.php +++ b/app/Model/Role.php @@ -254,4 +254,34 @@ class Role extends AppModel } return true; } + + /* + * Helper function to find out if a list of registrations has the same role requirements + * If no role requirements have been passed yet, null is assumed for $suggestedRole + * Returns an array with the permission flags required + */ + public function checkDesiredRole($suggestedRole, $registration) + { + if ($suggestedRole !== false) { + $currentRole = array(); + $roleFlags = array('perm_publish', 'perm_admin', 'perm_sync'); + foreach ($roleFlags as $roleFlag) { + if (isset($registration['Inbox']['data'][$roleFlag])) { + $currentRole[$roleFlag] = $registration['Inbox']['data'][$roleFlag]; + } + } + if ($suggestedRole !== null) { + if (count($suggestedRole) != count($currentRole) || !empty(array_diff_key($suggestedRole, $currentRole))) { + return false; + } + foreach (array_keys($currentRole) as $perm) { + if ($currentRole[$perm] !== $suggestedRole[$perm]) { + return false; + } + } + } + return $currentRole; + } + return $suggestedRole; + } } diff --git a/app/Model/Server.php b/app/Model/Server.php index 522c4ea20..f09117fd6 100644 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -1243,6 +1243,24 @@ class Server extends AppModel 'type' => 'boolean', 'null' => true ), + 'allow_self_registration' => array( + 'level' => 1, + 'description' => __('Enabling this setting will allow users to have access to the pre-auth registration form. This will create an inbox entry for administrators to review.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'self_registration_message' => array( + 'level' => 1, + 'bigField' => true, + 'description' => __('The message sent shown to anyone trying to self-register.'), + 'value' => 'If you would like to send us a registration request, please fill out the form below. Make sure you fill out as much information as possible in order to ease the task of the administrators.', + 'errorMessage' => '', + 'test' => false, + 'type' => 'string' + ), 'password_policy_length' => array( 'level' => 2, 'description' => __('Password length requirement. If it is not set or it is set to 0, then the default value is assumed (12).'), diff --git a/app/Model/User.php b/app/Model/User.php index f8019163e..fa5a36366 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -1502,4 +1502,51 @@ class User extends AppModel } Configure::write('Security.monitored', 0); } + + public function registerUser($added_by, $registration, $org_id, $role_id) { + $user = array( + 'email' => $registration['data']['email'], + 'gpgkey' => empty($registration['data']['pgp']) ? '' : $registration['data']['pgp'], + 'disabled' => 0, + 'newsread' => 0, + 'change_pw' => 1, + 'authkey' => $this->generateAuthKey(), + 'termsaccepted' => 0, + 'org_id' => $org_id, + 'role_id' => $role_id, + 'invited_by' => $added_by['id'], + 'contactalert' => 1, + 'autoalert' => Configure::check('MISP.default_publish_alert') ? Configure::read('MISP.default_publish_alert') : 1 + ); + $this->create(); + $this->Log = ClassRegistry::init('Log'); + $result = $this->save(array('User' => $user)); + if (empty($result)) { + $error = array(); + foreach ($this->validationErrors as $key => $errors) { + $error[$key] = $key . ': ' . implode(', ', $errors); + } + $error = implode(PHP_EOL, $error); + $this->Log->save(array( + 'org' => 'SYSTEM', + 'model' => 'User', + 'model_id' => $added_by['id'], + 'email' => $added_by['email'], + 'action' => 'failed_registration', + 'title' => 'User registration failed for ' . $user['email'] . '. Reason(s): ' . $error, + 'change' => null, + )); + return false; + } else { + $user = $this->find('first', array( + 'recursive' => -1, + 'conditions' => array('id' => $this->id) + )); + $this->initiatePasswordReset($user, true, true, false); + $this->Inbox = ClassRegistry::init('Inbox'); + $this->Inbox->delete($registration['id']); + return true; + } + + } } diff --git a/app/View/Elements/genericElements/Form/genericForm.ctp b/app/View/Elements/genericElements/Form/genericForm.ctp index 3d088243c..92bef0440 100644 --- a/app/View/Elements/genericElements/Form/genericForm.ctp +++ b/app/View/Elements/genericElements/Form/genericForm.ctp @@ -17,7 +17,7 @@ h($data['model']); $fieldsString = ''; $simpleFieldWhitelist = array( - 'default', 'type', 'options', 'placeholder', 'label', 'empty', 'rows', 'div' + 'default', 'type', 'options', 'placeholder', 'label', 'empty', 'rows', 'div', 'required' ); $fieldsArrayForPersistence = array(); $formCreate = $this->Form->create($modelForForm); @@ -116,10 +116,12 @@ ); } else { echo sprintf( - '
%s
%s%s%s
%s%s%s
', + '
%s
%s%s
%s
%s
%s%s%s
', + empty($data['skip_side_menu']) ? 'form' : 'menuless-form', $formCreate, empty($data['title']) ? h(Inflector::humanize($this->request->params['action'])) . ' ' . $modelForForm : h($data['title']), $ajaxFlashMessage, + empty($data['description']) ? '' : $data['description'], $fieldsString, $formEnd, $metaFieldString, diff --git a/app/View/Elements/genericElements/IndexTable/Fields/list.ctp b/app/View/Elements/genericElements/IndexTable/Fields/list.ctp index 7b7bb3ac1..f3cd5c331 100644 --- a/app/View/Elements/genericElements/IndexTable/Fields/list.ctp +++ b/app/View/Elements/genericElements/IndexTable/Fields/list.ctp @@ -1,7 +1,15 @@ $element) { + if (!is_numeric($key)) { + $data[$key] = sprintf( + '%s: %s', + h($key), + h($element) + ); + } else { + $data[$key] = h($element); + } } $data = implode('
', $data); echo $data; diff --git a/app/View/Elements/genericElements/IndexTable/headers.ctp b/app/View/Elements/genericElements/IndexTable/headers.ctp index 20d8c7f50..68abf9d51 100644 --- a/app/View/Elements/genericElements/IndexTable/headers.ctp +++ b/app/View/Elements/genericElements/IndexTable/headers.ctp @@ -12,7 +12,7 @@ } else { if (!empty($header['element']) && $header['element'] === 'selector') { $header_data = sprintf( - '', + '', empty($header['select_all_class']) ? 'select_all' : $header['select_all_class'], empty($header['select_all_function']) ? 'onclick="toggleAllAttributeCheckboxes();"' : 'onclick="' . $header['select_all_function'] . '"' ); @@ -38,3 +38,14 @@ $thead .= ''; echo $thead; ?> + diff --git a/app/View/Elements/genericElements/SideMenu/side_menu.ctp b/app/View/Elements/genericElements/SideMenu/side_menu.ctp index 01fdac18a..7d904f288 100644 --- a/app/View/Elements/genericElements/SideMenu/side_menu.ctp +++ b/app/View/Elements/genericElements/SideMenu/side_menu.ctp @@ -734,6 +734,11 @@ 'url' => '/admin/users/index', 'text' => __('List Users') )); + echo $this->element('/genericElements/SideMenu/side_menu_link', array( + 'element_id' => 'registrations', + 'url' => '/users/registrations', + 'text' => __('Pending registrations') + )); } if ($isAdmin) { echo $this->element('/genericElements/SideMenu/side_menu_link', array( @@ -804,6 +809,10 @@ 'url' => '/servers/serverSettings', 'text' => __('Server Settings & Maintenance') )); + echo $this->element('/genericElements/SideMenu/side_menu_link', array( + 'url' => '/inbox', + 'text' => __('Inbox') + )); echo $this->element('/genericElements/SideMenu/side_menu_link', array( 'url' => '/servers/updateProgress', 'text' => __('Update Progress') diff --git a/app/View/Elements/global_menu.ctp b/app/View/Elements/global_menu.ctp index 5e761290f..a1dc73d1f 100755 --- a/app/View/Elements/global_menu.ctp +++ b/app/View/Elements/global_menu.ctp @@ -304,6 +304,10 @@ 'text' => __('Contact Users'), 'url' => '/admin/users/email' ), + array( + 'text' => __('User Registrations'), + 'url' => '/users/registrations' + ), array( 'type' => 'separator' ), diff --git a/app/View/Inbox/index.ctp b/app/View/Inbox/index.ctp new file mode 100644 index 000000000..91096bc7b --- /dev/null +++ b/app/View/Inbox/index.ctp @@ -0,0 +1,64 @@ +'; + echo $this->element('/genericElements/IndexTable/index_table', array( + 'data' => array( + 'data' => $data, + 'top_bar' => array( + 'children' => array( + array( + 'type' => 'search', + 'button' => __('Filter'), + 'placeholder' => __('Enter value to search'), + 'data' => '', + 'searchKey' => 'value' + ) + ) + ), + 'fields' => array( + array( + 'name' => __('Id'), + 'sort' => 'id', + 'class' => 'short', + 'data_path' => 'Inbox.id', + ), + array( + 'name' => __('Type'), + 'sort' => 'type', + 'class' => 'short', + 'data_path' => 'Inbox.type', + ), + array( + 'name' => __('Title'), + 'sort' => 'Inbox.title', + 'data_path' => 'Inbox.title', + ), + array( + 'name' => __('Comment'), + 'data_path' => 'Inbox.comment', + ) + ), + 'title' => __('Instance inbox'), + 'description' => __('You can find messages sent to this instance in the following list. Type denotes the type of request (such as registration). View each entry to see more details about the request\'s contents.'), + 'actions' => array( + array( + 'url' => '/inbox/view', + 'url_params_data_paths' => array( + 'Inbox.uuid' + ), + 'icon' => 'eye' + ), + array( + 'url' => '/inbox/delete', + 'url_params_data_paths' => array( + 'Inbox.uuid' + ), + 'postLink' => 1, + 'postLinkConfirm' => __('Are you sure you want to delete the message from the inbox?'), + 'icon' => 'trash' + ) + ) + ) + )); + echo ''; + echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'administration', 'menuItem' => 'inbox')); +?> diff --git a/app/View/Users/accept_registrations.ctp b/app/View/Users/accept_registrations.ctp new file mode 100644 index 000000000..4e34f34ad --- /dev/null +++ b/app/View/Users/accept_registrations.ctp @@ -0,0 +1,106 @@ +  ' . __('Conflicting requirements') . ''; + } else if ($suggestedOrg === -1){ + $suggestedOrgText = sprintf( + '%s%s%s ', + empty($registration['Inbox']['data']['org_name']) ? '' : h($registration['Inbox']['data']['org_name']) . ' ', + empty($registration['Inbox']['data']['org_uuid']) ? '' : h($registration['Inbox']['data']['org_uuid']) . ' ', + __('Requested organisation not found.'), + $baseurl, + empty($registration['Inbox']['data']['org_name']) ? '' : '/name:' . h($registration['Inbox']['data']['org_name']), + empty($registration['Inbox']['data']['org_uuid']) ? '' : '/uuid:' . h($registration['Inbox']['data']['org_uuid']), + ); + } else { + $suggestedOrgText = sprintf( + '(%s)%s%s', + $suggestedOrg[2] ? 'green' : 'orange', + h($suggestedOrg[0]), + h($suggestedOrg[1]), + $suggestedOrg[2] ? '' : ' - ' . __('known remote organisation, will be converted to local') . '' + ); + } + } + if ($suggestedRole !== null) { + if ($suggestedRole === false) { + $suggestedRoleText = '
  ' . __('Conflicting requirements') . ''; + } else { + foreach ($suggestedRole as $perm_flag => $perm_flag_value) { + $perm_flag_name = substr($perm_flag, 5); + if ($perm_flag_value) { + $suggestedRoleText .= sprintf( + '
  %s ', + h($perm_flag_name), + $perm_flag_value ? '1' : '0', + h($perm_flag_name), + ); + } + } + } + } else { + $suggestedRoleText = '
  ' . __('No preference') . ''; + } + $description = __( + "The requested details were as follows\n\nOrganisation:\n  %s\nRole: %s\n\n", + $suggestedOrgText, + $suggestedRoleText + ); + echo $this->element('genericElements/Form/genericForm', array( + 'form' => $this->Form, + 'data' => array( + 'title' => __('Accept registrations'), + 'description' => nl2br($description), + 'model' => 'User', + 'fields' => array( + array( + 'field' => 'org_id', + 'label' => __('Organisation'), + 'class' => 'input-xxlarge', + 'required' => 1, + 'options' => $orgs, + 'default' => empty($suggestedOrg) ? false : $suggestedOrg[0] + ), + array( + 'field' => 'role_id', + 'label' => __('Role'), + 'class' => 'input-xxlarge', + 'required' => 1, + 'options' => $roles + ) + ), + 'submit' => array( + 'ajaxSubmit' => sprintf( + 'submitPopoverForm(%s, %s, 0, 1)', + "'acceptUserRegistrations'", + "' . $id . '" + ) + ) + ) + )); +?> + + diff --git a/app/View/Users/ajax/discardRegistrations.ctp b/app/View/Users/ajax/discardRegistrations.ctp new file mode 100644 index 000000000..671a64f25 --- /dev/null +++ b/app/View/Users/ajax/discardRegistrations.ctp @@ -0,0 +1,15 @@ +element('genericElements/Form/genericForm', array( + 'form' => $this->Form, + 'data' => array( + 'title' => __('Discard User Registrations'), + 'model' => 'User', + 'fields' => array( + ), + 'description' => __('Are you sure you wish to remove the registration request(s) selected?'), + 'submit' => array( + 'ajaxSubmit' => "$('#UserDiscardRegistrationsForm').submit();" + ) + ) + )); +?> diff --git a/app/View/Users/login.ctp b/app/View/Users/login.ctp index e839cd072..d8c8a94e5 100644 --- a/app/View/Users/login.ctp +++ b/app/View/Users/login.ctp @@ -39,16 +39,22 @@ echo $this->Form->input('email', array('autocomplete' => 'off', 'autofocus')); echo $this->Form->input('password', array('autocomplete' => 'off')); ?> -
+
+ %s', + $baseurl, + __('Registration will be sent to the administrators of the instance for consideration.'), + __('No account yet? Register now!') + ); + ?> +
Form->button(__('Login'), array('class' => 'btn btn-primary')); echo $this->Form->end(); - if (true == Configure::read('ApacheShibbAuth')): - ?> -
- Login with SAML - Login with SAML'; + } ?> diff --git a/app/View/Users/register.ctp b/app/View/Users/register.ctp new file mode 100644 index 000000000..dc5fc6fd5 --- /dev/null +++ b/app/View/Users/register.ctp @@ -0,0 +1,82 @@ +element('genericElements/Form/genericForm', array( + 'form' => $this->Form, + 'data' => array( + 'description' => nl2br(h($message)), + 'title' => __('Register for a new user account'), + 'model' => 'User', + 'skip_side_menu' => 1, + 'fields' => array( + array( + 'field' => 'email', + 'label' => __('Your email address'), + 'class' => 'input-xxlarge', + 'required' => 1 + ), + array( + 'field' => 'org_name', + 'label' => __('Your organisation\'s name'), + 'class' => 'input-xxlarge', + 'required' => 1 + ), + array( + 'field' => 'org_uuid', + 'label' => __('Your MISP org uuid (optional)'), + 'class' => 'input-xxlarge' + ), + array( + 'field' => 'custom_perms', + 'type' => 'checkbox', + 'label' => __("Request custom role") + ), + array( + 'field' => 'perm_publish', + 'type' => 'checkbox', + 'label' => __("Publish permission"), + 'class' => 'role-field', + 'hidden' => 1 + ), + array( + 'field' => 'perm_admin', + 'type' => 'checkbox', + 'label' => __("Org admin permission"), + 'class' => 'role-field', + 'hidden' => 1 + ), + array( + 'field' => 'perm_sync', + 'type' => 'checkbox', + 'class' => 'role-field', + 'label' => __("Sync permission"), + 'hidden' => 1 + ), + array( + 'field' => 'pgp', + 'label' => __('PGP key (optional)'), + 'class' => 'input-xxlarge', + 'type' => 'textarea' + ), + array( + 'field' => 'message', + 'label' => __('Message to the admins'), + 'class' => 'input-xxlarge', + 'type' => 'textarea' + ) + ), + 'submit' => array( + 'action' => $this->request->params['action'] + ) + ) + )); +?> + + diff --git a/app/View/Users/registrations.ctp b/app/View/Users/registrations.ctp new file mode 100644 index 000000000..32e2a64cc --- /dev/null +++ b/app/View/Users/registrations.ctp @@ -0,0 +1,138 @@ +'; + echo $this->element('/genericElements/IndexTable/index_table', array( + 'data' => array( + 'data' => $data, + 'top_bar' => array( + 'children' => array( + array( + 'children' => array( + array( + 'fa-icon' => 'check', + 'title' => __('Accept the selected registrations'), + 'id' => 'multi-accept-button', + 'class' => 'btn btn-small btn-inverse mass-select hidden' + ), + array( + 'fa-icon' => 'times', + 'title' => __('Discard the selected registrations'), + 'id' => 'multi-discard-button', + 'class' => 'btn btn-small btn-inverse mass-select hidden' + ) + ) + ), + array( + 'type' => 'search', + 'button' => __('Filter'), + 'placeholder' => __('Enter value to search'), + 'data' => '', + 'searchKey' => 'value' + ) + ) + ), + 'fields' => array( + array( + 'element' => 'selector', + 'class' => 'short' + ), + array( + 'name' => __('Id'), + 'class' => 'short', + 'data_path' => 'Inbox.id', + ), + array( + 'name' => __('IP'), + 'class' => 'short', + 'data_path' => 'Inbox.ip', + ), + array( + 'name' => __('User Agent'), + 'class' => 'shortish', + 'data_path' => 'Inbox.user_agent', + ), + array( + 'name' => __('Email'), + 'class' => 'short', + 'data_path' => 'Inbox.data.email', + ), + array( + 'name' => __('Org'), + 'class' => 'short', + 'data_path' => 'Inbox.data.org_name', + ), + array( + 'name' => __('Org uuid'), + 'class' => 'shortish', + 'data_path' => 'Inbox.data.org_uuid', + ), + array( + 'name' => __('Requested role'), + 'class' => 'short', + 'element' => 'list', + 'data_path' => 'Inbox.requested_role', + ), + array( + 'name' => __('PGP'), + 'class' => 'short', + 'element' => 'boolean', + 'data_path' => 'Inbox.data.pgp' + ), + array( + 'name' => __('Comment'), + 'data_path' => 'Inbox.comment', + ) + ), + 'title' => __('Registrations index'), + 'description' => __('You can find messages sent to this instance in the following list. Type denotes the type of request (such as registration). View each entry to see more details about the request\'s contents.'), + 'actions' => array( + array( + 'onclick' => sprintf( + 'openGenericModal(\'%s/users/acceptRegistrations/[onclick_params_data_path]\')', + $baseurl + ), + 'onclick_params_data_path' => 'Inbox.id', + 'icon' => 'check' + ), + array( + 'onclick' => sprintf( + 'openGenericModal(\'%s/users/discardRegistrations/[onclick_params_data_path]\')', + $baseurl + ), + 'onclick_params_data_path' => 'Inbox.id', + 'icon' => 'times' + ) + ) + ) + )); + echo ''; + echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'admin', 'menuItem' => 'registrations')); +?> + diff --git a/app/webroot/css/main.css b/app/webroot/css/main.css index 60fd8be45..1383da6c8 100644 --- a/app/webroot/css/main.css +++ b/app/webroot/css/main.css @@ -191,6 +191,13 @@ div.actions { width:170px; } +div.menuless-form { + padding: 10px; + padding-bottom:40px; + margin: 0 auto; + width:550px; +} + div.actions h3 { padding-top:0; diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index 23a513d97..5aa6331b8 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -1201,7 +1201,7 @@ function openGenericModalPost(url, body) { }); } -function submitPopoverForm(context_id, referer, update_context_id, modal, popover_dissmis_id_to_close) { +function submitPopoverForm(context_id, referer, update_context_id, modal, popover_dismiss_id_to_close) { var url = null; var context = 'event'; var contextNamingConvention = 'Attribute'; @@ -1241,8 +1241,11 @@ function submitPopoverForm(context_id, referer, update_context_id, modal, popove url = "/objectReferences/add/" + context_id; break; case 'quickAddAttributeForm': - url = "/objects/quickAddAttributeForm/" + context_id; - break; + url = "/objects/quickAddAttributeForm/" + context_id; + break; + case 'acceptUserRegistrations': + url = "/users/acceptRegistrations/" + context_id + break; } if ($("#submitButton").parent().hasClass('modal-footer')) { var $form = $("#submitButton").parent().parent().find('.modal-body form'); @@ -1261,8 +1264,8 @@ function submitPopoverForm(context_id, referer, update_context_id, modal, popove if (closePopover) { $("#gray_out").fadeOut(); $("#popover_form").fadeOut(); - if (popover_dissmis_id_to_close !== undefined) { - $('[data-dismissid="' + popover_dissmis_id_to_close + '"]').popover('destroy'); + if (popover_dismiss_id_to_close !== undefined) { + $('[data-dismissid="' + popover_dismiss_id_to_close + '"]').popover('destroy'); } $(".loading").show(); } From 60cb23287e4365ace4ec972a2d883b5403260359 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 7 Apr 2020 13:31:18 +0200 Subject: [PATCH 07/22] new: [inbox] stub controller --- app/Controller/InboxController.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 app/Controller/InboxController.php diff --git a/app/Controller/InboxController.php b/app/Controller/InboxController.php new file mode 100644 index 000000000..542c261aa --- /dev/null +++ b/app/Controller/InboxController.php @@ -0,0 +1,17 @@ + 60, + 'maxLimit' => 9999 + ); +} From ae3f2356376799cfaebf96bf3a99af469445e747 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 7 Apr 2020 13:41:53 +0200 Subject: [PATCH 08/22] fix: [trialing commas] removed --- app/View/Users/accept_registrations.ctp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/View/Users/accept_registrations.ctp b/app/View/Users/accept_registrations.ctp index 4e34f34ad..752396d2c 100644 --- a/app/View/Users/accept_registrations.ctp +++ b/app/View/Users/accept_registrations.ctp @@ -12,7 +12,7 @@ __('Requested organisation not found.'), $baseurl, empty($registration['Inbox']['data']['org_name']) ? '' : '/name:' . h($registration['Inbox']['data']['org_name']), - empty($registration['Inbox']['data']['org_uuid']) ? '' : '/uuid:' . h($registration['Inbox']['data']['org_uuid']), + empty($registration['Inbox']['data']['org_uuid']) ? '' : '/uuid:' . h($registration['Inbox']['data']['org_uuid']) ); } else { $suggestedOrgText = sprintf( @@ -35,7 +35,7 @@ '
  %s ', h($perm_flag_name), $perm_flag_value ? '1' : '0', - h($perm_flag_name), + h($perm_flag_name) ); } } From 3241e957305e7aadc5fbcd0cd3d8cae31cfc2ae4 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 7 Apr 2020 14:27:21 +0200 Subject: [PATCH 09/22] fix: [user registration] automatically convert selected orgs to local as described in the tool --- app/Controller/UsersController.php | 6 +++++- app/Model/User.php | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php index 8148345de..9db08e3da 100644 --- a/app/Controller/UsersController.php +++ b/app/Controller/UsersController.php @@ -2451,10 +2451,14 @@ class UsersController extends AppController } $this->set('roles', $roles); $this->set('role_perms', $role_perms); + $orgConditions = array('OR' => array('local' => 1)); + if (!empty($suggestedOrg)) { + $orgConditions['OR'] = array('Organisation.id' => $suggestedOrg[0]); + } $this->set('orgs', $this->User->Organisation->find('list', array( 'fields' => array('id', 'name'), 'recursive' => -1, - 'conditions' => array('local' => 1) + 'conditions' => $orgConditions ))); $this->set('registration', $registrations[$k]); $this->set('suggestedOrg', $suggestedOrg); diff --git a/app/Model/User.php b/app/Model/User.php index fa5a36366..8369fe874 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -1521,6 +1521,14 @@ class User extends AppModel $this->create(); $this->Log = ClassRegistry::init('Log'); $result = $this->save(array('User' => $user)); + $currentOrg = $this->Organisation->find('first', array( + 'recursive' => -1, + 'conditions' => array('Organisation.id' => $org_id) + )); + if (!empty($currentOrg) && empty($currentOrg['Organisation']['local'])) { + $currentOrg['Organisation']['local'] = 1; + $this->Organisation->save($currentOrg); + } if (empty($result)) { $error = array(); foreach ($this->validationErrors as $key => $errors) { From fd3ad03ac523c74efcada0d9cd86befdff417aaa Mon Sep 17 00:00:00 2001 From: mokaddem Date: Tue, 7 Apr 2020 14:35:37 +0200 Subject: [PATCH 10/22] chg: [registration:index] Added titles to buttons --- app/View/Users/accept_registrations.ctp | 7 +++++-- app/View/Users/registrations.ctp | 8 +++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/View/Users/accept_registrations.ctp b/app/View/Users/accept_registrations.ctp index 752396d2c..4117d632e 100644 --- a/app/View/Users/accept_registrations.ctp +++ b/app/View/Users/accept_registrations.ctp @@ -6,13 +6,14 @@ $suggestedOrgText = '
  ' . __('Conflicting requirements') . ''; } else if ($suggestedOrg === -1){ $suggestedOrgText = sprintf( - '%s%s%s ', + '%s%s%s ', empty($registration['Inbox']['data']['org_name']) ? '' : h($registration['Inbox']['data']['org_name']) . ' ', empty($registration['Inbox']['data']['org_uuid']) ? '' : h($registration['Inbox']['data']['org_uuid']) . ' ', __('Requested organisation not found.'), $baseurl, empty($registration['Inbox']['data']['org_name']) ? '' : '/name:' . h($registration['Inbox']['data']['org_name']), - empty($registration['Inbox']['data']['org_uuid']) ? '' : '/uuid:' . h($registration['Inbox']['data']['org_uuid']) + empty($registration['Inbox']['data']['org_uuid']) ? '' : '/uuid:' . h($registration['Inbox']['data']['org_uuid']), + __('Create a new organisation') ); } else { $suggestedOrgText = sprintf( @@ -91,9 +92,11 @@ if (selectedRoleDetails["perm_" + $(this).data('perm')] != $(this).data('value')) { $(this).removeClass('green'); $(this).addClass('red'); + $(this).attr('title', '') } else { $(this).removeClass('red'); $(this).addClass('green'); + $(this).attr('title', '') } }); } diff --git a/app/View/Users/registrations.ctp b/app/View/Users/registrations.ctp index 32e2a64cc..7b329f71c 100644 --- a/app/View/Users/registrations.ctp +++ b/app/View/Users/registrations.ctp @@ -9,7 +9,7 @@ 'children' => array( array( 'fa-icon' => 'check', - 'title' => __('Accept the selected registrations'), + 'title' => __('Process the selected registrations'), 'id' => 'multi-accept-button', 'class' => 'btn btn-small btn-inverse mass-select hidden' ), @@ -91,7 +91,8 @@ $baseurl ), 'onclick_params_data_path' => 'Inbox.id', - 'icon' => 'check' + 'icon' => 'check', + 'title' => __('Process registration') ), array( 'onclick' => sprintf( @@ -99,7 +100,8 @@ $baseurl ), 'onclick_params_data_path' => 'Inbox.id', - 'icon' => 'times' + 'icon' => 'times', + 'title' => __('Discard registration') ) ) ) From 1b65bfb8433f63896fb8e5cf55953571c45d1c76 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 7 Apr 2020 14:47:25 +0200 Subject: [PATCH 11/22] fix: [user registration] minor bug fixes --- app/Controller/UsersController.php | 2 +- app/Model/Organisation.php | 8 ++++---- app/View/Users/accept_registrations.ctp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php index 9db08e3da..46a47707e 100644 --- a/app/Controller/UsersController.php +++ b/app/Controller/UsersController.php @@ -2452,7 +2452,7 @@ class UsersController extends AppController $this->set('roles', $roles); $this->set('role_perms', $role_perms); $orgConditions = array('OR' => array('local' => 1)); - if (!empty($suggestedOrg)) { + if (!empty($suggestedOrg) && is_array($suggestedOrg)) { $orgConditions['OR'] = array('Organisation.id' => $suggestedOrg[0]); } $this->set('orgs', $this->User->Organisation->find('list', array( diff --git a/app/Model/Organisation.php b/app/Model/Organisation.php index dd08545bd..3e6a4f4b8 100644 --- a/app/Model/Organisation.php +++ b/app/Model/Organisation.php @@ -431,7 +431,7 @@ class Organisation extends AppModel public function checkDesiredOrg($suggestedOrg, $registration) { - if ($suggestedOrg !== false) { + if ($suggestedOrg !== false && $suggestedOrg !== -1) { $conditions = array(); if (!empty($registration['Inbox']['data']['org_uuid'])) { $conditions = array('Organisation.uuid' => $registration['Inbox']['data']['org_uuid']); @@ -443,10 +443,10 @@ class Organisation extends AppModel 'fields' => array('id', 'name', 'local'), 'conditions' => $conditions )); - if (!empty($suggestedOrg) && $suggestedOrg[0] !== $identifiedOrg['Organisation']['id']) { + if (empty($identifiedOrg)) { + $suggestedOrg = -1; + } else if (!empty($suggestedOrg) && $suggestedOrg[0] !== $identifiedOrg['Organisation']['id']) { $suggestedOrg = false; - } else if (empty($identifiedOrg)) { - $suggestedOrg = -1; } else { $suggestedOrg = array($identifiedOrg['Organisation']['id'], $identifiedOrg['Organisation']['name'], $identifiedOrg['Organisation']['local']); } diff --git a/app/View/Users/accept_registrations.ctp b/app/View/Users/accept_registrations.ctp index 752396d2c..79ed80790 100644 --- a/app/View/Users/accept_registrations.ctp +++ b/app/View/Users/accept_registrations.ctp @@ -61,7 +61,7 @@ 'class' => 'input-xxlarge', 'required' => 1, 'options' => $orgs, - 'default' => empty($suggestedOrg) ? false : $suggestedOrg[0] + 'default' => empty($suggestedOrg[0]) ? false : $suggestedOrg[0] ), array( 'field' => 'role_id', From f7b5eb9628404fffbf05c45d8c9c3d8365a00fda Mon Sep 17 00:00:00 2001 From: mokaddem Date: Tue, 7 Apr 2020 14:56:26 +0200 Subject: [PATCH 12/22] fix: [user:email] Replaced query parameters by cake's named parameters. Hopefully fix #5745 --- app/Controller/UsersController.php | 6 +++--- app/View/Users/admin_email.ctp | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php index 9db08e3da..10d8abbfe 100644 --- a/app/Controller/UsersController.php +++ b/app/Controller/UsersController.php @@ -1527,17 +1527,17 @@ class UsersController extends AppController if ($isPostOrPut) { $recipient = $this->request->data['User']['recipient']; } else { - $recipient = isset($this->request->query['recipient']) ? $this->request->query['recipient'] : null; + $recipient = isset($this->params['named']['recipient']) ? $this->params['named']['recipient'] : null; } if ($isPostOrPut) { $recipientEmailList = $this->request->data['User']['recipientEmailList']; } else { - $recipientEmailList = isset($this->request->query['recipientEmailList']) ? $this->request->query['recipientEmailList'] : null; + $recipientEmailList = isset($this->params['named']['recipientEmailList']) ? $this->params['named']['recipientEmailList'] : null; } if ($isPostOrPut) { $orgNameList = $this->request->data['User']['orgNameList']; } else { - $orgNameList = isset($this->request->query['orgNameList']) ? $this->request->query['orgNameList'] : null; + $orgNameList = isset($this->params['named']['orgNameList']) ? $this->params['named']['orgNameList'] : null; } if (!is_null($recipient) && $recipient == 0) { diff --git a/app/View/Users/admin_email.ctp b/app/View/Users/admin_email.ctp index d47b0bb9a..67ac9f291 100644 --- a/app/View/Users/admin_email.ctp +++ b/app/View/Users/admin_email.ctp @@ -76,10 +76,10 @@ $(document).ready(function() { // Confirm before submit $('#UserAdminEmailForm').submit(function(e) { - var url = '/admin/users/email/true?'; - url += 'recipient=' + $('#recipient').val(); - url += '&recipientEmailList=' + $('#UserRecipientEmailList').val(); - url += '&orgNameList=' + $('#UserOrgNameList').val(); + var url = '/admin/users/email/true'; + url += '/recipient:' + $('#recipient').val(); + url += '/recipientEmailList:' + $('#UserRecipientEmailList').val(); + url += '/orgNameList:' + $('#UserOrgNameList').val(); $.get(url, function(data) { $("#confirmation_box").html(data); openPopup("#confirmation_box"); From 0eb4e729702f9263632b994847d7ba2a48a3b7b6 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Tue, 7 Apr 2020 16:00:41 +0200 Subject: [PATCH 13/22] chg: [db_schema] Bumped schema --- db_schema.json | 163 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 147 insertions(+), 16 deletions(-) diff --git a/db_schema.json b/db_schema.json index 52dd4917a..0b46109b1 100644 --- a/db_schema.json +++ b/db_schema.json @@ -1649,6 +1649,16 @@ "column_type": "int(11)", "column_default": null }, + { + "column_name": "uuid", + "is_nullable": "NO", + "data_type": "varchar", + "character_maximum_length": "255", + "numeric_precision": null, + "collation_name": "utf8_bin", + "column_type": "varchar(255)", + "column_default": "" + }, { "column_name": "collection_uuid", "is_nullable": "NO", @@ -1738,16 +1748,6 @@ "collation_name": null, "column_type": "int(11)", "column_default": "0" - }, - { - "column_name": "uuid", - "is_nullable": "NO", - "data_type": "varchar", - "character_maximum_length": "255", - "numeric_precision": null, - "collation_name": "utf8_bin", - "column_type": "varchar(255)", - "column_default": "" } ], "galaxy_elements": [ @@ -1854,6 +1854,128 @@ "column_default": null } ], + "inbox": [ + { + "column_name": "id", + "is_nullable": "NO", + "data_type": "int", + "character_maximum_length": null, + "numeric_precision": "10", + "collation_name": null, + "column_type": "int(11)", + "column_default": null + }, + { + "column_name": "uuid", + "is_nullable": "NO", + "data_type": "varchar", + "character_maximum_length": "40", + "numeric_precision": null, + "collation_name": "utf8_bin", + "column_type": "varchar(40)", + "column_default": null + }, + { + "column_name": "title", + "is_nullable": "NO", + "data_type": "varchar", + "character_maximum_length": "191", + "numeric_precision": null, + "collation_name": "utf8mb4_general_ci", + "column_type": "varchar(191)", + "column_default": null + }, + { + "column_name": "type", + "is_nullable": "NO", + "data_type": "varchar", + "character_maximum_length": "191", + "numeric_precision": null, + "collation_name": "utf8mb4_general_ci", + "column_type": "varchar(191)", + "column_default": null + }, + { + "column_name": "ip", + "is_nullable": "NO", + "data_type": "varchar", + "character_maximum_length": "191", + "numeric_precision": null, + "collation_name": "utf8mb4_general_ci", + "column_type": "varchar(191)", + "column_default": null + }, + { + "column_name": "user_agent", + "is_nullable": "YES", + "data_type": "text", + "character_maximum_length": "65535", + "numeric_precision": null, + "collation_name": "utf8mb4_general_ci", + "column_type": "text", + "column_default": null + }, + { + "column_name": "user_agent_sha256", + "is_nullable": "NO", + "data_type": "varchar", + "character_maximum_length": "64", + "numeric_precision": null, + "collation_name": "utf8mb4_general_ci", + "column_type": "varchar(64)", + "column_default": null + }, + { + "column_name": "comment", + "is_nullable": "YES", + "data_type": "text", + "character_maximum_length": "65535", + "numeric_precision": null, + "collation_name": "utf8mb4_general_ci", + "column_type": "text", + "column_default": null + }, + { + "column_name": "deleted", + "is_nullable": "NO", + "data_type": "tinyint", + "character_maximum_length": null, + "numeric_precision": "3", + "collation_name": null, + "column_type": "tinyint(1)", + "column_default": "0" + }, + { + "column_name": "timestamp", + "is_nullable": "NO", + "data_type": "int", + "character_maximum_length": null, + "numeric_precision": "10", + "collation_name": null, + "column_type": "int(11)", + "column_default": null + }, + { + "column_name": "store_as_file", + "is_nullable": "NO", + "data_type": "tinyint", + "character_maximum_length": null, + "numeric_precision": "3", + "collation_name": null, + "column_type": "tinyint(1)", + "column_default": "0" + }, + { + "column_name": "data", + "is_nullable": "YES", + "data_type": "longtext", + "character_maximum_length": "4294967295", + "numeric_precision": null, + "collation_name": "utf8mb4_general_ci", + "column_type": "longtext", + "column_default": null + } + ], "jobs": [ { "column_name": "id", @@ -6162,12 +6284,12 @@ "galaxy_clusters": [ "id", "value", + "uuid", + "collection_uuid", "galaxy_id", "version", "tag_name", - "type", - "uuid", - "collection_uuid" + "type" ], "galaxy_elements": [ "id", @@ -6182,6 +6304,15 @@ "referenced_galaxy_cluster_value", "referenced_galaxy_cluster_type" ], + "inbox": [ + "id", + "title", + "type", + "uuid", + "user_agent_sha256", + "ip", + "timestamp" + ], "jobs": [ "id" ], @@ -6273,8 +6404,8 @@ "servers": [ "id", "org_id", - "remote_org_id", - "priority" + "priority", + "remote_org_id" ], "shadow_attribute_correlations": [ "id", @@ -6426,5 +6557,5 @@ "id" ] }, - "db_version": "49" + "db_version": "50" } \ No newline at end of file From 78c1357593072a160aea12b7717345cdbc9ee78b Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 7 Apr 2020 22:20:56 +0200 Subject: [PATCH 14/22] fix: [user registration] reverted bug introduced in previous commit restricting the org choice to the suggested org if there was a match --- app/Controller/UsersController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php index 46a47707e..88caa0e54 100644 --- a/app/Controller/UsersController.php +++ b/app/Controller/UsersController.php @@ -2452,8 +2452,8 @@ class UsersController extends AppController $this->set('roles', $roles); $this->set('role_perms', $role_perms); $orgConditions = array('OR' => array('local' => 1)); - if (!empty($suggestedOrg) && is_array($suggestedOrg)) { - $orgConditions['OR'] = array('Organisation.id' => $suggestedOrg[0]); + if (!empty($suggestedOrg)) { + $orgConditions['OR'][] = array('Organisation.id' => $suggestedOrg[0]); } $this->set('orgs', $this->User->Organisation->find('list', array( 'fields' => array('id', 'name'), From 48cbfd7536045d4db2ce884f293028b611e77f99 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 7 Apr 2020 22:46:35 +0200 Subject: [PATCH 15/22] new: [registration] fall back to the e-mail domain if no org info is provided - also, make the org info optional --- app/Controller/UsersController.php | 4 ++-- app/Model/Organisation.php | 3 +++ app/View/Users/accept_registrations.ctp | 5 ++++- app/View/Users/register.ctp | 5 ++--- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php index a412bd7c5..00ab8c811 100644 --- a/app/Controller/UsersController.php +++ b/app/Controller/UsersController.php @@ -2271,8 +2271,8 @@ class UsersController extends AppController $requestObject[$key] = trim($this->request->data[$key]); } } - if (empty($requestObject['email']) || empty($requestObject['org_name'])) { - throw new InvalidArgumentException(__('We require at least the email and org_name fields to be filled.')); + if (empty($requestObject['email'])) { + throw new InvalidArgumentException(__('We require at least the email field to be filled.')); } $this->loadModel('Inbox'); $this->Inbox->create(); diff --git a/app/Model/Organisation.php b/app/Model/Organisation.php index 3e6a4f4b8..e309faf08 100644 --- a/app/Model/Organisation.php +++ b/app/Model/Organisation.php @@ -437,6 +437,9 @@ class Organisation extends AppModel $conditions = array('Organisation.uuid' => $registration['Inbox']['data']['org_uuid']); } else if (!empty($registration['Inbox']['data']['org_name'])) { $conditions = array('Organisation.name' => $registration['Inbox']['data']['org_name']); + } else { + $domain = explode('@', $registration['Inbox']['data']['email'])[1]; + $conditions = array('LOWER(Organisation.name)' => strtolower($domain)); } $identifiedOrg = $this->User->Organisation->find('first', array( 'recursive' => -1, diff --git a/app/View/Users/accept_registrations.ctp b/app/View/Users/accept_registrations.ctp index da16e2959..a4f828a15 100644 --- a/app/View/Users/accept_registrations.ctp +++ b/app/View/Users/accept_registrations.ctp @@ -1,16 +1,19 @@   ' . __('Conflicting requirements') . ''; } else if ($suggestedOrg === -1){ $suggestedOrgText = sprintf( - '%s%s%s ', + '%s%s%s%s ', + (empty($registration['Inbox']['data']['org_name']) && empty($registration['Inbox']['data']['org_uuid'])) ? h($domain) . ' ' : '', empty($registration['Inbox']['data']['org_name']) ? '' : h($registration['Inbox']['data']['org_name']) . ' ', empty($registration['Inbox']['data']['org_uuid']) ? '' : h($registration['Inbox']['data']['org_uuid']) . ' ', __('Requested organisation not found.'), $baseurl, + (empty($registration['Inbox']['data']['org_name']) && empty($registration['Inbox']['data']['org_uuid'])) ? '/name:' . h($domain) : '', empty($registration['Inbox']['data']['org_name']) ? '' : '/name:' . h($registration['Inbox']['data']['org_name']), empty($registration['Inbox']['data']['org_uuid']) ? '' : '/uuid:' . h($registration['Inbox']['data']['org_uuid']), __('Create a new organisation') diff --git a/app/View/Users/register.ctp b/app/View/Users/register.ctp index dc5fc6fd5..954b837f3 100644 --- a/app/View/Users/register.ctp +++ b/app/View/Users/register.ctp @@ -15,9 +15,8 @@ ), array( 'field' => 'org_name', - 'label' => __('Your organisation\'s name'), - 'class' => 'input-xxlarge', - 'required' => 1 + 'label' => __('Your organisation\'s name (optional)'), + 'class' => 'input-xxlarge' ), array( 'field' => 'org_uuid', From 34201e72489e43affe93f7ae2596927b8ef171d4 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 7 Apr 2020 22:57:12 +0200 Subject: [PATCH 16/22] chg: [registrations] show the time of request's creations --- .../genericElements/IndexTable/Fields/timestamp.ctp | 6 +++++- app/View/Users/registrations.ctp | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/View/Elements/genericElements/IndexTable/Fields/timestamp.ctp b/app/View/Elements/genericElements/IndexTable/Fields/timestamp.ctp index d6431dca6..0abf4ed9f 100644 --- a/app/View/Elements/genericElements/IndexTable/Fields/timestamp.ctp +++ b/app/View/Elements/genericElements/IndexTable/Fields/timestamp.ctp @@ -1,3 +1,7 @@ diff --git a/app/View/Users/registrations.ctp b/app/View/Users/registrations.ctp index 7b329f71c..a32da1631 100644 --- a/app/View/Users/registrations.ctp +++ b/app/View/Users/registrations.ctp @@ -40,6 +40,13 @@ 'class' => 'short', 'data_path' => 'Inbox.id', ), + array( + 'name' => __('Time'), + 'class' => 'short', + 'element' => 'timestamp', + 'time_format' => 'Y-m-d H:i:s', + 'data_path' => 'Inbox.timestamp', + ), array( 'name' => __('IP'), 'class' => 'short', From 00d13492f65910e9301aac09ef7c2cae31f99b02 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 8 Apr 2020 10:11:57 +0200 Subject: [PATCH 17/22] chg: [index field] org field updated to allow for org information not local to the current instance (no ID set) --- app/View/Elements/genericElements/IndexTable/Fields/org.ctp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/View/Elements/genericElements/IndexTable/Fields/org.ctp b/app/View/Elements/genericElements/IndexTable/Fields/org.ctp index b5e811998..e08dc8297 100644 --- a/app/View/Elements/genericElements/IndexTable/Fields/org.ctp +++ b/app/View/Elements/genericElements/IndexTable/Fields/org.ctp @@ -14,8 +14,8 @@ $i = 0; foreach ($orgs as $org) { $i++; - if (!empty($org['id'])) { - if ($field['fields']['allow_picture']) { + if (!empty($org['id']) || !empty($org['name'])) { + if ($field['fields']['allow_picture'] && !empty($org['id'])) { echo $this->OrgImg->getOrgImg(array('name' => $org['name'], 'id' => $org['id'], 'size' => 24)); } else { echo sprintf( From 876bc9285c5f9406abed05bc00a1a178ffdc0c83 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 8 Apr 2020 10:12:44 +0200 Subject: [PATCH 18/22] new: [communities] self-registration links now exposed in the communities index --- app/View/Communities/index.ctp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/View/Communities/index.ctp b/app/View/Communities/index.ctp index 7c8735069..962e257a8 100644 --- a/app/View/Communities/index.ctp +++ b/app/View/Communities/index.ctp @@ -58,6 +58,14 @@ array( 'name' => __('Description'), 'data_path' => 'description', + ), + array( + 'name' => __('Self-reg'), + 'element' => 'self_registration', + 'class' => 'short', + 'title' => __('This community allows for self-registration'), + 'data_path' => 'url', + 'data_path_requirement' => 'self_registration' ) ), 'title' => __('Communities index'), From 27c7d2aa403fea1ef5ed9fdda45cfca3d209026b Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 8 Apr 2020 10:34:48 +0200 Subject: [PATCH 19/22] new: [community] added the COVID-19 MISP community to the list --- app/files/community-metadata/defaults.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/files/community-metadata/defaults.json b/app/files/community-metadata/defaults.json index 22dec880e..bd57d1b8f 100644 --- a/app/files/community-metadata/defaults.json +++ b/app/files/community-metadata/defaults.json @@ -92,5 +92,21 @@ "pgp_key": "\r\n\r\n-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n\r\n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\r\nWw==\r\n=4aXp\r\n-----END PGP PUBLIC KEY BLOCK-----\r\n", "misp_project_vetted": true, "scope_of_data_to_be_shared": "Information Operation Threat Intelligence including disinformation, indicators, threat intelligence information, reports, contextual threat actor information or financial fraud information." +}, + { + "name": "COVID-19 MISP community", + "uuid": "5e59659e-8e24-4e5d-b3fa-2ba744b7dd05", + "org_uuid": "55f6e5ae-2c60-40e5-964f-47a8950d210f", + "org_name": "CIRCL", + "description": "A community for tracking both health and cyber threat related information around COVID-19.", + "url": "https://covid-19.iglocska.eu", + "sector": "undefined", + "nationality": "International", + "type": "Open topical community", + "email": "info@circl.lu", + "pgp_key": "\r\n\r\n-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n\r\n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\r\nxJlLUC2lY9a62qs8kTCX88338AuN\r\n=+5Rm\r\n-----END PGP PUBLIC KEY BLOCK-----\r\n", + "misp_project_vetted": true, + "scope_of_data_to_be_shared": "Anything around COVID-19 that might help the community.", + "self_registration": true } ] From e10c755e8c7ef94520f831a07f9955fbd3279e68 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 8 Apr 2020 10:45:44 +0200 Subject: [PATCH 20/22] fix: [self-registration] added missing field --- .../IndexTable/Fields/self_registration.ctp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 app/View/Elements/genericElements/IndexTable/Fields/self_registration.ctp diff --git a/app/View/Elements/genericElements/IndexTable/Fields/self_registration.ctp b/app/View/Elements/genericElements/IndexTable/Fields/self_registration.ctp new file mode 100644 index 000000000..7026ef7bf --- /dev/null +++ b/app/View/Elements/genericElements/IndexTable/Fields/self_registration.ctp @@ -0,0 +1,17 @@ +'; + } else { + $url = Hash::extract($row, $field['data_path'])[0]; + echo sprintf( + '%s', + (!empty($self_registration_flag[0])) ? 'check' : 'times', + (empty($self_registration_flag[0])) ? '' : + sprintf( + ' (' . __('click here') . ')', + h($url) + ) + ); + } +?> From 56f178e7e87ebc1b018f2b9ed45cb63e3e32ff28 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 8 Apr 2020 10:46:18 +0200 Subject: [PATCH 21/22] fix: [UI] Added missing delete button for organisations, fixes #5773 --- app/View/Elements/genericElements/SideMenu/side_menu.ctp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/View/Elements/genericElements/SideMenu/side_menu.ctp b/app/View/Elements/genericElements/SideMenu/side_menu.ctp index 7d904f288..7999e5517 100644 --- a/app/View/Elements/genericElements/SideMenu/side_menu.ctp +++ b/app/View/Elements/genericElements/SideMenu/side_menu.ctp @@ -778,13 +778,16 @@ ), 'text' => __('Merge Organisation') )); - } - if ($menuItem === 'editOrg' || $menuItem === 'viewOrg') { echo $this->element('/genericElements/SideMenu/side_menu_link', array( 'element_id' => 'viewOrg', 'url' => '/organisations/view/' . h($id), 'text' => __('View Organisation') )); + echo $this->element('/genericElements/SideMenu/side_menu_post_link', array( + 'url' => '/admin/organisations/delete/' . h($id), + 'text' => __('Delete Organisation'), + 'message' => __('Are you sure you want to delete # %s?', h($id)) + )); } echo $this->element('/genericElements/SideMenu/side_menu_link', array( 'element_id' => 'indexOrg', From b61ac5be6a6d08409997264ff95b64209b1af4b7 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Wed, 8 Apr 2020 11:37:18 +0200 Subject: [PATCH 22/22] fix: [server:DBSchemaDiagnostic] Quote index column's name and added missing keyword --- app/Model/Server.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Model/Server.php b/app/Model/Server.php index 2a698b828..110051e4d 100644 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -4551,7 +4551,7 @@ class Server extends AppModel $field['column_name'], $field['expected']['data_type'], $length !== null ? sprintf('(%d)', $length) : '', - isset($field['expected']['column_default']) ? $field['expected']['column_default'] . '"' : '', + isset($field['expected']['column_default']) ? 'DEFAULT "' . $field['expected']['column_default'] . '"' : '', $field['expected']['is_nullable'] === 'NO' ? 'NOT NULL' : 'NULL', empty($field['expected']['collation_name']) ? '' : 'COLLATE ' . $field['expected']['collation_name'] ); @@ -4795,7 +4795,7 @@ class Server extends AppModel } else { $keyLength = ''; } - $sql = sprintf('CREATE INDEX `%s` ON `%s` (%s%s);', + $sql = sprintf('CREATE INDEX `%s` ON `%s` (`%s`%s);', $columnDiff, $tableName, $columnDiff,