Merge branch 'develop' of github.com:MISP/MISP into feature_workflows/enrichment-improvements

pull/9193/head
Sami Mokaddem 2023-07-28 10:30:29 +02:00
commit 36c4100568
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
67 changed files with 1102 additions and 330 deletions

View File

@ -1509,9 +1509,17 @@ coreCAKE () {
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "GnuPG.homedir" "${PATH_TO_MISP}/.gnupg"
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "GnuPG.password" "${GPG_PASSPHRASE}"
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "GnuPG.obscure_subject" true
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "GnuPG.key_fetching_disabled" false
# FIXME: what if we have not gpg binary but a gpg2 one?
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "GnuPG.binary" "$(which gpg)"
# LinOTP
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "LinOTPAuth.enabled" false
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "LinOTPAuth.baseUrl" "https://<your-linotp-baseUrl>"
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "LinOTPAuth.realm" "lino"
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "LinOTPAuth.verifyssl" true
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "LinOTPAuth.mixedauth" false
# Enable installer org and tune some configurables
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.host_org_id" 1
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "MISP.email" "info@admin.test"
@ -1870,7 +1878,7 @@ mispmodules () {
modulesCAKE () {
# Enable Enrichment, set better timeouts
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Enrichment_services_enable" true
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Enrichment_hover_enable" true
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Enrichment_hover_enable" false
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Enrichment_hover_popover_only" false
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Enrichment_hover_timeout" 150
${SUDO_WWW} ${RUN_PHP} -- ${CAKE} Admin setSetting "Plugin.Enrichment_timeout" 300
@ -2543,7 +2551,7 @@ apacheConfig_RHEL7 () {
#sudo sed -i "s/SetHandler/\#SetHandler/g" /etc/httpd/conf.d/misp.ssl.conf
sudo rm /etc/httpd/conf.d/ssl.conf
sudo chmod 644 /etc/httpd/conf.d/misp.ssl.conf
sudo sed -i '/Listen 80/a Listen 443' /etc/httpd/conf/httpd.conf
sudo sed -i '/Listen 443/!s/Listen 80/a Listen 443/' /etc/httpd/conf/httpd.conf
# If a valid SSL certificate is not already created for the server, create a self-signed certificate:
echo "The Common Name used below will be: ${OPENSSL_CN}"
@ -2591,7 +2599,7 @@ apacheConfig_RHEL8 () {
#sudo sed -i "s/SetHandler/\#SetHandler/g" /etc/httpd/conf.d/misp.ssl.conf
sudo rm /etc/httpd/conf.d/ssl.conf
sudo chmod 644 /etc/httpd/conf.d/misp.ssl.conf
sudo sed -i '/Listen 80/a Listen 443' /etc/httpd/conf/httpd.conf
sudo sed -i '/Listen 443/!s/Listen 80/a Listen 443/' /etc/httpd/conf/httpd.conf
# If a valid SSL certificate is not already created for the server, create a self-signed certificate:
echo "The Common Name used below will be: ${OPENSSL_CN}"

View File

@ -1,5 +1,5 @@
; Generated by RHash v1.4.2 on 2022-05-23 at 12:45.34
; Generated by RHash v1.4.2 on 2023-07-01 at 17:15.04
; Written by Kravchenko Aleksey (Akademgorodok) - http://rhash.sf.net/
;
; 160126 12:45.34 2022-05-23 INSTALL.sh
INSTALL.sh 4296D40B11B3002DF3FDFD69A508ED5ECACB8C13 D32E5A4B0F37F4C937CD4F85927E998D917BCBE89E4E0E864FFD7EA09E29ADEF BD093D8018C351E3D3722646E269C4B60E6DA19F42150338CE6FD72FEE293B8B89AA69D48A84B19D3EFDDAE25EC9E646 ECACC3071E130058C3DDECC86E1CBF27DD4F11389D10F43B14293B1915F7A24F02D0DA51E299706A38C00F2D2A7505B0FE46E33B705E53594383CE65461F2B08
; 160686 17:15.04 2023-07-01 INSTALL.sh
INSTALL.sh 9576C31EC5BD942E1C9B12413E6408E4623252F7 78B708FE1FC6B39BE081B9F05C6AA5E1478F8762CAF5A8A7671A12EBA4D3C1C5 27991471FB5788F42AF3BBF86FC80A95341AA17AE9487016EEC94961A48437172702EB8E2D6CB300387E87D9E8E0E3E5 C1C21FD491AEFD662C87C3EF62837D769E63E9CF2446B9BD607CCEF8AFD72528824A8F408C6892FD51109390104010EF90DA7F4828950A8671D2986A6B8E216F

View File

@ -1 +1 @@
4296d40b11b3002df3fdfd69a508ed5ecacb8c13 INSTALL.sh
9576c31ec5bd942e1c9b12413e6408e4623252f7 INSTALL.sh

View File

@ -1 +1 @@
d32e5a4b0f37f4c937cd4f85927e998d917bcbe89e4e0e864ffd7ea09e29adef INSTALL.sh
78b708fe1fc6b39be081b9f05c6aa5e1478f8762caf5a8a7671a12eba4d3c1c5 INSTALL.sh

View File

@ -1 +1 @@
bd093d8018c351e3d3722646e269c4b60e6da19f42150338ce6fd72fee293b8b89aa69d48a84b19d3efddae25ec9e646 INSTALL.sh
27991471fb5788f42af3bbf86fc80a95341aa17ae9487016eec94961a48437172702eb8e2d6cb300387e87d9e8e0e3e5 INSTALL.sh

View File

@ -1 +1 @@
ecacc3071e130058c3ddecc86e1cbf27dd4f11389d10f43b14293b1915f7a24f02d0da51e299706a38c00f2d2a7505b0fe46e33b705e53594383ce65461f2b08 INSTALL.sh
c1c21fd491aefd662c87c3ef62837d769e63e9cf2446b9bd607ccef8afd72528824a8f408c6892fd51109390104010ef90da7f4828950a8671d2986a6b8e216f INSTALL.sh

2
PyMISP

@ -1 +1 @@
Subproject commit 7d1d8b6f38f210b28934a206f9c1470542e9da7e
Subproject commit ccae32ae716c143bea09954e860238e193bc78c6

View File

@ -1 +1 @@
{"major":2, "minor":4, "hotfix":172}
{"major":2, "minor":4, "hotfix":173}

View File

@ -1,5 +1,5 @@
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
</IfModule>
RewriteRule ^$ webroot/ "[B= ,L]"
RewriteRule (.*) webroot/$1 "[B= ,L]"
</IfModule>

View File

@ -112,6 +112,18 @@ class AdminShell extends AppShell
return $parser;
}
public function jobForgot()
{
if (empty($this->args[0])) {
die('Usage: ' . $this->Server->command_line_functions['console_admin_tasks']['data']['Forgot'] . PHP_EOL);
}
$email = $this->args[0];
$ip = empty($this->args[1]) ? null : $this->args[1];
$jobId = empty($this->args[2]) ? null : $this->args[2];
$this->User->forgot($email, $ip, $jobId);
}
public function jobGenerateCorrelation()
{
if (empty($this->args[0])) {

View File

@ -405,7 +405,17 @@ class EventShell extends AppShell
$jobId = $this->args[2];
$userId = $this->args[3];
$user = $this->getUser($userId);
$job = $this->Job->read(null, $jobId);
$job = $this->Job->find('first', [
'recursive' => -1,
'conditions' => [
'Job.id' => $jobId
]
]);
if (empty($job)) {
$log = ClassRegistry::init('Log');
$log->createLogEntry($user, 'publish', 'Event', $id, 'Event (' . $id . '): could not be published - valid job not found.', '');
return true;
}
$this->Event->Behaviors->unload('SysLogLogable.SysLogLogable');
$result = $this->Event->publish($id, $passAlong);
$job['Job']['progress'] = 100;

View File

@ -33,8 +33,8 @@ class AppController extends Controller
public $helpers = array('OrgImg', 'FontAwesome', 'UserName');
private $__queryVersion = '151';
public $pyMispVersion = '2.4.172';
private $__queryVersion = '152';
public $pyMispVersion = '2.4.173';
public $phpmin = '7.2';
public $phprec = '7.4';
public $phptoonew = '8.0';
@ -111,6 +111,7 @@ class AppController extends Controller
$this->_setupBaseurl();
$this->Auth->loginRedirect = $this->baseurl . '/users/routeafterlogin';
$this->Auth->loginAction = $this->baseurl . '/users/login';
$customLogout = Configure::read('Plugin.CustomAuth_custom_logout');
$this->Auth->logoutRedirect = $customLogout ?: ($this->baseurl . '/users/login');
@ -222,8 +223,10 @@ class AppController extends Controller
!$userLoggedIn &&
(
$controller !== 'users' ||
$action !== 'register' ||
empty(Configure::read('Security.allow_self_registration'))
(
($action !== 'register' || empty(Configure::read('Security.allow_self_registration'))) &&
(!in_array($action, ['forgot', 'password_reset']) || empty(Configure::read('Security.allow_password_forgotten')))
)
)
) {
// REST authentication
@ -314,6 +317,10 @@ class AppController extends Controller
if (!empty(Configure::read('Security.email_otp_enabled'))) {
$preAuthActions[] = 'email_otp';
}
if (!empty(Configure::read('Security.allow_password_forgotten'))) {
$preAuthActions[] = 'forgot';
$preAuthActions[] = 'password_reset';
}
if (!$this->_isControllerAction(['users' => $preAuthActions, 'servers' => ['cspReport']])) {
if ($isAjax) {
$response = $this->RestResponse->throwException(401, "Unauthorized");
@ -1112,7 +1119,7 @@ class AppController extends Controller
$user['User'] = $temp;
if ($user['User']) {
$this->User->updateLoginTimes($user['User']);
$this->Session->renew();
//$this->Session->renew();
$this->Session->write(AuthComponent::$sessionKey, $user['User']);
if (Configure::read('MISP.log_auth')) {
$this->Log = ClassRegistry::init('Log');

View File

@ -1539,6 +1539,7 @@ class AttributesController extends AppController
$user = $this->Auth->user();
$exception = null;
$filters = $this->__getSearchFilters($exception);
$this->set('passedArgsArray', ['results' => $continue]);
if ($this->request->is('post') || !empty($this->request->params['named']['tags'])) {
if ($filters === false) {
return $exception;

View File

@ -613,7 +613,7 @@ class ACLComponent extends Component
),
'sightings' => array(
'add' => array('perm_sighting'),
'restSearch' => array('perm_sighting'),
'restSearch' => array('*'),
'advanced' => array('perm_sighting'),
'delete' => ['AND' => ['perm_sighting', 'perm_modify_org']],
'index' => array('*'),
@ -748,6 +748,7 @@ class ACLComponent extends Component
'downloadTerms' => array('*'),
'edit' => array('self_management_enabled'),
'email_otp' => array('*'),
'forgot' => array('*'),
'otp' => array('*'),
'hotp' => array('*'),
'totp_new' => array('*'),
@ -760,6 +761,7 @@ class ACLComponent extends Component
'logout' => array('*'),
'logout401' => array('*'),
'notificationSettings' => ['*'],
'password_reset' => array('*'),
'register' => array('*'),
'registrations' => array(),
'resetAllSyncAuthKeys' => array(),

View File

@ -192,6 +192,10 @@ class DashboardsController extends AppController
'widget_config' => empty($valueConfig['widget_config']) ? array() : $valueConfig['widget_config']
);
if (!empty($this->request->params['named']['exportjson'])) {
return $this->RestResponse->viewData($data);
}
$this->layout = false;
$this->set('title', $dashboardWidget->title);
$this->set('widget_id', $widget_id);

View File

@ -38,7 +38,7 @@ class GalaxyClustersController extends AppController
public function index($galaxyId)
{
$galaxyId = $this->Toolbox->findIdByUuid($this->GalaxyCluster->Galaxy, $galaxyId);
$filters = $this->IndexFilter->harvestParameters(array('context', 'searchall'));
$filters = $this->_harvestParameters(array('context', 'searchall'));
$aclConditions = $this->GalaxyCluster->buildConditions($this->Auth->user());
$contextConditions = array();
if (empty($filters['context'])) {

View File

@ -915,30 +915,52 @@ class ServersController extends AppController
App::uses('File', 'Utility');
App::uses('Folder', 'Utility');
App::uses('FileAccessTool', 'Tools');
App::uses('SyncTool', 'Tools');
if (isset($server['Server'][$subm]['name'])) {
if ($this->request->data['Server'][$subm]['size'] != 0) {
if (!$this->Server->checkFilename($server['Server'][$subm]['name'])) {
throw new Exception(__('Filename not allowed'));
}
$file = new File($server['Server'][$subm]['name']);
$ext = $file->ext();
if (!is_uploaded_file($server['Server'][$subm]['tmp_name'])) {
throw new Exception(__('File not uploaded correctly'));
}
$ext = pathinfo($server['Server'][$subm]['name'], PATHINFO_EXTENSION);
if (!in_array($ext, SyncTool::ALLOWED_CERT_FILE_EXTENSIONS)) {
$this->Flash->error(__('Invalid extension.'));
$this->redirect(array('action' => 'index'));
}
if (!$server['Server'][$subm]['size'] > 0) {
$this->Flash->error(__('Incorrect extension or empty file.'));
$this->redirect(array('action' => 'index'));
}
// read pem file data
$pemData = FileAccessTool::readFromFile($server['Server'][$subm]['tmp_name'], $server['Server'][$subm]['size']);
// read certificate file data
$certData = FileAccessTool::readFromFile($server['Server'][$subm]['tmp_name'], $server['Server'][$subm]['size']);
} else {
return true;
}
} else {
$pemData = base64_decode($server['Server'][$subm]);
$ext = 'pem';
$certData = base64_decode($server['Server'][$subm]);
}
// check if the file is a valid x509 certificate
try {
$cert = openssl_x509_parse($certData);
if (!$cert) {
throw new Exception(__('Invalid certificate.'));
}
} catch (Exception $e) {
$this->Flash->error(__('Invalid certificate.'));
$this->redirect(array('action' => 'index'));
}
$destpath = APP . "files" . DS . "certs" . DS;
$dir = new Folder(APP . "files" . DS . "certs", true);
$pemfile = new File($destpath . $id . $ins . '.' . $ext);
$result = $pemfile->write($pemData);
$result = $pemfile->write($certData);
$s = $this->Server->read(null, $id);
$s['Server'][$attr] = $s['Server']['id'] . $ins . '.' . $ext;
if ($result) {

View File

@ -78,7 +78,7 @@ class ShadowAttributesController extends AppController
}
if (isset($shadow['proposal_to_delete']) && $shadow['proposal_to_delete']) {
$this->Attribute->delete($activeAttribute['Attribute']['id']);
$this->Attribute->deleteAttribute($activeAttribute['Attribute']['id'], $this->Auth->user(), false);
} else {
// Update the live attribute with the shadow data
$fieldsToUpdate = array('value1', 'value2', 'value', 'type', 'category', 'comment', 'to_ids', 'first_seen', 'last_seen');
@ -906,7 +906,24 @@ class ShadowAttributesController extends AppController
}
$params = array(
'conditions' => $conditions,
'fields' => array('ShadowAttribute.id', 'ShadowAttribute.old_id', 'ShadowAttribute.event_id', 'ShadowAttribute.type', 'ShadowAttribute.category', 'ShadowAttribute.uuid', 'ShadowAttribute.to_ids', 'ShadowAttribute.value', 'ShadowAttribute.comment', 'ShadowAttribute.org_id', 'ShadowAttribute.timestamp', 'ShadowAttribute.first_seen', 'ShadowAttribute.last_seen'),
'fields' => array(
'ShadowAttribute.id',
'ShadowAttribute.old_id',
'ShadowAttribute.event_id',
'ShadowAttribute.type',
'ShadowAttribute.category',
'ShadowAttribute.uuid',
'ShadowAttribute.to_ids',
'ShadowAttribute.value',
'ShadowAttribute.comment',
'ShadowAttribute.org_id',
'ShadowAttribute.timestamp',
'ShadowAttribute.first_seen',
'ShadowAttribute.last_seen',
'ShadowAttribute.deleted',
'ShadowAttribute.proposal_to_delete',
'ShadowAttribute.disable_correlation'
),
'contain' => array(
'Event' => array(
'fields' => array('id', 'org_id', 'info', 'orgc_id', 'uuid'),

View File

@ -66,7 +66,8 @@ class SightingsController extends AppController
$filters = !empty($this->request->data['filters']) ? $this->request->data['filters'] : false;
}
if (!$error) {
$result = $this->Sighting->saveSightings($id, $values, $timestamp, $this->Auth->user(), $type, $source, false, true, false, $filters);
$publish_sighting = !empty(Configure::read('Sightings_enable_realtime_publish'));
$result = $this->Sighting->saveSightings($id, $values, $timestamp, $this->Auth->user(), $type, $source, false, $publish_sighting, false, $filters);
}
if (!is_numeric($result)) {
$error = $result;

View File

@ -30,6 +30,10 @@ class UsersController extends AppController
// what pages are allowed for non-logged-in users
$allowedActions = array('login', 'logout', 'getGpgPublicKey', 'logout401', 'otp');
if (!empty(Configure::read('Security.allow_password_forgotten'))) {
$allowedActions[] = 'forgot';
$allowedActions[] = 'password_reset';
}
if(!empty(Configure::read('Security.email_otp_enabled'))) {
$allowedActions[] = 'email_otp';
}
@ -262,6 +266,77 @@ class UsersController extends AppController
$this->set('canFetchPgpKey', $this->__canFetchPgpKey());
}
private function __pw_change($user, $source, &$abortPost, $token = false)
{
if (!isset($this->request->data['User'])) {
$this->request->data = array('User' => $this->request->data);
}
if (Configure::read('Security.require_password_confirmation')) {
if (!empty($this->request->data['User']['current_password'])) {
$hashed = $this->User->verifyPassword($this->Auth->user('id'), $this->request->data['User']['current_password']);
if (!$hashed) {
$message = __('Invalid password. Please enter your current password to continue.');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Users', $source, false, $message, $this->response->type());
}
$abortPost = true;
$this->Flash->error($message);
}
unset($this->request->data['User']['current_password']);
} else if (!$this->_isRest()) {
$message = __('Please enter your current password to continue.');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Users', $source, false, $message, $this->response->type());
}
$abortPost = true;
$this->Flash->info($message);
}
}
$hashed = $this->User->verifyPassword($this->Auth->user('id'), $this->request->data['User']['password']);
if ($hashed) {
$message = __('Submitted new password cannot be the same as the current one');
$abortPost = true;
}
if (!$abortPost) {
// What fields should be saved (allowed to be saved)
$user['User']['change_pw'] = 0;
$user['User']['password'] = $this->request->data['User']['password'];
if ($this->_isRest()) {
$user['User']['confirm_password'] = $this->request->data['User']['password'];
} else {
$user['User']['confirm_password'] = $this->request->data['User']['confirm_password'];
}
$temp = $user['User']['password'];
// Save the data
if ($this->User->save($user)) {
if ($token) {
$this->User->purgeForgetToken($token);
}
$message = __('Password Changed.');
// log as System if the reset comes from an unauthed user using password_reset tokens
$logUser = empty($this->Auth->user()) ? 'SYSTEM' : $this->Auth->user();
$this->User->extralog($logUser, $source, null, null, $user);
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('User', $source, false, $this->response->type(), $message);
}
$this->Flash->success($message);
$this->redirect(array('action' => 'view', $user['User']['id']));
} else {
$message = __('The password could not be updated. Make sure you meet the minimum password length / complexity requirements.');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Users', $source, false, $message, $this->response->type());
}
$this->Flash->error($message);
}
} else {
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Users', $source, false, $message, $this->response->type());
} else {
$this->Flash->error($message);
}
}
}
public function change_pw()
{
$id = $this->Auth->user('id');
@ -270,69 +345,8 @@ class UsersController extends AppController
'recursive' => -1
));
if ($this->request->is('post') || $this->request->is('put')) {
if (!isset($this->request->data['User'])) {
$this->request->data = array('User' => $this->request->data);
}
$abortPost = false;
if (Configure::read('Security.require_password_confirmation')) {
if (!empty($this->request->data['User']['current_password'])) {
$hashed = $this->User->verifyPassword($this->Auth->user('id'), $this->request->data['User']['current_password']);
if (!$hashed) {
$message = __('Invalid password. Please enter your current password to continue.');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Users', 'change_pw', false, $message, $this->response->type());
}
$abortPost = true;
$this->Flash->error($message);
}
unset($this->request->data['User']['current_password']);
} else if (!$this->_isRest()) {
$message = __('Please enter your current password to continue.');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Users', 'change_pw', false, $message, $this->response->type());
}
$abortPost = true;
$this->Flash->info($message);
}
}
$hashed = $this->User->verifyPassword($this->Auth->user('id'), $this->request->data['User']['password']);
if ($hashed) {
$message = __('Submitted new password cannot be the same as the current one');
$abortPost = true;
}
if (!$abortPost) {
// What fields should be saved (allowed to be saved)
$user['User']['change_pw'] = 0;
$user['User']['password'] = $this->request->data['User']['password'];
if ($this->_isRest()) {
$user['User']['confirm_password'] = $this->request->data['User']['password'];
} else {
$user['User']['confirm_password'] = $this->request->data['User']['confirm_password'];
}
$temp = $user['User']['password'];
// Save the data
if ($this->User->save($user)) {
$message = __('Password Changed.');
$this->User->extralog($this->Auth->user(), "change_pw", null, null, $user);
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('User', 'change_pw', false, $this->response->type(), $message);
}
$this->Flash->success($message);
$this->redirect(array('action' => 'view', $id));
} else {
$message = __('The password could not be updated. Make sure you meet the minimum password length / complexity requirements.');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Users', 'change_pw', false, $message, $this->response->type());
}
$this->Flash->error($message);
}
} else {
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Users', 'change_pw', false, $message, $this->response->type());
} else {
$this->Flash->error($message);
}
}
return $this->__pw_change($user, 'change_pw', $abortPost);
}
if ($this->_isRest()) {
return $this->RestResponse->describe('Users', 'change_pw', false, $this->response->type());
@ -1369,6 +1383,7 @@ class UsersController extends AppController
unset($user['User']['password']);
$user['User']['action'] = 'logout';
$this->User->save($user['User'], true, array('id'));
$this->Session->write('otp_secret', null);
$this->redirect($this->Auth->logout());
}
@ -1878,7 +1893,11 @@ class UsersController extends AppController
);
$writer = new \BaconQrCode\Writer($renderer);
$totp->setLabel($user['User']['email']);
$totp->setIssuer(Configure::read('MISP.org') . ' MISP');
if (Configure::read('Security.otp_issuer')) {
$totp->setIssuer(Configure::read('Security.otp_issuer'));
} else {
$totp->setIssuer(Configure::read('MISP.org') . ' MISP');
}
$qrcode = $writer->writeString($totp->getProvisioningUri());
$qrcode = preg_replace('/^.+\n/', '', $qrcode); // ignore first <?xml version line
@ -3080,4 +3099,69 @@ class UsersController extends AppController
# To use this, set Plugin.CustomAuth_custom_logout to /users/logout401
$this->response->statusCode(401);
}
public function forgot()
{
if (empty(Configure::read('Security.allow_password_forgotten'))) {
$this->Flash->error(__('This feature is disabled.'));
$this->redirect('/');
}
if (!empty($this->Auth->user()) && !$this->_isRest()) {
$this->Flash->info(__('You are already logged in, no need to ask for a password reset. Log out first.'));
$this->redirect('/');
}
if ($this->request->is('post')) {
if (empty($this->request->data['User'])) {
$this->request->data = ['User' => $this->request->data];
}
if (empty($this->request->data['User']['email'])) {
throw new MethodNotAllowedException(__('No email provided, cannot generate password reset message.'));
}
$user = [
'id' => 0,
'email' => 'SYSTEM',
'Organisation' => [
'name' => 'SYSTEM'
]
];
$this->loadModel('Log');
$this->Log->createLogEntry($user, 'forgot', 'User', 0, 'Password reset requested for: ' . $this->request->data['User']['email']);
$this->User->forgotRouter($this->request->data['User']['email'], $this->_remoteIp());
$message = __('Password reset request submitted. If a valid user is found, you should receive an e-mail with a temporary reset link momentarily. Please be advised that this link is only valid for 10 minutes.');
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('User', 'forgot', false, $this->response->type(), $message);
}
$this->Flash->info($message);
$this->redirect('/');
}
}
public function password_reset($token)
{
if (empty(Configure::read('Security.allow_password_forgotten'))) {
$this->Flash->error(__('This feature is disabled.'));
$this->redirect('/');
}
$this->loadModel('Server');
$this->set('complexity', !empty(Configure::read('Security.password_policy_complexity')) ? Configure::read('Security.password_policy_complexity') : $this->Server->serverSettings['Security']['password_policy_complexity']['value']);
$this->set('length', !empty(Configure::read('Security.password_policy_length')) ? Configure::read('Security.password_policy_length') : $this->Server->serverSettings['Security']['password_policy_length']['value']);
if (!empty($this->Auth->user()) && !$this->_isRest()) {
$this->redirect('/');
}
$user = $this->User->fetchForgottenPasswordUser($token);
if (empty($user)) {
$message = __('Invalid token, or password request token already expired.');
if ($this->_isRest()) {
throw new MethodNotAllowedException($message);
} else {
$this->Flash->error($message);
$this->redirect('/');
}
}
if ($this->request->is('post') || $this->request->is('put')) {
$abortPost = false;
return $this->__pw_change(['User' => $user], 'password_reset', $abortPost, $token);
}
}
}

