mirror of https://github.com/MISP/MISP
Merge branch 'develop' of github.com:MISP/MISP into develop
commit
1c12de00fa
|
@ -1,27 +1,4 @@
|
|||
<?php
|
||||
/**
|
||||
* Application level Controller
|
||||
*
|
||||
* This file is application-wide controller file. You can put all
|
||||
* application-wide controller-related methods here.
|
||||
*
|
||||
* PHP 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package app.Controller
|
||||
* @since CakePHP(tm) v 0.2.9
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
|
||||
// TODO GnuPG encryption has issues when keys are expired
|
||||
|
||||
App::uses('ConnectionManager', 'Model');
|
||||
App::uses('Controller', 'Controller');
|
||||
App::uses('File', 'Utility');
|
||||
|
@ -36,18 +13,17 @@ App::uses('RequestRearrangeTool', 'Tools');
|
|||
* @package app.Controller
|
||||
* @link http://book.cakephp.org/2.0/en/controllers.html#the-app-controller
|
||||
*
|
||||
* @throws ForbiddenException // TODO Exception
|
||||
* @property ACLComponent $ACL
|
||||
* @property RestResponseComponent $RestResponse
|
||||
* @property CRUDComponent $CRUD
|
||||
* @property IndexFilterComponent $IndexFilter
|
||||
* @property RateLimitComponent $RateLimit
|
||||
*/
|
||||
class AppController extends Controller
|
||||
{
|
||||
public $defaultModel = '';
|
||||
|
||||
public $debugMode = false;
|
||||
|
||||
public $helpers = array('Utility', 'OrgImg', 'FontAwesome', 'UserName', 'DataPathCollector');
|
||||
public $helpers = array('OrgImg', 'FontAwesome', 'UserName', 'DataPathCollector');
|
||||
|
||||
private $__queryVersion = '119';
|
||||
public $pyMispVersion = '2.4.135';
|
||||
|
@ -55,12 +31,11 @@ class AppController extends Controller
|
|||
public $phprec = '7.4';
|
||||
public $pythonmin = '3.6';
|
||||
public $pythonrec = '3.7';
|
||||
public $isApiAuthed = false;
|
||||
private $isApiAuthed = false;
|
||||
|
||||
public $baseurl = '';
|
||||
public $sql_dump = false;
|
||||
|
||||
private $isRest = null;
|
||||
public $restResponsePayload = null;
|
||||
|
||||
// Used for _isAutomation(), a check that returns true if the controller & action combo matches an action that is a non-xml and non-json automation method
|
||||
|
@ -72,6 +47,8 @@ class AppController extends Controller
|
|||
);
|
||||
|
||||
protected $_legacyParams = array();
|
||||
/** @var array */
|
||||
public $userRole;
|
||||
|
||||
/** @var User */
|
||||
public $User;
|
||||
|
@ -114,14 +91,12 @@ class AppController extends Controller
|
|||
|
||||
public function beforeFilter()
|
||||
{
|
||||
$this->Auth->loginRedirect = Configure::read('MISP.baseurl') . '/users/routeafterlogin';
|
||||
$this->_setupBaseurl();
|
||||
$this->Auth->loginRedirect = $this->baseurl. '/users/routeafterlogin';
|
||||
|
||||
$customLogout = Configure::read('Plugin.CustomAuth_custom_logout');
|
||||
if ($customLogout) {
|
||||
$this->Auth->logoutRedirect = $customLogout;
|
||||
} else {
|
||||
$this->Auth->logoutRedirect = Configure::read('MISP.baseurl') . '/users/login';
|
||||
}
|
||||
$this->Auth->logoutRedirect = $customLogout ?: ($this->baseurl . '/users/login');
|
||||
|
||||
$this->__sessionMassage();
|
||||
if (Configure::read('Security.allow_cors')) {
|
||||
// Add CORS headers
|
||||
|
@ -152,8 +127,8 @@ class AppController extends Controller
|
|||
$this->sql_dump = intval($this->params['named']['sql']);
|
||||
}
|
||||
|
||||
$this->_setupDatabaseConnection();
|
||||
$this->_setupDebugMode();
|
||||
$this->_setupDatabaseConnection();
|
||||
|
||||
$this->set('ajax', $this->request->is('ajax'));
|
||||
$this->set('queryVersion', $this->__queryVersion);
|
||||
|
@ -166,17 +141,19 @@ class AppController extends Controller
|
|||
Configure::write('Config.language', 'eng');
|
||||
}
|
||||
|
||||
//if fresh installation (salt empty) generate a new salt
|
||||
// For fresh installation (salt empty) generate a new salt
|
||||
if (!Configure::read('Security.salt')) {
|
||||
$this->loadModel('Server');
|
||||
$this->Server->serverSettingsSaveValue('Security.salt', $this->User->generateRandomPassword(32));
|
||||
}
|
||||
|
||||
// Check if the instance has a UUID, if not assign one.
|
||||
if (!Configure::read('MISP.uuid')) {
|
||||
$this->loadModel('Server');
|
||||
$this->Server->serverSettingsSaveValue('MISP.uuid', CakeText::uuid());
|
||||
}
|
||||
// check if Apache provides kerberos authentication data
|
||||
|
||||
// Check if Apache provides kerberos authentication data
|
||||
$authUserFields = $this->User->describeAuthFields();
|
||||
$envvar = Configure::read('ApacheSecureAuth.apacheEnv');
|
||||
if ($envvar && isset($_SERVER[$envvar])) {
|
||||
|
@ -196,10 +173,9 @@ class AppController extends Controller
|
|||
}
|
||||
Configure::write('CurrentController', $this->params['controller']);
|
||||
Configure::write('CurrentAction', $this->params['action']);
|
||||
$versionArray = $this->{$this->modelClass}->checkMISPVersion();
|
||||
$versionArray = $this->User->checkMISPVersion();
|
||||
$this->mispVersion = implode('.', array_values($versionArray));
|
||||
$this->Security->blackHoleCallback = 'blackHole';
|
||||
$this->_setupBaseurl();
|
||||
|
||||
// send users away that are using ancient versions of IE
|
||||
// Make sure to update this if IE 20 comes out :)
|
||||
|
@ -227,232 +203,68 @@ class AppController extends Controller
|
|||
// REST authentication
|
||||
if ($this->_isRest() || $this->_isAutomation()) {
|
||||
// disable CSRF for REST access
|
||||
if (array_key_exists('Security', $this->components)) {
|
||||
if (isset($this->components['Security'])) {
|
||||
$this->Security->csrfCheck = false;
|
||||
}
|
||||
// If enabled, allow passing the API key via a named parameter (for crappy legacy systems only)
|
||||
$namedParamAuthkey = false;
|
||||
if (Configure::read('Security.allow_unsafe_apikey_named_param') && !empty($this->params['named']['apikey'])) {
|
||||
$namedParamAuthkey = $this->params['named']['apikey'];
|
||||
}
|
||||
// Authenticate user with authkey in Authorization HTTP header
|
||||
if (!empty($_SERVER['HTTP_AUTHORIZATION']) || !empty($namedParamAuthkey)) {
|
||||
$found_misp_auth_key = false;
|
||||
$authentication = explode(',', $_SERVER['HTTP_AUTHORIZATION']);
|
||||
if (!empty($namedParamAuthkey)) {
|
||||
$authentication[] = $namedParamAuthkey;
|
||||
}
|
||||
$user = false;
|
||||
foreach ($authentication as $auth_key) {
|
||||
if (preg_match('/^[a-zA-Z0-9]{40}$/', trim($auth_key))) {
|
||||
$found_misp_auth_key = true;
|
||||
$temp = $this->checkAuthUser(trim($auth_key));
|
||||
if ($temp) {
|
||||
$user['User'] = $temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($found_misp_auth_key) {
|
||||
if ($user) {
|
||||
unset($user['User']['gpgkey']);
|
||||
unset($user['User']['certif_public']);
|
||||
// User found in the db, add the user info to the session
|
||||
if (Configure::read('MISP.log_auth')) {
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$this->Log->create();
|
||||
$log = array(
|
||||
'org' => $user['User']['Organisation']['name'],
|
||||
'model' => 'User',
|
||||
'model_id' => $user['User']['id'],
|
||||
'email' => $user['User']['email'],
|
||||
'action' => 'auth',
|
||||
'title' => 'Successful authentication using API key',
|
||||
'change' => 'HTTP method: ' . $_SERVER['REQUEST_METHOD'] . PHP_EOL . 'Target: ' . $this->here,
|
||||
);
|
||||
$this->Log->save($log);
|
||||
}
|
||||
$this->Session->renew();
|
||||
$this->Session->write(AuthComponent::$sessionKey, $user['User']);
|
||||
$this->isApiAuthed = true;
|
||||
} else {
|
||||
// User not authenticated correctly
|
||||
// reset the session information
|
||||
$redis = $this->{$this->modelClass}->setupRedis();
|
||||
if ($redis && !$redis->exists('misp:auth_fail_throttling:' . trim($auth_key))) {
|
||||
$redis->set('misp:auth_fail_throttling:' . trim($auth_key), 1);
|
||||
$redis->expire('misp:auth_fail_throttling:' . trim($auth_key), 3600);
|
||||
$this->Session->destroy();
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$this->Log->create();
|
||||
$log = array(
|
||||
'org' => 'SYSTEM',
|
||||
'model' => 'User',
|
||||
'model_id' => 0,
|
||||
'email' => 'SYSTEM',
|
||||
'action' => 'auth_fail',
|
||||
'title' => 'Failed authentication using API key (' . trim($auth_key) . ')',
|
||||
'change' => null,
|
||||
);
|
||||
$this->Log->save($log);
|
||||
}
|
||||
throw new ForbiddenException('Authentication failed. Please make sure you pass the API key of an API enabled user along in the Authorization header.');
|
||||
}
|
||||
unset($user);
|
||||
}
|
||||
}
|
||||
if ($this->Auth->user() == null) {
|
||||
if ($this->__loginByAuthKey() === false || $this->Auth->user() === null) {
|
||||
throw new ForbiddenException('Authentication failed. Please make sure you pass the API key of an API enabled user along in the Authorization header.');
|
||||
}
|
||||
} elseif (!$this->Session->read(AuthComponent::$sessionKey)) {
|
||||
$this->_loadAuthenticationPlugins();
|
||||
}
|
||||
}
|
||||
$this->set('externalAuthUser', $userLoggedIn);
|
||||
// user must accept terms
|
||||
//
|
||||
// grab the base path from our base url for use in the following checks
|
||||
$base_dir = parse_url($this->baseurl, PHP_URL_PATH);
|
||||
|
||||
// if MISP is running out of the web root already, just set this variable to blank so we don't wind up with '//' in the following if statements
|
||||
if ($base_dir == '/') {
|
||||
$base_dir = '';
|
||||
}
|
||||
$user = $this->Auth->user();
|
||||
if ($user) {
|
||||
Configure::write('CurrentUserId', $user['id']);
|
||||
$this->__logAccess($user);
|
||||
|
||||
if ($this->Auth->user()) {
|
||||
Configure::write('CurrentUserId', $this->Auth->user('id'));
|
||||
$this->User->setMonitoring($this->Auth->user());
|
||||
if (Configure::read('MISP.log_user_ips')) {
|
||||
$redis = $this->{$this->modelClass}->setupRedis();
|
||||
if ($redis) {
|
||||
$redis->set('misp:ip_user:' . trim($_SERVER['REMOTE_ADDR']), $this->Auth->user('id'));
|
||||
$redis->expire('misp:ip_user:' . trim($_SERVER['REMOTE_ADDR']), 60*60*24*30);
|
||||
$redis->sadd('misp:user_ip:' . $this->Auth->user('id'), trim($_SERVER['REMOTE_ADDR']));
|
||||
// Try to run updates
|
||||
if ($user['Role']['perm_site_admin'] || (Configure::read('MISP.live') && !$this->_isRest())) {
|
||||
$this->User->runUpdates();
|
||||
}
|
||||
|
||||
// Put username to response header for webserver or proxy logging
|
||||
if (Configure::read('Security.username_in_response_header')) {
|
||||
$headerValue = $user['email'];
|
||||
if (isset($user['logged_by_authkey']) && $user['logged_by_authkey']) {
|
||||
$headerValue .= isset($user['authkey_id']) ? "/API/{$user['authkey_id']}" : '/API/default';
|
||||
}
|
||||
$this->response->header('X-Username', $headerValue);
|
||||
$this->RestResponse->setHeader('X-Username', $headerValue);
|
||||
}
|
||||
// update script
|
||||
if ($this->Auth->user('Role')['perm_site_admin'] || (Configure::read('MISP.live') && !$this->_isRest())) {
|
||||
$this->{$this->modelClass}->runUpdates();
|
||||
|
||||
if (!$this->__verifyUser($user)) {
|
||||
$this->_stop(); // just for sure
|
||||
}
|
||||
$user = $this->Auth->user();
|
||||
if (!isset($user['force_logout']) || $user['force_logout']) {
|
||||
$this->loadModel('User');
|
||||
$this->User->id = $this->Auth->user('id');
|
||||
$this->User->saveField('force_logout', false);
|
||||
|
||||
if (isset($user['logged_by_authkey']) && $user['logged_by_authkey'] && !($this->_isRest() || $this->_isAutomation())) {
|
||||
throw new ForbiddenException("When user is authenticated by authkey, just REST request can be processed");
|
||||
}
|
||||
if ($this->Auth->user('disabled')) {
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$this->Log->create();
|
||||
$log = array(
|
||||
'org' => $this->Auth->user('Organisation')['name'],
|
||||
'model' => 'User',
|
||||
'model_id' => $this->Auth->user('id'),
|
||||
'email' => $this->Auth->user('email'),
|
||||
'action' => 'auth_fail',
|
||||
'title' => 'Login attempt by disabled user.',
|
||||
'change' => null,
|
||||
);
|
||||
$this->Log->save($log);
|
||||
$this->Auth->logout();
|
||||
if ($this->_isRest()) {
|
||||
throw new ForbiddenException('Authentication failed. Your user account has been disabled.');
|
||||
} else {
|
||||
$this->Flash->error('Your user account has been disabled.', array('key' => 'error'));
|
||||
$this->_redirectToLogin();
|
||||
}
|
||||
|
||||
// Put token expiration time to response header that can be processed by automation tool
|
||||
if (isset($user['authkey_expiration']) && $user['authkey_expiration']) {
|
||||
$expiration = date('c', $user['authkey_expiration']);
|
||||
$this->response->header('X-Auth-Key-Expiration', $expiration);
|
||||
$this->RestResponse->setHeader('X-Auth-Key-Expiration', $expiration);
|
||||
}
|
||||
|
||||
$this->set('default_memory_limit', ini_get('memory_limit'));
|
||||
if (isset($this->Auth->user('Role')['memory_limit'])) {
|
||||
if ($this->Auth->user('Role')['memory_limit'] !== '') {
|
||||
ini_set('memory_limit', $this->Auth->user('Role')['memory_limit']);
|
||||
}
|
||||
if (isset($user['Role']['memory_limit']) && $user['Role']['memory_limit'] !== '') {
|
||||
ini_set('memory_limit', $user['Role']['memory_limit']);
|
||||
}
|
||||
$this->set('default_max_execution_time', ini_get('max_execution_time'));
|
||||
if (isset($this->Auth->user('Role')['max_execution_time'])) {
|
||||
if ($this->Auth->user('Role')['max_execution_time'] !== '') {
|
||||
ini_set('max_execution_time', $this->Auth->user('Role')['max_execution_time']);
|
||||
}
|
||||
if (isset($user['Role']['max_execution_time']) && $user['Role']['max_execution_time'] !== '') {
|
||||
ini_set('max_execution_time', $user['Role']['max_execution_time']);
|
||||
}
|
||||
} else {
|
||||
$pre_auth_actions = array('login', 'register', 'getGpgPublicKey');
|
||||
if (!empty(Configure::read('Security.email_otp_enabled'))) {
|
||||
$pre_auth_actions[] = 'email_otp';
|
||||
}
|
||||
if ($this->params['controller'] !== 'users' || !in_array($this->params['action'], $pre_auth_actions)) {
|
||||
if (!$this->request->is('ajax')) {
|
||||
$this->Session->write('pre_login_requested_url', $this->here);
|
||||
}
|
||||
$this->_redirectToLogin();
|
||||
}
|
||||
}
|
||||
|
||||
// check if MISP is live
|
||||
if ($this->Auth->user() && !Configure::read('MISP.live')) {
|
||||
$role = $this->getActions();
|
||||
if (!$role['perm_site_admin']) {
|
||||
$message = Configure::read('MISP.maintenance_message');
|
||||
if (empty($message)) {
|
||||
$this->loadModel('Server');
|
||||
$message = $this->Server->serverSettings['MISP']['maintenance_message']['value'];
|
||||
}
|
||||
if (strpos($message, '$email') && Configure::read('MISP.email')) {
|
||||
$email = Configure::read('MISP.email');
|
||||
$message = str_replace('$email', $email, $message);
|
||||
}
|
||||
$this->Flash->info($message);
|
||||
$this->Auth->logout();
|
||||
throw new MethodNotAllowedException($message);//todo this should pb be removed?
|
||||
} else {
|
||||
$this->Flash->error(__('Warning: MISP is currently disabled for all users. Enable it in Server Settings (Administration -> Server Settings -> MISP tab -> live). An update might also be in progress, you can see the progress in ') , array('params' => array('url' => $this->baseurl . '/servers/updateProgress/', 'urlName' => __('Update Progress')), 'clear' => 1));
|
||||
}
|
||||
}
|
||||
if ($this->Session->check(AuthComponent::$sessionKey)) {
|
||||
if ($this->action !== 'checkIfLoggedIn' || $this->request->params['controller'] !== 'users') {
|
||||
$this->User->id = $this->Auth->user('id');
|
||||
if (!$this->User->exists()) {
|
||||
$message = __('Something went wrong. Your user account that you are authenticated with doesn\'t exist anymore.');
|
||||
if ($this->_isRest) {
|
||||
echo $this->RestResponse->throwException(
|
||||
401,
|
||||
$message
|
||||
);
|
||||
} else {
|
||||
$this->Flash->info($message);
|
||||
}
|
||||
$this->Auth->logout();
|
||||
$this->_redirectToLogin();
|
||||
}
|
||||
if (!empty(Configure::read('MISP.terms_file')) && !$this->Auth->user('termsaccepted') && (!in_array($this->request->here, array($base_dir.'/users/terms', $base_dir.'/users/logout', $base_dir.'/users/login', $base_dir.'/users/downloadTerms')))) {
|
||||
//if ($this->_isRest()) throw new MethodNotAllowedException('You have not accepted the terms of use yet, please log in via the web interface and accept them.');
|
||||
if (!$this->_isRest()) {
|
||||
$this->redirect(array('controller' => 'users', 'action' => 'terms', 'admin' => false));
|
||||
}
|
||||
} elseif ($this->Auth->user('change_pw') && (!in_array($this->request->here, array($base_dir.'/users/terms', $base_dir.'/users/change_pw', $base_dir.'/users/logout', $base_dir.'/users/login')))) {
|
||||
//if ($this->_isRest()) throw new MethodNotAllowedException('Your user account is expecting a password change, please log in via the web interface and change it before proceeding.');
|
||||
if (!$this->_isRest()) {
|
||||
$this->redirect(array('controller' => 'users', 'action' => 'change_pw', 'admin' => false));
|
||||
}
|
||||
} elseif (!$this->_isRest() && !($this->params['controller'] == 'news' && $this->params['action'] == 'index') && (!in_array($this->request->here, array($base_dir.'/users/terms', $base_dir.'/users/change_pw', $base_dir.'/users/logout', $base_dir.'/users/login')))) {
|
||||
$newsread = $this->User->field('newsread', array('User.id' => $this->Auth->user('id')));
|
||||
$this->loadModel('News');
|
||||
$latest_news = $this->News->field('date_created', array(), 'date_created DESC');
|
||||
if ($latest_news && $newsread < $latest_news) {
|
||||
$this->redirect(array('controller' => 'news', 'action' => 'index', 'admin' => false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($base_dir);
|
||||
// We don't want to run these role checks before the user is logged in, but we want them available for every view once the user is logged on
|
||||
// instead of using checkAction(), like we normally do from controllers when trying to find out about a permission flag, we can use getActions()
|
||||
// getActions returns all the flags in a single SQL query
|
||||
if ($this->Auth->user()) {
|
||||
$this->set('mispVersion', implode('.', array($versionArray['major'], $versionArray['minor'], 0)));
|
||||
$this->set('mispVersion', "{$versionArray['major']}.{$versionArray['minor']}.0");
|
||||
$this->set('mispVersionFull', $this->mispVersion);
|
||||
$role = $this->getActions();
|
||||
$this->set('me', $this->Auth->user());
|
||||
$this->set('me', $user);
|
||||
$role = $user['Role'];
|
||||
$this->set('isAdmin', $role['perm_admin']);
|
||||
$this->set('isSiteAdmin', $role['perm_site_admin']);
|
||||
$this->set('hostOrgUser', $this->Auth->user('org_id') == Configure::read('MISP.host_org_id'));
|
||||
$this->set('hostOrgUser', $user['org_id'] == Configure::read('MISP.host_org_id'));
|
||||
$this->set('isAclAdd', $role['perm_add']);
|
||||
$this->set('isAclModify', $role['perm_modify']);
|
||||
$this->set('isAclModifyOrg', $role['perm_modify_org']);
|
||||
|
@ -475,48 +287,26 @@ class AppController extends Controller
|
|||
$this->set('aclComponent', $this->ACL);
|
||||
$this->userRole = $role;
|
||||
|
||||
$this->set('loggedInUserName', $this->__convertEmailToName($this->Auth->user('email')));
|
||||
$this->set('loggedInUserName', $this->__convertEmailToName($user['email']));
|
||||
$this->__accessMonitor($user);
|
||||
|
||||
if (
|
||||
Configure::read('MISP.log_paranoid') ||
|
||||
!empty(Configure::read('Security.monitored'))
|
||||
) {
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$this->Log->create();
|
||||
$change = 'HTTP method: ' . $_SERVER['REQUEST_METHOD'] . PHP_EOL . 'Target: ' . $this->here;
|
||||
if (
|
||||
(
|
||||
$this->request->is('post') ||
|
||||
$this->request->is('put')
|
||||
) &&
|
||||
(
|
||||
!empty(Configure::read('MISP.log_paranoid_include_post_body')) ||
|
||||
!empty(Configure::read('Security.monitored'))
|
||||
)
|
||||
) {
|
||||
$payload = $this->request->input();
|
||||
if (!empty($payload['_Token'])) {
|
||||
unset($payload['_Token']);
|
||||
}
|
||||
$change .= PHP_EOL . 'Request body: ' . json_encode($payload);
|
||||
}
|
||||
$log = array(
|
||||
'org' => $this->Auth->user('Organisation')['name'],
|
||||
'model' => 'User',
|
||||
'model_id' => $this->Auth->user('id'),
|
||||
'email' => $this->Auth->user('email'),
|
||||
'action' => 'request',
|
||||
'title' => 'Paranoid log entry',
|
||||
'change' => $change,
|
||||
);
|
||||
$this->Log->save($log);
|
||||
}
|
||||
} else {
|
||||
$pre_auth_actions = array('login', 'register', 'getGpgPublicKey');
|
||||
if (!empty(Configure::read('Security.email_otp_enabled'))) {
|
||||
$pre_auth_actions[] = 'email_otp';
|
||||
}
|
||||
if (!$this->_isControllerAction(['users' => $pre_auth_actions])) {
|
||||
if (!$this->request->is('ajax')) {
|
||||
$this->Session->write('pre_login_requested_url', $this->here);
|
||||
}
|
||||
$this->_redirectToLogin();
|
||||
}
|
||||
|
||||
$this->set('me', false);
|
||||
}
|
||||
|
||||
if ($this->Auth->user() && $this->_isSiteAdmin()) {
|
||||
if (Configure::read('Session.defaults') == 'database') {
|
||||
if (Configure::read('Session.defaults') === 'database') {
|
||||
$db = ConnectionManager::getDataSource('default');
|
||||
$sqlResult = $db->query('SELECT COUNT(id) AS session_count FROM cake_sessions WHERE expires < ' . time() . ';');
|
||||
if (isset($sqlResult[0][0]['session_count']) && $sqlResult[0][0]['session_count'] > 1000) {
|
||||
|
@ -545,28 +335,326 @@ class AppController extends Controller
|
|||
}
|
||||
}
|
||||
}
|
||||
$this->components['RestResponse']['sql_dump'] = $this->sql_dump;
|
||||
|
||||
// Notifications and homepage is not necessary for AJAX or REST requests
|
||||
if ($this->Auth->user() && !$this->_isRest() && !$this->request->is('ajax')) {
|
||||
if ($this->request->params['controller'] === 'users' && $this->request->params['action'] === 'dashboard') {
|
||||
$notifications = $this->{$this->modelClass}->populateNotifications($this->Auth->user());
|
||||
$notifications = $this->User->populateNotifications($this->Auth->user());
|
||||
} else {
|
||||
$notifications = $this->{$this->modelClass}->populateNotifications($this->Auth->user(), 'fast');
|
||||
$notifications = $this->User->populateNotifications($this->Auth->user(), 'fast');
|
||||
}
|
||||
$this->set('notifications', $notifications);
|
||||
|
||||
$this->loadModel('UserSetting');
|
||||
$homepage = $this->UserSetting->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'UserSetting.user_id' => $this->Auth->user('id'),
|
||||
'UserSetting.setting' => 'homepage'
|
||||
),
|
||||
'contain' => array('User.id', 'User.org_id')
|
||||
));
|
||||
$homepage = $this->User->UserSetting->getValueForUser($this->Auth->user('id'), 'homepage');
|
||||
if (!empty($homepage)) {
|
||||
$this->set('homepage', $homepage['UserSetting']['value']);
|
||||
$this->set('homepage', $homepage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|bool True if authkey was correct, False if incorrect and Null if not provided
|
||||
* @throws Exception
|
||||
*/
|
||||
private function __loginByAuthKey()
|
||||
{
|
||||
if (Configure::read('Security.authkey_keep_session') && $this->Auth->user()) {
|
||||
// Do not check authkey if session is establish and correct, just close session to allow multiple requests
|
||||
session_write_close();
|
||||
return true;
|
||||
}
|
||||
|
||||
// If enabled, allow passing the API key via a named parameter (for crappy legacy systems only)
|
||||
$namedParamAuthkey = false;
|
||||
if (Configure::read('Security.allow_unsafe_apikey_named_param') && !empty($this->params['named']['apikey'])) {
|
||||
$namedParamAuthkey = $this->params['named']['apikey'];
|
||||
}
|
||||
// Authenticate user with authkey in Authorization HTTP header
|
||||
if (!empty($_SERVER['HTTP_AUTHORIZATION']) || !empty($namedParamAuthkey)) {
|
||||
$foundMispAuthKey = false;
|
||||
$authentication = explode(',', $_SERVER['HTTP_AUTHORIZATION']);
|
||||
if (!empty($namedParamAuthkey)) {
|
||||
$authentication[] = $namedParamAuthkey;
|
||||
}
|
||||
$user = false;
|
||||
foreach ($authentication as $authKey) {
|
||||
$authKey = trim($authKey);
|
||||
if (preg_match('/^[a-zA-Z0-9]{40}$/', $authKey)) {
|
||||
$foundMispAuthKey = true;
|
||||
$temp = $this->checkAuthUser($authKey);
|
||||
if ($temp) {
|
||||
$user = $temp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($foundMispAuthKey) {
|
||||
$authKeyToStore = substr($authKey, 0, 4)
|
||||
. str_repeat('*', 32)
|
||||
. substr($authKey, -4);
|
||||
if ($user) {
|
||||
unset($user['gpgkey']);
|
||||
unset($user['certif_public']);
|
||||
// User found in the db, add the user info to the session
|
||||
if (Configure::read('MISP.log_auth')) {
|
||||
$this->loadModel('Log');
|
||||
$this->Log->create();
|
||||
$log = array(
|
||||
'org' => $user['Organisation']['name'],
|
||||
'model' => 'User',
|
||||
'model_id' => $user['id'],
|
||||
'email' => $user['email'],
|
||||
'action' => 'auth',
|
||||
'title' => "Successful authentication using API key ($authKeyToStore)",
|
||||
'change' => 'HTTP method: ' . $_SERVER['REQUEST_METHOD'] . PHP_EOL . 'Target: ' . $this->here,
|
||||
);
|
||||
$this->Log->save($log);
|
||||
}
|
||||
$this->Session->renew();
|
||||
$this->Session->write(AuthComponent::$sessionKey, $user);
|
||||
$this->isApiAuthed = true;
|
||||
return true;
|
||||
} 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);
|
||||
$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->Session->destroy();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if:
|
||||
* - user exists in database
|
||||
* - is not disabled
|
||||
* - need to force logout
|
||||
* - accepted terms and conditions
|
||||
* - must change password
|
||||
* - reads latest news
|
||||
*
|
||||
* @param array $user
|
||||
* @return bool
|
||||
*/
|
||||
private function __verifyUser(array $user)
|
||||
{
|
||||
// Skip these checks for 'checkIfLoggedIn' action to make that call fast
|
||||
if ($this->_isControllerAction(['users' => ['checkIfLoggedIn']])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load last user profile modification from database
|
||||
$userFromDb = $this->User->find('first', [
|
||||
'conditions' => ['id' => $user['id']],
|
||||
'recursive' => -1,
|
||||
'fields' => ['date_modified'],
|
||||
]);
|
||||
|
||||
// Check if user with given ID exists
|
||||
if (!$userFromDb) {
|
||||
$message = __('Something went wrong. Your user account that you are authenticated with doesn\'t exist anymore.');
|
||||
if ($this->_isRest()) {
|
||||
// TODO: Why not exception?
|
||||
$response = $this->RestResponse->throwException(401, $message);
|
||||
$response->send();
|
||||
$this->_stop();
|
||||
} else {
|
||||
$this->Flash->info($message);
|
||||
$this->Auth->logout();
|
||||
$this->_redirectToLogin();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if session data contain latest changes from db
|
||||
if ((int)$user['date_modified'] < (int)$userFromDb['User']['date_modified']) {
|
||||
$user = $this->_refreshAuth(); // session data are old, reload from database
|
||||
}
|
||||
|
||||
// Check if MISP access is enabled
|
||||
if (!Configure::read('MISP.live')) {
|
||||
if (!$user['Role']['perm_site_admin']) {
|
||||
$message = Configure::read('MISP.maintenance_message');
|
||||
if (empty($message)) {
|
||||
$this->loadModel('Server');
|
||||
$message = $this->Server->serverSettings['MISP']['maintenance_message']['value'];
|
||||
}
|
||||
if (strpos($message, '$email') && Configure::read('MISP.email')) {
|
||||
$email = Configure::read('MISP.email');
|
||||
$message = str_replace('$email', $email, $message);
|
||||
}
|
||||
$this->Flash->info($message);
|
||||
$this->Auth->logout();
|
||||
throw new MethodNotAllowedException($message);//todo this should pb be removed?
|
||||
} else {
|
||||
$this->Flash->error(__('Warning: MISP is currently disabled for all users. Enable it in Server Settings (Administration -> Server Settings -> MISP tab -> live). An update might also be in progress, you can see the progress in ') , array('params' => array('url' => $this->baseurl . '/servers/updateProgress/', 'urlName' => __('Update Progress')), 'clear' => 1));
|
||||
}
|
||||
}
|
||||
|
||||
// Force logout doesn't make sense for API key authentication
|
||||
if (!$this->isApiAuthed && $user['force_logout']) {
|
||||
$this->User->id = $user['id'];
|
||||
$this->User->saveField('force_logout', false);
|
||||
$this->Auth->logout();
|
||||
$this->_redirectToLogin();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($user['disabled']) {
|
||||
$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()) {
|
||||
throw new ForbiddenException('Authentication failed. Your user account has been disabled.');
|
||||
} else {
|
||||
$this->Flash->error(__('Your user account has been disabled.'));
|
||||
$this->_redirectToLogin();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if auth key is not expired. Make sense when Security.authkey_keep_session is enabled.
|
||||
if (isset($user['authkey_expiration']) && $user['authkey_expiration']) {
|
||||
$time = isset($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : time();
|
||||
if ($user['authkey_expiration'] < $time) {
|
||||
$this->Auth->logout();
|
||||
throw new ForbiddenException('Auth key is expired');
|
||||
}
|
||||
}
|
||||
|
||||
$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) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if user accepted terms and conditions
|
||||
if (!$user['termsaccepted'] && !empty(Configure::read('MISP.terms_file')) && !$this->_isControllerAction(['users' => ['terms', 'logout', 'login', 'downloadTerms']])) {
|
||||
//if ($this->_isRest()) throw new MethodNotAllowedException('You have not accepted the terms of use yet, please log in via the web interface and accept them.');
|
||||
$this->redirect(array('controller' => 'users', 'action' => 'terms', 'admin' => false));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if user must change password
|
||||
if ($user['change_pw'] && !$this->_isControllerAction(['users' => ['terms', 'change_pw', 'logout', 'login']])) {
|
||||
//if ($this->_isRest()) throw new MethodNotAllowedException('Your user account is expecting a password change, please log in via the web interface and change it before proceeding.');
|
||||
$this->redirect(array('controller' => 'users', 'action' => 'change_pw', 'admin' => false));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if user must read news
|
||||
if (!$this->_isControllerAction(['news' => ['index'], 'users' => ['terms', 'change_pw', 'login', 'logout']])) {
|
||||
$this->loadModel('News');
|
||||
$latestNewsCreated = $this->News->field('date_created', array(), 'date_created DESC');
|
||||
if ($latestNewsCreated && $user['newsread'] < $latestNewsCreated) {
|
||||
$this->redirect(array('controller' => 'news', 'action' => 'index', 'admin' => false));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $actionsToCheck
|
||||
* @return bool
|
||||
*/
|
||||
private function _isControllerAction($actionsToCheck = [])
|
||||
{
|
||||
$controller = Inflector::variable($this->request->params['controller']);
|
||||
if (!isset($actionsToCheck[$controller])) {
|
||||
return false;
|
||||
}
|
||||
return in_array($this->action, $actionsToCheck[$controller], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* User access monitoring
|
||||
* @param array $user
|
||||
*/
|
||||
private function __logAccess(array $user)
|
||||
{
|
||||
$logUserIps = Configure::read('MISP.log_user_ips');
|
||||
if (!$logUserIps) {
|
||||
return;
|
||||
}
|
||||
|
||||
$redis = $this->User->setupRedis();
|
||||
if (!$redis) {
|
||||
return;
|
||||
}
|
||||
|
||||
$remoteAddress = trim($_SERVER['REMOTE_ADDR']);
|
||||
|
||||
$pipe = $redis->multi(Redis::PIPELINE);
|
||||
// keep for 30 days
|
||||
$pipe->setex('misp:ip_user:' . $remoteAddress, 60 * 60 * 24 * 30, $user['id']);
|
||||
$pipe->sadd('misp:user_ip:' . $user['id'], $remoteAddress);
|
||||
|
||||
// Log key usage if enabled
|
||||
if (isset($user['authkey_id']) && Configure::read('MISP.log_user_ips_authkeys')) {
|
||||
// Use request time if defined
|
||||
$time = isset($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : time();
|
||||
$hashKey = date("Y-m-d", $time) . ":$remoteAddress";
|
||||
$pipe->hIncrBy("misp:authkey_usage:{$user['authkey_id']}", $hashKey, 1);
|
||||
// delete after one year of inactivity
|
||||
$pipe->expire("misp:authkey_usage:{$user['authkey_id']}", 3600 * 24 * 365);
|
||||
$pipe->set("misp:authkey_last_usage:{$user['authkey_id']}", $time);
|
||||
}
|
||||
$pipe->exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $user
|
||||
* @throws Exception
|
||||
*/
|
||||
private function __accessMonitor(array $user)
|
||||
{
|
||||
$userMonitoringEnabled = Configure::read('Security.user_monitoring_enabled');
|
||||
if ($userMonitoringEnabled) {
|
||||
$redis = $this->User->setupRedis();
|
||||
$userMonitoringEnabled = $redis && $redis->sismember('misp:monitored_users', $user['id']);
|
||||
}
|
||||
|
||||
if (Configure::read('MISP.log_paranoid') || $userMonitoringEnabled) {
|
||||
$change = 'HTTP method: ' . $_SERVER['REQUEST_METHOD'] . PHP_EOL . 'Target: ' . $this->here;
|
||||
if (
|
||||
(
|
||||
$this->request->is('post') ||
|
||||
$this->request->is('put')
|
||||
) &&
|
||||
(
|
||||
!empty(Configure::read('MISP.log_paranoid_include_post_body')) ||
|
||||
$userMonitoringEnabled
|
||||
)
|
||||
) {
|
||||
$payload = $this->request->input();
|
||||
$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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -601,7 +689,7 @@ class AppController extends Controller
|
|||
|
||||
public function afterFilter()
|
||||
{
|
||||
if ($this->isApiAuthed && $this->_isRest() && $this->Session->started()) {
|
||||
if ($this->isApiAuthed && $this->_isRest() && !Configure::read('Security.authkey_keep_session')) {
|
||||
$this->Session->destroy();
|
||||
}
|
||||
}
|
||||
|
@ -648,16 +736,17 @@ class AppController extends Controller
|
|||
/*
|
||||
* Sanitize the configured `MISP.baseurl` and expose it to the view as `baseurl`.
|
||||
*/
|
||||
protected function _setupBaseurl() {
|
||||
protected function _setupBaseurl()
|
||||
{
|
||||
// Let us access $baseurl from all views
|
||||
$baseurl = Configure::read('MISP.baseurl');
|
||||
if (substr($baseurl, -1) == '/') {
|
||||
if (substr($baseurl, -1) === '/') {
|
||||
// if the baseurl has a trailing slash, remove it. It can lead to issues with the CSRF protection
|
||||
$baseurl = rtrim($baseurl, '/');
|
||||
$this->loadModel('Server');
|
||||
$this->Server->serverSettingsSaveValue('MISP.baseurl', $baseurl);
|
||||
}
|
||||
if (trim($baseurl) == 'http://') {
|
||||
if (trim($baseurl) === 'http://') {
|
||||
$this->Server->serverSettingsSaveValue('MISP.baseurl', '');
|
||||
}
|
||||
$this->baseurl = $baseurl;
|
||||
|
@ -683,8 +772,6 @@ class AppController extends Controller
|
|||
throw new BadRequestException('The request has been black-holed');
|
||||
}
|
||||
|
||||
public $userRole = null;
|
||||
|
||||
protected function _isRest()
|
||||
{
|
||||
return $this->IndexFilter->isRest();
|
||||
|
@ -692,12 +779,7 @@ class AppController extends Controller
|
|||
|
||||
protected function _isAutomation()
|
||||
{
|
||||
foreach ($this->automationArray as $controllerName => $controllerActions) {
|
||||
if ($this->params['controller'] == $controllerName && in_array($this->params['action'], $controllerActions)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return $this->IndexFilter->isApiFunction($this->params['controller'], $this->params['action']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -829,34 +911,12 @@ class AppController extends Controller
|
|||
return $data;
|
||||
}
|
||||
|
||||
// pass an action to this method for it to check the active user's access to the action
|
||||
public function checkAction($action = 'perm_sync')
|
||||
{
|
||||
$this->loadModel('Role');
|
||||
$this->Role->recursive = -1;
|
||||
$role = $this->Role->findById($this->Auth->user('role_id'));
|
||||
if ($role['Role'][$action]) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// returns the role of the currently authenticated user as an array, used to set the permission variables for views in the AppController's beforeFilter() method
|
||||
public function getActions()
|
||||
{
|
||||
$this->loadModel('Role');
|
||||
$this->Role->recursive = -1;
|
||||
$role = $this->Role->findById($this->Auth->user('role_id'));
|
||||
return $role['Role'];
|
||||
}
|
||||
|
||||
public function checkAuthUser($authkey)
|
||||
{
|
||||
if (Configure::read('Security.advanced_authkeys')) {
|
||||
$this->loadModel('AuthKey');
|
||||
$user = $this->AuthKey->getAuthUserByAuthKey($authkey);
|
||||
} else {
|
||||
$this->loadModel('User');
|
||||
$user = $this->User->getAuthUserByAuthKey($authkey);
|
||||
}
|
||||
|
||||
|
@ -866,22 +926,16 @@ class AppController extends Controller
|
|||
if (!$user['Role']['perm_auth']) {
|
||||
return false;
|
||||
}
|
||||
if ($user['Role']['perm_site_admin']) {
|
||||
$user['siteadmin'] = true;
|
||||
}
|
||||
$user['logged_by_authkey'] = true;
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function checkExternalAuthUser($authkey)
|
||||
{
|
||||
$this->loadModel('User');
|
||||
$user = $this->User->getAuthUserByExternalAuth($authkey);
|
||||
if (empty($user)) {
|
||||
return false;
|
||||
}
|
||||
if ($user['Role']['perm_site_admin']) {
|
||||
$user['siteadmin'] = true;
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
|
@ -1270,7 +1324,7 @@ class AppController extends Controller
|
|||
$final = $this->$scope->restSearch($user, $returnFormat, $filters, false, false, $elementCounter, $renderView);
|
||||
if (!empty($renderView) && !empty($final)) {
|
||||
$this->layout = false;
|
||||
$final = json_decode($final, true);
|
||||
$final = json_decode($final->intoString(), true);
|
||||
foreach ($final as $key => $data) {
|
||||
$this->set($key, $data);
|
||||
}
|
||||
|
@ -1331,4 +1385,30 @@ class AppController extends Controller
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh user data in session, but keep information about authkey.
|
||||
* @return array User data in auth format
|
||||
*/
|
||||
protected function _refreshAuth()
|
||||
{
|
||||
$sessionUser = $this->Auth->user();
|
||||
$user = $this->User->getAuthUser($sessionUser['id']);
|
||||
if (!$user) {
|
||||
throw new RuntimeException("User with ID {$sessionUser['id']} not exists.");
|
||||
}
|
||||
if (isset($sessionUser['authkey_id'])) {
|
||||
$this->loadModel('AuthKey');
|
||||
if (!$this->AuthKey->exists($sessionUser['authkey_id'])) {
|
||||
throw new RuntimeException("Auth key with ID {$sessionUser['authkey_id']} not exists.");
|
||||
}
|
||||
}
|
||||
foreach (['authkey_id', 'authkey_expiration', 'logged_by_authkey'] as $copy) {
|
||||
if (isset($sessionUser[$copy])) {
|
||||
$user[$copy] = $sessionUser[$copy];
|
||||
}
|
||||
}
|
||||
$this->Auth->login($user);
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property AuthKey $AuthKey
|
||||
*/
|
||||
class AuthKeysController extends AppController
|
||||
{
|
||||
public $components = array(
|
||||
|
@ -23,13 +26,22 @@ class AuthKeysController extends AppController
|
|||
$this->set('user_id', $id);
|
||||
$conditions['AND'][] = ['AuthKey.user_id' => $id];
|
||||
}
|
||||
$keyUsageEnabled = Configure::read('MISP.log_user_ips') && Configure::read('MISP.log_user_ips_authkeys');
|
||||
$this->CRUD->index([
|
||||
'filters' => ['User.username', 'authkey', 'comment', 'User.id'],
|
||||
'quickFilters' => ['authkey', 'comment'],
|
||||
'contain' => ['User'],
|
||||
'filters' => ['User.email', 'authkey_start', 'authkey_end', 'comment', 'User.id'],
|
||||
'quickFilters' => ['comment', 'authkey_start', 'authkey_end', 'User.email'],
|
||||
'contain' => ['User.id', 'User.email'],
|
||||
'conditions' => $conditions,
|
||||
'afterFind' => function (array $authKeys) {
|
||||
'afterFind' => function (array $authKeys) use ($keyUsageEnabled) {
|
||||
if ($keyUsageEnabled) {
|
||||
$keyIds = Hash::extract($authKeys, "{n}.AuthKey.id");
|
||||
$lastUsedById = $this->AuthKey->getLastUsageForKeys($keyIds);
|
||||
}
|
||||
foreach ($authKeys as &$authKey) {
|
||||
if ($keyUsageEnabled) {
|
||||
$lastUsed = $lastUsedById[$authKey['AuthKey']['id']];
|
||||
$authKey['AuthKey']['last_used'] = $lastUsed;
|
||||
}
|
||||
unset($authKey['AuthKey']['authkey']);
|
||||
}
|
||||
return $authKeys;
|
||||
|
@ -38,8 +50,12 @@ class AuthKeysController extends AppController
|
|||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('metaGroup', $this->_isAdmin ? 'admin' : 'globalActions');
|
||||
$this->set('metaAction', 'authkeys_index');
|
||||
$this->set('title_for_layout', __('Auth Keys'));
|
||||
$this->set('keyUsageEnabled', $keyUsageEnabled);
|
||||
$this->set('menuData', [
|
||||
'menuList' => $this->_isSiteAdmin() ? 'admin' : 'globalActions',
|
||||
'menuItem' => 'authkeys_index',
|
||||
]);
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
|
@ -61,18 +77,22 @@ class AuthKeysController extends AppController
|
|||
|
||||
public function add($user_id = false)
|
||||
{
|
||||
$this->set('menuData', array('menuList' => $this->_isSiteAdmin() ? 'admin' : 'globalActions', 'menuItem' => 'authKeyAdd'));
|
||||
$params = [
|
||||
'displayOnSuccess' => 'authkey_display',
|
||||
'saveModelVariable' => ['authkey_raw']
|
||||
'saveModelVariable' => ['authkey_raw'],
|
||||
'override' => ['authkey' => null], // do not allow to use own key, always generate random one
|
||||
'afterFind' => function ($authKey) { // remove hashed key from response
|
||||
unset($authKey['AuthKey']['authkey']);
|
||||
return $authKey;
|
||||
}
|
||||
];
|
||||
$selectConditions = [];
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
$selectConditions['AND'][] = ['User.id' => $this->Auth->user('id')];
|
||||
$params['override'] = ['user_id' => $this->Auth->user('id')];
|
||||
$params['override']['user_id'] = $this->Auth->user('id');
|
||||
} else if ($user_id) {
|
||||
$selectConditions['AND'][] = ['User.id' => $user_id];
|
||||
$params['override'] = ['user_id' => $user_id];
|
||||
$params['override']['user_id'] = $user_id;
|
||||
}
|
||||
$this->CRUD->add($params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
|
@ -86,6 +106,11 @@ class AuthKeysController extends AppController
|
|||
])
|
||||
];
|
||||
$this->set(compact('dropdownData'));
|
||||
$this->set('menuData', [
|
||||
'menuList' => $this->_isSiteAdmin() ? 'admin' : 'globalActions',
|
||||
'menuItem' => 'authKeyAdd',
|
||||
]);
|
||||
$this->set('validity', Configure::read('Security.advanced_authkeys_validity'));
|
||||
}
|
||||
|
||||
public function view($id = false)
|
||||
|
@ -101,6 +126,15 @@ class AuthKeysController extends AppController
|
|||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
|
||||
if (Configure::read('MISP.log_user_ips') && Configure::read('MISP.log_user_ips_authkeys')) {
|
||||
list($keyUsage, $lastUsed, $uniqueIps) = $this->AuthKey->getKeyUsage($id);
|
||||
$this->set('keyUsage', $keyUsage);
|
||||
$this->set('lastUsed', $lastUsed);
|
||||
$this->set('uniqueIps', $uniqueIps);
|
||||
}
|
||||
|
||||
$this->set('title_for_layout', __('Auth Key'));
|
||||
$this->set('menuData', [
|
||||
'menuList' => $this->_isSiteAdmin() ? 'admin' : 'globalActions',
|
||||
'menuItem' => 'authKeyView',
|
||||
|
|
|
@ -15,13 +15,11 @@ class ACLComponent extends Component
|
|||
private $__aclList = array(
|
||||
'*' => array(
|
||||
'blackhole' => array(),
|
||||
'checkAction' => array(),
|
||||
'checkAuthUser' => array(),
|
||||
'checkExternalAuthUser' => array(),
|
||||
'cleanModelCaches' => array(),
|
||||
'debugACL' => array(),
|
||||
'generateCount' => array(),
|
||||
'getActions' => array(),
|
||||
'pruneDuplicateUUIDs' => array(),
|
||||
'queryACL' => array(),
|
||||
'removeDuplicateEvents' => array(),
|
||||
|
|
|
@ -15,7 +15,7 @@ class CRUDComponent extends Component
|
|||
}
|
||||
}
|
||||
|
||||
public function index($options)
|
||||
public function index(array $options)
|
||||
{
|
||||
$this->prepareResponse();
|
||||
if (!empty($options['quickFilters'])) {
|
||||
|
@ -75,8 +75,6 @@ class CRUDComponent extends Component
|
|||
$input[$modelName][$field] = $value;
|
||||
}
|
||||
}
|
||||
if (isset($input[$modelName]['id'])) {
|
||||
}
|
||||
unset($input[$modelName]['id']);
|
||||
if (!empty($params['fields'])) {
|
||||
$data = [];
|
||||
|
@ -86,20 +84,25 @@ class CRUDComponent extends Component
|
|||
} else {
|
||||
$data = $input;
|
||||
}
|
||||
if ($this->Controller->{$modelName}->save($data)) {
|
||||
$data = $this->Controller->{$modelName}->find('first', [
|
||||
/** @var Model $model */
|
||||
$model = $this->Controller->{$modelName};
|
||||
if ($model->save($data)) {
|
||||
$data = $model->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => [
|
||||
'id' => $this->Controller->{$modelName}->id
|
||||
'id' => $model->id
|
||||
]
|
||||
]);
|
||||
if (!empty($params['saveModelVariable'])) {
|
||||
foreach ($params['saveModelVariable'] as $var) {
|
||||
if (isset($this->Controller->{$modelName}->$var)) {
|
||||
$data[$modelName][$var] = $this->Controller->{$modelName}->$var;
|
||||
if (isset($model->$var)) {
|
||||
$data[$modelName][$var] = $model->$var;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($params['afterFind'])) {
|
||||
$data = $params['afterFind']($data);
|
||||
}
|
||||
$message = __('%s added.', $modelName);
|
||||
if ($this->Controller->IndexFilter->isRest()) {
|
||||
$this->Controller->restResponsePayload = $this->Controller->RestResponse->viewData($data, 'json');
|
||||
|
@ -116,7 +119,9 @@ class CRUDComponent extends Component
|
|||
} else {
|
||||
$message = __('%s could not be added.', $modelName);
|
||||
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 {
|
||||
$this->Controller->Flash->error($message);
|
||||
}
|
||||
|
@ -246,24 +251,25 @@ class CRUDComponent extends Component
|
|||
$this->Controller->render('/genericTemplates/delete');
|
||||
}
|
||||
|
||||
|
||||
protected function setQuickFilters($params, $query, $quickFilterFields)
|
||||
protected function setQuickFilters($params, array $query, $quickFilterFields)
|
||||
{
|
||||
$queryConditions = [];
|
||||
if (!empty($params['quickFilter']) && !empty($quickFilterFields)) {
|
||||
$queryConditions = [];
|
||||
$filter = '%' . strtolower($params['quickFilter']) . '%';
|
||||
foreach ($quickFilterFields as $filterField) {
|
||||
$queryConditions[$filterField] = $params['quickFilter'];
|
||||
$queryConditions["LOWER($filterField) LIKE"] = $filter;
|
||||
}
|
||||
$query['conditions']['OR'][] = $queryConditions;
|
||||
$query['conditions']['OR'] = $queryConditions;
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function setFilters($params, $query)
|
||||
protected function setFilters(array $params, array $query)
|
||||
{
|
||||
$params = $this->massageFilters($params);
|
||||
if (!empty($params['simpleFilters'])) {
|
||||
foreach ($params['simpleFilters'] as $filter => $filterValue) {
|
||||
// For CakePHP 2, we don't need to distinguish between simpleFilters and relatedFilters
|
||||
//$params = $this->massageFilters($params);
|
||||
if (!empty($params)) {
|
||||
foreach ($params as $filter => $filterValue) {
|
||||
if ($filter === 'quickFilter') {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
|
||||
class IndexFilterComponent extends Component
|
||||
{
|
||||
public $Controller = false;
|
||||
/** @var Controller */
|
||||
public $Controller;
|
||||
public $isRest = null;
|
||||
|
||||
public function initialize(Controller $controller) {
|
||||
|
@ -74,7 +75,7 @@ class IndexFilterComponent extends Component
|
|||
}
|
||||
}
|
||||
}
|
||||
$this->Controller->set('passedArgs', json_encode($this->Controller->passedArgs, true));
|
||||
$this->Controller->set('passedArgs', json_encode($this->Controller->passedArgs));
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
@ -85,7 +86,7 @@ class IndexFilterComponent extends Component
|
|||
return $this->isRest;
|
||||
}
|
||||
$api = $this->isApiFunction($this->Controller->request->params['controller'], $this->Controller->request->params['action']);
|
||||
if (isset($this->Controller->RequestHandler) && ($api || $this->Controller->RequestHandler->isXml() || $this->isJson() || $this->isCsv())) {
|
||||
if (isset($this->Controller->RequestHandler) && ($api || $this->isJson() || $this->Controller->RequestHandler->isXml() || $this->isCsv())) {
|
||||
if ($this->isJson()) {
|
||||
if (!empty($this->Controller->request->input()) && empty($this->Controller->request->input('json_decode'))) {
|
||||
throw new MethodNotAllowedException('Invalid JSON input. Make sure that the JSON input is a correctly formatted JSON string. This request has been blocked to avoid an unfiltered request.');
|
||||
|
@ -117,12 +118,13 @@ class IndexFilterComponent extends Component
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $controller
|
||||
* @param string $action
|
||||
* @return bool
|
||||
*/
|
||||
public function isApiFunction($controller, $action)
|
||||
{
|
||||
if (isset($this->Controller->automationArray[$controller]) && in_array($action, $this->Controller->automationArray[$controller])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return isset($this->Controller->automationArray[$controller]) && in_array($action, $this->Controller->automationArray[$controller], true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -519,11 +519,12 @@ class RestResponseComponent extends Component
|
|||
} else {
|
||||
$type = $format;
|
||||
}
|
||||
$dumpSql = !empty($this->Controller->sql_dump) && Configure::read('debug') > 1;
|
||||
if (!$raw) {
|
||||
if (is_string($response)) {
|
||||
$response = array('message' => $response);
|
||||
}
|
||||
if (Configure::read('debug') > 1 && !empty($this->Controller->sql_dump)) {
|
||||
if ($dumpSql) {
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
if ($this->Controller->sql_dump === 2) {
|
||||
$response = array('sql_dump' => $this->Log->getDataSource()->getLog(false, false));
|
||||
|
@ -533,7 +534,7 @@ class RestResponseComponent extends Component
|
|||
}
|
||||
$response = json_encode($response, JSON_PRETTY_PRINT);
|
||||
} else {
|
||||
if (Configure::read('debug') > 1 && !empty($this->Controller->sql_dump)) {
|
||||
if ($dumpSql) {
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
if ($this->Controller->sql_dump === 2) {
|
||||
$response = json_encode(array('sql_dump' => $this->Log->getDataSource()->getLog(false, false)));
|
||||
|
@ -547,7 +548,15 @@ class RestResponseComponent extends Component
|
|||
}
|
||||
}
|
||||
}
|
||||
$cakeResponse = new CakeResponse(array('body' => $response, 'status' => $code, 'type' => $type));
|
||||
|
||||
App::uses('TmpFileTool', 'Tools');
|
||||
if ($response instanceof TmpFileTool) {
|
||||
App::uses('CakeResponseTmp', 'Tools');
|
||||
$cakeResponse = new CakeResponseTmp(['status' => $code, 'type' => $type]);
|
||||
$cakeResponse->file($response);
|
||||
} else {
|
||||
$cakeResponse = new CakeResponse(array('body' => $response, 'status' => $code, 'type' => $type));
|
||||
}
|
||||
|
||||
if (Configure::read('Security.allow_cors')) {
|
||||
$headers["Access-Control-Allow-Headers"] = "Origin, Content-Type, Authorization, Accept";
|
||||
|
|
|
@ -17,24 +17,19 @@ class NewsController extends AppController
|
|||
{
|
||||
$this->paginate['contain'] = array('User' => array('fields' => array('User.email')));
|
||||
$newsItems = $this->paginate();
|
||||
$this->loadModel('User');
|
||||
$currentUser = $this->User->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('User.id' => $this->Auth->user('id')),
|
||||
'fields' => array('User.newsread')
|
||||
));
|
||||
|
||||
$newsread = $this->Auth->user('newsread');
|
||||
foreach ($newsItems as $key => $item) {
|
||||
if ($item['News']['date_created'] > $currentUser['User']['newsread']) {
|
||||
if ($item['News']['date_created'] > $newsread) {
|
||||
$newsItems[$key]['News']['new'] = true;
|
||||
} else {
|
||||
$newsItems[$key]['News']['new'] = false;
|
||||
}
|
||||
}
|
||||
$this->User->id = $this->Auth->user('id');
|
||||
//if ($this->User->exists()) {
|
||||
$this->User->saveField('newsread', time());
|
||||
$this->set('newsItems', $newsItems);
|
||||
//}
|
||||
|
||||
$this->loadModel('User');
|
||||
$this->User->updateField($this->Auth->user(), 'newsread', time());
|
||||
}
|
||||
|
||||
public function add()
|
||||
|
|
|
@ -1448,7 +1448,7 @@ class ServersController extends AppController
|
|||
if (!isset($this->request->data['Server'])) {
|
||||
$this->request->data = array('Server' => $this->request->data);
|
||||
}
|
||||
if (!isset($this->request->data['Server']['value'])) {
|
||||
if (!isset($this->request->data['Server']['value']) || !is_scalar($this->request->data['Server']['value'])) {
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('Servers', 'serverSettingsEdit', false, 'Invalid input. Expected: {"value": "new_setting"}', $this->response->type());
|
||||
}
|
||||
|
@ -1491,7 +1491,7 @@ class ServersController extends AppController
|
|||
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Field updated.')), 'status'=>200, 'type' => 'json'));
|
||||
}
|
||||
} else {
|
||||
if ($this->_isRest) {
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('Servers', 'serverSettingsEdit', false, $result, $this->response->type());
|
||||
} else {
|
||||
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $result)), 'status'=>200, 'type' => 'json'));
|
||||
|
|
|
@ -51,14 +51,6 @@ class UsersController extends AppController
|
|||
if (!$this->_isSiteAdmin() && $this->Auth->user('id') != $id) {
|
||||
throw new NotFoundException(__('Invalid user or not authorised.'));
|
||||
}
|
||||
if (!is_numeric($id) && !empty($id)) {
|
||||
$userId = $this->User->find('first', array(
|
||||
'conditions' => array('email' => $id),
|
||||
'fields' => array('id')
|
||||
));
|
||||
$id = $userid['User']['id'];
|
||||
}
|
||||
$user = $this->User->read(null, $id);
|
||||
$user = $this->User->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('User.id' => $id),
|
||||
|
@ -182,7 +174,7 @@ class UsersController extends AppController
|
|||
}
|
||||
if (!$abortPost) {
|
||||
// What fields should be saved (allowed to be saved)
|
||||
$fieldList = array('autoalert', 'gpgkey', 'certif_public', 'nids_sid', 'contactalert', 'disabled');
|
||||
$fieldList = array('autoalert', 'gpgkey', 'certif_public', 'nids_sid', 'contactalert', 'disabled', 'date_modified');
|
||||
if ($this->__canChangeLogin()) {
|
||||
$fieldList[] = 'email';
|
||||
}
|
||||
|
@ -217,7 +209,6 @@ class UsersController extends AppController
|
|||
return $this->RestResponse->viewData($this->__massageUserObject($user), $this->response->type());
|
||||
} else {
|
||||
$this->Flash->success(__('The profile has been updated'));
|
||||
$this->_refreshAuth();
|
||||
$this->redirect(array('action' => 'view', $id));
|
||||
}
|
||||
} else {
|
||||
|
@ -305,7 +296,6 @@ class UsersController extends AppController
|
|||
return $this->RestResponse->saveSuccessResponse('User', 'change_pw', false, $this->response->type(), $message);
|
||||
}
|
||||
$this->Flash->success($message);
|
||||
$this->_refreshAuth();
|
||||
$this->redirect(array('action' => 'view', $id));
|
||||
} else {
|
||||
$message = __('The password could not be updated. Make sure you meet the minimum password length / complexity requirements.');
|
||||
|
@ -915,8 +905,8 @@ class UsersController extends AppController
|
|||
if (isset($this->request->data['User']['role_id']) && !array_key_exists($this->request->data['User']['role_id'], $syncRoles)) {
|
||||
$this->request->data['User']['server_id'] = 0;
|
||||
}
|
||||
$fields = array();
|
||||
$blockedFields = array('id', 'invited_by');
|
||||
$fields = [];
|
||||
$blockedFields = array('id', 'invited_by', 'date_modified');
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
$blockedFields[] = 'org_id';
|
||||
}
|
||||
|
@ -967,11 +957,15 @@ class UsersController extends AppController
|
|||
throw new Exception('You are not authorised to assign that role to a user.');
|
||||
}
|
||||
}
|
||||
if (!empty($fields) && $this->User->save($this->request->data, true, $fields)) {
|
||||
$fields[] = 'date_modified'; // time will be inserted in `beforeSave` action
|
||||
if ($this->User->save($this->request->data, true, $fields)) {
|
||||
// newValues to array
|
||||
$fieldsNewValues = array();
|
||||
foreach ($fields as $field) {
|
||||
if ($field != 'confirm_password') {
|
||||
if ($field === 'date_modified') {
|
||||
continue;
|
||||
}
|
||||
if ($field !== 'confirm_password') {
|
||||
$newValue = $this->data['User'][$field];
|
||||
if (gettype($newValue) == 'array') {
|
||||
$newValueStr = '';
|
||||
|
@ -1014,7 +1008,6 @@ class UsersController extends AppController
|
|||
return $this->RestResponse->viewData($user, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->success(__('The user has been saved'));
|
||||
$this->_refreshAuth(); // in case we modify ourselves
|
||||
$this->redirect(array('action' => 'index'));
|
||||
}
|
||||
} else {
|
||||
|
@ -1252,16 +1245,9 @@ class UsersController extends AppController
|
|||
// Events list
|
||||
$url = $this->Session->consume('pre_login_requested_url');
|
||||
if (empty($url)) {
|
||||
$homepage = $this->User->UserSetting->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'UserSetting.user_id' => $this->Auth->user('id'),
|
||||
'UserSetting.setting' => 'homepage'
|
||||
),
|
||||
'contain' => array('User.id', 'User.org_id')
|
||||
));
|
||||
$homepage = $this->User->UserSetting->getValueForUser($this->Auth->user('id'), 'homepage');
|
||||
if (!empty($homepage)) {
|
||||
$url = $homepage['UserSetting']['value']['path'];
|
||||
$url = $homepage['path'];
|
||||
} else {
|
||||
$url = array('controller' => 'events', 'action' => 'index');
|
||||
}
|
||||
|
@ -1309,7 +1295,6 @@ class UsersController extends AppController
|
|||
}
|
||||
if (!$this->_isRest()) {
|
||||
$this->Flash->success(__('New authkey generated.', true));
|
||||
$this->_refreshAuth();
|
||||
$this->redirect($this->referer());
|
||||
} else {
|
||||
return $this->RestResponse->saveSuccessResponse('User', 'resetauthkey', $id, $this->response->type(), 'Authkey updated: ' . $newkey);
|
||||
|
@ -1436,9 +1421,7 @@ class UsersController extends AppController
|
|||
public function terms()
|
||||
{
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$this->User->id = $this->Auth->user('id');
|
||||
$this->User->saveField('termsaccepted', true);
|
||||
$this->_refreshAuth(); // refresh auth info
|
||||
$this->User->updateField($this->Auth->user(), 'termsaccepted', true);
|
||||
$this->Flash->success(__('You accepted the Terms and Conditions.'));
|
||||
$this->redirect(array('action' => 'routeafterlogin'));
|
||||
}
|
||||
|
@ -2278,18 +2261,6 @@ class UsersController extends AppController
|
|||
$this->set('users', $user_results);
|
||||
}
|
||||
|
||||
// Refreshes the Auth session with new/updated data
|
||||
protected function _refreshAuth()
|
||||
{
|
||||
$oldUser = $this->Auth->user();
|
||||
$newUser = $this->User->find('first', array('conditions' => array('User.id' => $oldUser['id']), 'recursive' => -1,'contain' => array('Organisation', 'Role')));
|
||||
// Rearrange it a bit to match the Auth object created during the login
|
||||
$newUser['User']['Role'] = $newUser['Role'];
|
||||
$newUser['User']['Organisation'] = $newUser['Organisation'];
|
||||
unset($newUser['Organisation'], $newUser['Role']);
|
||||
$this->Auth->login($newUser['User']);
|
||||
}
|
||||
|
||||
public function searchGpgKey($email = false)
|
||||
{
|
||||
if (!$email) {
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
class CakeResponseTmp extends CakeResponse
|
||||
{
|
||||
public function file($path, $options = array())
|
||||
{
|
||||
if ($path instanceof TmpFileTool) {
|
||||
$this->header('Content-Length', $path->size());
|
||||
$this->_clearBuffer();
|
||||
$this->_file = $path;
|
||||
} else {
|
||||
parent::file($path, $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param File|TmpFileTool $file
|
||||
* @param array $range
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function _sendFile($file, $range)
|
||||
{
|
||||
if ($file instanceof TmpFileTool) {
|
||||
set_time_limit(0);
|
||||
session_write_close();
|
||||
|
||||
foreach ($file->intoChunks() as $chunk) {
|
||||
if (!$this->_isActive()) {
|
||||
$file->close();
|
||||
return false;
|
||||
}
|
||||
echo $chunk;
|
||||
$this->_flushBuffer();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return parent::_sendFile($file, $range);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -177,7 +177,7 @@ class ComplexTypeTool
|
|||
unset($input);
|
||||
|
||||
$iocArray = [];
|
||||
foreach ($tmpFile->csv($delimiter) as $row) {
|
||||
foreach ($tmpFile->intoParsedCsv($delimiter) as $row) {
|
||||
if (!empty($row[0][0]) && $row[0][0] === '#') { // Comment
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ class TmpFileTool
|
|||
}
|
||||
|
||||
/**
|
||||
* Get one line from file parsed as CSV.
|
||||
* Returns generator of parsed CSV line from file.
|
||||
*
|
||||
* @param string $delimiter
|
||||
* @param string $enclosure
|
||||
|
@ -76,7 +76,7 @@ class TmpFileTool
|
|||
* @return Generator
|
||||
* @throws Exception
|
||||
*/
|
||||
public function csv($delimiter = ',', $enclosure = '"', $escape = "\\")
|
||||
public function intoParsedCsv($delimiter = ',', $enclosure = '"', $escape = "\\")
|
||||
{
|
||||
$this->rewind();
|
||||
$line = 0;
|
||||
|
@ -88,15 +88,16 @@ class TmpFileTool
|
|||
$line++;
|
||||
yield $result;
|
||||
}
|
||||
fclose($this->tmpfile);
|
||||
$this->tmpfile = null;
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns generator of line from file.
|
||||
*
|
||||
* @return Generator
|
||||
* @throws Exception
|
||||
*/
|
||||
public function lines()
|
||||
public function intoLines()
|
||||
{
|
||||
$this->rewind();
|
||||
while (!feof($this->tmpfile)) {
|
||||
|
@ -106,24 +107,64 @@ class TmpFileTool
|
|||
}
|
||||
yield $result;
|
||||
}
|
||||
fclose($this->tmpfile);
|
||||
$this->tmpfile = null;
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkSize In bytes
|
||||
* @return Generator
|
||||
* @throws Exception
|
||||
*/
|
||||
public function intoChunks($chunkSize = 8192)
|
||||
{
|
||||
$this->rewind();
|
||||
while (!feof($this->tmpfile)) {
|
||||
$result = fread($this->tmpfile, $chunkSize);
|
||||
if ($result === false) {
|
||||
throw new Exception('Could not read from temporary file.');
|
||||
}
|
||||
yield $result;
|
||||
}
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function finish()
|
||||
public function intoString()
|
||||
{
|
||||
$this->rewind();
|
||||
$final = stream_get_contents($this->tmpfile);
|
||||
if ($final === false) {
|
||||
$string = stream_get_contents($this->tmpfile);
|
||||
if ($string === false) {
|
||||
throw new Exception('Could not read from temporary file.');
|
||||
}
|
||||
fclose($this->tmpfile);
|
||||
$this->tmpfile = null;
|
||||
return $final;
|
||||
$this->close();
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass data to output.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function intoOutput()
|
||||
{
|
||||
$this->rewind();
|
||||
if (fpassthru($this->tmpfile) === false) {
|
||||
throw new Exception('Could not pass temporary file to output.');
|
||||
}
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @throws Exception
|
||||
*/
|
||||
public function size()
|
||||
{
|
||||
$this->isOpen();
|
||||
return fstat($this->tmpfile)['size'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,7 +173,30 @@ class TmpFileTool
|
|||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->finish();
|
||||
return $this->intoString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if ($this->tmpfile) {
|
||||
$result = fclose($this->tmpfile);
|
||||
$this->tmpfile = null;
|
||||
return $result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private function isOpen()
|
||||
{
|
||||
if ($this->tmpfile === null) {
|
||||
throw new Exception('Temporary file is already closed.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -142,6 +206,7 @@ class TmpFileTool
|
|||
*/
|
||||
private function rewind()
|
||||
{
|
||||
$this->isOpen();
|
||||
if (fseek($this->tmpfile, 0) === -1) {
|
||||
throw new Exception('Could not seek to start of temporary file.');
|
||||
}
|
||||
|
|
|
@ -1627,10 +1627,8 @@ class AppModel extends Model
|
|||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
$now = new DateTime();
|
||||
// switch MISP instance live to false
|
||||
if ($liveOff) {
|
||||
$this->Server = Classregistry::init('Server');
|
||||
|
@ -1643,7 +1641,7 @@ class AppModel extends Model
|
|||
$this->__setUpdateProgress(0, $total_update_count, $command);
|
||||
$str_index_array = array();
|
||||
foreach($indexArray as $toIndex) {
|
||||
$str_index_array[] = __('Indexing ') . sprintf('%s -> %s', $toIndex[0], $toIndex[1]);
|
||||
$str_index_array[] = __('Indexing %s -> %s', $toIndex[0], $toIndex[1]);
|
||||
}
|
||||
$this->__setUpdateCmdMessages(array_merge($sqlArray, $str_index_array));
|
||||
$flagStop = false;
|
||||
|
@ -1679,10 +1677,10 @@ class AppModel extends Model
|
|||
'email' => 'SYSTEM',
|
||||
'action' => 'update_database',
|
||||
'user_id' => 0,
|
||||
'title' => __('Successfuly executed the SQL query for ') . $command,
|
||||
'title' => __('Successfully executed the SQL query for ') . $command,
|
||||
'change' => sprintf(__('The executed SQL query was: %s'), $sql)
|
||||
));
|
||||
$this->__setUpdateResMessages($i, sprintf(__('Successfuly executed the SQL query for %s'), $command));
|
||||
$this->__setUpdateResMessages($i, sprintf(__('Successfully executed the SQL query for %s'), $command));
|
||||
} catch (Exception $e) {
|
||||
$errorMessage = $e->getMessage();
|
||||
$this->Log->create();
|
||||
|
@ -1736,14 +1734,13 @@ class AppModel extends Model
|
|||
}
|
||||
}
|
||||
}
|
||||
$this->__setUpdateProgress(count($sqlArray)+count($indexArray), false);
|
||||
$this->__setUpdateProgress(count($sqlArray) + count($indexArray), false);
|
||||
}
|
||||
if ($clean) {
|
||||
$this->cleanCacheFiles();
|
||||
}
|
||||
if ($liveOff) {
|
||||
$liveSetting = 'MISP.live';
|
||||
$this->Server->serverSettingsSaveValue($liveSetting, true);
|
||||
$this->Server->serverSettingsSaveValue('MISP.live', true);
|
||||
}
|
||||
if (!$flagStop && $errorCount == 0) {
|
||||
$this->__postUpdate($command);
|
||||
|
@ -2132,11 +2129,20 @@ class AppModel extends Model
|
|||
}
|
||||
}
|
||||
if ($requiresLogout) {
|
||||
$this->updateDatabase('destroyAllSessions');
|
||||
$this->refreshSessions();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update date_modified for all users, this will ensure that all users will refresh their session data.
|
||||
*/
|
||||
private function refreshSessions()
|
||||
{
|
||||
$this->User = ClassRegistry::init('User');
|
||||
$this->User->updateAll(['date_modified' => time()]);
|
||||
}
|
||||
|
||||
private function __setUpdateProgress($current, $total=false, $toward_db_version=false)
|
||||
{
|
||||
$updateProgress = $this->getUpdateProgress();
|
||||
|
@ -2735,7 +2741,7 @@ class AppModel extends Model
|
|||
{
|
||||
static $versionArray;
|
||||
if ($versionArray === null) {
|
||||
$file = new File(ROOT . DS . 'VERSION.json', true);
|
||||
$file = new File(ROOT . DS . 'VERSION.json');
|
||||
$versionArray = $this->jsonDecode($file->read());
|
||||
$file->close();
|
||||
}
|
||||
|
|
|
@ -4697,7 +4697,7 @@ class Attribute extends AppModel
|
|||
$elementCounter = $this->__iteratedFetch($user, $params, $loop, $tmpfile, $exportTool, $exportToolParams);
|
||||
}
|
||||
$tmpfile->write($exportTool->footer($exportToolParams));
|
||||
return $tmpfile->finish();
|
||||
return $tmpfile;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -42,22 +42,38 @@ 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'] = 0;
|
||||
$this->data['AuthKey']['expiration'] = $validity ? strtotime("+$validity days") : 0;
|
||||
} else {
|
||||
$this->data['AuthKey']['expiration'] = strtotime($this->data['AuthKey']['expiration']);
|
||||
$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;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $authkey
|
||||
* @return array|false
|
||||
*/
|
||||
public function getAuthUserByAuthKey($authkey)
|
||||
{
|
||||
$start = substr($authkey, 0, 4);
|
||||
$end = substr($authkey, -4);
|
||||
$existing_authkeys = $this->find('all', [
|
||||
'recursive' => -1,
|
||||
'fields' => ['authkey', 'user_id'],
|
||||
'fields' => ['id', 'authkey', 'user_id', 'expiration'],
|
||||
'conditions' => [
|
||||
'OR' => [
|
||||
'expiration >' => time(),
|
||||
|
@ -70,7 +86,12 @@ class AuthKey extends AppModel
|
|||
$passwordHasher = $this->getHasher();
|
||||
foreach ($existing_authkeys as $existing_authkey) {
|
||||
if ($passwordHasher->check($authkey, $existing_authkey['AuthKey']['authkey'])) {
|
||||
return $this->User->getAuthUser($existing_authkey['AuthKey']['user_id']);
|
||||
$user = $this->User->getAuthUser($existing_authkey['AuthKey']['user_id']);
|
||||
if ($user) {
|
||||
$user['authkey_id'] = $existing_authkey['AuthKey']['id'];
|
||||
$user['authkey_expiration'] = $existing_authkey['AuthKey']['expiration'];
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -105,6 +126,67 @@ class AuthKey extends AppModel
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getKeyUsage($id)
|
||||
{
|
||||
$redis = $this->setupRedisWithException();
|
||||
$data = $redis->hGetAll("misp:authkey_usage:$id");
|
||||
|
||||
$output = [];
|
||||
$uniqueIps = [];
|
||||
foreach ($data as $key => $count) {
|
||||
list($date, $ip) = explode(':', $key);
|
||||
$uniqueIps[$ip] = true;
|
||||
if (isset($output[$date])) {
|
||||
$output[$date] += $count;
|
||||
} else {
|
||||
$output[$date] = $count;
|
||||
}
|
||||
}
|
||||
// Data from redis are not sorted
|
||||
ksort($output);
|
||||
|
||||
$lastUsage = $redis->get("misp:authkey_last_usage:$id");
|
||||
$lastUsage = $lastUsage === false ? null : (int)$lastUsage;
|
||||
|
||||
return [$output, $lastUsage, count($uniqueIps)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $ids
|
||||
* @return array<DateTime|null>
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getLastUsageForKeys(array $ids)
|
||||
{
|
||||
$redis = $this->setupRedisWithException();
|
||||
$keys = array_map(function($id) {
|
||||
return "misp:authkey_last_usage:$id";
|
||||
}, $ids);
|
||||
$lastUsages = $redis->mget($keys);
|
||||
$output = [];
|
||||
foreach (array_values($ids) as $i => $id) {
|
||||
$output[$id] = $lastUsages[$i] === false ? null : (int)$lastUsages[$i];
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* When key is deleted, update after `date_modified` for user that was assigned to that key, so session data
|
||||
* will be realoaded and canceled.
|
||||
* @see AppController::_refreshAuth
|
||||
*/
|
||||
public function afterDelete()
|
||||
{
|
||||
parent::afterDelete();
|
||||
$userId = $this->data['AuthKey']['user_id'];
|
||||
$this->User->updateAll(['date_modified' => time()], ['User.id' => $userId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractPasswordHasher
|
||||
*/
|
||||
|
|
|
@ -6999,7 +6999,7 @@ class Event extends AppModel
|
|||
unset($result);
|
||||
unset($temp);
|
||||
$tmpfile->write($exportTool->footer($exportToolParams));
|
||||
return $tmpfile->finish();
|
||||
return $tmpfile;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -180,7 +180,7 @@ class Feed extends AppModel
|
|||
$tmpFile->write(trim($data));
|
||||
unset($data);
|
||||
|
||||
return $tmpFile->csv();
|
||||
return $tmpFile->intoParsedCsv();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -336,6 +336,11 @@ class Log extends AppModel
|
|||
$elasticSearchClient->pushDocument($logIndex, "log", $data);
|
||||
}
|
||||
|
||||
// Do not save request action logs to syslog, because they contain no information
|
||||
if ($data['Log']['action'] === 'request') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// write to syslogd as well if enabled
|
||||
if ($this->syslog === null) {
|
||||
if (Configure::read('Security.syslog')) {
|
||||
|
|
|
@ -1443,7 +1443,7 @@ class MispObject extends AppModel
|
|||
}
|
||||
$this->__iteratedFetch($user, $params, $loop, $tmpfile, $exportTool, $exportToolParams, $elementCounter);
|
||||
$tmpfile->write($exportTool->footer($exportToolParams));
|
||||
return $tmpfile->finish();
|
||||
return $tmpfile;
|
||||
}
|
||||
|
||||
private function __iteratedFetch($user, &$params, &$loop, TmpFileTool $tmpfile, $exportTool, $exportToolParams, &$elementCounter = 0)
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
|
||||
/**
|
||||
* @property User $User
|
||||
*/
|
||||
class Role extends AppModel
|
||||
{
|
||||
public $validate = array(
|
||||
|
@ -232,6 +235,18 @@ class Role extends AppModel
|
|||
return true;
|
||||
}
|
||||
|
||||
public function afterSave($created, $options = array())
|
||||
{
|
||||
// After role change, update `date_modified` field for all user with this role to apply this change to already
|
||||
// logged users.
|
||||
if (!$created && !empty($this->data)) {
|
||||
$roleId = $this->data['Role']['id'];
|
||||
$this->User->updateAll(['date_modified' => time()], ['role_id' => $roleId]);
|
||||
}
|
||||
|
||||
parent::afterSave($created, $options);
|
||||
}
|
||||
|
||||
public function afterFind($results, $primary = false)
|
||||
{
|
||||
foreach ($results as $key => $val) {
|
||||
|
|
|
@ -865,6 +865,15 @@ class Server extends AppModel
|
|||
'type' => 'boolean',
|
||||
'null' => true
|
||||
),
|
||||
'log_user_ips_authkeys' => [
|
||||
'level' => self::SETTING_RECOMMENDED,
|
||||
'description' => __('Log user IP and key usage on each API request. All logs for given keys are deleted after one year when this key is not used.'),
|
||||
'value' => false,
|
||||
'errorMessage' => '',
|
||||
'test' => 'testBool',
|
||||
'type' => 'boolean',
|
||||
'null' => true
|
||||
],
|
||||
'delegation' => array(
|
||||
'level' => 1,
|
||||
'description' => __('This feature allows users to create org only events and ask another organisation to take ownership of the event. This allows organisations to remain anonymous by asking a partner to publish an event for them.'),
|
||||
|
@ -1156,7 +1165,7 @@ class Server extends AppModel
|
|||
'test' => 'testForPositiveInteger',
|
||||
'type' => 'numeric',
|
||||
'null' => true,
|
||||
]
|
||||
],
|
||||
),
|
||||
'GnuPG' => array(
|
||||
'branch' => 1,
|
||||
|
@ -1343,6 +1352,24 @@ class Server extends AppModel
|
|||
'test' => 'testBool',
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'advanced_authkeys_validity' => [
|
||||
'level' => self::SETTING_OPTIONAL,
|
||||
'description' => __('Maximal key lifetime in days. Use can limit that validity even more. Just newly created keys will be affected. When not set, key validity is not limited.'),
|
||||
'value' => '',
|
||||
'errorMessage' => '',
|
||||
'type' => 'numeric',
|
||||
'test' => 'testForNumeric',
|
||||
'null' => true,
|
||||
],
|
||||
'authkey_keep_session' => [
|
||||
'level' => self::SETTING_OPTIONAL,
|
||||
'description' => __('When enabled, session is kept between API requests.'),
|
||||
'value' => false,
|
||||
'errorMessage' => '',
|
||||
'test' => 'testBool',
|
||||
'type' => 'boolean',
|
||||
'null' => true,
|
||||
],
|
||||
'auth_enforced' => [
|
||||
'level' => self::SETTING_OPTIONAL,
|
||||
'description' => __('This optional can be enabled if external auth provider is used and when set to true, it will disable default form authentication.'),
|
||||
|
@ -1585,7 +1612,16 @@ class Server extends AppModel
|
|||
'test' => 'testBool',
|
||||
'type' => 'boolean',
|
||||
'null' => true
|
||||
)
|
||||
),
|
||||
'username_in_response_header' => [
|
||||
'level' => self::SETTING_OPTIONAL,
|
||||
'description' => __('When enabled, logged in username will be included in X-Username HTTP response header. This is useful for request logging on webserver/proxy side.'),
|
||||
'value' => false,
|
||||
'errorMessage' => '',
|
||||
'test' => 'testBool',
|
||||
'type' => 'boolean',
|
||||
'null' => true
|
||||
]
|
||||
),
|
||||
'SecureAuth' => array(
|
||||
'branch' => 1,
|
||||
|
@ -4916,6 +4952,7 @@ class Server extends AppModel
|
|||
|
||||
public function dbSchemaDiagnostic()
|
||||
{
|
||||
$this->AdminSetting = ClassRegistry::init('AdminSetting');
|
||||
$actualDbVersion = $this->AdminSetting->find('first', array(
|
||||
'conditions' => array('setting' => 'db_version')
|
||||
))['AdminSetting']['value'];
|
||||
|
@ -6599,7 +6636,7 @@ class Server extends AppModel
|
|||
} catch (Exception $e) {
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$this->Log->create();
|
||||
$message = __('Could not reset fetch remote user account.');
|
||||
$message = __('Could not fetch remote user account.');
|
||||
$this->Log->save(array(
|
||||
'org' => 'SYSTEM',
|
||||
'model' => 'Server',
|
||||
|
@ -6612,14 +6649,18 @@ class Server extends AppModel
|
|||
return $message;
|
||||
}
|
||||
if ($response->isOk()) {
|
||||
$user = json_decode($response->body, true);
|
||||
$user = $this->jsonDecode($response->body);
|
||||
if (!empty($user['User'])) {
|
||||
$result = array(
|
||||
'Email' => $user['User']['email'],
|
||||
'Role name' => isset($user['Role']['name']) ? $user['Role']['name'] : 'Unknown, outdated instance',
|
||||
'Sync flag' => isset($user['Role']['perm_sync']) ? ($user['Role']['perm_sync'] ? 1 : 0) : 'Unknown, outdated instance'
|
||||
);
|
||||
return $result;
|
||||
$results = [
|
||||
__('User') => $user['User']['email'],
|
||||
__('Role name') => isset($user['Role']['name']) ? $user['Role']['name'] : __('Unknown, outdated instance'),
|
||||
__('Sync flag') => isset($user['Role']['perm_sync']) ? ($user['Role']['perm_sync'] ? __('Yes') : __('No')) : __('Unknown, outdated instance'),
|
||||
];
|
||||
if (isset($response->headers['X-Auth-Key-Expiration'])) {
|
||||
$date = new DateTime($response->headers['X-Auth-Key-Expiration']);
|
||||
$results[__('Auth key expiration')] = $date->format('Y-m-d H:i:s');
|
||||
}
|
||||
return $results;
|
||||
} else {
|
||||
return __('No user object received in response.');
|
||||
}
|
||||
|
|
|
@ -140,7 +140,13 @@ class Sighting extends AppModel
|
|||
return $this->save($sighting);
|
||||
}
|
||||
|
||||
public function getSighting($id, $user)
|
||||
/**
|
||||
* @param int $id
|
||||
* @param array $user
|
||||
* @param bool $withEvent
|
||||
* @return array
|
||||
*/
|
||||
public function getSighting($id, array $user, $withEvent = true)
|
||||
{
|
||||
$sighting = $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
|
@ -149,10 +155,7 @@ class Sighting extends AppModel
|
|||
'fields' => array('Attribute.value', 'Attribute.id', 'Attribute.uuid', 'Attribute.type', 'Attribute.category', 'Attribute.to_ids')
|
||||
),
|
||||
'Event' => array(
|
||||
'fields' => array('Event.id', 'Event.uuid', 'Event.orgc_id', 'Event.org_id', 'Event.info'),
|
||||
'Orgc' => array(
|
||||
'fields' => array('Orgc.name')
|
||||
)
|
||||
'fields' => $withEvent ? ['Event.id', 'Event.uuid', 'Event.orgc_id', 'Event.org_id', 'Event.info'] : ['Event.org_id'],
|
||||
)
|
||||
),
|
||||
'conditions' => array('Sighting.id' => $id)
|
||||
|
@ -161,11 +164,7 @@ class Sighting extends AppModel
|
|||
return array();
|
||||
}
|
||||
|
||||
if (!isset($event)) {
|
||||
$event = array('Event' => $sighting['Event']);
|
||||
}
|
||||
|
||||
$ownEvent = $user['Role']['perm_site_admin'] || $event['Event']['org_id'] == $user['org_id'];
|
||||
$ownEvent = $user['Role']['perm_site_admin'] || $sighting['Event']['org_id'] == $user['org_id'];
|
||||
if (!$ownEvent) {
|
||||
$sightingPolicy = $this->sightingsPolicy();
|
||||
// if sighting policy == 0 then return false if the sighting doesn't belong to the user
|
||||
|
@ -181,26 +180,24 @@ class Sighting extends AppModel
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Put event organisation name from cache
|
||||
if ($withEvent) {
|
||||
$sighting['Event']['Orgc']['name'] = $this->getOrganisationById($sighting['Event']['orgc_id'])['name'];
|
||||
}
|
||||
|
||||
$anonymise = Configure::read('Plugin.Sightings_anonymise');
|
||||
if ($anonymise) {
|
||||
if ($sighting['Sighting']['org_id'] != $user['org_id']) {
|
||||
unset($sighting['Sighting']['org_id']);
|
||||
unset($sighting['Organisation']);
|
||||
}
|
||||
if ($anonymise && $sighting['Sighting']['org_id'] != $user['org_id']) {
|
||||
unset($sighting['Sighting']['org_id']);
|
||||
}
|
||||
// rearrange it to match the event format of fetchevent
|
||||
if (isset($sighting['Organisation'])) {
|
||||
$sighting['Sighting']['Organisation'] = $sighting['Organisation'];
|
||||
unset($sighting['Organisation']);
|
||||
}
|
||||
$result = array(
|
||||
'Sighting' => $sighting['Sighting']
|
||||
);
|
||||
$result['Sighting']['Event'] = $sighting['Event'];
|
||||
$result['Sighting']['Attribute'] = $sighting['Attribute'];
|
||||
if (!empty($sighting['Organisation'])) {
|
||||
$result['Sighting']['Organisation'] = $sighting['Organisation'];
|
||||
if ($withEvent) {
|
||||
$result['Sighting']['Event'] = $sighting['Event'];
|
||||
}
|
||||
$result['Sighting']['Attribute'] = $sighting['Attribute'];
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
@ -332,7 +329,7 @@ class Sighting extends AppModel
|
|||
'fields' => ['org_id', 'attribute_id', 'type', 'date', 'last_timestamp', 'sighting_count'],
|
||||
'recursive' => -1,
|
||||
'group' => ['org_id', 'attribute_id', 'type', 'date'],
|
||||
'order' => ['date_sighting'], // from oldest
|
||||
'order' => ['date'], // from oldest
|
||||
));
|
||||
unset(
|
||||
$this->virtualFields['date'],
|
||||
|
@ -369,7 +366,7 @@ class Sighting extends AppModel
|
|||
'conditions' => $conditions,
|
||||
'fields' => [ucfirst($context) . 'Tag.tag_id', 'date', 'sighting_count'],
|
||||
'group' => [ucfirst($context) . 'Tag.id', 'date'],
|
||||
'order' => ['date_sighting'], // from oldest
|
||||
'order' => ['date'], // from oldest
|
||||
]);
|
||||
unset($this->virtualFields['date'], $this->virtualFields['sighting_count']);
|
||||
return $sightings;
|
||||
|
@ -659,6 +656,10 @@ class Sighting extends AppModel
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @deprecated
|
||||
*/
|
||||
public function addUuids()
|
||||
{
|
||||
$sightings = $this->find('all', array(
|
||||
|
@ -864,48 +865,27 @@ class Sighting extends AppModel
|
|||
}
|
||||
|
||||
// fetch sightings matching the query
|
||||
$sightings = $this->find('list', array(
|
||||
$sightingIds = $this->find('list', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => $conditions,
|
||||
'fields' => array('id'),
|
||||
'contain' => $contain,
|
||||
));
|
||||
|
||||
$filters['requested_attributes'] = array('id', 'attribute_id', 'event_id', 'org_id', 'date_sighting', 'uuid', 'source', 'type');
|
||||
|
||||
// apply ACL and sighting policies
|
||||
$allowedSightings = array();
|
||||
$additional_attribute_added = false;
|
||||
$additional_event_added = false;
|
||||
foreach ($sightings as $sid) {
|
||||
$sight = $this->getSighting($sid, $user);
|
||||
if (!empty($sight)) {
|
||||
$sight['Sighting']['value'] = $sight['Sighting']['Attribute']['value'];
|
||||
// by default, do not include event and attribute
|
||||
if (!isset($filters['includeAttribute']) || !$filters['includeAttribute']) {
|
||||
unset($sight["Sighting"]["Attribute"]);
|
||||
} else if (!$additional_attribute_added) {
|
||||
$filters['requested_attributes'] = array_merge($filters['requested_attributes'], array('attribute_uuid', 'attribute_type', 'attribute_category', 'attribute_to_ids', 'attribute_value'));
|
||||
$additional_attribute_added = true;
|
||||
}
|
||||
|
||||
if (!isset($filters['includeEvent']) || !$filters['includeEvent']) {
|
||||
unset($sight["Sighting"]["Event"]);
|
||||
} else if (!$additional_event_added) {
|
||||
$filters['requested_attributes'] = array_merge($filters['requested_attributes'], array('event_uuid', 'event_orgc_id', 'event_org_id', 'event_info', 'event_Orgc_name'));
|
||||
$additional_event_added = true;
|
||||
}
|
||||
$allowedSightings[] = $sight;
|
||||
}
|
||||
$includeAttribute = isset($filters['includeAttribute']) && $filters['includeAttribute'];
|
||||
$includeEvent = isset($filters['includeEvent']) && $filters['includeEvent'];
|
||||
$requestedAttributes = ['id', 'attribute_id', 'event_id', 'org_id', 'date_sighting', 'uuid', 'source', 'type'];
|
||||
if ($includeAttribute) {
|
||||
$requestedAttributes = array_merge($requestedAttributes, ['attribute_uuid', 'attribute_type', 'attribute_category', 'attribute_to_ids', 'attribute_value']);
|
||||
}
|
||||
|
||||
$params = array(
|
||||
'conditions' => array(), //result already filtered
|
||||
);
|
||||
if ($includeEvent) {
|
||||
$requestedAttributes = array_merge($requestedAttributes, ['event_uuid', 'event_orgc_id', 'event_org_id', 'event_info', 'event_Orgc_name']);
|
||||
}
|
||||
$filters['requested_attributes'] = $requestedAttributes;
|
||||
|
||||
$exportToolParams = array(
|
||||
'user' => $user,
|
||||
'params' => $params,
|
||||
'params' => ['conditions' => []], //result already filtered
|
||||
'returnFormat' => $returnFormat,
|
||||
'scope' => 'Sighting',
|
||||
'filters' => $filters
|
||||
|
@ -913,21 +893,22 @@ class Sighting extends AppModel
|
|||
|
||||
$tmpfile = new TmpFileTool();
|
||||
$tmpfile->write($exportTool->header($exportToolParams));
|
||||
$separator = $exportTool->separator($exportToolParams);
|
||||
|
||||
$temp = '';
|
||||
$i = 0;
|
||||
foreach ($allowedSightings as $sighting) {
|
||||
$temp .= $exportTool->handler($sighting, $exportToolParams);
|
||||
if ($temp !== '') {
|
||||
if ($i != count($allowedSightings) -1) {
|
||||
$temp .= $exportTool->separator($exportToolParams);
|
||||
foreach ($sightingIds as $sightingId) {
|
||||
// apply ACL and sighting policies
|
||||
$sighting = $this->getSighting($sightingId, $user, $includeEvent);
|
||||
if (!empty($sighting)) {
|
||||
$sighting['Sighting']['value'] = $sighting['Sighting']['Attribute']['value'];
|
||||
if (!$includeAttribute) {
|
||||
unset($sighting['Sighting']['Attribute']);
|
||||
}
|
||||
$tmpfile->writeWithSeparator($exportTool->handler($sighting, $exportToolParams), $separator);
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
$tmpfile->write($temp);
|
||||
|
||||
$tmpfile->write($exportTool->footer($exportToolParams));
|
||||
return $tmpfile->finish();
|
||||
return $tmpfile;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -641,39 +641,50 @@ class User extends AppModel
|
|||
// get the current user and rearrange it to be in the same format as in the auth component
|
||||
public function getAuthUser($id)
|
||||
{
|
||||
$user = $this->getUserById($id);
|
||||
if (empty($user)) {
|
||||
return $user;
|
||||
if (empty($id)) {
|
||||
throw new InvalidArgumentException('Invalid user ID.');
|
||||
}
|
||||
return $this->rearrangeToAuthForm($user);
|
||||
$conditions = ['User.id' => $id];
|
||||
return $this->getAuthUserByConditions($conditions);
|
||||
}
|
||||
|
||||
// get the current user and rearrange it to be in the same format as in the auth component
|
||||
public function getAuthUserByAuthkey($id)
|
||||
public function getAuthUserByAuthkey($authkey)
|
||||
{
|
||||
$conditions = array('User.authkey' => $id);
|
||||
$user = $this->find('first', array('conditions' => $conditions, 'recursive' => -1,'contain' => array('Organisation', 'Role', 'Server')));
|
||||
if (empty($user)) {
|
||||
return $user;
|
||||
if (empty($authkey)) {
|
||||
throw new InvalidArgumentException('Invalid user auth key.');
|
||||
}
|
||||
return $this->rearrangeToAuthForm($user);
|
||||
$conditions = array('User.authkey' => $authkey);
|
||||
return $this->getAuthUserByConditions($conditions);
|
||||
}
|
||||
|
||||
public function getAuthUserByExternalAuth($auth_key)
|
||||
{
|
||||
if (empty($auth_key)) {
|
||||
throw new InvalidArgumentException('Invalid user external auth key.');
|
||||
}
|
||||
$conditions = array(
|
||||
'User.external_auth_key' => $auth_key,
|
||||
'User.external_auth_required' => true
|
||||
);
|
||||
$user = $this->find('first', array(
|
||||
return $this->getAuthUserByConditions($conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $conditions
|
||||
* @return array|null
|
||||
*/
|
||||
private function getAuthUserByConditions(array $conditions)
|
||||
{
|
||||
$user = $this->find('first', [
|
||||
'conditions' => $conditions,
|
||||
'recursive' => -1,
|
||||
'contain' => array(
|
||||
'contain' => [
|
||||
'Organisation',
|
||||
'Role',
|
||||
'Server'
|
||||
)
|
||||
));
|
||||
'Server',
|
||||
],
|
||||
]);
|
||||
if (empty($user)) {
|
||||
return $user;
|
||||
}
|
||||
|
@ -696,9 +707,6 @@ class User extends AppModel
|
|||
$user['User']['Role'] = $user['Role'];
|
||||
$user['User']['Organisation'] = $user['Organisation'];
|
||||
$user['User']['Server'] = $user['Server'];
|
||||
if (isset($user['UserSetting'])) {
|
||||
$user['User']['UserSetting'] = $user['UserSetting'];
|
||||
}
|
||||
return $user['User'];
|
||||
}
|
||||
|
||||
|
@ -967,7 +975,7 @@ class User extends AppModel
|
|||
if ($result) {
|
||||
$this->id = $user['User']['id'];
|
||||
$this->saveField('password', $password);
|
||||
$this->saveField('change_pw', '1');
|
||||
$this->updateField($user['User'], 'change_pw', 1);
|
||||
if ($simpleReturn) {
|
||||
return true;
|
||||
} else {
|
||||
|
@ -1142,7 +1150,7 @@ class User extends AppModel
|
|||
if (empty(Configure::read('Security.advanced_authkeys'))) {
|
||||
$oldKey = $this->data['User']['authkey'];
|
||||
$newkey = $this->generateAuthKey();
|
||||
$this->saveField('authkey', $newkey);
|
||||
$this->updateField($updatedUser['User'], 'authkey', $newkey);
|
||||
$this->extralog(
|
||||
$user,
|
||||
'reset_auth_key',
|
||||
|
@ -1286,24 +1294,6 @@ class User extends AppModel
|
|||
return $data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the monitoring flag in Configure for the current user
|
||||
* Reads the state from redis
|
||||
*/
|
||||
public function setMonitoring($user)
|
||||
{
|
||||
if (
|
||||
!empty(Configure::read('Security.user_monitoring_enabled'))
|
||||
) {
|
||||
$redis = $this->setupRedis();
|
||||
if (!empty($redis->sismember('misp:monitored_users', $user['id']))) {
|
||||
Configure::write('Security.monitored', 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Configure::write('Security.monitored', 0);
|
||||
}
|
||||
|
||||
public function registerUser($added_by, $registration, $org_id, $role_id) {
|
||||
$user = array(
|
||||
'email' => $registration['data']['email'],
|
||||
|
@ -1403,7 +1393,7 @@ class User extends AppModel
|
|||
$name => $value,
|
||||
], true, ['id', $name, 'date_modified']);
|
||||
if (!$success) {
|
||||
throw new RuntimeException("Could not save field `$name` with value `$value` for user `{$user['id']}`.");
|
||||
throw new RuntimeException("Could not save setting $name for user {$user['id']}.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -201,34 +201,33 @@ class UserSetting extends AppModel
|
|||
|
||||
public function getDefaulRestSearchParameters($user)
|
||||
{
|
||||
$setting = $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'UserSetting.user_id' => $user['id'],
|
||||
'UserSetting.setting' => 'default_restsearch_parameters'
|
||||
)
|
||||
));
|
||||
$parameters = array();
|
||||
if (!empty($setting)) {
|
||||
$parameters = $setting['UserSetting']['value'];
|
||||
}
|
||||
return $parameters;
|
||||
return $this->getValueForUser($user['id'], 'default_restsearch_parameters') ?: [];
|
||||
}
|
||||
|
||||
public function getTagNumericalValueOverride($userId)
|
||||
{
|
||||
$setting = $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'UserSetting.user_id' => $userId,
|
||||
'UserSetting.setting' => 'tag_numerical_value_override'
|
||||
)
|
||||
));
|
||||
$parameters = array();
|
||||
if (!empty($setting)) {
|
||||
$parameters = $setting['UserSetting']['value'];
|
||||
}
|
||||
return $parameters;
|
||||
return $this->getValueForUser($userId, 'tag_numerical_value_override') ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
* @param string $setting
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getValueForUser($userId, $setting)
|
||||
{
|
||||
$output = $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
'fields' => ['value'],
|
||||
'conditions' => array(
|
||||
'UserSetting.user_id' => $userId,
|
||||
'UserSetting.setting' => $setting,
|
||||
)
|
||||
));
|
||||
if ($output) {
|
||||
return $output['UserSetting']['value'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
|
||||
echo $this->element('genericElements/Form/genericForm', [
|
||||
'data' => [
|
||||
'title' => __('Add auth key'),
|
||||
|
@ -13,11 +12,12 @@ echo $this->element('genericElements/Form/genericForm', [
|
|||
],
|
||||
[
|
||||
'field' => 'comment',
|
||||
'label' => __('Comment'),
|
||||
'class' => 'span6'
|
||||
],
|
||||
[
|
||||
'field' => 'expiration',
|
||||
'label' => 'Expiration',
|
||||
'label' => __('Expiration (%s)', $validity ? __('keep empty for maximal validity of %s days', $validity) : __('keep empty for indefinite')),
|
||||
'class' => 'datepicker span6',
|
||||
'placeholder' => "YYYY-MM-DD",
|
||||
'type' => 'text'
|
||||
|
|
|
@ -28,8 +28,7 @@
|
|||
'type' => 'search',
|
||||
'button' => __('Filter'),
|
||||
'placeholder' => __('Enter value to search'),
|
||||
'data' => '',
|
||||
'searchKey' => 'value'
|
||||
'searchKey' => 'quickFilter',
|
||||
]
|
||||
]
|
||||
],
|
||||
|
@ -43,9 +42,12 @@
|
|||
'name' => __('User'),
|
||||
'sort' => 'User.email',
|
||||
'data_path' => 'User.email',
|
||||
'element' => empty($user_id) ? 'links' : 'generic_field',
|
||||
'url' => $baseurl . '/users/view',
|
||||
'url_params_data_paths' => ['User.id'],
|
||||
],
|
||||
[
|
||||
'name' => __('Auth key'),
|
||||
'name' => __('Auth Key'),
|
||||
'sort' => 'AuthKey.authkey_start',
|
||||
'element' => 'authkey',
|
||||
'data_path' => 'AuthKey',
|
||||
|
@ -56,6 +58,12 @@
|
|||
'data_path' => 'AuthKey.expiration',
|
||||
'element' => 'expiration'
|
||||
],
|
||||
[
|
||||
'name' => ('Last used'),
|
||||
'data_path' => 'AuthKey.last_used',
|
||||
'element' => 'datetime',
|
||||
'requirements' => $keyUsageEnabled,
|
||||
],
|
||||
[
|
||||
'name' => __('Comment'),
|
||||
'sort' => 'AuthKey.comment',
|
||||
|
@ -66,6 +74,14 @@
|
|||
'description' => empty($ajax) ? __('A list of API keys bound to a user.') : false,
|
||||
'pull' => 'right',
|
||||
'actions' => [
|
||||
[
|
||||
'url' => $baseurl . '/auth_keys/view',
|
||||
'url_params_data_paths' => array(
|
||||
'AuthKey.id'
|
||||
),
|
||||
'icon' => 'eye',
|
||||
'dbclickAction' => true
|
||||
],
|
||||
[
|
||||
'onclick' => sprintf(
|
||||
'openGenericModal(\'%s/authKeys/delete/[onclick_params_data_path]\');',
|
||||
|
@ -80,19 +96,14 @@
|
|||
]);
|
||||
echo '</div>';
|
||||
if (empty($ajax)) {
|
||||
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => $metaGroup, 'menuItem' => $this->action));
|
||||
echo $this->element('/genericElements/SideMenu/side_menu', $menuData);
|
||||
}
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
var passedArgsArray = <?php echo $passedArgs; ?>;
|
||||
$(document).ready(function() {
|
||||
$(function() {
|
||||
$('#quickFilterButton').click(function() {
|
||||
runIndexQuickFilter();
|
||||
});
|
||||
$('#quickFilterField').on('keypress', function (e) {
|
||||
if(e.which === 13) {
|
||||
runIndexQuickFilter();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,4 +1,17 @@
|
|||
<?php
|
||||
$keyUsageCsv = null;
|
||||
if (isset($keyUsage)) {
|
||||
$todayString = date('Y-m-d');
|
||||
$today = strtotime($todayString);
|
||||
$startDate = key($keyUsage); // oldest date for sparkline
|
||||
$startDate = strtotime($startDate) - (3600 * 24 * 3);
|
||||
$keyUsageCsv = 'Date,Close\n';
|
||||
for ($date = $startDate; $date <= $today; $date += (3600 * 24)) {
|
||||
$dateAsString = date('Y-m-d', $date);
|
||||
$keyUsageCsv .= $dateAsString . ',' . (isset($keyUsage[$dateAsString]) ? $keyUsage[$dateAsString] : 0) . '\n';
|
||||
}
|
||||
}
|
||||
|
||||
echo $this->element(
|
||||
'genericElements/SingleViews/single_view',
|
||||
[
|
||||
|
@ -14,10 +27,21 @@ echo $this->element(
|
|||
'path' => 'AuthKey.uuid',
|
||||
],
|
||||
[
|
||||
'key' => __('Auth key'),
|
||||
'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',
|
||||
|
@ -29,18 +53,24 @@ echo $this->element(
|
|||
'type' => 'expiration'
|
||||
],
|
||||
[
|
||||
'key' => __('User'),
|
||||
'path' => 'User.id',
|
||||
'pathName' => 'User.email',
|
||||
'model' => 'users',
|
||||
'type' => 'model'
|
||||
'key' => __('Key usage'),
|
||||
'type' => 'sparkline',
|
||||
'path' => 'AuthKey.id',
|
||||
'csv' => [
|
||||
'data' => $keyUsageCsv,
|
||||
],
|
||||
'requirement' => isset($keyUsage),
|
||||
],
|
||||
[
|
||||
'key' => __('Comment'),
|
||||
'path' => 'AuthKey.comment'
|
||||
'key' => __('Last used'),
|
||||
'raw' => $lastUsed ? date('Y-m-d H:i:s', $lastUsed) : __('Not used yet'),
|
||||
'requirement' => isset($keyUsage),
|
||||
],
|
||||
[
|
||||
'key' => __('Unique IPs'),
|
||||
'raw' => $uniqueIps,
|
||||
'requirement' => isset($keyUsage),
|
||||
]
|
||||
],
|
||||
'children' => [
|
||||
]
|
||||
]
|
||||
);
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
$title = __('Expired at %s', date('Y-m-d H:i:s', $data));
|
||||
$data = '<span class="red bold" title="' . $title . '">' . __('Expired') . '</span>';
|
||||
} else {
|
||||
$diffInDays = floor(($data - time()) / 3600 * 24);
|
||||
$diffInDays = floor(($data - time()) / (3600 * 24));
|
||||
$class = $diffInDays <= 14 ? 'text-warning bold' : 'text-success';
|
||||
$title = __n('Will expire at %s day', 'Will expire at %s days', $diffInDays, $diffInDays);
|
||||
$title = __n('Will expire in %s day', 'Will expire in %s days', $diffInDays, $diffInDays);
|
||||
$data = '<span class="' . $class . '" title="' . $title . '">' . date('Y-m-d H:i:s', $data) . '</span>';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
$title = __('Expired at %s', date('Y-m-d H:i:s', $data));
|
||||
$data = '<span class="red bold" title="' . $title . '">' . __('Expired') . '</span>';
|
||||
} else {
|
||||
$diffInDays = floor(($data - time()) / 3600 * 24);
|
||||
$diffInDays = floor(($data - time()) / (3600 * 24));
|
||||
$class = $diffInDays <= 14 ? 'text-warning bold' : 'text-success';
|
||||
$title = __n('Will expire at %s day', 'Will expire at %s days', $diffInDays, $diffInDays);
|
||||
$title = __n('Will expire in %s day', 'Will expire in %s days', $diffInDays, $diffInDays);
|
||||
$data = '<span class="' . $class . '" title="' . $title . '">' . date('Y-m-d H:i:s', $data) . '</span>';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
if (!empty($field['raw'])) {
|
||||
if (isset($field['raw'])) {
|
||||
$string = $field['raw'];
|
||||
} else {
|
||||
$value = Hash::extract($data, $field['path']);
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
$elementId = Hash::extract($data, $field['path'])[0];
|
||||
if (!empty($field['csv_data_path'])) {
|
||||
$csv = Hash::extract($data, $field['csv_data_path']);
|
||||
if (!empty($csv)) {
|
||||
$csv = $csv[0];
|
||||
}
|
||||
} else {
|
||||
$csv = $field['csv']['data'];
|
||||
}
|
||||
if (!empty($csv)) {
|
||||
$scope = empty($field['csv']['scope']) ? '' : $field['csv']['scope'];
|
||||
echo $this->element('sparkline', array('scope' => $scope, 'id' => $elementId, 'csv' => $csv));
|
||||
}
|
|
@ -30,6 +30,10 @@
|
|||
$listElements = '';
|
||||
if (!empty($fields)) {
|
||||
foreach ($fields as $field) {
|
||||
if (isset($field['requirement']) && !$field['requirement']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($field['type'])) {
|
||||
$field['type'] = 'generic';
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
<?php
|
||||
App::uses('AppHelper', 'View/Helper');
|
||||
|
||||
class UtilityHelper extends AppHelper {
|
||||
public function space2nbsp($string) {
|
||||
$string = str_replace("\t", "    ", $string);
|
||||
$string = preg_replace("/\s\s+/", " ", $string);
|
||||
//$string = str_replace(' ', " ", $string);
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
|
|
@ -86,7 +86,7 @@
|
|||
'type' => 'checkbox',
|
||||
'checked' => isset($this->request->data['User']['contactalert']) ? $this->request->data['User']['contactalert'] : true
|
||||
));
|
||||
echo $this->Form->input('disabled', array('type' => 'checkbox', 'label' => __('Disable this user account')));
|
||||
echo $this->Form->input('disabled', array('type' => 'checkbox', 'label' => __('Immediately disable this user account')));
|
||||
echo $this->Form->input('notify', array(
|
||||
'label' => __('Send credentials automatically'),
|
||||
'type' => 'checkbox',
|
||||
|
|
|
@ -81,13 +81,13 @@
|
|||
echo $this->Form->input('termsaccepted', array('type' => 'checkbox', 'label' => __('Terms accepted')));
|
||||
echo $this->Form->input('change_pw', [
|
||||
'type' => 'checkbox',
|
||||
'label' => __('User must change password after next login'),
|
||||
'label' => __('User must change password'),
|
||||
'disabled' => !$canChangePassword,
|
||||
'data-disabled-reason' => !$canChangePassword ? __('User password change is disabled on this instance') : '',
|
||||
]);
|
||||
echo $this->Form->input('autoalert', array('label' => __('Receive email alerts when events are published'), 'type' => 'checkbox'));
|
||||
echo $this->Form->input('contactalert', array('label' => __('Receive email alerts from "Contact reporter" requests'), 'type' => 'checkbox'));
|
||||
echo $this->Form->input('disabled', array('type' => 'checkbox', 'label' => __('Disable this user account')));
|
||||
echo $this->Form->input('disabled', array('type' => 'checkbox', 'label' => __('Immediately disable this user account')));
|
||||
echo '</div>';
|
||||
?>
|
||||
</fieldset>
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
<?php
|
||||
$title = Inflector::singularize(Inflector::humanize(Inflector::underscore($this->params['controller'])));
|
||||
?>
|
||||
<div id="genericModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="genericModalLabel" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h3 id="genericModalLabel"><?= __('Delete %s', Inflector::singularize(Inflector::humanize($this->params['controller']))) ?></h3>
|
||||
<h3 id="genericModalLabel"><?= __('Delete %s', h($title)) ?></h3>
|
||||
</div>
|
||||
<div class="modal-body modal-body-long">
|
||||
<p><?= __('Are you sure you want to delete %s #%s?', h(Inflector::singularize($this->params['controller'])), h($id)) ?></p>
|
||||
<p><?= __('Are you sure you want to delete %s #%s?', h($title), h($id)) ?></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<?= $this->Form->postLink(
|
||||
|
@ -20,7 +23,7 @@
|
|||
</div>
|
||||
<script type="text/javascript">
|
||||
$(document).keydown(function(e) {
|
||||
if(e.which === 13 && e.ctrlKey) {
|
||||
if (e.which === 13 && e.ctrlKey) {
|
||||
$('.button-execute').click();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -3371,15 +3371,15 @@ function getRemoteSyncUser(id) {
|
|||
$.ajax({
|
||||
url: baseurl + '/servers/getRemoteUser/' + id,
|
||||
type:'GET',
|
||||
beforeSend: function (XMLHttpRequest) {
|
||||
beforeSend: function () {
|
||||
resultContainer.html('Running test...');
|
||||
},
|
||||
error: function(){
|
||||
error: function() {
|
||||
resultContainer.html('Internal error.');
|
||||
},
|
||||
success: function(response) {
|
||||
resultContainer.empty();
|
||||
if (typeof(response.message) != 'undefined') {
|
||||
resultContainer.empty();
|
||||
resultContainer.append(
|
||||
$('<span>')
|
||||
.attr('class', 'red bold')
|
||||
|
@ -3389,7 +3389,6 @@ function getRemoteSyncUser(id) {
|
|||
.text(': #' + response.message)
|
||||
);
|
||||
} else {
|
||||
resultContainer.empty();
|
||||
Object.keys(response).forEach(function(key) {
|
||||
var value = response[key];
|
||||
resultContainer.append(
|
||||
|
@ -3404,7 +3403,6 @@ function getRemoteSyncUser(id) {
|
|||
);
|
||||
});
|
||||
}
|
||||
var result = response;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import json
|
||||
import datetime
|
||||
import unittest
|
||||
from typing import Union
|
||||
from typing import Union, List
|
||||
import urllib3 # type: ignore
|
||||
import logging
|
||||
import uuid
|
||||
|
@ -14,7 +16,7 @@ from lxml.html import fromstring
|
|||
from enum import Enum
|
||||
|
||||
try:
|
||||
from pymisp import PyMISP, MISPOrganisation, MISPUser, MISPRole, MISPSharingGroup, MISPEvent
|
||||
from pymisp import PyMISP, MISPOrganisation, MISPUser, MISPRole, MISPSharingGroup, MISPEvent, MISPLog
|
||||
from pymisp.exceptions import PyMISPError, NoKey, MISPServerError
|
||||
except ImportError:
|
||||
if sys.version_info < (3, 6):
|
||||
|
@ -45,7 +47,7 @@ def check_response(response):
|
|||
raise Exception(response["errors"])
|
||||
|
||||
|
||||
def login(url: str, email: str, password: str) -> bool:
|
||||
def login(url: str, email: str, password: str) -> requests.Session:
|
||||
session = requests.Session()
|
||||
|
||||
r = session.get(url)
|
||||
|
@ -77,7 +79,7 @@ def login(url: str, email: str, password: str) -> bool:
|
|||
r = r.json()
|
||||
if email != r["User"]["email"]:
|
||||
raise Exception(r) # logged in as different user
|
||||
return True
|
||||
return session
|
||||
|
||||
|
||||
class MISPSetting:
|
||||
|
@ -166,7 +168,7 @@ class TestSecurity(unittest.TestCase):
|
|||
# Try to connect as user to check if everything works
|
||||
PyMISP(url, cls.test_usr.authkey)
|
||||
# Check if user can login with given password
|
||||
assert login(url, cls.test_usr.email, cls.test_usr_password)
|
||||
assert isinstance(login(url, cls.test_usr.email, cls.test_usr_password), requests.Session)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
|
@ -179,8 +181,6 @@ class TestSecurity(unittest.TestCase):
|
|||
def setUp(self):
|
||||
# Do not show warning about not closed resources, because that something we want
|
||||
warnings.simplefilter("ignore", ResourceWarning)
|
||||
# TODO: Try to reload config cache
|
||||
self.admin_misp_connector.get_server_setting("MISP.live")
|
||||
|
||||
def test_not_logged_in(self):
|
||||
session = requests.Session()
|
||||
|
@ -302,7 +302,7 @@ class TestSecurity(unittest.TestCase):
|
|||
self.assertFalse(updated_user.disabled)
|
||||
|
||||
# Try to login
|
||||
self.assertTrue(login(url, self.test_usr.email, self.test_usr_password))
|
||||
self.assertIsInstance(login(url, self.test_usr.email, self.test_usr_password), requests.Session)
|
||||
|
||||
def test_disabled_user_api_access(self):
|
||||
# Disable user
|
||||
|
@ -327,7 +327,7 @@ class TestSecurity(unittest.TestCase):
|
|||
self.assertFalse(login(url, self.test_usr.email, self.test_usr_password))
|
||||
|
||||
# Check if user can login with given password
|
||||
self.assertTrue(login(url, self.test_usr.email, self.test_usr_password))
|
||||
self.assertIsInstance(login(url, self.test_usr.email, self.test_usr_password), requests.Session)
|
||||
|
||||
def test_disabled_misp_api_access(self):
|
||||
with self.__setting("MISP.live", False):
|
||||
|
@ -342,6 +342,7 @@ class TestSecurity(unittest.TestCase):
|
|||
with self.__setting("Security.advanced_authkeys", True):
|
||||
# Create advanced authkey
|
||||
auth_key = self.__create_advanced_authkey(self.test_usr.id)
|
||||
self.assertNotIn("authkey", auth_key)
|
||||
|
||||
# Try to login
|
||||
logged_in = PyMISP(url, auth_key["authkey_raw"])
|
||||
|
@ -374,6 +375,46 @@ class TestSecurity(unittest.TestCase):
|
|||
|
||||
self.__delete_advanced_authkey(auth_key["id"])
|
||||
|
||||
def test_advanced_authkeys_deleted(self):
|
||||
with self.__setting("Security.advanced_authkeys", True):
|
||||
auth_key = self.__create_advanced_authkey(self.test_usr.id)
|
||||
|
||||
logged_in = PyMISP(url, auth_key["authkey_raw"])
|
||||
self.assertEqual(logged_in._current_user.id, self.test_usr.id)
|
||||
|
||||
self.__delete_advanced_authkey(auth_key["id"])
|
||||
|
||||
self.__assertErrorResponse(logged_in.get_user())
|
||||
|
||||
def test_advanced_authkeys_deleted_keep_session(self):
|
||||
with self.__setting({
|
||||
"Security": {
|
||||
"advanced_authkeys": True,
|
||||
"authkey_keep_session": True,
|
||||
}
|
||||
}):
|
||||
auth_key = self.__create_advanced_authkey(self.test_usr.id)
|
||||
|
||||
logged_in = PyMISP(url, auth_key["authkey_raw"])
|
||||
self.assertEqual(logged_in._current_user.id, self.test_usr.id)
|
||||
|
||||
# Wait one second to really know that session will be reloaded
|
||||
time.sleep(1)
|
||||
|
||||
self.__delete_advanced_authkey(auth_key["id"])
|
||||
|
||||
with self.assertRaises(MISPServerError):
|
||||
logged_in.get_user()
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
def test_advanced_authkeys_own_key_not_possible(self):
|
||||
with self.__setting("Security.advanced_authkeys", True):
|
||||
authkey = ("a" * 40)
|
||||
auth_key = self.__create_advanced_authkey(self.test_usr.id, {"authkey": authkey})
|
||||
self.__delete_advanced_authkey(auth_key["id"])
|
||||
self.assertNotEqual(authkey, auth_key["authkey_raw"])
|
||||
|
||||
def test_advanced_authkeys_reset_own(self):
|
||||
with self.__setting("Security.advanced_authkeys", True):
|
||||
# Create advanced authkey
|
||||
|
@ -441,6 +482,46 @@ class TestSecurity(unittest.TestCase):
|
|||
self.__delete_advanced_authkey(auth_key["id"])
|
||||
# TODO: Delete new key
|
||||
|
||||
def test_advanced_authkeys_expiration_invalid(self):
|
||||
with self.__setting("Security.advanced_authkeys", True):
|
||||
with self.assertRaises(Exception) as cm:
|
||||
self.__create_advanced_authkey(self.test_usr.id, {"expiration": "__nonsense__"})
|
||||
self.assertIn("expiration", cm.exception.args[0][1]["errors"])
|
||||
|
||||
def test_advanced_authkeys_validity_autoset(self):
|
||||
with self.__setting({
|
||||
"Security": {
|
||||
"advanced_authkeys": True,
|
||||
"advanced_authkeys_validity": 365,
|
||||
}
|
||||
}):
|
||||
auth_key = self.__create_advanced_authkey(self.test_usr.id)
|
||||
self.assertNotEqual(0, auth_key["expiration"])
|
||||
|
||||
def test_advanced_authkeys_validity_in_range(self):
|
||||
with self.__setting({
|
||||
"Security": {
|
||||
"advanced_authkeys": True,
|
||||
"advanced_authkeys_validity": 365,
|
||||
}
|
||||
}):
|
||||
expiration = int((datetime.datetime.now() + datetime.timedelta(days=300)).timestamp())
|
||||
auth_key = self.__create_advanced_authkey(self.test_usr.id, {"expiration": expiration})
|
||||
self.__delete_advanced_authkey(auth_key["id"])
|
||||
self.assertEqual(expiration, int(auth_key["expiration"]))
|
||||
|
||||
def test_advanced_authkeys_validity_not_in_range(self):
|
||||
with self.__setting({
|
||||
"Security": {
|
||||
"advanced_authkeys": True,
|
||||
"advanced_authkeys_validity": 365,
|
||||
}
|
||||
}):
|
||||
expiration = int((datetime.datetime.now() + datetime.timedelta(days=400)).timestamp())
|
||||
with self.assertRaises(Exception) as cm:
|
||||
self.__create_advanced_authkey(self.test_usr.id, {"expiration": expiration})
|
||||
self.assertIn("expiration", cm.exception.args[0][1]["errors"])
|
||||
|
||||
def test_advanced_authkeys_view(self):
|
||||
with self.__setting("Security.advanced_authkeys", True):
|
||||
auth_key = self.__create_advanced_authkey(self.test_usr.id)
|
||||
|
@ -481,6 +562,12 @@ class TestSecurity(unittest.TestCase):
|
|||
|
||||
self.__delete_advanced_authkey(auth_key["id"])
|
||||
|
||||
def test_authkey_keep_session(self):
|
||||
with self.__setting( "Security.authkey_keep_session", True):
|
||||
logged_in = PyMISP(url, self.test_usr.authkey)
|
||||
check_response(logged_in.get_user())
|
||||
check_response(logged_in.get_user())
|
||||
|
||||
def test_change_login(self):
|
||||
new_email = 'testusr@user' + random() + '.local'
|
||||
|
||||
|
@ -530,7 +617,7 @@ class TestSecurity(unittest.TestCase):
|
|||
# Try to change email as org admin
|
||||
new_email = 'testusr@user' + random() + '.local'
|
||||
updated_user = self.org_admin_misp_connector.update_user({'email': new_email}, self.test_usr)
|
||||
self.__assertErrorResponse(updated_user)
|
||||
self.assertEqual(self.test_usr.email, updated_user.email, "Email should be still same")
|
||||
|
||||
def test_change_pw_disabled(self):
|
||||
with self.__setting("MISP.disable_user_password_change", True):
|
||||
|
@ -539,7 +626,7 @@ class TestSecurity(unittest.TestCase):
|
|||
logged_in.change_user_password(str(uuid.uuid4()))
|
||||
|
||||
# Password should be still the same
|
||||
self.assertTrue(login(url, self.test_usr.email, self.test_usr_password))
|
||||
self.assertIsInstance(login(url, self.test_usr.email, self.test_usr_password), requests.Session)
|
||||
|
||||
def test_change_pw_disabled_different_way(self):
|
||||
with self.__setting("MISP.disable_user_password_change", True):
|
||||
|
@ -548,14 +635,14 @@ class TestSecurity(unittest.TestCase):
|
|||
logged_in.update_user({"password": str(uuid.uuid4())}, self.test_usr.id)
|
||||
|
||||
# Password should be still the same
|
||||
self.assertTrue(login(url, self.test_usr.email, self.test_usr_password))
|
||||
self.assertIsInstance(login(url, self.test_usr.email, self.test_usr_password), requests.Session)
|
||||
|
||||
def test_change_pw_disabled_by_org_admin(self):
|
||||
with self.__setting("MISP.disable_user_password_change", True):
|
||||
self.org_admin_misp_connector.update_user({"password": str(uuid.uuid4())}, self.test_usr.id)
|
||||
|
||||
# Password should be still the same
|
||||
self.assertTrue(login(url, self.test_usr.email, self.test_usr_password))
|
||||
self.assertIsInstance(login(url, self.test_usr.email, self.test_usr_password), requests.Session)
|
||||
|
||||
def test_add_user_by_org_admin(self):
|
||||
user = MISPUser()
|
||||
|
@ -784,7 +871,7 @@ class TestSecurity(unittest.TestCase):
|
|||
def test_shibb_form_login(self):
|
||||
with self.__setting(self.__default_shibb_config()):
|
||||
# Form login should still works when no header provided
|
||||
self.assertTrue(login(url, self.test_usr.email, self.test_usr_password))
|
||||
self.assertIsInstance(login(url, self.test_usr.email, self.test_usr_password), requests.Session)
|
||||
|
||||
def test_shibb_api_login(self):
|
||||
with self.__setting(self.__default_shibb_config()):
|
||||
|
@ -819,6 +906,139 @@ class TestSecurity(unittest.TestCase):
|
|||
with self.__setting(config):
|
||||
PyMISP(url, self.test_usr.authkey)
|
||||
|
||||
def test_user_monitoring_enabled_no_user(self):
|
||||
request_logs_before = self.__get_logs(action="request")
|
||||
|
||||
with self.__setting("Security.user_monitoring_enabled", True):
|
||||
logged_in = PyMISP(url, self.test_usr.authkey)
|
||||
check_response(logged_in.get_user())
|
||||
|
||||
request_logs_after = self.__get_logs(action="request")
|
||||
# Number of logs should be same, because user is not monitored
|
||||
self.assertEqual(len(request_logs_after), len(request_logs_before))
|
||||
|
||||
def test_user_monitoring_enabled_add_user(self):
|
||||
request_logs_before = self.__get_logs(action="request")
|
||||
|
||||
with self.__setting("Security.user_monitoring_enabled", True):
|
||||
# Enable monitoring of test user
|
||||
send(self.admin_misp_connector, "POST", f"/admin/users/monitor/{self.test_usr.id}", {
|
||||
"value": 1,
|
||||
})
|
||||
|
||||
logged_in = PyMISP(url, self.test_usr.authkey)
|
||||
check_response(logged_in.get_user())
|
||||
|
||||
# Disable monitoring of test user
|
||||
send(self.admin_misp_connector, "POST", f"/admin/users/monitor/{self.test_usr.id}", {
|
||||
"value": 0,
|
||||
})
|
||||
|
||||
request_logs_after = self.__get_logs(action="request")
|
||||
self.assertGreater(len(request_logs_after), len(request_logs_before))
|
||||
|
||||
def test_log_paranoid(self):
|
||||
request_logs_before = self.__get_logs(action="request")
|
||||
|
||||
with self.__setting("MISP.log_paranoid", True):
|
||||
logged_in = PyMISP(url, self.test_usr.authkey)
|
||||
check_response(logged_in.get_user())
|
||||
|
||||
request_logs_after = self.__get_logs(action="request")
|
||||
self.assertGreater(len(request_logs_after), len(request_logs_before), "Number of logs should be greater")
|
||||
|
||||
def test_log_paranoid_include_post_body(self):
|
||||
request_logs_before = self.__get_logs(action="request")
|
||||
|
||||
with self.__setting({
|
||||
"MISP": {
|
||||
"log_paranoid": True,
|
||||
"log_paranoid_include_post_body": True,
|
||||
}
|
||||
}):
|
||||
logged_in = PyMISP(url, self.test_usr.authkey)
|
||||
check_response(logged_in.get_user())
|
||||
|
||||
request_logs_after = self.__get_logs(action="request")
|
||||
self.assertGreater(len(request_logs_after), len(request_logs_before), "Number of logs should be greater")
|
||||
|
||||
def test_log_paranoid_skip_db(self):
|
||||
request_logs_before = self.__get_logs(action="request")
|
||||
|
||||
with self.__setting({
|
||||
"MISP": {
|
||||
"log_paranoid": True,
|
||||
"log_paranoid_skip_db": True,
|
||||
}
|
||||
}):
|
||||
logged_in = PyMISP(url, self.test_usr.authkey)
|
||||
check_response(logged_in.get_user())
|
||||
|
||||
request_logs_after = self.__get_logs(action="request")
|
||||
# Number of logs should be same, because saving to database is disabled
|
||||
self.assertEqual(len(request_logs_after), len(request_logs_before))
|
||||
|
||||
def test_log_auth_fail_multiple(self):
|
||||
request_logs_before = self.__get_logs(action="auth_fail")
|
||||
|
||||
with self.assertRaises(PyMISPError):
|
||||
PyMISP(url, "JCZDbBr3wYPlY0DrlQzoD8EWrcClGc0Dqu2yMYyE")
|
||||
with self.assertRaises(PyMISPError):
|
||||
PyMISP(url, "JCZDbBr3wYPlY0DrlQzoD8EWrcClGc0Dqu2yMYyE")
|
||||
|
||||
request_logs_after = self.__get_logs(action="auth_fail")
|
||||
# Just one new record should be logged for multiple tries with same key
|
||||
self.assertEqual(len(request_logs_after), len(request_logs_before) + 1)
|
||||
|
||||
def test_log_user_ips(self):
|
||||
with self.__setting("MISP.log_user_ips", True):
|
||||
logged_in = PyMISP(url, self.test_usr.authkey)
|
||||
check_response(logged_in.get_user())
|
||||
|
||||
def test_log_user_ips_auth(self):
|
||||
with self.__setting({
|
||||
"MISP": {
|
||||
"log_user_ips": True,
|
||||
"log_user_ips_authkeys": True,
|
||||
}
|
||||
}):
|
||||
logged_in = PyMISP(url, self.test_usr.authkey)
|
||||
check_response(logged_in.get_user())
|
||||
|
||||
def test_username_in_response_header(self):
|
||||
with self.__setting("Security.username_in_response_header", True):
|
||||
logged_in = login(url, self.test_usr.email, self.test_usr_password)
|
||||
self.assertIsInstance(logged_in, requests.Session)
|
||||
|
||||
response = logged_in.get(url + "/users/view/me.json")
|
||||
self.assertIn("X-Username", response.headers)
|
||||
self.assertEqual(self.test_usr.email, response.headers["X-Username"])
|
||||
|
||||
def test_username_in_response_header_api_access(self):
|
||||
with self.__setting("Security.username_in_response_header", True):
|
||||
logged_in = PyMISP(url, self.test_usr.authkey)
|
||||
|
||||
response = logged_in._prepare_request('GET', 'users/view/me')
|
||||
self.assertIn("X-Username", response.headers)
|
||||
self.assertEqual(self.test_usr.email + "/API/default", response.headers["X-Username"])
|
||||
|
||||
def test_username_in_response_header_advanced_api_access(self):
|
||||
with self.__setting({
|
||||
"Security": {
|
||||
"advanced_authkeys": True,
|
||||
"username_in_response_header": True,
|
||||
}
|
||||
}):
|
||||
auth_key = self.__create_advanced_authkey(self.test_usr.id)
|
||||
|
||||
logged_in = PyMISP(url, auth_key["authkey_raw"])
|
||||
response = logged_in._prepare_request('GET', 'users/view/me')
|
||||
|
||||
self.__delete_advanced_authkey(auth_key["id"])
|
||||
|
||||
self.assertIn("X-Username", response.headers)
|
||||
self.assertEqual(f"{self.test_usr.email}/API/{auth_key['id']}", response.headers["X-Username"])
|
||||
|
||||
def test_sg_index_user_cannot_see(self):
|
||||
org = self.__create_org()
|
||||
hidden_sg = self.__create_sharing_group()
|
||||
|
@ -1119,6 +1339,11 @@ class TestSecurity(unittest.TestCase):
|
|||
def __delete_advanced_authkey(self, key_id: int):
|
||||
return send(self.admin_misp_connector, "POST", f'authKeys/delete/{key_id}')
|
||||
|
||||
def __get_logs(self, action: str) -> List[MISPLog]:
|
||||
response = self.admin_misp_connector.search_logs(action=action)
|
||||
check_response(response)
|
||||
return response
|
||||
|
||||
def __setting(self, key, value=None) -> MISPSetting:
|
||||
if not isinstance(key, dict):
|
||||
new_setting = {key: value}
|
||||
|
|
Loading…
Reference in New Issue