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) { diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php index ce3679999..15bcecc51 100755 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -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/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 74dc7098e..4256d7fae 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -3088,7 +3088,9 @@ class EventsController extends AppController 'named_params' => $this->params['named'], 'ordered_url_params' => func_get_args(), 'injectedParams' => array( - 'returnFormat' => 'csv' + 'returnFormat' => 'csv', + 'to_ids' => '1', + 'published' => '1' ) )); return $this->restSearch(); @@ -3996,26 +3998,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 +4063,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 +4084,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 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 + ); +} 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 c68abdf53..00ab8c811 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']); } @@ -1523,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) { @@ -2241,4 +2245,262 @@ 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'])) { + throw new InvalidArgumentException(__('We require at least the email field 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); + $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' => $orgConditions + ))); + $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/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 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 e041fce97..a1b34f2ae 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..e309faf08 100644 --- a/app/Model/Organisation.php +++ b/app/Model/Organisation.php @@ -428,4 +428,32 @@ class Organisation extends AppModel } return array_values($orgIds); } + + public function checkDesiredOrg($suggestedOrg, $registration) + { + if ($suggestedOrg !== false && $suggestedOrg !== -1) { + $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']); + } else { + $domain = explode('@', $registration['Inbox']['data']['email'])[1]; + $conditions = array('LOWER(Organisation.name)' => strtolower($domain)); + } + $identifiedOrg = $this->User->Organisation->find('first', array( + 'recursive' => -1, + 'fields' => array('id', 'name', 'local'), + 'conditions' => $conditions + )); + if (empty($identifiedOrg)) { + $suggestedOrg = -1; + } else if (!empty($suggestedOrg) && $suggestedOrg[0] !== $identifiedOrg['Organisation']['id']) { + $suggestedOrg = false; + } 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 aac7b5aac..110051e4d 100644 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -1252,6 +1252,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).'), @@ -4533,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'] ); @@ -4777,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, diff --git a/app/Model/User.php b/app/Model/User.php index f8019163e..8369fe874 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -1502,4 +1502,59 @@ 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)); + $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) { + $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/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'), 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/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( 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) + ) + ); + } +?> 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/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..7999e5517 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( @@ -773,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', @@ -804,6 +812,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..a4f828a15 --- /dev/null +++ b/app/View/Users/accept_registrations.ctp @@ -0,0 +1,112 @@ +  ' . __('Conflicting requirements') . ''; + } else if ($suggestedOrg === -1){ + $suggestedOrgText = sprintf( + '%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') + ); + } 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[0]) ? 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/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"); 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..954b837f3 --- /dev/null +++ b/app/View/Users/register.ctp @@ -0,0 +1,81 @@ +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 (optional)'), + 'class' => 'input-xxlarge' + ), + 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..a32da1631 --- /dev/null +++ b/app/View/Users/registrations.ctp @@ -0,0 +1,147 @@ +'; + 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' => __('Process 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' => __('Time'), + 'class' => 'short', + 'element' => 'timestamp', + 'time_format' => 'Y-m-d H:i:s', + 'data_path' => 'Inbox.timestamp', + ), + 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', + 'title' => __('Process registration') + ), + array( + 'onclick' => sprintf( + 'openGenericModal(\'%s/users/discardRegistrations/[onclick_params_data_path]\')', + $baseurl + ), + 'onclick_params_data_path' => 'Inbox.id', + 'icon' => 'times', + 'title' => __('Discard registration') + ) + ) + ) + )); + echo ''; + echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'admin', 'menuItem' => 'registrations')); +?> + 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 } ] 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 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(); } 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