View File

@ -11,6 +11,7 @@ class APIActivityWidget
'limit' => 'Limits the number of displayed APIkeys. (-1 will list all) Default: -1',
'days' => 'How many days back should the list go - for example, setting 7 will only show contributions in the past 7 days. (integer)',
'month' => 'Who contributed most this month? (boolean)',
'previous_month' => 'Who contributed most the previous, finished month? (boolean)',
'year' => 'Which contributed most this year? (boolean)',
];
public $description = 'Basic widget showing some server statistics in regards to MISP.';
@ -26,16 +27,20 @@ class APIActivityWidget
$begin = new DateTime(date('Y-m-d', strtotime(sprintf("-%s days", $options['days']))));
} else if (!empty($options['month'])) {
$begin = new DateTime(date('Y-m-d', strtotime('first day of this month 00:00:00', time())));
} else if (!empty($options['previous_month'])) {
$begin = new DateTime(date('Y-m-d', strtotime('first day of last month 00:00:00', time())));
$end = new DateTime(date('Y-m-d', strtotime('last day of last month 23:59:59', time())));
} else if (!empty($options['year'])) {
$begin = new DateTime(date('Y-m-d', strtotime('first day of this year 00:00:00', time())));
} else {
$begin = new DateTime(date('Y-m-d', strtotime('-7 days', time())));;
}
$now = new DateTime();
$end = isset($end) ? $end : new DateTime();
$dates = new DatePeriod(
$begin,
new DateInterval('P1D'),
$now
$end
);
$results = [];
foreach ($dates as $date) {
@ -87,6 +92,7 @@ class APIActivityWidget
'recursive' => 1
]);
}
$results = [];
$baseurl = empty(Configure::read('MISP.external_baseurl')) ? h(Configure::read('MISP.baseurl')) : Configure::read('MISP.external_baseurl');
foreach ($counts as $key => $junk) {
$data = $temp_apikeys[$key];

View File

@ -0,0 +1,38 @@
<?php
class AttackWidget
{
public $title = 'ATT&CK heatmap';
public $render = 'Attack';
public $description = 'Retrieve an ATT&CK (or ATT&CK like) heatmap for the current instance.';
public $width = 3;
public $height = 4;
public $params = [
'filters' => 'A list of restsearch filters to apply to the heatmap. (dictionary, prepending values with ! uses them as a negation)'
];
public $cacheLifetime = 1200;
public $autoRefreshDelay = false;
private $validFilterKeys = [
'filters'
];
private $Event = null;
public $placeholder =
'{
"filters": {
"attackGalaxy": "mitre-attack-pattern",
"timestamp": ["2023-01-01", "2023-03-31"],
"published": [0,1]
}
}';
public function handler($user, $options = array())
{
$this->Event = ClassRegistry::init('Event');
$data = null;
if (!empty($options['filters'])) {
$data = $this->Event->restSearch($user, 'attack', $options['filters']);
$data = JsonTool::decode($data->intoString());
}
return $data;
}
}
?>

