diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql
index 4c0dee91e..284128120 100644
--- a/INSTALL/MYSQL.sql
+++ b/INSTALL/MYSQL.sql
@@ -243,7 +243,8 @@ CREATE TABLE IF NOT EXISTS event_reports (
`deleted` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id),
CONSTRAINT u_uuid UNIQUE (uuid),
- INDEX `name` (`name`)
+ INDEX `name` (`name`),
+ INDEX `event_id` (`event_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- --------------------------------------------------------
@@ -455,7 +456,7 @@ CREATE TABLE IF NOT EXISTS `galaxy_clusters` (
`collection_uuid` varchar(255) COLLATE utf8_bin NOT NULL,
`type` varchar(255) COLLATE utf8_bin NOT NULL,
`value` text COLLATE utf8_bin NOT NULL,
- `tag_name` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '',
+ `tag_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`description` text COLLATE utf8_bin NOT NULL,
`galaxy_id` int(11) NOT NULL,
`source` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '',
diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php
index 7397c2082..68349da20 100755
--- a/app/Controller/AppController.php
+++ b/app/Controller/AppController.php
@@ -125,8 +125,8 @@ class AppController extends Controller
}
if (!$this->_isRest()) {
$this->__contentSecurityPolicy();
+ $this->response->header('X-XSS-Protection', '1; mode=block');
}
- $this->response->header('X-XSS-Protection', '1; mode=block');
if (!empty($this->params['named']['sql'])) {
$this->sql_dump = intval($this->params['named']['sql']);
@@ -446,22 +446,9 @@ class AppController extends Controller
} else {
// User not authenticated correctly
// reset the session information
- $redis = $this->User->setupRedis();
- // Do not log every fail, but just once per hour
- if ($redis && !$redis->exists('misp:auth_fail_throttling:' . $authKeyToStore)) {
- $redis->setex('misp:auth_fail_throttling:' . $authKeyToStore, 3600, 1);
+ if ($this->_shouldLog($authKeyToStore)) {
$this->loadModel('Log');
- $this->Log->create();
- $log = array(
- 'org' => 'SYSTEM',
- 'model' => 'User',
- 'model_id' => 0,
- 'email' => 'SYSTEM',
- 'action' => 'auth_fail',
- 'title' => "Failed authentication using API key ($authKeyToStore)",
- 'change' => null,
- );
- $this->Log->save($log);
+ $this->Log->createLogEntry('SYSTEM', 'auth_fail', 'User', 0, "Failed authentication using API key ($authKeyToStore)");
}
$this->Session->destroy();
}
@@ -548,8 +535,10 @@ class AppController extends Controller
}
if ($user['disabled']) {
- $this->Log = ClassRegistry::init('Log');
- $this->Log->createLogEntry($user, 'auth_fail', 'User', $user['id'], 'Login attempt by disabled user.');
+ if ($this->_shouldLog('disabled:' . $user['id'])) {
+ $this->Log = ClassRegistry::init('Log');
+ $this->Log->createLogEntry($user, 'auth_fail', 'User', $user['id'], 'Login attempt by disabled user.');
+ }
$this->Auth->logout();
if ($this->_isRest()) {
@@ -565,11 +554,33 @@ class AppController extends Controller
if (isset($user['authkey_expiration']) && $user['authkey_expiration']) {
$time = isset($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : time();
if ($user['authkey_expiration'] < $time) {
+ if ($this->_shouldLog('expired:' . $user['authkey_id'])) {
+ $this->Log = ClassRegistry::init('Log');
+ $this->Log->createLogEntry($user, 'auth_fail', 'User', $user['id'], "Login attempt by expired auth key {$user['authkey_id']}.");
+ }
$this->Auth->logout();
throw new ForbiddenException('Auth key is expired');
}
}
+ if (!empty($user['allowed_ips'])) {
+ App::uses('CidrTool', 'Tools');
+ $cidrTool = new CidrTool($user['allowed_ips']);
+ $remoteIp = $this->_remoteIp();
+ if ($remoteIp === null) {
+ $this->Auth->logout();
+ throw new ForbiddenException('Auth key is limited to IP address, but IP address not found');
+ }
+ if (!$cidrTool->contains($remoteIp)) {
+ if ($this->_shouldLog('not_allowed_ip:' . $user['authkey_id'] . ':' . $remoteIp)) {
+ $this->Log = ClassRegistry::init('Log');
+ $this->Log->createLogEntry($user, 'auth_fail', 'User', $user['id'], "Login attempt from not allowed IP address for auth key {$user['authkey_id']}.");
+ }
+ $this->Auth->logout();
+ throw new ForbiddenException('It is not possible to use this Auth key from your IP address');
+ }
+ }
+
$isUserRequest = !$this->_isRest() && !$this->request->is('ajax') && !$this->_isAutomation();
// Next checks makes sense just for user direct HTTP request, so skip REST and AJAX calls
if (!$isUserRequest) {
@@ -632,7 +643,7 @@ class AppController extends Controller
return;
}
- $remoteAddress = trim($_SERVER['REMOTE_ADDR']);
+ $remoteAddress = $this->_remoteIp();
$pipe = $redis->multi(Redis::PIPELINE);
// keep for 30 days
@@ -680,11 +691,7 @@ class AppController extends Controller
$change .= PHP_EOL . 'Request body: ' . $payload;
}
$this->Log = ClassRegistry::init('Log');
- try {
- $this->Log->createLogEntry($user, 'request', 'User', $user['id'], 'Paranoid log entry', $change);
- } catch (Exception $e) {
- // When `MISP.log_skip_db_logs_completely` is enabled, Log::createLogEntry method throws exception
- }
+ $this->Log->createLogEntry($user, 'request', 'User', $user['id'], 'Paranoid log entry', $change);
}
}
@@ -1261,7 +1268,7 @@ class AppController extends Controller
private function __sessionMassage()
{
- if (!empty(Configure::read('MISP.uuid'))) {
+ if (empty(Configure::read('Session.cookie')) && !empty(Configure::read('MISP.uuid'))) {
Configure::write('Session.cookie', 'MISP-' . Configure::read('MISP.uuid'));
}
if (!empty(Configure::read('Session.cookieTimeout')) || !empty(Configure::read('Session.timeout'))) {
@@ -1454,10 +1461,10 @@ class AppController extends Controller
if ($this->userRole['perm_site_admin']) {
return true;
}
- if ($this->userRole['perm_modify_org'] && $event['Event']['orgc_id'] == $this->Auth->user('org_id')) {
+ if ($this->userRole['perm_modify_org'] && $event['Event']['orgc_id'] == $this->Auth->user()['org_id']) {
return true;
}
- if ($this->userRole['perm_modify'] && $event['Event']['user_id'] == $this->Auth->user('id')) {
+ if ($this->userRole['perm_modify'] && $event['Event']['user_id'] == $this->Auth->user()['id']) {
return true;
}
return false;
@@ -1501,17 +1508,47 @@ class AppController extends Controller
throw new RuntimeException("User with ID {$sessionUser['id']} not exists.");
}
if (isset($sessionUser['authkey_id'])) {
+ // Reload authkey
$this->loadModel('AuthKey');
- if (!$this->AuthKey->exists($sessionUser['authkey_id'])) {
+ $authKey = $this->AuthKey->find('first', [
+ 'conditions' => ['id' => $sessionUser['authkey_id'], 'user_id' => $user['id']],
+ 'fields' => ['id', 'expiration', 'allowed_ips'],
+ 'recursive' => -1,
+ ]);
+ if (empty($authKey)) {
throw new RuntimeException("Auth key with ID {$sessionUser['authkey_id']} not exists.");
}
+ $user['authkey_id'] = $authKey['AuthKey']['id'];
+ $user['authkey_expiration'] = $authKey['AuthKey']['expiration'];
+ $user['allowed_ips'] = $authKey['AuthKey']['allowed_ips'];
}
- foreach (['authkey_id', 'authkey_expiration', 'logged_by_authkey'] as $copy) {
- if (isset($sessionUser[$copy])) {
- $user[$copy] = $sessionUser[$copy];
- }
+ if (isset($sessionUser['logged_by_authkey'])) {
+ $user['logged_by_authkey'] = $sessionUser['logged_by_authkey'];
}
$this->Auth->login($user);
return $user;
}
+
+ /**
+ * @return string|null
+ */
+ protected function _remoteIp()
+ {
+ $ipHeader = Configure::read('MISP.log_client_ip_header') ?: 'REMOTE_ADDR';
+ return isset($_SERVER[$ipHeader]) ? trim($_SERVER[$ipHeader]) : null;
+ }
+
+ /**
+ * @param string $key
+ * @return bool Returns true if the same log defined by $key was not stored in last hour
+ */
+ protected function _shouldLog($key)
+ {
+ $redis = $this->User->setupRedis();
+ if ($redis && !$redis->exists('misp:auth_fail_throttling:' . $key)) {
+ $redis->setex('misp:auth_fail_throttling:' . $key, 3600, 1);
+ return true;
+ }
+ return false;
+ }
}
diff --git a/app/Controller/AuthKeysController.php b/app/Controller/AuthKeysController.php
index b90bb2fa4..47e1fef57 100644
--- a/app/Controller/AuthKeysController.php
+++ b/app/Controller/AuthKeysController.php
@@ -71,8 +71,34 @@ class AuthKeysController extends AppController
public function edit($id)
{
- $this->set('metaGroup', 'admin');
- $this->set('metaAction', 'authkeys_edit');
+ $this->CRUD->edit($id, [
+ 'conditions' => $this->__prepareConditions(),
+ 'afterFind' => function (array $authKey) {
+ unset($authKey['AuthKey']['authkey']);
+ if (is_array($authKey['AuthKey']['allowed_ips'])) {
+ $authKey['AuthKey']['allowed_ips'] = implode("\n", $authKey['AuthKey']['allowed_ips']);
+ }
+ $authKey['AuthKey']['expiration'] = date('Y-m-d H:i:s', $authKey['AuthKey']['expiration']);
+ return $authKey;
+ },
+ 'fields' => ['comment', 'allowed_ips', 'expiration'],
+ ]);
+ if ($this->IndexFilter->isRest()) {
+ return $this->restResponsePayload;
+ }
+ $this->set('dropdownData', [
+ 'user' => $this->User->find('list', [
+ 'sort' => ['username' => 'asc'],
+ 'conditions' => ['id' => $this->request->data['AuthKey']['user_id']],
+ ])
+ ]);
+ $this->set('menuData', [
+ 'menuList' => $this->_isSiteAdmin() ? 'admin' : 'globalActions',
+ 'menuItem' => 'authKeyAdd',
+ ]);
+ $this->set('edit', true);
+ $this->set('validity', Configure::read('Security.advanced_authkeys_validity'));
+ $this->render('add');
}
public function add($user_id = false)
diff --git a/app/Controller/Component/ACLComponent.php b/app/Controller/Component/ACLComponent.php
index fb398c910..783329be2 100644
--- a/app/Controller/Component/ACLComponent.php
+++ b/app/Controller/Component/ACLComponent.php
@@ -687,7 +687,7 @@ class ACLComponent extends Component
'register' => array('*'),
'registrations' => array('perm_site_admin'),
'resetAllSyncAuthKeys' => array(),
- 'resetauthkey' => array('*'),
+ 'resetauthkey' => ['AND' => ['self_management_enabled', 'perm_auth']],
'request_API' => array('*'),
'routeafterlogin' => array('*'),
'statistics' => array('*'),
diff --git a/app/Controller/Component/CRUDComponent.php b/app/Controller/Component/CRUDComponent.php
index 1e682c197..5a63087c4 100644
--- a/app/Controller/Component/CRUDComponent.php
+++ b/app/Controller/Component/CRUDComponent.php
@@ -145,13 +145,21 @@ class CRUDComponent extends Component
if (empty($id)) {
throw new NotFoundException(__('Invalid %s.', $modelName));
}
- $data = $this->Controller->{$modelName}->find('first',
- isset($params['get']) ? $params['get'] : [
- 'recursive' => -1,
- 'conditions' => [
- 'id' => $id
- ]
- ]);
+ $query = isset($params['get']) ? $params['get'] : [
+ 'recursive' => -1,
+ 'conditions' => [
+ 'id' => $id
+ ],
+ ];
+ if (!empty($params['conditions'])) {
+ $query['conditions']['AND'][] = $params['conditions'];
+ }
+ /** @var Model $model */
+ $model = $this->Controller->{$modelName};
+ $data = $model->find('first', $query);
+ if (isset($params['afterFind'])) {
+ $data = $params['afterFind']($data);
+ }
if ($this->Controller->request->is('post') || $this->Controller->request->is('put')) {
$input = $this->Controller->request->data;
if (empty($input[$modelName])) {
@@ -171,7 +179,10 @@ class CRUDComponent extends Component
$data[$modelName][$field] = $fieldData;
}
}
- if ($this->Controller->{$modelName}->save($data)) {
+ if (isset($params['beforeSave'])) {
+ $data = $params['beforeSave']($data);
+ }
+ if ($model->save($data)) {
$message = __('%s updated.', $modelName);
if ($this->Controller->IndexFilter->isRest()) {
$this->Controller->restResponsePayload = $this->Controller->RestResponse->viewData($data, 'json');
@@ -182,7 +193,9 @@ class CRUDComponent extends Component
}
} else {
if ($this->Controller->IndexFilter->isRest()) {
-
+ $controllerName = $this->Controller->params['controller'];
+ $actionName = $this->Controller->params['action'];
+ $this->Controller->restResponsePayload = $this->Controller->RestResponse->saveFailResponse($controllerName, $actionName, false, $model->validationErrors, 'json');
}
}
} else {
diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php
index 3a64d2f49..829853d94 100644
--- a/app/Controller/EventsController.php
+++ b/app/Controller/EventsController.php
@@ -4938,6 +4938,7 @@ class EventsController extends AppController
if (!$event) {
throw new NotFoundException(__('Invalid event.'));
}
+ $mayModify = $this->__canModifyEvent($event);
$eventId = $event['Event']['id'];
$this->loadModel('Module');
@@ -5108,7 +5109,7 @@ class EventsController extends AppController
$this->set('module', $module);
$this->set('eventId', $eventId);
$this->set('event', $event);
- $this->set('mayModify', $this->__canModifyEvent($event));
+ $this->set('mayModify', $mayModify);
}
public function exportModule($module, $id, $standard = false)
diff --git a/app/Controller/GalaxiesController.php b/app/Controller/GalaxiesController.php
index 15e609fc6..917a6fe01 100644
--- a/app/Controller/GalaxiesController.php
+++ b/app/Controller/GalaxiesController.php
@@ -558,8 +558,8 @@ class GalaxiesController extends AppController
if (empty($clusters)) {
throw new MethodNotAllowedException('Invalid Galaxy.');
}
+ $this->Galaxy->GalaxyCluster->attachExtendByInfo($this->Auth->user(), $clusters);
foreach ($clusters as $k => $cluster) {
- $clusters[$k] = $this->Galaxy->GalaxyCluster->attachExtendByInfo($this->Auth->user(), $clusters[$k]);
$clusters[$k] = $this->Galaxy->GalaxyCluster->attachExtendFromInfo($this->Auth->user(), $clusters[$k]);
}
$galaxy = $this->Galaxy->find('first', array(
diff --git a/app/Controller/GalaxyClustersController.php b/app/Controller/GalaxyClustersController.php
index db5ef3108..dcd313c7d 100644
--- a/app/Controller/GalaxyClustersController.php
+++ b/app/Controller/GalaxyClustersController.php
@@ -96,62 +96,64 @@ class GalaxyClustersController extends AppController
)
);
return $this->RestResponse->viewData($clusters, $this->response->type());
- } else {
- $this->paginate['conditions']['AND'][] = $contextConditions;
- $this->paginate['conditions']['AND'][] = $searchConditions;
- $this->paginate['conditions']['AND'][] = $aclConditions;
- $this->paginate['contain'] = array_merge($this->paginate['contain'], array('Org', 'Orgc', 'SharingGroup', 'GalaxyClusterRelation', 'TargetingClusterRelation'));
- $clusters = $this->paginate();
-
- $tagIds = array();
- foreach ($clusters as $k => $cluster) {
- $clusters[$k] = $this->GalaxyCluster->attachExtendByInfo($this->Auth->user(), $clusters[$k]);
- $clusters[$k] = $this->GalaxyCluster->attachExtendFromInfo($this->Auth->user(), $clusters[$k]);
- $clusters[$k]['GalaxyCluster']['relation_counts'] = array(
- 'out' => count($clusters[$k]['GalaxyClusterRelation']),
- 'in' => count($clusters[$k]['TargetingClusterRelation']),
- );
-
- if (isset($cluster['Tag']['id'])) {
- $tagIds[] = $cluster['Tag']['id'];
- $clusters[$k]['GalaxyCluster']['tag_id'] = $cluster['Tag']['id'];
- }
- $clusters[$k]['GalaxyCluster']['synonyms'] = array();
- foreach ($cluster['GalaxyElement'] as $element) {
- $clusters[$k]['GalaxyCluster']['synonyms'][] = $element['value'];
- }
- $clusters[$k]['GalaxyCluster']['event_count'] = 0; // real number is assigned later
- }
-
- $eventCountsForTags = $this->GalaxyCluster->Tag->EventTag->countForTags($tagIds, $this->Auth->user());
-
- $this->loadModel('Sighting');
- $csvForTags = $this->Sighting->tagsSparkline($tagIds, $this->Auth->user(), '0');
- foreach ($clusters as $k => $cluster) {
- if (isset($cluster['GalaxyCluster']['tag_id'])) {
- if (isset($csvForTags[$cluster['GalaxyCluster']['tag_id']])) {
- $clusters[$k]['csv'] = $csvForTags[$cluster['GalaxyCluster']['tag_id']];
- }
- if (isset($eventCountsForTags[$cluster['GalaxyCluster']['tag_id']])) {
- $clusters[$k]['GalaxyCluster']['event_count'] = $eventCountsForTags[$cluster['GalaxyCluster']['tag_id']];
- }
- }
- }
- $customClusterCount = $this->GalaxyCluster->fetchGalaxyClusters($this->Auth->user(), [
- 'count' => true,
- 'conditions' => [
- 'AND' => [$searchConditions, $aclConditions],
- 'GalaxyCluster.default' => 0,
- ]
- ]);
- $this->loadModel('Attribute');
- $distributionLevels = $this->Attribute->distributionLevels;
- unset($distributionLevels[5]);
- $this->set('distributionLevels', $distributionLevels);
- $this->set('list', $clusters);
- $this->set('galaxy_id', $galaxyId);
- $this->set('custom_cluster_count', $customClusterCount);
}
+
+ $this->paginate['conditions']['AND'][] = $contextConditions;
+ $this->paginate['conditions']['AND'][] = $searchConditions;
+ $this->paginate['conditions']['AND'][] = $aclConditions;
+ $this->paginate['contain'] = array_merge($this->paginate['contain'], array('Org', 'Orgc', 'SharingGroup', 'GalaxyClusterRelation', 'TargetingClusterRelation'));
+ $clusters = $this->paginate();
+
+ $this->GalaxyCluster->attachExtendByInfo($this->Auth->user(), $clusters);
+
+ $tagIds = array();
+ foreach ($clusters as $k => $cluster) {
+ $clusters[$k] = $this->GalaxyCluster->attachExtendFromInfo($this->Auth->user(), $clusters[$k]);
+ $clusters[$k]['GalaxyCluster']['relation_counts'] = array(
+ 'out' => count($clusters[$k]['GalaxyClusterRelation']),
+ 'in' => count($clusters[$k]['TargetingClusterRelation']),
+ );
+
+ if (isset($cluster['Tag']['id'])) {
+ $tagIds[] = $cluster['Tag']['id'];
+ $clusters[$k]['GalaxyCluster']['tag_id'] = $cluster['Tag']['id'];
+ }
+ $clusters[$k]['GalaxyCluster']['synonyms'] = array();
+ foreach ($cluster['GalaxyElement'] as $element) {
+ $clusters[$k]['GalaxyCluster']['synonyms'][] = $element['value'];
+ }
+ $clusters[$k]['GalaxyCluster']['event_count'] = 0; // real number is assigned later
+ }
+
+ $eventCountsForTags = $this->GalaxyCluster->Tag->EventTag->countForTags($tagIds, $this->Auth->user());
+
+ $this->loadModel('Sighting');
+ $csvForTags = $this->Sighting->tagsSparkline($tagIds, $this->Auth->user(), '0');
+ foreach ($clusters as $k => $cluster) {
+ if (isset($cluster['GalaxyCluster']['tag_id'])) {
+ if (isset($csvForTags[$cluster['GalaxyCluster']['tag_id']])) {
+ $clusters[$k]['csv'] = $csvForTags[$cluster['GalaxyCluster']['tag_id']];
+ }
+ if (isset($eventCountsForTags[$cluster['GalaxyCluster']['tag_id']])) {
+ $clusters[$k]['GalaxyCluster']['event_count'] = $eventCountsForTags[$cluster['GalaxyCluster']['tag_id']];
+ }
+ }
+ }
+ $customClusterCount = $this->GalaxyCluster->fetchGalaxyClusters($this->Auth->user(), [
+ 'count' => true,
+ 'conditions' => [
+ 'AND' => [$searchConditions, $aclConditions],
+ 'GalaxyCluster.default' => 0,
+ ]
+ ]);
+ $this->loadModel('Attribute');
+ $distributionLevels = $this->Attribute->distributionLevels;
+ unset($distributionLevels[5]);
+ $this->set('distributionLevels', $distributionLevels);
+ $this->set('list', $clusters);
+ $this->set('galaxy_id', $galaxyId);
+ $this->set('custom_cluster_count', $customClusterCount);
+
if ($this->request->is('ajax')) {
$this->layout = 'ajax';
$this->render('ajax/index');
@@ -179,7 +181,9 @@ class GalaxyClustersController extends AppController
if ($this->_isRest()) {
return $this->RestResponse->viewData($cluster, $this->response->type());
} else {
- $cluster = $this->GalaxyCluster->attachExtendByInfo($this->Auth->user(), $cluster);
+ $clusters = [$cluster];
+ $this->GalaxyCluster->attachExtendByInfo($this->Auth->user(), $clusters);
+ $cluster = $clusters[0];
$cluster = $this->GalaxyCluster->attachExtendFromInfo($this->Auth->user(), $cluster);
$this->set('id', $id);
$this->set('galaxy', ['Galaxy' => $cluster['GalaxyCluster']['Galaxy']]);
diff --git a/app/Controller/ServersController.php b/app/Controller/ServersController.php
index 7e07d5b3d..52a95d388 100644
--- a/app/Controller/ServersController.php
+++ b/app/Controller/ServersController.php
@@ -2434,9 +2434,9 @@ misp.direct_call(relative_path, body)
}
$message = 'CSP reported violation';
- $ipHeader = Configure::read('MISP.log_client_ip_header') ?: 'REMOTE_ADDR';
- if (isset($_SERVER[$ipHeader])) {
- $message .= ' from IP ' . $_SERVER[$ipHeader];
+ $remoteIp = $this->_remoteIp();
+ if ($remoteIp) {
+ $message .= ' from IP ' . $remoteIp;
}
$this->log("$message: " . json_encode($report['csp-report'], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php
index 966eb329f..3d3c47d04 100644
--- a/app/Controller/UsersController.php
+++ b/app/Controller/UsersController.php
@@ -1294,24 +1294,22 @@ class UsersController extends AppController
public function resetauthkey($id = null, $alert = false)
{
- if (!$this->_isAdmin() && Configure::read('MISP.disableUserSelfManagement')) {
- throw new MethodNotAllowedException('User self-management has been disabled on this instance.');
- }
if (!$this->request->is('post') && !$this->request->is('put')) {
throw new MethodNotAllowedException(__('This functionality is only accessible via POST requests.'));
}
- if ($id == 'me') {
+ if ($id === 'me') {
$id = $this->Auth->user('id');
+ // Reset just current auth key
+ $keyId = isset($this->Auth->user()['authkey_id']) ? $this->Auth->user()['authkey_id'] : null;
+ } else {
+ $keyId = null;
}
- if (!$this->userRole['perm_auth']) {
- throw new MethodNotAllowedException(__('Invalid action.'));
- }
- $newkey = $this->User->resetauthkey($this->Auth->user(), $id, $alert);
+ $newkey = $this->User->resetauthkey($this->Auth->user(), $id, $alert, $keyId);
if ($newkey === false) {
throw new MethodNotAllowedException(__('Invalid user.'));
}
if (!$this->_isRest()) {
- $this->Flash->success(__('New authkey generated.', true));
+ $this->Flash->success(__('New authkey generated.'));
$this->redirect($this->referer());
} else {
return $this->RestResponse->saveSuccessResponse('User', 'resetauthkey', $id, $this->response->type(), 'Authkey updated: ' . $newkey);
diff --git a/app/Lib/Tools/CidrTool.php b/app/Lib/Tools/CidrTool.php
index faf978f3d..b21351c0c 100644
--- a/app/Lib/Tools/CidrTool.php
+++ b/app/Lib/Tools/CidrTool.php
@@ -66,6 +66,26 @@ class CidrTool
return $match;
}
+ /**
+ * @param string $cidr
+ * @return bool
+ */
+ public static function validate($cidr)
+ {
+ $parts = explode('/', $cidr, 2);
+ $ipBytes = inet_pton($parts[0]);
+ if ($ipBytes === false) {
+ return false;
+ }
+
+ $maximumNetmask = strlen($ipBytes) === 4 ? 32 : 128;
+ if (isset($parts[1]) && ($parts[1] > $maximumNetmask || $parts[1] < 0)) {
+ return false; // Netmask part of CIDR is invalid
+ }
+
+ return true;
+ }
+
/**
* Using solution from https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/IpUtils.php
*
diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php
index 3348e9ecc..3f4e0d216 100644
--- a/app/Model/AppModel.php
+++ b/app/Model/AppModel.php
@@ -89,7 +89,7 @@ class AppModel extends Model
45 => false, 46 => false, 47 => false, 48 => false, 49 => false, 50 => false,
51 => false, 52 => false, 53 => false, 54 => false, 55 => false, 56 => false,
57 => false, 58 => false, 59 => false, 60 => false, 61 => false, 62 => false,
- 63 => true, 64 => false, 65 => false
+ 63 => true, 64 => false, 65 => false, 66 => false, 67 => false,
);
public $advanced_updates_description = array(
@@ -1568,6 +1568,13 @@ class AppModel extends Model
INDEX `value` (`value`(255))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
break;
+ case 66:
+ $sqlArray[] = "ALTER TABLE `galaxy_clusters` MODIFY COLUMN `tag_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '';";
+ $indexArray[] = ['event_reports', 'event_id'];
+ break;
+ case 67:
+ $sqlArray[] = "ALTER TABLE `auth_keys` ADD `allowed_ips` text DEFAULT NULL;";
+ 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;';
@@ -1821,18 +1828,19 @@ class AppModel extends Model
}
}
- private function __addIndex($table, $field, $length = false)
+ private function __addIndex($table, $field, $length = null, $unique = false)
{
$dataSourceConfig = ConnectionManager::getDataSource('default')->config;
$dataSource = $dataSourceConfig['datasource'];
$this->Log = ClassRegistry::init('Log');
+ $index = $unique ? 'UNIQUE INDEX' : 'INDEX';
if ($dataSource == 'Database/Postgres') {
- $addIndex = "CREATE INDEX idx_" . $table . "_" . $field . " ON " . $table . " (" . $field . ");";
+ $addIndex = "CREATE $index idx_" . $table . "_" . $field . " ON " . $table . " (" . $field . ");";
} else {
if (!$length) {
- $addIndex = "ALTER TABLE `" . $table . "` ADD INDEX `" . $field . "` (`" . $field . "`);";
+ $addIndex = "ALTER TABLE `" . $table . "` ADD $index `" . $field . "` (`" . $field . "`);";
} else {
- $addIndex = "ALTER TABLE `" . $table . "` ADD INDEX `" . $field . "` (`" . $field . "`(" . $length . "));";
+ $addIndex = "ALTER TABLE `" . $table . "` ADD $index `" . $field . "` (`" . $field . "`(" . $length . "));";
}
}
$result = true;
@@ -1841,7 +1849,7 @@ class AppModel extends Model
try {
$this->query($addIndex);
} catch (Exception $e) {
- $duplicate = (strpos($e->getMessage(), '1061') !== false);
+ $duplicate = strpos($e->getMessage(), '1061') !== false;
$errorMessage = $e->getMessage();
$result = false;
}
diff --git a/app/Model/AuthKey.php b/app/Model/AuthKey.php
index f4711b6b7..5dbfb8ac2 100644
--- a/app/Model/AuthKey.php
+++ b/app/Model/AuthKey.php
@@ -1,6 +1,7 @@
data['AuthKey']['id'])) {
if (empty($this->data['AuthKey']['uuid'])) {
$this->data['AuthKey']['uuid'] = CakeText::uuid();
@@ -42,22 +42,66 @@ class AuthKey extends AppModel
$this->data['AuthKey']['authkey_end'] = substr($authkey, -4);
$this->data['AuthKey']['authkey_raw'] = $authkey;
$this->authkey_raw = $authkey;
+ }
- $validity = Configure::read('Security.advanced_authkeys_validity');
- if (empty($this->data['AuthKey']['expiration'])) {
- $this->data['AuthKey']['expiration'] = $validity ? strtotime("+$validity days") : 0;
+ if (!empty($this->data['AuthKey']['allowed_ips'])) {
+ if (is_string($this->data['AuthKey']['allowed_ips'])) {
+ $this->data['AuthKey']['allowed_ips'] = trim($this->data['AuthKey']['allowed_ips']);
+ if (empty($this->data['AuthKey']['allowed_ips'])) {
+ $this->data['AuthKey']['allowed_ips'] = [];
+ } else {
+ $this->data['AuthKey']['allowed_ips'] = explode("\n", $this->data['AuthKey']['allowed_ips']);
+ $this->data['AuthKey']['allowed_ips'] = array_map('trim', $this->data['AuthKey']['allowed_ips']);
+ }
+ }
+ if (!is_array($this->data['AuthKey']['allowed_ips'])) {
+ $this->invalidate('allowed_ips', 'Allowed IPs must be array');
+ }
+ foreach ($this->data['AuthKey']['allowed_ips'] as $cidr) {
+ if (!CidrTool::validate($cidr)) {
+ $this->invalidate('allowed_ips', "$cidr is not valid IP range");
+ }
+ }
+ }
+
+ $creationTime = isset($this->data['AuthKey']['created']) ? $this->data['AuthKey']['created'] : time();
+ $validity = Configure::read('Security.advanced_authkeys_validity');
+ if (empty($this->data['AuthKey']['expiration'])) {
+ $this->data['AuthKey']['expiration'] = $validity ? strtotime("+$validity days", $creationTime) : 0;
+ } else {
+ $expiration = is_numeric($this->data['AuthKey']['expiration']) ?
+ (int)$this->data['AuthKey']['expiration'] :
+ strtotime($this->data['AuthKey']['expiration']);
+
+ if ($expiration === false) {
+ $this->invalidate('expiration', __('Expiration must be in YYYY-MM-DD format.'));
+ }
+ if ($validity && $expiration > strtotime("+$validity days", $creationTime)) {
+ $this->invalidate('expiration', __('Maximal key validity is %s days.', $validity));
+ }
+ $this->data['AuthKey']['expiration'] = $expiration;
+ }
+
+ return true;
+ }
+
+ public function afterFind($results, $primary = false)
+ {
+ foreach ($results as $key => $val) {
+ if (isset($val['AuthKey']['allowed_ips'])) {
+ $results[$key]['AuthKey']['allowed_ips'] = $this->jsonDecode($val['AuthKey']['allowed_ips']);
+ }
+ }
+ return $results;
+ }
+
+ public function beforeSave($options = array())
+ {
+ if (isset($this->data['AuthKey']['allowed_ips'])) {
+ if (empty($this->data['AuthKey']['allowed_ips'])) {
+ $this->data['AuthKey']['allowed_ips'] = null;
} else {
- $expiration = is_numeric($this->data['AuthKey']['expiration']) ?
- (int)$this->data['AuthKey']['expiration'] :
- strtotime($this->data['AuthKey']['expiration']);
-
- if ($expiration === false) {
- $this->invalidate('expiration', __('Expiration must be in YYYY-MM-DD format.'));
- }
- if ($validity && $expiration > strtotime("+$validity days")) {
- $this->invalidate('expiration', __('Maximal key validity is %s days.', $validity));
- }
- $this->data['AuthKey']['expiration'] = $expiration;
+ $this->data['AuthKey']['allowed_ips'] = json_encode($this->data['AuthKey']['allowed_ips']);
}
}
return true;
@@ -71,9 +115,9 @@ class AuthKey extends AppModel
{
$start = substr($authkey, 0, 4);
$end = substr($authkey, -4);
- $existing_authkeys = $this->find('all', [
+ $possibleAuthkeys = $this->find('all', [
'recursive' => -1,
- 'fields' => ['id', 'authkey', 'user_id', 'expiration'],
+ 'fields' => ['id', 'authkey', 'user_id', 'expiration', 'allowed_ips'],
'conditions' => [
'OR' => [
'expiration >' => time(),
@@ -84,12 +128,13 @@ class AuthKey extends AppModel
]
]);
$passwordHasher = $this->getHasher();
- foreach ($existing_authkeys as $existing_authkey) {
- if ($passwordHasher->check($authkey, $existing_authkey['AuthKey']['authkey'])) {
- $user = $this->User->getAuthUser($existing_authkey['AuthKey']['user_id']);
+ foreach ($possibleAuthkeys as $possibleAuthkey) {
+ if ($passwordHasher->check($authkey, $possibleAuthkey['AuthKey']['authkey'])) {
+ $user = $this->User->getAuthUser($possibleAuthkey['AuthKey']['user_id']);
if ($user) {
- $user['authkey_id'] = $existing_authkey['AuthKey']['id'];
- $user['authkey_expiration'] = $existing_authkey['AuthKey']['expiration'];
+ $user['authkey_id'] = $possibleAuthkey['AuthKey']['id'];
+ $user['authkey_expiration'] = $possibleAuthkey['AuthKey']['expiration'];
+ $user['allowed_ips'] = $possibleAuthkey['AuthKey']['allowed_ips'];
}
return $user;
}
@@ -97,26 +142,67 @@ class AuthKey extends AppModel
return false;
}
- public function resetauthkey($id)
+ /**
+ * @param int $userId
+ * @param int|null $keyId
+ * @return false|string
+ * @throws Exception
+ */
+ public function resetAuthKey($userId, $keyId = null)
{
- $existing_authkeys = $this->find('all', [
- 'recursive' => -1,
- 'conditions' => [
- 'user_id' => $id
- ]
- ]);
- foreach ($existing_authkeys as $key) {
- $key['AuthKey']['expiration'] = time();
- $this->save($key);
+ $time = time();
+
+ if ($keyId) {
+ $currentAuthkey = $this->find('first', [
+ 'recursive' => -1,
+ 'conditions' => [
+ 'id' => $keyId,
+ 'user_id' => $userId,
+ ],
+ ]);
+ if (empty($currentAuthkey)) {
+ throw new RuntimeException("Key with ID $keyId for user with ID $userId not found.");
+ }
+ $currentAuthkey['AuthKey']['expiration'] = $time;
+ if (!$this->save($currentAuthkey)) {
+ throw new RuntimeException("Key with ID $keyId could not be saved.");
+ }
+ $comment = __("Created by resetting auth key %s\n%s", $keyId, $currentAuthkey['AuthKey']['comment']);
+ $allowedIps = isset($currentAuthkey['AuthKey']['allowed_ips']) ? $currentAuthkey['AuthKey']['allowed_ips'] : [];
+ return $this->createnewkey($userId, $comment, $allowedIps);
+ } else {
+ $existingAuthkeys = $this->find('all', [
+ 'recursive' => -1,
+ 'conditions' => [
+ 'OR' => [
+ 'expiration >' => $time,
+ 'expiration' => 0
+ ],
+ 'user_id' => $userId
+ ]
+ ]);
+ foreach ($existingAuthkeys as $key) {
+ $key['AuthKey']['expiration'] = $time;
+ $this->save($key);
+ }
+ return $this->createnewkey($userId);
}
- return $this->createnewkey($id);
}
- public function createnewkey($id)
+ /**
+ * @param int $userId
+ * @param string $comment
+ * @param array $allowedIps
+ * @return false|string
+ * @throws Exception
+ */
+ public function createnewkey($userId, $comment = '', array $allowedIps = [])
{
$newKey = [
'authkey' => (new RandomTool())->random_str(true, 40),
- 'user_id' => $id
+ 'user_id' => $userId,
+ 'comment' => $comment,
+ 'allowed_ips' => empty($allowedIps) ? null : $allowedIps,
];
$this->create();
if ($this->save($newKey)) {
@@ -175,6 +261,18 @@ class AuthKey extends AppModel
return $output;
}
+ /**
+ * When key is modified, update `date_modified` for user that was assigned to that key, so session data
+ * will be realoaded.
+ * @see AppController::_refreshAuth
+ */
+ public function afterSave($created, $options = array())
+ {
+ parent::afterSave($created, $options);
+ $userId = $this->data['AuthKey']['user_id'];
+ $this->User->updateAll(['date_modified' => time()], ['User.id' => $userId]);
+ }
+
/**
* When key is deleted, update after `date_modified` for user that was assigned to that key, so session data
* will be realoaded and canceled.
diff --git a/app/Model/Feed.php b/app/Model/Feed.php
index 4d9cdb8af..037bc3dd8 100644
--- a/app/Model/Feed.php
+++ b/app/Model/Feed.php
@@ -1226,7 +1226,7 @@ class Feed extends AppModel
$md5Values = array_map('md5', array_column($values, 'value'));
$redis->del('misp:feed_cache:' . $feedId);
- foreach (array_chunk($md5Values, 1000) as $k => $chunk) {
+ foreach (array_chunk($md5Values, 5000) as $k => $chunk) {
$pipe = $redis->multi(Redis::PIPELINE);
if (method_exists($redis, 'sAddArray')) {
$redis->sAddArray('misp:feed_cache:' . $feedId, $chunk);
@@ -1238,7 +1238,7 @@ class Feed extends AppModel
}
}
$pipe->exec();
- $this->jobProgress($jobId, __('Feed %s: %s/%s values cached.', $feedId, $k * 1000, count($md5Values)));
+ $this->jobProgress($jobId, __('Feed %s: %s/%s values cached.', $feedId, $k * 5000, count($md5Values)));
}
$redis->set('misp:feed_cache_timestamp:' . $feedId, time());
return true;
diff --git a/app/Model/GalaxyCluster.php b/app/Model/GalaxyCluster.php
index 76cf6eeb4..0fb8d4d30 100644
--- a/app/Model/GalaxyCluster.php
+++ b/app/Model/GalaxyCluster.php
@@ -832,11 +832,30 @@ class GalaxyCluster extends AppModel
return $element;
}
- public function attachExtendByInfo($user, $cluster)
+ /**
+ * @param array $user
+ * @param array $clusters
+ * @return void
+ */
+ public function attachExtendByInfo(array $user, array &$clusters)
{
- $extensions = $this->fetchGalaxyClusters($user, array('conditions' => array('extends_uuid' => $cluster['GalaxyCluster']['uuid'])));
- $cluster['GalaxyCluster']['extended_by'] = $extensions;
- return $cluster;
+ if (empty($clusters)) {
+ return;
+ }
+
+ $clusterUuids = array_column(array_column($clusters, 'GalaxyCluster'), 'uuid');
+ $extensions = $this->fetchGalaxyClusters($user, [
+ 'conditions' => ['extends_uuid' => $clusterUuids],
+ ]);
+ foreach ($clusters as &$cluster) {
+ $extendedBy = [];
+ foreach ($extensions as $extension) {
+ if ($cluster['GalaxyCluster']['uuid'] === $extension['GalaxyCluster']['extends_uuid']) {
+ $extendedBy[] = $extension;
+ }
+ }
+ $cluster['GalaxyCluster']['extended_by'] = $extendedBy;
+ }
}
public function attachExtendFromInfo($user, $cluster)
@@ -896,7 +915,7 @@ class GalaxyCluster extends AppModel
if (!$isGalaxyTag) {
return null;
}
- $conditions = array('LOWER(GalaxyCluster.tag_name)' => strtolower($name));
+ $conditions = array('GalaxyCluster.tag_name' => $name);
}
$cluster = $this->fetchGalaxyClusters($user, array(
'conditions' => $conditions,
@@ -924,7 +943,7 @@ class GalaxyCluster extends AppModel
if (count(array_filter($namesOrIds, 'is_numeric')) === count($namesOrIds)) { // all elements are numeric
$conditions = array('GalaxyCluster.id' => $namesOrIds);
} else {
- $conditions = array('LOWER(GalaxyCluster.tag_name)' => array_map('strtolower', $namesOrIds));
+ $conditions = array('GalaxyCluster.tag_name' => $namesOrIds);
}
$options = ['conditions' => $conditions];
@@ -1451,7 +1470,7 @@ class GalaxyCluster extends AppModel
foreach ($events as $event) {
foreach ($event['EventTag'] as $eventTag) {
if ($eventTag['Tag']['is_galaxy']) {
- $clusterTagNames[$eventTag['Tag']['id']] = strtolower($eventTag['Tag']['name']);
+ $clusterTagNames[$eventTag['Tag']['id']] = $eventTag['Tag']['name'];
}
}
}
@@ -1461,7 +1480,7 @@ class GalaxyCluster extends AppModel
}
$options = [
- 'conditions' => ['LOWER(GalaxyCluster.tag_name)' => $clusterTagNames],
+ 'conditions' => ['GalaxyCluster.tag_name' => $clusterTagNames],
'contain' => ['Galaxy', 'GalaxyElement'],
];
$clusters = $this->fetchGalaxyClusters($user, $options);
diff --git a/app/Model/Job.php b/app/Model/Job.php
index ecb8a846c..9193a2366 100644
--- a/app/Model/Job.php
+++ b/app/Model/Job.php
@@ -92,7 +92,7 @@ class Job extends AppModel
}
}
try {
- if ($this->save($jobData)) {
+ if ($this->save($jobData, ['atomic' => false])) {
return true;
}
$this->log("Could not save progress for job $jobId because of validation errors: " . json_encode($this->validationErrors), LOG_NOTICE);
diff --git a/app/Model/Log.php b/app/Model/Log.php
index 1cf7aee90..19b7aeb64 100644
--- a/app/Model/Log.php
+++ b/app/Model/Log.php
@@ -199,7 +199,7 @@ class Log extends AppModel
* @param int $modelId
* @param string $title
* @param string|array $change
- * @return array
+ * @return array|null
* @throws Exception
* @throws InvalidArgumentException
*/
@@ -238,6 +238,10 @@ class Log extends AppModel
));
if (!$result) {
+ if ($action === 'request' && !empty(Configure::read('MISP.log_paranoid_skip_db'))) {
+ return null;
+ }
+
throw new Exception("Cannot save log because of validation errors: " . json_encode($this->validationErrors));
}
diff --git a/app/Model/Server.php b/app/Model/Server.php
index c4c938583..e0dc2c339 100644
--- a/app/Model/Server.php
+++ b/app/Model/Server.php
@@ -4,6 +4,7 @@ App::uses('GpgTool', 'Tools');
/**
* @property-read array $serverSettings
+ * @property Organisation $Organisation
*/
class Server extends AppModel
{
@@ -1259,16 +1260,23 @@ class Server extends AppModel
return true;
}
+ /**
+ * @return array
+ */
public function getCurrentServerSettings()
{
- $this->Module = ClassRegistry::init('Module');
$serverSettings = $this->serverSettings;
$moduleTypes = array('Enrichment', 'Import', 'Export', 'Cortex');
$serverSettings = $this->readModuleSettings($serverSettings, $moduleTypes);
return $serverSettings;
}
- private function readModuleSettings($serverSettings, $moduleTypes)
+ /**
+ * @param array $serverSettings
+ * @param array $moduleTypes
+ * @return array
+ */
+ private function readModuleSettings(array $serverSettings, array $moduleTypes)
{
$this->Module = ClassRegistry::init('Module');
foreach ($moduleTypes as $moduleType) {
@@ -1277,12 +1285,12 @@ class Server extends AppModel
foreach ($results as $module => $data) {
foreach ($data as $result) {
$setting = array('level' => 1, 'errorMessage' => '');
- if ($result['type'] == 'boolean') {
+ if ($result['type'] === 'boolean') {
$setting['test'] = 'testBool';
$setting['type'] = 'boolean';
$setting['description'] = __('Enable or disable the %s module.', $module);
$setting['value'] = false;
- } elseif ($result['type'] == 'orgs') {
+ } elseif ($result['type'] === 'orgs') {
$setting['description'] = __('Restrict the %s module to the given organisation.', $module);
$setting['value'] = 0;
$setting['test'] = 'testLocalOrg';
@@ -1359,15 +1367,11 @@ class Server extends AppModel
public function serverSettingsRead($unsorted = false)
{
- $this->Module = ClassRegistry::init('Module');
$serverSettings = $this->getCurrentServerSettings();
$currentSettings = Configure::read();
- if (Configure::read('Plugin.Enrichment_services_enable')) {
- $this->readModuleSettings($serverSettings, array('Enrichment'));
- }
$finalSettingsUnsorted = $this->__serverSettingsRead($serverSettings, $currentSettings);
foreach ($finalSettingsUnsorted as $key => $temp) {
- if (in_array($temp['tab'], array_keys($this->__settingTabMergeRules))) {
+ if (isset($this->__settingTabMergeRules[$temp['tab']])) {
$finalSettingsUnsorted[$key]['tab'] = $this->__settingTabMergeRules[$temp['tab']];
}
}
diff --git a/app/Model/User.php b/app/Model/User.php
index e8f70527d..ca505519c 100644
--- a/app/Model/User.php
+++ b/app/Model/User.php
@@ -1106,7 +1106,7 @@ class User extends AppModel
return $results;
}
- public function resetauthkey($user, $id, $alert = false)
+ public function resetauthkey($user, $id, $alert = false, $keyId = null)
{
$this->id = $id;
if (!$id || !$this->exists($id)) {
@@ -1123,8 +1123,7 @@ class User extends AppModel
$this->extralog(
$user,
'reset_auth_key',
- sprintf(
- __('Authentication key for user %s (%s) updated.'),
+ __('Authentication key for user %s (%s) updated.',
$updatedUser['User']['id'],
$updatedUser['User']['email']
),
@@ -1133,7 +1132,7 @@ class User extends AppModel
);
} else {
$this->AuthKey = ClassRegistry::init('AuthKey');
- $newkey = $this->AuthKey->resetauthkey($id);
+ $newkey = $this->AuthKey->resetAuthKey($id, $keyId);
}
if ($alert) {
$baseurl = Configure::read('MISP.external_baseurl');
diff --git a/app/Plugin/OidcAuth/Controller/Component/Auth/OidcAuthenticate.php b/app/Plugin/OidcAuth/Controller/Component/Auth/OidcAuthenticate.php
index 5031c65c9..4ec5befbe 100644
--- a/app/Plugin/OidcAuth/Controller/Component/Auth/OidcAuthenticate.php
+++ b/app/Plugin/OidcAuth/Controller/Component/Auth/OidcAuthenticate.php
@@ -72,9 +72,9 @@ class OidcAuthenticate extends BaseAuthenticate
}
if ($user['role_id'] != $roleId) {
- $user['role_id'] = $roleId;
$this->userModel()->updateField($user, 'role_id', $roleId);
$this->log($mispUsername, "User role changed from {$user['role_id']} to $roleId.");
+ $user['role_id'] = $roleId;
}
$this->log($mispUsername, 'Logged in.');
@@ -182,20 +182,18 @@ class OidcAuthenticate extends BaseAuthenticate
]);
$roleNameToId = array_change_key_case($roleNameToId); // normalize role names to lowercase
- $userRole = null;
- foreach ($roles as $role) {
- if (isset($roleMapper[$role])) {
- $roleId = $roleMapper[$role];
- if (!is_numeric($roleId)) {
- $roleId = mb_strtolower($roleId);
- if (isset($roleNameToId[$roleId])) {
- $roleId = $roleNameToId[$roleId];
+ foreach ($roleMapper as $oidcRole => $mispRole) {
+ if (in_array($oidcRole, $roles, true)) {
+ if (!is_numeric($mispRole)) {
+ $mispRole = mb_strtolower($mispRole);
+ if (isset($roleNameToId[$mispRole])) {
+ $mispRole = $roleNameToId[$mispRole];
} else {
- $this->log($mispUsername, "MISP Role with name `$roleId` not found, skipping.");
+ $this->log($mispUsername, "MISP Role with name `$mispRole` not found, skipping.");
continue;
}
}
- return $roleId; // first match wins
+ return $mispRole; // first match wins
}
}
diff --git a/app/View/AuthKeys/add.ctp b/app/View/AuthKeys/add.ctp
index 62e5a40db..a3cd94042 100644
--- a/app/View/AuthKeys/add.ctp
+++ b/app/View/AuthKeys/add.ctp
@@ -1,7 +1,7 @@
element('genericElements/Form/genericForm', [
'data' => [
- 'title' => __('Add auth key'),
+ 'title' => isset($edit) ? __('Edit auth key') : __('Add auth key'),
'description' => __('Auth keys are used for API access. A user can have more than one authkey, so if you would like to use separate keys per tool that queries MISP, add additional keys. Use the comment field to make identifying your keys easier.'),
'fields' => [
[
@@ -13,7 +13,14 @@ echo $this->element('genericElements/Form/genericForm', [
[
'field' => 'comment',
'label' => __('Comment'),
- 'class' => 'span6'
+ 'class' => 'span6',
+ 'rows' => 4,
+ ],
+ [
+ 'field' => 'allowed_ips',
+ 'label' => __('Allowed IPs'),
+ 'class' => 'span6',
+ 'rows' => 4,
],
[
'field' => 'expiration',
diff --git a/app/View/AuthKeys/index.ctp b/app/View/AuthKeys/index.ctp
index 127132961..77c3ab45f 100644
--- a/app/View/AuthKeys/index.ctp
+++ b/app/View/AuthKeys/index.ctp
@@ -63,12 +63,17 @@
'data_path' => 'AuthKey.last_used',
'element' => 'datetime',
'requirements' => $keyUsageEnabled,
+ 'empty' => __('Never'),
],
[
'name' => __('Comment'),
'sort' => 'AuthKey.comment',
'data_path' => 'AuthKey.comment',
],
+ [
+ 'name' => __('Allowed IPs'),
+ 'data_path' => 'AuthKey.allowed_ips',
+ ],
],
'title' => empty($ajax) ? __('Authentication key Index') : false,
'description' => empty($ajax) ? __('A list of API keys bound to a user.') : false,
@@ -80,7 +85,16 @@
'AuthKey.id'
),
'icon' => 'eye',
- 'dbclickAction' => true
+ 'dbclickAction' => true,
+ 'title' => 'View auth key',
+ ],
+ [
+ 'url' => $baseurl . '/auth_keys/edit',
+ 'url_params_data_paths' => array(
+ 'AuthKey.id'
+ ),
+ 'icon' => 'edit',
+ 'title' => 'Edit auth key',
],
[
'onclick' => sprintf(
diff --git a/app/View/AuthKeys/view.ctp b/app/View/AuthKeys/view.ctp
index 37f786371..43b79780b 100644
--- a/app/View/AuthKeys/view.ctp
+++ b/app/View/AuthKeys/view.ctp
@@ -15,65 +15,72 @@ if (isset($keyUsage)) {
$uniqueIps = null;
}
-echo $this->element(
- 'genericElements/SingleViews/single_view',
- [
- 'title' => 'Auth key view',
- 'data' => $data,
- 'fields' => [
- [
- 'key' => __('ID'),
- 'path' => 'AuthKey.id'
- ],
- [
- 'key' => __('UUID'),
- 'path' => 'AuthKey.uuid',
- ],
- [
- 'key' => __('Auth Key'),
- 'path' => 'AuthKey',
- 'type' => 'authkey'
- ],
- [
- 'key' => __('User'),
- 'path' => 'User.id',
- 'pathName' => 'User.email',
- 'model' => 'users',
- 'type' => 'model'
- ],
- [
- 'key' => __('Comment'),
- 'path' => 'AuthKey.comment'
- ],
- [
- 'key' => __('Created'),
- 'path' => 'AuthKey.created',
- 'type' => 'datetime'
- ],
- [
- 'key' => __('Expiration'),
- 'path' => 'AuthKey.expiration',
- 'type' => 'expiration'
- ],
- [
- 'key' => __('Key usage'),
- 'type' => 'sparkline',
- 'path' => 'AuthKey.id',
- 'csv' => [
- 'data' => $keyUsageCsv,
- ],
- 'requirement' => isset($keyUsage),
- ],
- [
- 'key' => __('Last used'),
- 'raw' => $lastUsed ? $this->Time->time($lastUsed) : __('Not used yet'),
- 'requirement' => isset($keyUsage),
- ],
- [
- 'key' => __('Unique IPs'),
- 'raw' => $uniqueIps,
- 'requirement' => isset($keyUsage),
- ]
+echo $this->element('genericElements/SingleViews/single_view', [
+ 'title' => 'Auth key view',
+ 'data' => $data,
+ 'fields' => [
+ [
+ 'key' => __('ID'),
+ 'path' => 'AuthKey.id'
],
- ]
-);
+ [
+ 'key' => __('UUID'),
+ 'path' => 'AuthKey.uuid',
+ ],
+ [
+ 'key' => __('Auth Key'),
+ 'path' => 'AuthKey',
+ 'type' => 'authkey'
+ ],
+ [
+ 'key' => __('User'),
+ 'path' => 'User.id',
+ 'pathName' => 'User.email',
+ 'model' => 'users',
+ 'type' => 'model'
+ ],
+ [
+ 'key' => __('Comment'),
+ 'path' => 'AuthKey.comment'
+ ],
+ [
+ 'key' => __('Allowed IPs'),
+ 'type' => 'custom',
+ 'function' => function (array $data) {
+ if (is_array($data['AuthKey']['allowed_ips'])) {
+ return implode("
", array_map('h', $data['AuthKey']['allowed_ips']));
+ }
+ return __('All');
+ }
+ ],
+ [
+ 'key' => __('Created'),
+ 'path' => 'AuthKey.created',
+ 'type' => 'datetime'
+ ],
+ [
+ 'key' => __('Expiration'),
+ 'path' => 'AuthKey.expiration',
+ 'type' => 'expiration'
+ ],
+ [
+ 'key' => __('Key usage'),
+ 'type' => 'sparkline',
+ 'path' => 'AuthKey.id',
+ 'csv' => [
+ 'data' => $keyUsageCsv,
+ ],
+ 'requirement' => isset($keyUsage),
+ ],
+ [
+ 'key' => __('Last used'),
+ 'raw' => $lastUsed ? $this->Time->time($lastUsed) : __('Not used yet'),
+ 'requirement' => isset($keyUsage),
+ ],
+ [
+ 'key' => __('Unique IPs'),
+ 'raw' => $uniqueIps,
+ 'requirement' => isset($keyUsage),
+ ]
+ ],
+]);
diff --git a/app/View/Elements/genericElements/IndexTable/Fields/datetime.ctp b/app/View/Elements/genericElements/IndexTable/Fields/datetime.ctp
index 34d3f3bbf..b78c98c5a 100644
--- a/app/View/Elements/genericElements/IndexTable/Fields/datetime.ctp
+++ b/app/View/Elements/genericElements/IndexTable/Fields/datetime.ctp
@@ -1,26 +1,27 @@
1) {
- $data = implode(', ', $data);
+$data = Hash::extract($row, $field['data_path']);
+if (is_array($data)) {
+ if (count($data) > 1) {
+ $data = implode(', ', $data);
+ } else {
+ if (count($data) > 0) {
+ $data = $data[0];
} else {
- if (count($data) > 0) {
- $data = $data[0];
- } else {
- $data = '';
- }
+ $data = '';
}
}
- if (empty($data) && !empty($field['empty'])) {
- $data = $field['empty'];
- }
+}
+if (empty($data) && !empty($field['empty'])) {
+ $data = $field['empty'];
+} else {
$data = $this->Time->time($data);
- if (!empty($field['onClick'])) {
- $data = sprintf(
- '%s',
- $field['onClick'],
- $data
- );
- }
- echo $data;
+}
+if (!empty($field['onClick'])) {
+ $data = sprintf(
+ '%s',
+ $field['onClick'],
+ $data
+ );
+}
+echo $data;
diff --git a/app/View/Elements/genericElements/SingleViews/Fields/customField.ctp b/app/View/Elements/genericElements/SingleViews/Fields/customField.ctp
new file mode 100644
index 000000000..a056047b4
--- /dev/null
+++ b/app/View/Elements/genericElements/SingleViews/Fields/customField.ctp
@@ -0,0 +1 @@
+= $field['function']($data);
diff --git a/app/View/Elements/global_menu.ctp b/app/View/Elements/global_menu.ctp
index 60feef643..1935e0c87 100755
--- a/app/View/Elements/global_menu.ctp
+++ b/app/View/Elements/global_menu.ctp
@@ -526,4 +526,3 @@
-
diff --git a/app/View/Events/index.ctp b/app/View/Events/index.ctp
index cacdfb804..f54efefbe 100644
--- a/app/View/Events/index.ctp
+++ b/app/View/Events/index.ctp
@@ -127,6 +127,5 @@
echo $this->Html->css('distribution-graph');
echo $this->Html->script('network-distribution-graph');
?>
-
element('/genericElements/SideMenu/side_menu', array('menuList' => 'event-collection', 'menuItem' => 'index'));
diff --git a/app/View/Events/view.ctp b/app/View/Events/view.ctp
index b52438a09..361adec22 100644
--- a/app/View/Events/view.ctp
+++ b/app/View/Events/view.ctp
@@ -580,4 +580,3 @@ $(function () {
});
});
-
diff --git a/app/View/GalaxyClusters/view.ctp b/app/View/GalaxyClusters/view.ctp
index cf91eff31..f7d869b04 100755
--- a/app/View/GalaxyClusters/view.ctp
+++ b/app/View/GalaxyClusters/view.ctp
@@ -1,85 +1,91 @@
element('/genericElements/SideMenu/side_menu', array('menuList' => 'galaxies', 'menuItem' => 'view_cluster'));
-
- $extendedFromHtml = '';
- if (!empty($cluster['GalaxyCluster']['extended_from'])) {
- $element = $this->element('genericElements/IndexTable/Fields/links', array(
- 'url' => $baseurl . '/galaxy_clusters/view/',
- 'row' => $cluster,
- 'field' => array(
- 'data_path' => 'GalaxyCluster.extended_from.GalaxyCluster.id',
- 'title' => sprintf(__('%s (version: %s)'), $cluster['GalaxyCluster']['extended_from']['GalaxyCluster']['value'], $cluster['GalaxyCluster']['extends_version'])
- ),
- ));
- $extendedFromHtml = sprintf('
blocks and replace by ticks
+ $string = preg_replace('/([^<]+)<\/code>/', '`$1`', $string);
+ return $string;
+ }
+}
diff --git a/app/View/Jobs/index.ctp b/app/View/Jobs/index.ctp
index fd1fd4b30..162e9a22d 100644
--- a/app/View/Jobs/index.ctp
+++ b/app/View/Jobs/index.ctp
@@ -32,7 +32,7 @@
function queueInterval(k, id) {
intervalArray[k] = setInterval(function() {
- if (tabIsActive) {
+ if (!document.hidden) {
$.getJSON('/jobs/getGenerateCorrelationProgress/' + id, function(data) {
var x = document.getElementById("bar" + id);
x.style.width = data+"%";
diff --git a/app/View/Layouts/dashboard.ctp b/app/View/Layouts/dashboard.ctp
index 839d0dad8..479080ccb 100644
--- a/app/View/Layouts/dashboard.ctp
+++ b/app/View/Layouts/dashboard.ctp
@@ -102,7 +102,6 @@
- var tabIsActive = true;
var baseurl = '';
var here = 'params['action'], 0, 6) === 'admin_') {
@@ -111,13 +110,6 @@
echo $baseurl . '/' . h($this->params['controller']) . '/' . h($this->params['action']);
}
?>';
- $(document).ready(function(){
- $(window).blur(function() {
- tabIsActive = false;
- });
- $(window).focus(function() {
- tabIsActive = true;
- });
@@ -125,7 +117,6 @@
- });