View File

@ -11,6 +11,7 @@ class LoginsWidget
'limit' => 'Limits the number of displayed APIkeys. (-1 will list all) Default: -1',
'days' => 'How many days back should the list go - for example, setting 7 will only show contributions in the past 7 days. (integer)',
'month' => 'Who contributed most this month? (boolean)',
'previous_month' => 'Who contributed most the previous, finished month? (boolean)',
'year' => 'Which contributed most this year? (boolean)',
];
public $description = 'Basic widget showing some server statistics in regards to MISP.';
@ -26,12 +27,22 @@ class LoginsWidget
$begin = date('Y-m-d H:i:s', strtotime(sprintf("-%s days", $options['days'])));
} else if (!empty($options['month'])) {
$begin = date('Y-m-d H:i:s', strtotime('first day of this month 00:00:00', time()));
} else if (!empty($options['previous_month'])) {
$begin = date('Y-m-d H:i:s', strtotime('first day of last month 00:00:00', time()));
$end = date('Y-m-d H:i:s', strtotime('last day of last month 23:59:59', time()));
} else if (!empty($options['year'])) {
$begin = date('Y-m-d', strtotime('first day of this year 00:00:00', time()));
} else {
$begin = date('Y-m-d H:i:s', strtotime('-7 days', time()));
}
return $begin ? ['Log.created >=' => $begin] : [];
$params = [];
if (!empty($end)) {
$params['Log.created <='] = $end;
}
if (!empty($begin)) {
$params['Log.created >='] = $begin;
}
return $params;
}
public function handler($user, $options = array())

View File

@ -15,6 +15,7 @@ class NewOrgsWidget
'filter' => 'A list of filters by organisation meta information (nationality, sector, type, name, uuid) to include. (dictionary, prepending values with ! uses them as a negation)',
'days' => 'How many days back should the list go - for example, setting 7 will only show the organisations that were added in the past 7 days. (integer)',
'month' => 'Which organisations have been added this month? (boolean)',
'previous_month' => 'Who contributed most the previous, finished month? (boolean)',
'year' => 'Which organisations have been added this year? (boolean)',
'local' => 'Should the list only show local organisations? (boolean or list of booleans, defaults to 1. To get both sets, use [0,1])',
'fields' => 'Which fields should be displayed, by default all are selected. Pass a list with the following options: [id, uuid, name, sector, type, nationality, creation_date]'
@ -51,6 +52,10 @@ class NewOrgsWidget
} else if (!empty($options['month'])) {
$condition = strtotime('first day of this month 00:00:00', time());
$this->tableDescription = __('The %d newest organisations created during the current month', $limit);
} else if (!empty($options['previous_month'])) {
$condition = strtotime('first day of last month 00:00:00', time());
$end_condition = strtotime('last day of last month 23:59:59', time());
$this->tableDescription = __('The %d newest organisations created during the previous month', $limit);
} else if (!empty($options['year'])) {
$condition = strtotime('first day of this year 00:00:00', time());
$this->tableDescription = __('The %d newest organisations created during the current year', $limit);
@ -58,9 +63,18 @@ class NewOrgsWidget
$this->tableDescription = __('The %d newest organisations created', $limit);
return null;
}
$datetime = new DateTime();
$datetime->setTimestamp($condition);
return $datetime->format('Y-m-d H:i:s');
$conditions = [];
if (!empty($condition)) {
$datetime = new DateTime();
$datetime->setTimestamp($condition);
$conditions['Organisation.date_created >='] = $datetime->format('Y-m-d H:i:s');
}
if (!empty($end_condition)) {
$datetime = new DateTime();
$datetime->setTimestamp($end_condition);
$conditions['Organisation.date_created <='] = $datetime->format('Y-m-d H:i:s');
}
return $conditions;
}
public function handler($user, $options = array())

View File

@ -15,6 +15,7 @@ class NewUsersWidget
'filter' => 'A list of filters for the organisations (nationality, sector, type, name, uuid) to include. (dictionary, prepending values with ! uses them as a negation)',
'days' => 'How many days back should the list go - for example, setting 7 will only show the organisations that were added in the past 7 days. (integer)',
'month' => 'Which organisations have been added this month? (boolean)',
'previous_month' => 'Who contributed most the previous, finished month? (boolean)',
'year' => 'Which organisations have been added this year? (boolean)',
'fields' => 'Which fields should be displayed, by default all are selected. Pass a list with the following options: [id, email, Organisation.name, Role.name, date_created]'
];
@ -57,6 +58,10 @@ class NewUsersWidget
} else if (!empty($options['month'])) {
$condition = strtotime('first day of this month 00:00:00', time());
$this->tableDescription = __('The %d newest users created during the current month', $limit);
} else if (!empty($options['previous_month'])) {
$condition = strtotime('first day of last month 00:00:00', time());
$end_condition = strtotime('last day of last month 23:59:59', time());
$this->tableDescription = __('The %d newest organisations created during the previous month', $limit);
} else if (!empty($options['year'])) {
$condition = strtotime('first day of this year 00:00:00', time());
$this->tableDescription = __('The %d newest users created during the current year', $limit);
@ -64,7 +69,18 @@ class NewUsersWidget
$this->tableDescription = __('The %d newest users created', $limit);
return null;
}
return $condition;
$conditions = [];
if (!empty($condition)) {
$datetime = new DateTime();
$datetime->setTimestamp($condition);
$conditions['Organisation.date_created >='] = $datetime->format('Y-m-d H:i:s');
}
if (!empty($end_condition)) {
$datetime = new DateTime();
$datetime->setTimestamp($end_condition);
$conditions['Organisation.date_created <='] = $datetime->format('Y-m-d H:i:s');
}
return $conditions;
}
public function handler($user, $options = array())
@ -123,7 +139,7 @@ class NewUsersWidget
}
$timeConditions = $this->timeConditions($options);
if ($timeConditions) {
$params['conditions']['AND'][] = ['User.date_created >=' => $timeConditions];
$params['conditions']['AND'][] = $timeConditions;
}
if (isset($options['fields'])) {
$fields = [];

View File

@ -9,6 +9,7 @@ class OrgContributionToplistWidget
public $params = [
'days' => 'How many days back should the list go - for example, setting 7 will only show contributions in the past 7 days. (integer)',
'month' => 'Who contributed most this month? (boolean)',
'previous_month' => 'Who contributed most the previous, finished month? (boolean)',
'year' => 'Which contributed most this year? (boolean)',
'filter' => 'A list of filters by organisation meta information (nationality, sector, type, name, uuid, local (- expects a boolean or a list of boolean values)) to include. (dictionary, prepending values with ! uses them as a negation)',
'limit' => 'Limits the number of displayed tags. Default: 10'
@ -41,12 +42,26 @@ class OrgContributionToplistWidget
$condition = strtotime(sprintf("-%s days", $options['days']));
} else if (!empty($options['month'])) {
$condition = strtotime('first day of this month 00:00:00', time());
} else if (!empty($options['previous_month'])) {
$condition = strtotime('first day of previous month 00:00:00', time());
$end_condition = strtotime('last day of last month 23:59:59', time());
} else if (!empty($options['year'])) {
$condition = strtotime('first day of this year 00:00:00', time());
} else {
return null;
}
return $condition;
$conditions = [];
if (!empty($condition)) {
$datetime = new DateTime();
$datetime->setTimestamp($condition);
$conditions['Event.timestamp >='] = $datetime->format('Y-m-d H:i:s');
}
if (!empty($end_condition)) {
$datetime = new DateTime();
$datetime->setTimestamp($end_condition);
$conditions['Event.timestamp <='] = $datetime->format('Y-m-d H:i:s');
}
return $conditions;
}
@ -55,7 +70,7 @@ class OrgContributionToplistWidget
$params = ['conditions' => []];
$timeConditions = $this->timeConditions($options);
if ($timeConditions) {
$params['conditions']['AND'][] = ['Event.timestamp >=' => $timeConditions];
$params['conditions']['AND'][] = $timeConditions;
}
if (!empty($options['filter']) && is_array($options['filter'])) {
foreach ($this->validFilterKeys as $filterKey) {

View File

@ -0,0 +1,121 @@
<?php
/**
* Org Events widget which reportes the number of events created monthly by each local organizations
*
*/
class OrgEventsWidget
{
public $title = 'Org Events';
public $render = 'MultiLineChart';
public $width = 8;
public $height = 6;
public $description = 'A graph to show the monthly number of events per organisation';
public $cacheLifetime = 10;
public $autoRefreshDelay = false;
public $params = array (
'blocklist_orgs' => 'A list of organisation names to filter out',
'months' => 'Number of past months to consider for the graph',
'logarithmic' => 'Visualize data on logarithmic scale'
);
public $placeholder =
'{
"blocklist_orgs": ["Orgs to filter"],
"months": "6",
"logarithmic": "true"
}';
/*
* Target_month must be from 1 to 12
* Target year must be 4 digits
*/
private function org_events_count($user, $org, $target_month, $target_year) {
$events_count = 0;
$start_date = $target_year.'-'.$target_month.'-01';
if($target_month == 12) {
$end_date = ($target_year+1).'-01-01';
} else {
$end_date = $target_year.'-'.($target_month+1).'-01';
}
$conditions = array('Event.orgc_id' => $org['Organisation']['id'], 'Event.date >=' => $start_date, 'Event.date <' => $end_date);
//This is required to enforce the ACL (not pull directly from the DB)
$eventIds = $this->Event->fetchSimpleEventIds($user, array('conditions' => $conditions));
if(!empty($eventIds)) {
$params = array('Event.id' => $eventIds);
$events = $this->Event->find('all', array('conditions' => array('AND' => $params)));
foreach($events as $event) {
$events_count+= 1;
}
}
return $events_count;
}
private function filter_ghost_orgs(&$data, $orgs){
foreach ($data['data'] as &$item) {
foreach(array_keys($orgs) as $org_name) {
unset($item[$org_name]);
}
}
}
public function handler($user, $options = array())
{
$this->Log = ClassRegistry::init('Log');
$this->Org = ClassRegistry::init('Organisation');
$this->Event = ClassRegistry::init('Event');
$orgs = $this->Org->find('all', array( 'conditions' => array('Organisation.local' => 1)));
$current_month = date('n');
$current_year = date('Y');
$limit = 6; // months
if(!empty($options['months'])) {
$limit = (int) ($options['months']);
}
$offset = 0;
$ghost_orgs = array(); // track orgs without any contribution
// We start by putting all orgs_id in there:
foreach($orgs as $org) {
// We check for blocklisted orgs
if(!empty($options['blocklist_orgs']) && in_array($org['Organisation']['name'], $options['blocklist_orgs'])) {
unset($orgs[$offset]);
} else {
$ghost_orgs[$org['Organisation']['name']] = true;
}
$offset++;
}
$data = array();
$data['data'] = array();
for ($i=0; $i < $limit; $i++) {
$target_month = $current_month - $i;
$target_year = $current_year;
if ($target_month < 1) {
$target_month += 12;
$target_year -= 1;
}
$item = array();
$item ['date'] = $target_year.'-'.$target_month.'-01';
foreach($orgs as $org) {
$count = $this->org_events_count($user, $org, $target_month, $target_year);
if($options['logarithmic'] === "true" || $options['logarithmic'] === "1") {
$item[$org['Organisation']['name']] = (int) round(log($count, 1.1)); // taking the logarithmic view
} else if(empty($options['logarithmic']) || $options['logarithmic'] === "true" || $options['logarithmic'] === "1"){
$item[$org['Organisation']['name']] = $count;
}
// if a positive score is detected at least once it's enough to be
// considered for the graph
if($count > 0) {
unset($ghost_orgs[$org['Organisation']['name']]);
}
}
$data['data'][] = $item;
}
$this->filter_ghost_orgs($data, $ghost_orgs);
return $data;
}
}

View File

@ -0,0 +1,81 @@
<?php
class OrganisationListWidget
{
public $title = 'Organisation list';
public $render = 'BarChart';
public $description = 'The countries represented via organisations on the current instance.';
public $width = 3;
public $height = 4;
public $params = [
'filter' => 'A list of filters by organisation meta information (sector, type, local (- expects a boolean or a list of boolean values)) to include. (dictionary, prepending values with ! uses them as a negation)',
'limit' => 'Limits the number of displayed tags. Default: 10'
];
public $cacheLifetime = null;
public $autoRefreshDelay = false;
private $validFilterKeys = [
'sector',
'type',
'local'
];
public $placeholder =
'{
"filter": {
"type": "Member",
"local": [0,1]
}
}';
private $Organisation = null;
public $countryCodes = [];
public function handler($user, $options = array())
{
App::uses('WidgetToolkit', 'Lib/Dashboard/Tools');
$WidgetToolkit = new WidgetToolkit();
$this->countryCodes = $WidgetToolkit->getCountryCodeMapping();
$params = [
'conditions' => [
'Nationality !=' => ''
]
];
if (!empty($options['filter']) && is_array($options['filter'])) {
foreach ($this->validFilterKeys as $filterKey) {
if (!empty($options['filter'][$filterKey])) {
if (!is_array($options['filter'][$filterKey])) {
$options['filter'][$filterKey] = [$options['filter'][$filterKey]];
}
$tempConditionBucket = [];
foreach ($options['filter'][$filterKey] as $value) {
if ($value[0] === '!') {
$tempConditionBucket['Organisation.' . $filterKey . ' NOT IN'][] = mb_substr($value, 1);
} else {
$tempConditionBucket['Organisation.' . $filterKey . ' IN'][] = $value;
}
}
if (!empty($tempConditionBucket)) {
$params['conditions']['AND'][] = $tempConditionBucket;
}
}
}
}
$this->Organisation = ClassRegistry::init('Organisation');
$orgs = $this->Organisation->find('all', [
'recursive' => -1,
'fields' => ['Organisation.nationality', 'COUNT(Organisation.nationality) AS frequency'],
'conditions' => $params['conditions'],
'group' => ['Organisation.nationality']
]);
$results = [];
foreach($orgs as $org) {
$country = $org['Organisation']['nationality'];
$count = $org['0']['frequency'];
if (isset($this->countryCodes[$country])) {
$countryCode = $this->countryCodes[$country];
$results[$countryCode] = $count;
}
}
arsort($results);
return ['data' => $results];
}
}
?>

View File

@ -26,188 +26,13 @@ class OrganisationMapWidget
}';
private $Organisation = null;
public $countryCodes = array(
'Afghanistan' => 'AF',
'Albania' => 'AL',
'Algeria' => 'DZ',
'Angola' => 'AO',
'Argentina' => 'AR',
'Armenia' => 'AM',
'Australia' => 'AU',
'Austria' => 'AT',
'Azerbaijan' => 'AZ',
'Bahamas' => 'BS',
'Bangladesh' => 'BD',
'Belarus' => 'BY',
'Belgium' => 'BE',
'Belize' => 'BZ',
'Benin' => 'BJ',
'Bhutan' => 'BT',
'Bolivia' => 'BO',
'Bosnia and Herz.' => 'BA',
'Botswana' => 'BW',
'Brazil' => 'BR',
'Brunei' => 'BN',
'Bulgaria' => 'BG',
'Burkina Faso' => 'BF',
'Burundi' => 'BI',
'Cambodia' => 'KH',
'Cameroon' => 'CM',
'Canada' => 'CA',
'Central African Rep.' => 'CF',
'Chad' => 'TD',
'Chile' => 'CL',
'China' => 'CN',
'Colombia' => 'CO',
'Congo' => 'CG',
'Costa Rica' => 'CR',
'Croatia' => 'HR',
'Cuba' => 'CU',
'Cyprus' => 'CY',
'Czech Rep.' => 'CZ',
'Czech Republic' => 'CZ',
'Côte d\'Ivoire' => 'CI',
'Dem. Rep. Congo' => 'CD',
'Dem. Rep. Korea' => 'KP',
'Denmark' => 'DK',
'Djibouti' => 'DJ',
'Dominican Rep.' => 'DO',
'Ecuador' => 'EC',
'Egypt' => 'EG',
'El Salvador' => 'SV',
'Eq. Guinea' => 'GQ',
'Eritrea' => 'ER',
'Estonia' => 'EE',
'Ethiopia' => 'ET',
'Falkland Is.' => 'FK',
'Fiji' => 'FJ',
'Finland' => 'FI',
'Fr. S. Antarctic Lands' => 'TF',
'France' => 'FR',
'Gabon' => 'GA',
'Gambia' => 'GM',
'Georgia' => 'GE',
'Germany' => 'DE',
'Ghana' => 'GH',
'Greece' => 'GR',
'Greenland' => 'GL',
'Guatemala' => 'GT',
'Guinea' => 'GN',
'Guinea-Bissau' => 'GW',
'Guyana' => 'GY',
'Haiti' => 'HT',
'Honduras' => 'HN',
'Hungary' => 'HU',
'Iceland' => 'IS',
'India' => 'IN',
'Indonesia' => 'ID',
'Iran' => 'IR',
'Iraq' => 'IQ',
'Ireland' => 'IE',
'Ireland {Republic}' => 'IE',
'Israel' => 'IL',
'Italy' => 'IT',
'Jamaica' => 'JM',
'Japan' => 'JP',
'Jordan' => 'JO',
'Kazakhstan' => 'KZ',
'Kenya' => 'KE',
'Korea' => 'KR',
'Kuwait' => 'KW',
'Kyrgyzstan' => 'KG',
'Lao PDR' => 'LA',
'Latvia' => 'LV',
'Lebanon' => 'LB',
'Lesotho' => 'LS',
'Liberia' => 'LR',
'Libya' => 'LY',
'Lithuania' => 'LT',
'Luxembourg' => 'LU',
'Macedonia' => 'MK',
'Madagascar' => 'MG',
'Mainland China' => 'CN',
'Malawi' => 'MW',
'Malaysia' => 'MY',
'Mali' => 'ML',
'Malta' => 'MT',
'Mauritania' => 'MR',
'Mexico' => 'MX',
'Moldova' => 'MD',
'Mongolia' => 'MN',
'Montenegro' => 'ME',
'Morocco' => 'MA',
'Mozamb' => 'MZ',
'Myanmar' => 'MM',
'Namibia' => 'NA',
'Nepal' => 'NP',
'Netherlands' => 'NL',
'New Caledonia' => 'NC',
'New Zealand' => 'NZ',
'Nicaragua' => 'NI',
'Niger' => 'NE',
'Nigeria' => 'NG',
'Norway' => 'NO',
'Oman' => 'OM',
'Pakistan' => 'PK',
'Palestine' => 'PS',
'Panama' => 'PA',
'Papua New Guinea' => 'PG',
'Paraguay' => 'PY',
'Peru' => 'PE',
'Philippines' => 'PH',
'Poland' => 'PL',
'Portugal' => 'PT',
'Puerto Rico' => 'PR',
'Qatar' => 'QA',
'Romania' => 'RO',
'Russia' => 'RU',
'Rwanda' => 'RW',
'S. Sudan' => 'SS',
'Saudi Arabia' => 'SA',
'Senegal' => 'SN',
'Serbia' => 'RS',
'Sierra Leone' => 'SL',
'Slovakia' => 'SK',
'Slovenia' => 'SI',
'Solomon Is.' => 'SB',
'Somalia' => 'SO',
'South Africa' => 'ZA',
'Spain' => 'ES',
'Sri Lanka' => 'LK',
'Sudan' => 'SD',
'Suriname' => 'SR',
'Swaziland' => 'SZ',
'Sweden' => 'SE',
'Switzerland' => 'CH',
'Syria' => 'SY',
'Taiwan' => 'TW',
'Tajikistan' => 'TJ',
'Tanzania' => 'TZ',
'Thailand' => 'TH',
'Timor-Leste' => 'TL',
'Togo' => 'TG',
'Trinidad and Tobago' => 'TT',
'Tunisia' => 'TN',
'Turkey' => 'TR',
'Turkmenistan' => 'TM',
'Uganda' => 'UG',
'Ukraine' => 'UA',
'United Arab Emirates' => 'AE',
'United Kingdom' => 'GB',
'United States' => 'US',
'Uruguay' => 'UY',
'Uzbekistan' => 'UZ',
'Vanuatu' => 'VU',
'Venezuela' => 'VE',
'Vietnam' => 'VN',
'W. Sahara' => 'EH',
'Yemen' => 'YE',
'Zambia' => 'ZM',
'Zimbabwe' => 'ZW'
);
public $countryCodes = [];
public function handler($user, $options = array())
{
App::uses('WidgetToolkit', 'Lib/Dashboard/Tools');
$WidgetToolkit = new WidgetToolkit();
$this->countryCodes = $WidgetToolkit->getCountryCodeMapping();
$params = [
'conditions' => [
'Nationality !=' => ''

View File

@ -0,0 +1,189 @@
<?php
class WidgetToolkit
{
public function getCountryCodeMapping(): array
{
return [
'Afghanistan' => 'AF',
'Albania' => 'AL',
'Algeria' => 'DZ',
'Angola' => 'AO',
'Argentina' => 'AR',
'Armenia' => 'AM',
'Australia' => 'AU',
'Austria' => 'AT',
'Azerbaijan' => 'AZ',
'Bahamas' => 'BS',
'Bangladesh' => 'BD',
'Belarus' => 'BY',
'Belgium' => 'BE',
'Belize' => 'BZ',
'Benin' => 'BJ',
'Bhutan' => 'BT',
'Bolivia' => 'BO',
'Bosnia and Herz.' => 'BA',
'Botswana' => 'BW',
'Brazil' => 'BR',
'Brunei' => 'BN',
'Bulgaria' => 'BG',
'Burkina Faso' => 'BF',
'Burundi' => 'BI',
'Cambodia' => 'KH',
'Cameroon' => 'CM',
'Canada' => 'CA',
'Central African Rep.' => 'CF',
'Chad' => 'TD',
'Chile' => 'CL',
'China' => 'CN',
'Colombia' => 'CO',
'Congo' => 'CG',
'Costa Rica' => 'CR',
'Croatia' => 'HR',
'Cuba' => 'CU',
'Cyprus' => 'CY',
'Czech Rep.' => 'CZ',
'Czech Republic' => 'CZ',
'Côte d\'Ivoire' => 'CI',
'Dem. Rep. Congo' => 'CD',
'Dem. Rep. Korea' => 'KP',
'Denmark' => 'DK',
'Djibouti' => 'DJ',
'Dominican Rep.' => 'DO',
'Ecuador' => 'EC',
'Egypt' => 'EG',
'El Salvador' => 'SV',
'Eq. Guinea' => 'GQ',
'Eritrea' => 'ER',
'Estonia' => 'EE',
'Ethiopia' => 'ET',
'Falkland Is.' => 'FK',
'Fiji' => 'FJ',
'Finland' => 'FI',
'Fr. S. Antarctic Lands' => 'TF',
'France' => 'FR',
'Gabon' => 'GA',
'Gambia' => 'GM',
'Georgia' => 'GE',
'Germany' => 'DE',
'Ghana' => 'GH',
'Greece' => 'GR',
'Greenland' => 'GL',
'Guatemala' => 'GT',
'Guinea' => 'GN',
'Guinea-Bissau' => 'GW',
'Guyana' => 'GY',
'Haiti' => 'HT',
'Honduras' => 'HN',
'Hungary' => 'HU',
'Iceland' => 'IS',
'India' => 'IN',
'Indonesia' => 'ID',
'Iran' => 'IR',
'Iraq' => 'IQ',
'Ireland' => 'IE',
'Ireland {Republic}' => 'IE',
'Israel' => 'IL',
'Italy' => 'IT',
'Jamaica' => 'JM',
'Japan' => 'JP',
'Jordan' => 'JO',
'Kazakhstan' => 'KZ',
'Kenya' => 'KE',
'Korea' => 'KR',
'Kuwait' => 'KW',
'Kyrgyzstan' => 'KG',
'Lao PDR' => 'LA',
'Latvia' => 'LV',
'Lebanon' => 'LB',
'Lesotho' => 'LS',
'Liberia' => 'LR',
'Libya' => 'LY',
'Lithuania' => 'LT',
'Luxembourg' => 'LU',
'Macedonia' => 'MK',
'Madagascar' => 'MG',
'Mainland China' => 'CN',
'Malawi' => 'MW',
'Malaysia' => 'MY',
'Mali' => 'ML',
'Malta' => 'MT',
'Mauritania' => 'MR',
'Mexico' => 'MX',
'Moldova' => 'MD',
'Mongolia' => 'MN',
'Montenegro' => 'ME',
'Morocco' => 'MA',
'Mozamb' => 'MZ',
'Myanmar' => 'MM',
'Namibia' => 'NA',
'Nepal' => 'NP',
'Netherlands' => 'NL',
'New Caledonia' => 'NC',
'New Zealand' => 'NZ',
'Nicaragua' => 'NI',
'Niger' => 'NE',
'Nigeria' => 'NG',
'Norway' => 'NO',
'Oman' => 'OM',
'Pakistan' => 'PK',
'Palestine' => 'PS',
'Panama' => 'PA',
'Papua New Guinea' => 'PG',
'Paraguay' => 'PY',
'Peru' => 'PE',
'Philippines' => 'PH',
'Poland' => 'PL',
'Portugal' => 'PT',
'Puerto Rico' => 'PR',
'Qatar' => 'QA',
'Romania' => 'RO',
'Russia' => 'RU',
'Russian Federation' => 'RU',
'Rwanda' => 'RW',
'S. Sudan' => 'SS',
'Saudi Arabia' => 'SA',
'Senegal' => 'SN',
'Serbia' => 'RS',
'Sierra Leone' => 'SL',
'Slovakia' => 'SK',
'Slovenia' => 'SI',
'Solomon Is.' => 'SB',
'Somalia' => 'SO',
'South Africa' => 'ZA',
'Spain' => 'ES',
'Sri Lanka' => 'LK',
'Sudan' => 'SD',
'Suriname' => 'SR',
'Swaziland' => 'SZ',
'Sweden' => 'SE',
'Switzerland' => 'CH',
'Syria' => 'SY',
'Taiwan' => 'TW',
'Tajikistan' => 'TJ',
'Tanzania' => 'TZ',
'Thailand' => 'TH',
'Timor-Leste' => 'TL',
'Togo' => 'TG',
'Trinidad and Tobago' => 'TT',
'Tunisia' => 'TN',
'Turkey' => 'TR',
'Turkmenistan' => 'TM',
'Uganda' => 'UG',
'Ukraine' => 'UA',
'United Arab Emirates' => 'AE',
'United Kingdom' => 'GB',
'United States' => 'US',
'Uruguay' => 'UY',
'Uzbekistan' => 'UZ',
'Vanuatu' => 'VU',
'Venezuela' => 'VE',
'Vietnam' => 'VN',
'W. Sahara' => 'EH',
'Yemen' => 'YE',
'Zambia' => 'ZM',
'Zimbabwe' => 'ZW'
];
}
}

View File

@ -38,7 +38,7 @@ class TrendingAttributesWidget
$organisationModel = ClassRegistry::init('Organisation');
if (!empty($options['org_filter']) && is_array($options['org_filter'])) {
foreach ($this->validOrgFilters as $filterKey) {
if (!empty($options['org_filter'][$filterKey])) {
if (isset($options['org_filter'][$filterKey])) {
if ($filterKey === 'local') {
$tempConditionBucket['Organisation.local'] = $options['org_filter']['local'];
} else {
@ -72,9 +72,10 @@ class TrendingAttributesWidget
/** @var Event $eventModel */
$attributeModel = ClassRegistry::init('Attribute');
$threshold = empty($options['threshold']) ? 10 : $options['threshold'];
$time_window = empty($options['time_window']) ? (7 * 24 * 60 * 60) : (int)$options['time_window'];
if (is_string($time_window) && substr($time_window, -1) === 'd') {
$time_window = ((int)substr($time_window, 0, -1)) * 24 * 60 * 60;
if (is_string($options['time_window']) && substr($options['time_window'], -1) === 'd') {
$time_window = ((int)substr($options['time_window'], 0, -1)) * 24 * 60 * 60;
} else {
$time_window = empty($options['time_window']) ? (7 * 24 * 60 * 60) : (int)$options['time_window'];
}
$conditions = $time_window === -1 ? [] : ['Attribute.timestamp >=' => time() - $time_window];
$conditions['Attribute.deleted'] = 0;

View File

@ -30,9 +30,10 @@ class TrendingTagsWidget
/** @var Event $eventModel */
$eventModel = ClassRegistry::init('Event');
$threshold = empty($options['threshold']) ? 10 : $options['threshold'];
$time_window = empty($options['time_window']) ? (7 * 24 * 60 * 60) : $options['time_window'];
if (is_string($time_window) && substr($time_window, -1) === 'd') {
$time_window = ((int)substr($time_window, 0, -1)) * 24 * 60 * 60;
if (is_string($options['time_window']) && substr($options['time_window'], -1) === 'd') {
$time_window = ((int)substr($options['time_window'], 0, -1)) * 24 * 60 * 60;
} else {
$time_window = empty($options['time_window']) ? (7 * 24 * 60 * 60) : (int)$options['time_window'];
}
$params = $time_window === -1 ? [] : ['timestamp' => time() - $time_window];

View File

@ -9,6 +9,7 @@ class UserContributionToplistWidget
public $params = [
'days' => 'How many days back should the list go - for example, setting 7 will only show contributions in the past 7 days. (integer)',
'month' => 'Who contributed most this month? (boolean)',
'previous_month' => 'Who contributed most the previous, finished month? (boolean)',
'year' => 'Which contributed most this year? (boolean)',
'filter' => 'A list of filters by organisation meta information (nationality, sector, type, name, uuid, local (- expects a boolean or a list of boolean values)) to include. (dictionary, prepending values with ! uses them as a negation)',
'limit' => 'Limits the number of displayed tags. Default: 10'
@ -41,12 +42,26 @@ class UserContributionToplistWidget
$condition = strtotime(sprintf("-%s days", $options['days']));
} else if (!empty($options['month'])) {
$condition = strtotime('first day of this month 00:00:00', time());
} else if (!empty($options['previous_month'])) {
$condition = strtotime('first day of previous month 00:00:00', time());
$end_condition = strtotime('last day of last month 23:59:59', time());
} else if (!empty($options['year'])) {
$condition = strtotime('first day of this year 00:00:00', time());
} else {
return null;
}
return $condition;
$conditions = [];
if (!empty($condition)) {
$datetime = new DateTime();
$datetime->setTimestamp($condition);
$conditions['Event.timestamp >='] = $datetime->format('Y-m-d H:i:s');
}
if (!empty($end_condition)) {
$datetime = new DateTime();
$datetime->setTimestamp($end_condition);
$conditions['Event.timestamp <='] = $datetime->format('Y-m-d H:i:s');
}
return $conditions;
}
@ -55,7 +70,7 @@ class UserContributionToplistWidget
$params = ['conditions' => []];
$timeConditions = $this->timeConditions($options);
if ($timeConditions) {
$params['conditions']['AND'][] = ['Event.timestamp >=' => $timeConditions];
$params['conditions']['AND'][] = $timeConditions;
}
if (!empty($options['filter']) && is_array($options['filter'])) {
foreach ($this->validFilterKeys as $filterKey) {

View File

@ -83,6 +83,12 @@ abstract class StixExport
if ($this->__empty_file) {
$this->__tmp_file->close();
$this->__tmp_file->delete();
if (empty($this->__filenames)) {
$framing = $this->getFraming();
$tmpFile = new TmpFileTool();
$tmpFile->write($framing['header'] . $framing['footer']);
return $tmpFile;
}
} else {
if (!empty($this->__event_galaxies)) {
$this->__write_event_galaxies();

View File

@ -224,7 +224,6 @@ class AttributeValidationTool
switch ($type) {
case 'md5':
case 'imphash':
case 'telfhash':
case 'sha1':
case 'sha224':
case 'sha256':
@ -255,6 +254,11 @@ class AttributeValidationTool
return true;
}
return __('Checksum has an invalid length or format (expected: at least 35 hexadecimal characters, optionally starting with t1 instead of hexadecimal characters). Please double check the value or select type "other".');
case 'telfhash':
if (self::isTelfhashValid($value)) {
return true;
}
return __('Checksum has an invalid length or format (expected: %s or %s hexadecimal characters). Please double check the value or select type "other".', 70, 72);
case 'pehash':
if (self::isHashValid('pehash', $value)) {
return true;
@ -635,6 +639,15 @@ class AttributeValidationTool
return strlen($value) > 35 && ctype_xdigit($value);
}
/**
* @param string $value
* @return bool
*/
private static function isTelfhashValid($value)
{
return strlen($value) == 70 || strlen($value) == 72;
}
/**
* @param string $type

View File

@ -2,6 +2,9 @@
class SyncTool
{
const ALLOWED_CERT_FILE_EXTENSIONS = ['pem', 'crt'];
/**
* Take a server as parameter and return a HttpSocket object using the ssl options defined in the server settings
* @param array|null $server
@ -15,10 +18,10 @@ class SyncTool
$params = ['compress' => true];
if (!empty($server)) {
if (!empty($server[$model]['cert_file'])) {
$params['ssl_cafile'] = APP . "files" . DS . "certs" . DS . $server[$model]['id'] . '.pem';
$params['ssl_cafile'] = APP . "files" . DS . "certs" . DS . $server[$model]['cert_file'];
}
if (!empty($server[$model]['client_cert_file'])) {
$params['ssl_local_cert'] = APP . "files" . DS . "certs" . DS . $server[$model]['id'] . '_client.pem';
$params['ssl_local_cert'] = APP . "files" . DS . "certs" . DS . $server[$model]['client_cert_file'];
}
if (!empty($server[$model]['self_signed'])) {
$params['ssl_allow_self_signed'] = true;

View File

@ -85,7 +85,7 @@ class AppModel extends Model
93 => false, 94 => false, 95 => true, 96 => false, 97 => true, 98 => false,
99 => false, 100 => false, 101 => false, 102 => false, 103 => false, 104 => false,
105 => false, 106 => false, 107 => false, 108 => false, 109 => false, 110 => false,
111 => false, 112 => false, 113 => true
111 => false, 112 => false, 113 => true, 114 => false
);
const ADVANCED_UPDATES_DESCRIPTION = array(
@ -1970,6 +1970,9 @@ class AppModel extends Model
$this->cleanCacheFiles();
$sqlArray[] = "UPDATE roles SET perm_view_feed_correlations = 1;";
break;
case 114:
$indexArray[] = ['object_references', 'uuid'];
break;
case 'fixNonEmptySharingGroupID':
$sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
$sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';

View File

@ -219,9 +219,9 @@ class AuthKey extends AppModel
$user['authkey_read_only'] = (bool)$authkey['AuthKey']['read_only'];
if ($authkey['AuthKey']['read_only']) {
// Disable all permissions, keep just `perm_auth` unchanged
// Disable all permissions, keep just `perm_auth` and `perm_audit` unchanged
foreach ($user['Role'] as $key => &$value) {
if (substr($key, 0, 5) === 'perm_' && $key !== 'perm_auth') {
if (substr($key, 0, 5) === 'perm_' && $key !== 'perm_auth' && $key !== 'perm_audit') {
$value = 0;
}
}

View File

@ -2862,10 +2862,35 @@ class Event extends AppModel
return $conditions;
}
/**
* @param string $value
* @return string
*/
private static function compressIpv6($value)
{
if (strpos($value, ':') && $converted = inet_pton($value)) {
return inet_ntop($converted);
}
return $value;
}
public function set_filter_value(&$params, $conditions, $options)
{
if (!empty($params['value'])) {
$params[$options['filter']] = $this->convert_filters($params['value']);
foreach (['OR', 'AND', 'NOT'] as $operand) {
if (!empty($params[$options['filter']][$operand])) {
foreach ($params[$options['filter']][$operand] as $k => $v) {
if ($operand === 'NOT') {
$v = mb_substr($v, 1);
}
if (filter_var($v, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
$v = $this->compressIpv6($v);
}
$params[$options['filter']][$operand][$k] = $operand === 'NOT' ? '!' . $v : $v;
}
}
}
$conditions = $this->generic_add_filter($conditions, $params['value'], ['Attribute.value1', 'Attribute.value2']);
}
@ -4478,7 +4503,7 @@ class Event extends AppModel
/** @var Job $job */
$job = ClassRegistry::init('Job');
$message = empty($sightingUuids) ? __('Publishing sightings.') : __('Publishing %s sightings.', count($sightingUuids));
$jobId = $job->createJob($user, Job::WORKER_PRIO, 'publish_event', "Event ID: $id", $message);
$jobId = $job->createJob($user, Job::WORKER_DEFAULT, 'publish_event', "Event ID: $id", $message);
$args = ['publish_sightings', $id, $passAlong, $jobId, $user['id']];
if (!empty($sightingUuids)) {

View File

@ -44,6 +44,7 @@ class Log extends AppModel
'export',
'fetchEvent',
'file_upload',
'forgot',
'galaxy',
'include_formula',
'load_module',
@ -51,6 +52,7 @@ class Log extends AppModel
'login_fail',
'logout',
'merge',
'password_reset',
'pruneUpdateLogs',
'publish',
'publish_sightings',

View File

@ -6108,7 +6108,7 @@ class Server extends AppModel
],
'thumbnail_in_redis' => [
'level' => self::SETTING_OPTIONAL,
'description' => __('Store image thumbnails in Redis insteadof file system.'),
'description' => __('Store image thumbnails in Redis instead of file system.'),
'value' => false,
'test' => 'testBool',
'type' => 'boolean',
@ -6406,6 +6406,14 @@ class Server extends AppModel
'type' => 'boolean',
'null' => true
),
'otp_issuer' => array(
'level' => 2,
'description' => __('If OTP is enabled, set the issuer string to an arbitrary value. Otherwise, MISP will default to "[MISP.org] MISP".'),
'value' => false,
'test' => 'testForEmpty',
'type' => 'string',
'null' => true
),
'email_otp_enabled' => array(
'level' => 2,
'description' => __('Enable two step authentication with a OTP sent by email. Requires e-mailing to be enabled. Warning: You cannot use it in combination with external authentication plugins.'),
@ -6457,6 +6465,14 @@ class Server extends AppModel
'type' => 'boolean',
'null' => true
),
'allow_password_forgotten' => array(
'level' => 1,
'description' => __('Enabling this setting will allow users to request automated password reset tokens via mail and initiate a reset themselves. Users with no encryption keys will not be able to use this feature.'),
'value' => false,
'test' => 'testBool',
'type' => 'boolean',
'null' => true
),
'self_registration_message' => array(
'level' => 1,
'bigField' => true,
@ -7243,6 +7259,13 @@ class Server extends AppModel
'test' => 'testBool',
'type' => 'boolean'
),
'Sightings_enable_realtime_publish' => array(
'level' => 1,
'description' => __('By default, sightings will not be immediately pushed to connected instances, as this can have a heavy impact on the performance of sighting attributes. Enable realtime publishing to trigger the publishing of sightings immediately as they are added.'),
'value' => false,
'test' => 'testBool',
'type' => 'boolean'
),
'CustomAuth_enable' => array(
'level' => 2,
'description' => __('Enable this functionality if you would like to handle the authentication via an external tool and authenticate with MISP using a custom header.'),

View File

@ -4,6 +4,8 @@ App::uses('EncryptedValue', 'Tools');
App::uses('Folder', 'Utility');
App::uses('File', 'Utility');
App::uses('RandomTool', 'Tools');
App::uses('JSONConverterTool', 'Tools');
App::uses('JsonTool', 'Tools');
class TaxiiServer extends AppModel
{
@ -102,7 +104,11 @@ class TaxiiServer extends AppModel
$this->Job->id = $jobId;
foreach ($result as $event) {
$temporaryFile = $this->temporaryFile($temporaryFolderPath);
$temporaryFile->write(json_encode($event));
$temporaryFile->write(
JsonTool::encode(
JSONConverterTool::convert($event, false, true)
)
);
$temporaryFile->close();
if ($jobId && $i % 10 == 0) {
$this->Job->saveField('progress', intval((100 * $i) / $eventCount));

View File

@ -1229,6 +1229,15 @@ class User extends AppModel
public function extralog($user, $action = null, $description = null, $fieldsResult = null, $modifiedUser = null)
{
if (!is_array($user) && $user === 'SYSTEM') {
$user = [
'id' => 0,
'email' => 'SYSTEM',
'Organisation' => [
'name' => 'SYSTEM'
]
];
}
// new data
$model = 'User';
$modelId = $user['id'];
@ -2007,4 +2016,89 @@ class User extends AppModel
}
return false;
}
public function forgotRouter($email, $ip)
{
if (Configure::read('MISP.background_jobs')) {
/** @var Job $job */
$job = ClassRegistry::init('Job');
$dummyUser = [
'email' => 'SYSTEM',
'org_id' => 0,
'role_id' => 0
];
$jobId = $job->createJob($dummyUser, Job::WORKER_EMAIL, 'forgot_password', $email, 'Sending...');
$args = [
'jobForgot',
$email,
$ip,
$jobId,
];
$this->getBackgroundJobsTool()->enqueue(
BackgroundJobsTool::EMAIL_QUEUE,
BackgroundJobsTool::CMD_ADMIN,
$args,
true,
$jobId
);
return true;
} else {
return $this->forgot($email);
}
}
public function forgot($email, $ip, $jobId = null)
{
$user = $this->find('first', [
'recursive' => -1,
'conditions' => [
'User.email' => $email,
'User.disabled' => 0
]
]);
if (empty($user)) {
return false;
}
$redis = $this->setupRedis();
$token = RandomTool::random_str(true, 40, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
$redis->set('misp:forgot:' . $token, $user['User']['id'], ['nx', 'ex' => 600]);
$baseurl = Configure::check('MISP.external_baseurl') ? Configure::read('MISP.external_baseurl') : Configure::read('MISP.baseurl');
$body = __(
"Dear MISP user,\n\nyou have requested a password reset on the MISP instance at %s. Click the link below to change your password.\n\n%s\n\nThe link above is only valid for 10 minutes, feel free to request a new one if it has expired.\n\nIf you haven't requested a password reset, reach out to your admin team and let them know that someone has attempted it in your stead.\n\nMake sure you keep the contents of this e-mail confidential, do NOT ever forward it as it contains a reset token that is equivalent of a password if acted upon. The IP used to trigger the request was: %s\n\nBest regards,\nYour MISP admin team",
$baseurl,
$baseurl . '/users/password_reset/' . $token,
$ip
);
$bodyNoEnc = __(
"Dear MISP user,\n\nyou have requested a password reset on the MISP instance at %s, however, no valid encryption key was found for your user and thus we cannot deliver your reset token. Please get in touch with your org admin / with an instance site admin to ask for a reset.\n\nThe IP used to trigger the request was: %s\n\nBest regards,\nYour MISP admin team",
$baseurl,
$ip
);
$this->sendEmail($user, $body, $bodyNoEnc, __('MISP password reset'));
return true;
}
public function fetchForgottenPasswordUser($token)
{
if (!ctype_alnum($token)) {
return false;
}
$redis = $this->setupRedis();
$userId = $redis->get('misp:forgot:' . $token);
if (empty($userId)) {
return false;
}
$user = $this->getAuthUser($userId, true);
return $user;
}
public function purgeForgetToken($token)
{
$redis = $this->setupRedis();
$userId = $redis->del('misp:forgot:' . $token);
return true;
}
}

View File

@ -250,7 +250,8 @@ echo $this->element('/genericElements/IndexTable/index_table', [
return $this->Acl->canModifyEvent($object) && empty($object['Event']['publish_timestamp']);
},
]
]
],
'persistUrlParams' => ['results']
]
]);

View File

@ -0,0 +1,6 @@
<div style="position:relative;margin-top:20px;">
<?php
echo $this->element('view_galaxy_matrix', $data);
?>
</div>

View File

@ -10,14 +10,18 @@
empty($widget['config']) ? '[]' : h(json_encode($widget['config'])),
h($widget['widget']),
sprintf(
'<div class="grid-stack-item-content"><div class="widgetTitle"><span class="widgetTitleText">%s</span> %s %s</div><div class="widgetContent">%s</div></div>',
'<div class="grid-stack-item-content"><div class="widgetTitle"><span class="widgetTitleText">%s</span> %s %s %s</div><div class="widgetContent">%s</div></div>',
empty($widget['config']['alias']) ? h($widget['title']) : h($widget['config']['alias']),
sprintf(
'<span class="fas fa-edit edit-widget" title="%s"></span>',
'<span class="fas fa-download export-widget useCursorPointer" title="%s"></span>',
__('Export raw data')
),
sprintf(
'<span class="fas fa-edit edit-widget useCursorPointer" title="%s"></span>',
__('Configure widget')
),
sprintf(
'<span class="fas fa-trash remove-widget" title="%s"></span>',
'<span class="fas fa-trash remove-widget useCursorPointer" title="%s"></span>',
__('Remove widget')
),
'&nbsp;'

View File

@ -40,6 +40,9 @@
} else {
$params['class'] = '';
}
if (!empty($fieldData['autofocus'])) {
$params['autofocus'] = 1;
}
if (empty($fieldData['type']) || $fieldData['type'] !== 'checkbox' ) {
$params['class'] .= ' form-control';
}

View File

@ -465,7 +465,7 @@
array(
'text' => __('Audit Logs'),
'url' => $baseurl . '/admin/audit_logs/index',
'requirement' => Configure::read('MISP.log_new_audit') && $isAdmin,
'requirement' => Configure::read('MISP.log_new_audit') && $this->Acl->canAccess('auditLogs', 'admin_index'),
),
array(
'text' => __('Access Logs'),
@ -475,7 +475,7 @@
array(
'text' => __('Search Logs'),
'url' => $baseurl . '/admin/logs/search',
'requirement' => $isAdmin
'requirement' => $this->Acl->canAccess('logs', 'admin_search')
)
)
),

View File

@ -229,8 +229,8 @@
}
?>
</td>
<td class="short">
<input type="text" class="ObjectComment" style="padding:0;height:20px;margin-bottom:0;" placeholder="<?php echo h($importComment); ?>"<?php if (!empty($object['comment'])) echo ' value="' . h($object['comment']) . '"';?>>
<td class="bitwider">
<textarea class="ObjectComment inline-input" cols="30" rows="6" placeholder="<?php echo h($importComment); ?>" oninput="autoresize(this)"><?php if (!empty($object['comment'])) echo h($object['comment']);?></textarea>
</td>
<td style="width:60px;text-align:center;">
<select class="ObjectDistribution" style="padding:0;height:20px;margin-bottom:0;">
@ -302,8 +302,8 @@
<td class="short" style="width:40px;text-align:center;">
<input type="checkbox" class="AttributeDisableCorrelation"<?php if (!empty($attribute['disable_correlation'])) echo ' checked'; ?>>
</td>
<td class="short">
<input type="text" class="AttributeComment" style="padding:0;height:20px;margin-bottom:0;"<?php if (!empty($attribute['comment'])) echo ' value="' . h($attribute['comment']) . '"';?>>
<td class="bitwider">
<textarea class="AttributeComment inline-input" rows="1" oninput="autoresize(this)"><?php if (!empty($attribute['comment'])) echo h($attribute['comment']);?></textarea>
</td>
<td class="short" style="width:40px;text-align:center;">
<select class="AttributeDistribution" style="padding:0;height:20px;margin-bottom:0;">
@ -412,8 +412,8 @@
<td class="short" style="width:40px;text-align:center;">
<input type="checkbox" class="AttributeDisableCorrelation"<?php if (isset($attribute['disable_correlation']) && $attribute['disable_correlation']) echo ' checked'; ?>>
</td>
<td class="short">
<input type="text" class="AttributeComment" style="padding:0;height:20px;margin-bottom:0;" placeholder="<?php echo h($importComment); ?>"<?php if (!empty($attribute['comment'])) echo ' value="' . h($attribute['comment']) . '"';?>>
<td class="bitwider">
<textarea class="AttributeComment inline-input" rows="1" oninput="autoresize(this)" placeholder="<?php echo h($importComment); ?>"><?php if (!empty($attribute['comment'])) echo h($attribute['comment']);?></textarea>
</td>
<td class="short" style="width:40px;text-align:center;">
<select class="AttributeDistribution" style="padding:0;height:20px;margin-bottom:0;">

View File

@ -13,6 +13,11 @@ class LightPaginatorHelper extends PaginatorHelper
return '';
}
public function last($last = 'last >>', $options = array())
{
return '';
}
public function hasNext($model = null)
{
$model = $this->defaultModel();

View File

@ -91,16 +91,19 @@ echo $this->element('/genericElements/IndexTable/index_table', [
],
[
'name' => __('Nationality'),
'sort' => 'Organisation.nationality',
'data_path' => 'Organisation',
'class' => 'short',
'element' => 'country',
],
[
'name' => __('Sector'),
'sort' => 'Organisation.sector',
'data_path' => 'Organisation.sector',
],
[
'name' => __('Type'),
'sort' => 'Organisation.type',
'data_path' => 'Organisation.type',
],
[

18
app/View/Users/forgot.ctp Normal file
View File

@ -0,0 +1,18 @@
<?php
echo $this->element('genericElements/Form/genericForm', [
'data' => [
'description' => __("If you already are enrolled on this instance, but forgot your password, you can request a new password below.") . '<br />' . __("An e-mail containing URL with an embedded token will be sent to you that you can use to reset the password within 10 minutes."),
'model' => 'User',
'title' => __('Forgotten password'),
'fields' => [
[
'field' => 'email',
'class' => 'span6'
],
],
'submit' => [
'action' => $this->request->params['action'],
'ajaxSubmit' => 'submitGenericFormInPlace();'
]
]
]);

View File

@ -59,6 +59,15 @@
__('No account yet? Register now!')
);
?>
<div class="clear">
<?php
echo empty(Configure::read('Security.allow_password_forgotten')) ? '' : sprintf(
'<a href="%s/users/forgot" title="%s">%s</a>',
$baseurl,
__('Initiate a password reset.'),
__('I have forgotten my password')
);
?>
</div>
<?= $this->Form->button(__('Login'), array('class' => 'btn btn-primary')); ?>
<?php

View File

@ -19,6 +19,7 @@ echo $this->element('/genericElements/Form/genericForm', array(
"label" => $label,
"type" => "text",
"placeholder" => __("Enter your OTP here"),
"autofocus" => 1
)
),
"submit" => array (

View File

@ -0,0 +1,25 @@
<div class="users form">
<?php echo $this->Form->create('User');?>
<fieldset>
<legend><?php echo __('Reset password'); ?></legend>
<?php
$passwordPopover = '<span class="blue bold">' . __('Minimal length') . '</span>: ' . h($length) . '<br>';
$passwordPopover .= '<span class="blue bold">' . __('Complexity') . '</span>: ' . h($complexity);
echo $this->Form->input('password', array(
'label' => __('New password') . ' <span id="PasswordPopover" data-content="' . h($passwordPopover) . '" class="fas fa-info-circle"></span>', 'autofocus'
));
echo $this->Form->input('confirm_password', [
'type' => 'password',
'label' => __('Confirm new password'),
'div' => array('class' => 'input password required'),
]);
?>
</fieldset>
<div style="border-bottom: 1px solid #e5e5e5;width:100%;">&nbsp;</div>
<div class="clear" style="margin-top:10px;">
</div>
<?php
echo $this->Form->button(__('Submit'), array('class' => 'btn btn-primary'));
echo $this->Form->end();
?>
</div>

View File

@ -193,7 +193,7 @@ $debugEnabled = !empty($selectedWorkflow['Workflow']['debug_enabled']);
<a href="#"><i class="fa-fw <?= $this->FontAwesome->getClass('file-import') ?>"></i> <?= __('Import blueprint') ?></a>
<ul class="dropdown-menu pull-right">
<?php if (empty($workflowBlueprints)) : ?>
<li><a href="#"><?= _('No workflow blueprints saved') ?></a></li>
<li><a href="#"><?= __('No workflow blueprints saved') ?></a></li>
<?php endif; ?>
<?php foreach ($workflowBlueprints as $workflowBlueprint) : ?>
<li>
@ -211,7 +211,7 @@ $debugEnabled = !empty($selectedWorkflow['Workflow']['debug_enabled']);
<a href="#"><i class="fa-fw <?= $this->FontAwesome->getClass('edit') ?>"></i> <?= __('Edit existing blueprint') ?></a>
<ul class="dropdown-menu pull-right disabled">
<?php if (empty($workflowBlueprints)) : ?>
<li><a href="#"><?= _('No workflow blueprints saved') ?></a></li>
<li><a href="#"><?= __('No workflow blueprints saved') ?></a></li>
<?php endif; ?>
<?php foreach ($workflowBlueprints as $workflowBlueprint) : ?>
<li class="control-edit-bp-blocks">

View File

@ -12,7 +12,6 @@
"kamisama/cake-resque": "4.1.2",
"pear/crypt_gpg": "1.6.7",
"monolog/monolog": "1.24.0",
"thecodingmachine/safe": "^1.0",
"spomky-labs/otphp": "^10.0",
"bacon/bacon-qr-code": "^2.0"
},
@ -39,14 +38,16 @@
"aws/aws-sdk-php": "To upload samples to S3",
"jakub-onderka/openid-connect-php": "For OIDC authentication",
"supervisorphp/supervisor": "For managing background jobs",
"lstrojny/fxmlrpc": "Required for supervisorphp/supervisor XML-RPC requests",
"guzzlehttp/guzzle": "Required for supervisorphp/supervisor XML-RPC requests",
"lstrojny/fxmlrpc": "Required for supervisorphp/supervisor XML-RPC requests",
"php-http/message": "Required for supervisorphp/supervisor XML-RPC requests",
"spomky-labs/otphp": "Required for strong authentication with TOTP",
"bacon/bacon-qr-code": "Required for strong authentication with TOTP"
"php-http/message-factory": "Required for supervisorphp/supervisor XML-RPC requests"
},
"config": {
"vendor-dir": "Vendor",
"optimize-autoloader": true
"optimize-autoloader": true,
"allow-plugins": {
"composer/installers": true
}
}
}

Binary file not shown.

View File

@ -168,7 +168,7 @@
"uuid": "53a830e6-8d07-4dd8-98d5-18cc4e66fc8c",
"org_uuid": "",
"org_name": "",
"description": "The ultimate goal of the project is to develop a NATO capability, available to all NATO nations, through which nations commit to sharing their information. This community is only open for official government entities, sponsored by their nation representative in the NATO Multinational MISP Steering Board.",
"description": "The ultimate goal of the project is to develop a NATO capability, available to all NATO nations, through which nations commit to sharing their information. This community is only open for official government cyber defense related entities, sponsored by their nation representative in the NATO Multinational MISP Steering Board.",
"url": "https://misp.ncirc.nato.int",
"sector": "Governmental",
"nationality": "International",

@ -1 +1 @@
Subproject commit 734d57edf5e76900cd0c8d5d48d6f5910e29b84e
Subproject commit 7028860c0aa8c471324008d3dc651b7ea9e07c0a

@ -1 +1 @@
Subproject commit 2ca2667d7668067f906e9601e0c97a79d4c7ccf1
Subproject commit da801ab146fb622a6447c8d2922a95b6049bb70a

@ -1 +1 @@
Subproject commit 911aafb91a38a68bbf6f5474c06e77a039469c93
Subproject commit 1a94fcd666bbf7eb505d4fbbc47ef6170c375706

View File

@ -2,7 +2,7 @@
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?/$1 [QSA,L]
RewriteRule ^(.*)$ index.php?/$1 "[QSA,L,B= ]"
# Adds AUTH support to Rest Plugin:
RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization},last]

View File

@ -5515,6 +5515,26 @@ function resetDashboardGrid(grid, save = true) {
grid.removeWidget(el);
saveDashboardState();
});
$('.export-widget').click(function() {
var $element = $(this).parent().parent().parent();
var container_id = $element.attr('id').substring(7);
$.ajax({
type: 'POST',
url: baseurl + '/dashboards/renderWidget/' + container_id + '/exportjson:1',
data: {
config: $element.attr('config'),
widget: $element.attr('widget')
},
success:function (data) {
data = JSON.stringify(data, null, 2);
var blob=new Blob([data], {type: 'application/json'});
var link=window.document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download=$element.attr('widget') + "_" + container_id + "_export.json";
link.click();
}
});
});
}
function setHomePage() {

View File

@ -9549,5 +9549,5 @@
"uuid": false
}
},
"db_version": "113"
"db_version": "114"
}

View File

@ -411,7 +411,7 @@ apacheConfig_RHEL7 () {
#sudo sed -i "s/SetHandler/\#SetHandler/g" /etc/httpd/conf.d/misp.ssl.conf
sudo rm /etc/httpd/conf.d/ssl.conf
sudo chmod 644 /etc/httpd/conf.d/misp.ssl.conf
sudo sed -i '/Listen 80/a Listen 443' /etc/httpd/conf/httpd.conf
sudo sed -i '/Listen 443/!s/Listen 80/a Listen 443/' /etc/httpd/conf/httpd.conf
# If a valid SSL certificate is not already created for the server, create a self-signed certificate:
echo "The Common Name used below will be: ${OPENSSL_CN}"

View File

@ -452,7 +452,7 @@ apacheConfig_RHEL8 () {
#sudo sed -i "s/SetHandler/\#SetHandler/g" /etc/httpd/conf.d/misp.ssl.conf
sudo rm /etc/httpd/conf.d/ssl.conf
sudo chmod 644 /etc/httpd/conf.d/misp.ssl.conf
sudo sed -i '/Listen 80/a Listen 443' /etc/httpd/conf/httpd.conf
sudo sed -i '/Listen 443/!s/Listen 80/a Listen 443/' /etc/httpd/conf/httpd.conf
# If a valid SSL certificate is not already created for the server, create a self-signed certificate:
echo "The Common Name used below will be: ${OPENSSL_CN}"