Merge branch 'pentest_fixes' into develop

pull/8063/merge
iglocska 2022-04-19 15:18:25 +02:00
commit 63840c8b68
No known key found for this signature in database
GPG Key ID: BEA224F1FEF113AC
50 changed files with 965 additions and 11658 deletions

2
PyMISP

@ -1 +1 @@
Subproject commit c5646d7463932f5c58b9569a7d771cdb8b2c048a
Subproject commit b1892efb6a078d1370cee51c9103f3a591c628d2

View File

@ -1 +1 @@
{"major":2, "minor":4, "hotfix":156}
{"major":2, "minor":4, "hotfix":157}

View File

@ -17,6 +17,7 @@ $config = array(
'user_monitoring_enabled' => false,
'authkey_keep_session' => false,
'disable_local_feed_access' => false,
'enable_svg_logos' => false,
//'auth' => array('CertAuth.Certificate'), // additional authentication methods
//'auth' => array('ShibbAuth.ApacheShibb'),
//'auth' => array('AadAuth.AadAuthenticate'),

View File

@ -34,8 +34,8 @@ class AppController extends Controller
public $helpers = array('OrgImg', 'FontAwesome', 'UserName');
private $__queryVersion = '136';
public $pyMispVersion = '2.4.155';
private $__queryVersion = '139';
public $pyMispVersion = '2.4.157';
public $phpmin = '7.2';
public $phprec = '7.4';
public $phptoonew = '8.0';
@ -1326,6 +1326,27 @@ class AppController extends Controller
return false;
}
/**
* Returns true if user can publish the given event.
*
* @param array $event
* @return bool
*/
protected function __canPublishEvent(array $event)
{
if (!isset($event['Event'])) {
throw new InvalidArgumentException('Passed object does not contain an Event.');
}
if ($this->userRole['perm_site_admin']) {
return true;
}
if ($this->userRole['perm_publish'] && $event['Event']['orgc_id'] == $this->Auth->user()['org_id']) {
return true;
}
return false;
}
/**
* Returns true if user can add or remove tags for given event.
*

View File

@ -21,10 +21,40 @@ class AuthKeysController extends AppController
public function index($id = false)
{
$conditions = $this->__prepareConditions();
if ($id !== false) {
$canCreateAuthkey = true;
if ($id) {
$this->set('user_id', $id);
if ($this->_isAdmin()) {
if ($this->_isSiteAdmin()) {
$canCreateAuthkey = true;
} else {
$user = $this->AuthKey->User->find('first', [
'recursive' => -1,
'conditions' => [
'User.id' => $id,
'User.disabled' => false
],
'fields' => ['User.id', 'User.org_id', 'User.disabled'],
'contain' => [
'Role' => [
'fields' => [
'Role.perm_site_admin', 'Role.perm_admin'
]
]
]
]);
if ($user['Role']['perm_site_admin'] || ($user['Role']['perm_admin'] && $user['User']['id'] !== $this->Auth->user('id'))) {
$canCreateAuthkey = false;
} else {
$canCreateAuthkey = true;
}
}
} else {
$canCreateAuthkey = (int)$id === (int)$this->Auth->user('id');
}
$conditions['AND'][] = ['AuthKey.user_id' => $id];
}
$this->set('canCreateAuthkey', $canCreateAuthkey);
$keyUsageEnabled = Configure::read('MISP.log_user_ips') && Configure::read('MISP.log_user_ips_authkeys');
$this->CRUD->index([
'filters' => ['User.email', 'authkey_start', 'authkey_end', 'comment', 'User.id'],
@ -123,8 +153,40 @@ class AuthKeysController extends AppController
}
$selectConditions = [];
if (!$this->_isSiteAdmin()) {
$selectConditions['AND'][] = ['User.id' => $this->Auth->user('id')];
$params['override']['user_id'] = $this->Auth->user('id');
if ($this->_isAdmin()) {
$role_ids = $this->AuthKey->User->Role->find('column', [
'fields' => ['Role.id'],
'conditions' => [
'AND' => [
'Role.perm_site_admin' => false,
'Role.perm_auth' => true,
'Role.perm_admin' => false
]
]
]);
$user_ids = $this->AuthKey->User->find('column', [
'fields' => ['User.id'],
'conditions' => [
'User.org_id' => $this->Auth->user('org_id'),
'OR' => [
'User.role_id' => $role_ids,
'User.id' => $this->Auth->user('id')
]
]
]);
if (!empty($user_id)) {
if (in_array($user_id, $user_ids)) {
$user_ids = [$user_id];
} else {
throw new MethodNotAllowedException(__('Invalid user or insufficient privileges to create an authkey for the given user.'));
}
}
$selectConditions['AND'][] = ['User.id' => $user_ids];
$params['override']['user_id'] = $user_ids[0];
} else {
$selectConditions['AND'][] = ['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;

View File

@ -1,18 +1,16 @@
<?php
App::uses('AppController', 'Controller');
/**
* @property CryptographicKey $CryptographicKey
*/
class CryptographicKeysController extends AppController
{
public $components = array('Session', 'RequestHandler');
public function beforeFilter()
{
parent::beforeFilter();
}
public $paginate = array(
'limit' => 60,
'maxLimit' => 9999
'limit' => 60,
'maxLimit' => 9999
);
public function add($type, $parent_id)
@ -85,7 +83,8 @@ class CryptographicKeysController extends AppController
{
$key = $this->CryptographicKey->find('first', [
'recursive' => -1,
'fields' => ['id', 'type', 'key_data', 'fingerprint']
'fields' => ['id', 'type', 'key_data', 'fingerprint'],
'conditions' => ['CryptographicKey.id' => $id]
]);
$this->set('id', $id);
$this->set('title', __('Viewing %s key #%s', h($key['CryptographicKey']['type']), h($key['CryptographicKey']['id'])));

View File

@ -837,7 +837,11 @@ class EventsController extends AppController
$events = $absolute_total === 0 ? [] : $this->Event->find('all', $rules);
}
$isCsvResponse = $this->response->type() === 'text/csv';
$instanceFingerprint = $this->Event->CryptographicKey->ingestInstanceKey();
try {
$instanceFingerprint = $this->Event->CryptographicKey->ingestInstanceKey();
} catch (Exception $e) {
$instanceFingerprint = null;
}
if (!$minimal) {
// Collect all tag IDs that are events
$tagIds = [];
@ -955,6 +959,9 @@ class EventsController extends AppController
}
foreach ($event['CryptographicKey'] as $cryptoKey) {
if ($instanceFingerprint === $cryptoKey['fingerprint']) {
$event['Event']['orgc_uuid'] = $event['Orgc']['uuid'];
unset($event['Event']['protected']);
$events[$key] = $event['Event'];
continue 2;
}
}
@ -1648,7 +1655,13 @@ class EventsController extends AppController
$this->set('warnings', $this->Event->generateWarnings($event));
$this->set('menuData', array('menuList' => 'event', 'menuItem' => 'viewEvent'));
$this->set('mayModify', $this->__canModifyEvent($event));
$this->set('instanceFingerprint', $this->Event->CryptographicKey->ingestInstanceKey());
$this->set('mayPublish', $this->__canPublishEvent($event));
try {
$instanceKey = $this->Event->CryptographicKey->ingestInstanceKey();
} catch (Exception $e) {
$instanceKey = null;
}
$this->set('instanceFingerprint', $instanceKey);
$this->__eventViewCommon($user);
}
@ -3337,6 +3350,8 @@ class EventsController extends AppController
foreach ($final as $key => $data) {
$this->set($key, $data);
}
$this->set('responseType', $responseType);
$this->set('returnFormat', $returnFormat);
$this->set('renderView', $renderView);
$this->render('/Events/eventRestSearchExportResult');
} else {

View File

@ -392,7 +392,7 @@ class OrganisationsController extends AppController
{
$this->layout = false;
$this->autoRender = false;
$this->set('id', $id);
$this->set('id', (int)$id);
$this->set('removable', $removable);
$this->set('extend', $extend);
$this->render('ajax/sg_org_row_empty');
@ -483,6 +483,12 @@ class OrganisationsController extends AppController
if ($logo['size'] > 0 && $logo['error'] == 0) {
$extension = pathinfo($logo['name'], PATHINFO_EXTENSION);
$filename = $orgId . '.' . ($extension === 'svg' ? 'svg' : 'png');
if ($extension === 'svg' && !Configure::read('Security.enable_svg_logos')) {
$this->Flash->error(__('Invalid file extension, SVG images are not allowed.'));
return false;
}
if (!empty($logo['tmp_name']) && is_uploaded_file($logo['tmp_name'])) {
return move_uploaded_file($logo['tmp_name'], APP . 'webroot/img/orgs/' . $filename);
}

View File

@ -30,7 +30,12 @@ class PagesController extends AppController
public function display()
{
$path = func_get_args();
foreach ($path as $k => $part) {
if (strpos($part, '..') !== false || strpos($part, '/') !== false) {
unset($path[$k]);
}
}
$path = array_values($path);
$count = count($path);
if (!$count) {
$this->redirect('/');

View File

@ -154,7 +154,7 @@ class UsersController extends AppController
}
}
}
if (!$abortPost && !$this->_isRest()) {
if (!$abortPost && (!$this->_isRest() || empty($this->request->header('Authorization')))) {
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']);
@ -853,7 +853,7 @@ class UsersController extends AppController
$this->request->data['User'] = $this->request->data;
}
$abortPost = false;
if (!$this->_isRest()) {
if (!$this->_isRest() || empty($this->request->header('Authorization'))) {
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']);

View File

@ -94,7 +94,7 @@ class BackgroundJobsTool
/**
* Initialize
*
*
* Settings should have the following format:
* [
* 'enabled' => true,
@ -111,6 +111,7 @@ class BackgroundJobsTool
* ]
*
* @param array $settings
* @throws Exception
*/
public function __construct(array $settings)
{
@ -233,8 +234,6 @@ class BackgroundJobsTool
* Get the job status.
*
* @param string $jobId Background Job Id.
*
*
*/
public function getJob(string $jobId)
{
@ -366,9 +365,10 @@ class BackgroundJobsTool
/**
* Start worker by queue
*
* @param string $name
* @param string $queue Queue name
* @param boolean $waitForRestart
* @return boolean
* @throws Exception
*/
public function startWorkerByQueue(string $queue, bool $waitForRestart = false): bool
{
@ -401,6 +401,7 @@ class BackgroundJobsTool
* @param string|int $id
* @param boolean $waitForRestart
* @return boolean
* @throws Exception
*/
public function stopWorker($id, bool $waitForRestart = false): bool
{
@ -428,6 +429,7 @@ class BackgroundJobsTool
*
* @param boolean $waitForRestart
* @return void
* @throws Exception
*/
public function restartWorkers(bool $waitForRestart = false)
{
@ -440,6 +442,7 @@ class BackgroundJobsTool
*
* @param boolean $waitForRestart
* @return void
* @throws Exception
*/
public function restartDeadWorkers(bool $waitForRestart = false)
{
@ -499,6 +502,7 @@ class BackgroundJobsTool
* Return true if Supervisor process is running.
*
* @return boolean
* @throws Exception
*/
public function getSupervisorStatus(): bool
{
@ -508,8 +512,8 @@ class BackgroundJobsTool
/**
* Validate queue
*
* @param string $queue
* @return boolean
* @throws InvalidArgumentException
*/
private function validateQueue(string $queue): bool
{
@ -529,8 +533,8 @@ class BackgroundJobsTool
/**
* Validate command
*
* @param string $command
* @return boolean
* @throws InvalidArgumentException
*/
private function validateCommand(string $command): bool
{
@ -569,9 +573,14 @@ class BackgroundJobsTool
/**
* @return Redis
* @throws Exception
*/
private function createRedisConnection(): Redis
{
if (!class_exists('Redis')) {
throw new Exception("Class Redis doesn't exists. Please install redis extension for PHP.");
}
$redis = new Redis();
$redis->connect($this->settings['redis_host'], $this->settings['redis_port']);
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_JSON);
@ -591,6 +600,7 @@ class BackgroundJobsTool
/**
* @return \Supervisor\Supervisor
* @throws Exception
*/
private function getSupervisor()
{

View File

@ -251,9 +251,14 @@ class PubSubTool
/**
* @param array $settings
* @return Redis
* @throws Exception
*/
private function createRedisConnection(array $settings)
{
if (!class_exists('Redis')) {
throw new Exception("Class Redis doesn't exists. Please install redis extension for PHP.");
}
$redis = new Redis();
$redis->connect($settings['redis_host'], $settings['redis_port']);
$redisPassword = $settings['redis_password'];

View File

@ -8,7 +8,8 @@ class ServerSyncTool
FEATURE_ORG_RULE = 'org_rule',
FEATURE_FILTER_SIGHTINGS = 'filter_sightings',
FEATURE_PROPOSALS = 'proposals',
FEATURE_POST_TEST = 'post_test';
FEATURE_POST_TEST = 'post_test',
FEATURE_PROTECTED_EVENT = 'protected_event';
/** @var array */
private $server;
@ -325,6 +326,9 @@ class ServerSyncTool
case self::FEATURE_POST_TEST:
$version = explode('.', $info['version']);
return $version[0] == 2 && $version[1] == 4 && $version[2] > 68;
case self::FEATURE_PROTECTED_EVENT:
$version = explode('.', $info['version']);
return $version[0] == 2 && $version[1] == 4 && $version[2] > 155;
default:
throw new InvalidArgumentException("Invalid flag `$flag` provided");
}
@ -379,7 +383,12 @@ class ServerSyncTool
}
$request = $this->request;
if (strlen($data) > 1024 && !$protectedMode) { // do not compress small body
if ($protectedMode) {
$request['header']['x-pgp-signature'] = $this->signEvent($data);
}
if (strlen($data) > 1024) { // do not compress small body
if ($this->isSupported(self::FEATURE_BR) && function_exists('brotli_compress')) {
$request['header']['Content-Encoding'] = 'br';
$data = brotli_compress($data, 1, BROTLI_TEXT);
@ -390,9 +399,6 @@ class ServerSyncTool
}
$url = $this->server['Server']['url'] . $url;
$start = microtime(true);
if ($protectedMode) {
$request = $this->signEvent($data, $this->server, $request, $this->socket);
}
$response = $this->socket->post($url, $data, $request);
$this->log($start, 'POST', $url, $response);
if (!$response->isOk()) {
@ -402,39 +408,22 @@ class ServerSyncTool
}
/**
* @param string $data
* @param array $server
* @param array $request
* @param HttpSocket $HttpSocket
* @return array
* @param string $data Data to sign
* @return string base64 encoded signature
* @throws Exception
* @throws HttpException
* @throws MethodNotAllowedException
*/
private function signEvent($data, $server, $request, $socket)
private function signEvent($data)
{
if (!$this->isSupported(self::FEATURE_PROTECTED_EVENT)) {
throw new Exception(__('Remote instance is not protected event aware yet (< 2.4.156), aborting.'));
}
$this->CryptographicKey = ClassRegistry::init('CryptographicKey');
$signature = $this->CryptographicKey->signWithInstanceKey($data);
$request['header']['x-pgp-signature'] = base64_encode($signature);
$this->Log = ClassRegistry::init('Log');
if (empty($signature)) {
$message = __("Invalid signing key. This should never happen.");
$this->Log->createLogEntry('SYSTEM', 'push', 'Server', $server['Server']['id'], $message);
throw new Exception($message);
throw new Exception(__("Invalid signing key. This should never happen."));
}
$response = $socket->get($server['Server']['url'] . '/servers/getVersion.json', null, $request);
if (!$response->isOk()) {
$message = __("Could not fetch remote version to negotiate protected event synchronisation.");
$this->Log->createLogEntry('SYSTEM', 'push', 'Server', $server['Server']['id'], $message);
throw new HttpException($response->body, $response->code);
}
$version = json_decode($response->body(), true)['version'];
if (version_compare($version, '2.4.156') < 0) {
$message = __('Remote instance is not protected event aware yet (< 2.4.156), aborting.');
$this->Log->createLogEntry('SYSTEM', 'push', 'Server', $server['Server']['id'], $message);
throw new MethodNotAllowedException($message);
}
return $request;
return base64_encode($signature);
}
/**

View File

@ -51,6 +51,9 @@ class AppModel extends Model
{
parent::__construct($id, $table, $ds);
$this->findMethods['column'] = true;
if (in_array('phar', stream_get_wrappers())) {
stream_wrapper_unregister('phar');
}
}
// deprecated, use $db_changes
@ -2539,7 +2542,7 @@ class AppModel extends Model
}
if (!class_exists('Redis')) {
throw new Exception("Class Redis doesn't exists.");
throw new Exception("Class Redis doesn't exists. Please install redis extension for PHP.");
}
$host = Configure::read('MISP.redis_host') ?: '127.0.0.1';
@ -2585,6 +2588,7 @@ class AppModel extends Model
App::uses('KafkaPubTool', 'Tools');
$kafkaPubTool = new KafkaPubTool();
$rdkafkaIni = Configure::read('Plugin.Kafka_rdkafka_config');
$rdkafkaIni = mb_ereg_replace("/\:\/\//", '', $rdkafkaIni);
$kafkaConf = array();
if (!empty($rdkafkaIni)) {
$kafkaConf = parse_ini_file($rdkafkaIni);

View File

@ -580,7 +580,13 @@ class Attribute extends AppModel
$this->validationErrors['type'] = ['No type set.'];
return false;
}
$type = $attribute['type'];
if (!isset($this->typeDefinitions[$type])) {
$this->validationErrors['type'] = ['Invalid type.'];
return false;
}
if (is_array($attribute['value'])) {
$this->validationErrors['value'] = ['Value is an array.'];
return false;

View File

@ -24,7 +24,8 @@ class CryptographicKey extends AppModel
const ERROR_MALFORMED_SIGNATURE = 'Malformed signature',
ERROR_INVALID_SIGNATURE = 'Invalid signature',
ERROR_WRONG_KEY = 'Wrong key';
ERROR_WRONG_KEY = 'Wrong key',
ERROR_INVALID_KEY = 'Invalid key';
public $validTypes = [
'pgp'
@ -98,6 +99,9 @@ class CryptographicKey extends AppModel
if (empty($fingerprint)) {
$file = new File(APP . '/webroot/gpg.asc');
$instanceKey = $file->read();
if (!$this->gpg) {
throw new MethodNotAllowedException("Could not initiate GPG");
}
try {
$this->gpg->importKey($instanceKey);
} catch (Crypt_GPG_NoDataException $e) {
@ -108,7 +112,14 @@ class CryptographicKey extends AppModel
$redis->setEx($redisKey, 300, $fingerprint);
}
}
$this->gpg->addSignKey(Configure::read('GnuPG.email'), Configure::read('GnuPG.password'));
if (!$this->gpg) {
throw new MethodNotAllowedException("Could not initiate GPG");
}
try {
$this->gpg->addSignKey(Configure::read('GnuPG.email'), Configure::read('GnuPG.password'));
} catch (Exception $e) {
throw new NotFoundException('Could not add signing key.');
}
return $fingerprint;
}
@ -135,25 +146,29 @@ class CryptographicKey extends AppModel
{
$this->error = false;
$fingerprint = $this->__extractPGPKeyData($key);
if ($fingerprint === false) {
$this->error = self::ERROR_INVALID_KEY;
return false;
}
$data = preg_replace("/\s+/", "", $data);
try {
$verifiedSignature = $this->gpg->verify($data, $signature);
} catch (Exception $e) {
$this->error = $this::ERROR_WRONG_KEY;
$this->error = self::ERROR_WRONG_KEY;
return false;
}
if (empty($verifiedSignature)) {
$this->error = $this::ERROR_MALFORMED_SIGNATURE;
$this->error = self::ERROR_MALFORMED_SIGNATURE;
return false;
}
if (!$verifiedSignature[0]->isValid()) {
$this->error = $this::ERROR_INVALID_SIGNATURE;
$this->error = self::ERROR_INVALID_SIGNATURE;
return false;
}
if ($verifiedSignature[0]->getKeyFingerprint() === $fingerprint) {
return true;
} else {
$this->error = $this::ERROR_WRONG_KEY;
$this->error = self::ERROR_WRONG_KEY;
return false;
}
}
@ -168,19 +183,22 @@ class CryptographicKey extends AppModel
}
/**
* @param string $data
* @return string|false Primary key fingerprint or false of key is invalid
*/
private function __extractPGPKeyData($data)
{
try {
$gpgTool = new GpgTool($this->gpg);
} catch (Exception $e) {
$this->logException("GPG couldn't be initialized, GPG encryption and signing will be not available.", $e, LOG_NOTICE);
return '';
return false;
}
try {
return $gpgTool->validateGpgKey($data);
} catch (Exception $e) {
$this->logException("Could not validate PGP key.", $e, LOG_NOTICE);
return '';
return false;
}
}
@ -241,7 +259,6 @@ class CryptographicKey extends AppModel
{
$existingKeys = $this->find('first', [
'recursive' => -1,
'fields' => 1,
'conditions' => [
'parent_type' => $type,
'parent_id' => $parent_id

View File

@ -17,6 +17,7 @@ App::uses('ProcessTool', 'Tools');
* @property ThreatLevel $ThreatLevel
* @property Sighting $Sighting
* @property Organisation $Org
* @property CryptographicKey $CryptographicKey
*/
class Event extends AppModel
{
@ -4430,6 +4431,7 @@ class Event extends AppModel
$servers = $this->Server->find('all', [
'conditions' => $conditions,
'recursive' => -1,
'contain' => ['RemoteOrg', 'Organisation'],
'order' => ['Server.priority ASC', 'Server.id ASC'],
]);
// iterate over the servers and upload the event
@ -6173,18 +6175,10 @@ class Event extends AppModel
$newTextBody = JsonTool::encode($newTextBody);
}
$this->Log = ClassRegistry::init('Log');
$this->Log->create();
$this->Log->save(array(
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => $server['Server']['id'],
'email' => 'SYSTEM',
'action' => 'warning',
'user_id' => 0,
'title' => 'Uploading Event (' . $event['Event']['id'] . ') to Server (' . $server['Server']['id'] . ')',
'change' => 'Returned message: ' . $newTextBody,
));
$title = 'Uploading Event (' . $event['Event']['id'] . ') to Server (' . $server['Server']['id'] . ')';
$change = 'Returned message: ' . $newTextBody;
$this->loadLog()->createLogEntry('SYSTEM', 'warning', 'Server', $server['Server']['id'], $title, $change);
}
/**

View File

@ -126,12 +126,13 @@ class Feed extends AppModel
public function urlOrExistingFilepath($fields)
{
if ($this->isFeedLocal($this->data)) {
$path = mb_ereg_replace("/\:\/\//", '', $this->data['Feed']['url']);
if ($this->data['Feed']['source_format'] == 'misp') {
if (!is_dir($this->data['Feed']['url'])) {
if (!is_dir($path)) {
return 'For MISP type local feeds, please specify the containing directory.';
}
} else {
if (!file_exists($this->data['Feed']['url'])) {
if (!file_exists($path)) {
return 'Invalid path or file not found. Make sure that the path points to an existing file that is readable and watch out for typos.';
}
}
@ -1929,6 +1930,7 @@ class Feed extends AppModel
private function feedGetUri($feed, $uri, HttpSocket $HttpSocket = null)
{
if ($this->isFeedLocal($feed)) {
$uri = mb_ereg_replace("/\:\/\//", '', $uri);
if (file_exists($uri)) {
return FileAccessTool::readFromFile($uri);
} else {

View File

@ -4683,6 +4683,14 @@ class Server extends AppModel
'type' => 'numeric',
'null' => true
),
'disable_event_locks' => [
'level' => 1,
'description' => __('Disable the event locks that are executed periodically when a user browses an event view. It can be useful to leave event locks enabled to warn users that someone else is editing the same event, but generally it\'s extremely verbose and can cause issues in certain setups, so it\'s recommended to disable this.'),
'value' => false,
'test' => 'testBoolTrue',
'type' => 'boolean',
'null' => true
],
'enable_advanced_correlations' => array(
'level' => 0,
'description' => __('Enable some performance heavy correlations (currently CIDR correlation)'),
@ -5867,7 +5875,8 @@ class Server extends AppModel
'value' => false,
'test' => 'testBool',
'type' => 'boolean',
'null' => true
'null' => true,
'cli_only' => 1
),
'rest_client_baseurl' => array(
'level' => 1,
@ -6136,6 +6145,14 @@ class Server extends AppModel
'tlsv1_3' => 'TLSv1.3',
],
],
'enable_svg_logos' => [
'level' => self::SETTING_OPTIONAL,
'description' => __('When enabled, organisations logos in svg format are allowed.'),
'value' => false,
'test' => 'testBool',
'type' => 'boolean',
'null' => true
]
),
'SecureAuth' => array(
'branch' => 1,
@ -7112,6 +7129,12 @@ class Server extends AppModel
),
'LinOTPAuth' => array(
'branch' => 1,
'enabled' => array(
'level' => 2,
'description' => __('Enable / Disable LinOTP'),
'value' => true,
'type' => 'boolean',
),
'baseUrl' => array(
'level' => 2,
'description' => __('The default LinOTP URL.'),

View File

@ -21,274 +21,275 @@ App::uses('BaseAuthenticate', 'Controller/Component/Auth');
class CertificateAuthenticate extends BaseAuthenticate
{
/**
* Holds the certificate issuer information (available at SSL_CLIENT_I_DN)
*
* @var array
*/
protected static $ca;
/**
* Holds the certificate issuer information (available at SSL_CLIENT_I_DN)
*
* @var array
*/
protected static $ca;
/**
* Holds the certificate user information (available at SSL_CLIENT_S_DN)
*
* @var array
*/
protected static $client;
/**
* Holds the certificate user information (available at SSL_CLIENT_S_DN)
*
* @var array
*/
protected static $client;
/**
* Holds the user information
*
* @var array
*/
protected static $user;
/**
* Holds the user information
*
* @var array
*/
protected static $user;
/**
* Class constructor.
*
* This should only be called once per request, so it doesn't need to store values in
* the instance. Simply checks if the certificate is valid (against configured valid issuers)
* and returns the user information encoded.
*/
public function __construct()
{
self::$ca = self::$client = false;
/**
* Class constructor.
*
* This should only be called once per request, so it doesn't need to store values in
* the instance. Simply checks if the certificate is valid (against configured valid issuers)
* and returns the user information encoded.
*/
public function __construct()
{
self::$ca = self::$client = false;
if (isset($_SERVER['SSL_CLIENT_I_DN'])) {
$CA = self::parse($_SERVER['SSL_CLIENT_I_DN'], Configure::read('CertAuth.mapCa'));
// only valid CAs, if this was configured
if ($ca=Configure::read('CertAuth.ca')) {
$k = Configure::read('CertAuth.caId');
if (!$k) $k = 'CN';
$id = (isset($CA[$k]))?($CA[$k]):(false);
if (isset($_SERVER['SSL_CLIENT_I_DN'])) {
$CA = self::parse($_SERVER['SSL_CLIENT_I_DN'], Configure::read('CertAuth.mapCa'));
// only valid CAs, if this was configured
if ($ca=Configure::read('CertAuth.ca')) {
$k = Configure::read('CertAuth.caId');
if (!$k) $k = 'CN';
$id = (isset($CA[$k]))?($CA[$k]):(false);
if (!$id) {
$CA = false;
} else if (is_array($ca)) {
if (!in_array($id, $ca)) $CA = false;
} else if ($ca!=$id) {
$CA = false;
}
unset($id, $k);
}
self::$ca = $CA;
unset($CA, $ca);
}
if (!$id) {
$CA = false;
} else if (is_array($ca)) {
if (!in_array($id, $ca)) $CA = false;
} else if ($ca!=$id) {
$CA = false;
}
unset($id, $k);
}
self::$ca = $CA;
unset($CA, $ca);
}
if (self::$ca) {
$map = Configure::read('CertAuth.map');
if(isset($_SERVER['SSL_CLIENT_S_DN'])) {
self::$client = self::parse($_SERVER['SSL_CLIENT_S_DN'], $map);
} else {
self::$client = array();
}
foreach($map as $n=>$d) {
if(isset($_SERVER[$n])) {
self::$client[$d] = $_SERVER[$n];
}
unset($map[$n], $n, $d);
}
unset($map);
if(!self::$client) {
self::$client = false;
}
}
}
if (self::$ca) {
$map = Configure::read('CertAuth.map');
if(isset($_SERVER['SSL_CLIENT_S_DN'])) {
self::$client = self::parse($_SERVER['SSL_CLIENT_S_DN'], $map);
} else {
self::$client = array();
}
foreach($map as $n=>$d) {
if(isset($_SERVER[$n])) {
self::$client[$d] = $_SERVER[$n];
}
unset($map[$n], $n, $d);
}
unset($map);
if(!self::$client) {
self::$client = false;
}
}
}
/**
* Parse certificate extensions
*
* @TODO this should properly address the RFC
* @param string $s text to be parsed
* @param (optional) array $map array of mapping extension to User fields
* @return array parsed values
*/
private static function parse($s, $map=null)
{
$r=array();
if (preg_match_all('#(^/?|\/|\,)([a-zA-Z]+)\=([^\/\,]+)#', $s, $m)) {
foreach ($m[2] as $i=>$k) {
if ($map) {
if (isset($map[$k])) {
$k = $map[$k];
} else {
$k = null;
}
}
if ($k) {
$v = $m[3][$i];
$r[$k] = $v;
}
unset($m[0][$i], $m[1][$i], $m[2][$i], $m[3][$i], $k, $v, $i);
}
}
return $r;
}
/**
* Parse certificate extensions
*
* @TODO this should properly address the RFC
* @param string $s text to be parsed
* @param (optional) array $map array of mapping extension to User fields
* @return array parsed values
*/
private static function parse($s, $map=null)
{
$r=array();
if (preg_match_all('#(^/?|\/|\,)([a-zA-Z]+)\=([^\/\,]+)#', $s, $m)) {
foreach ($m[2] as $i=>$k) {
if ($map) {
if (isset($map[$k])) {
$k = $map[$k];
} else {
$k = null;
}
}
if ($k) {
$v = $m[3][$i];
$r[$k] = $v;
}
unset($m[0][$i], $m[1][$i], $m[2][$i], $m[3][$i], $k, $v, $i);
}
}
return $r;
}
// to enable stateless authentication
public function getUser(CakeRequest $request)
{
if (empty(self::$user)) {
if (self::$client) {
self::$user = self::$client;
// If $sync is true, allow the creation of the user from the certificate
$sync = Configure::read('CertAuth.syncUser');
$url = Configure::read('CertAuth.restApi.url');
if ($sync && $url) {
if (!self::getRestUser()) return false;
}
// to enable stateless authentication
public function getUser(CakeRequest $request)
{
if (empty(self::$user)) {
if (self::$client) {
self::$user = self::$client;
// If $sync is true, allow the creation of the user from the certificate
$sync = Configure::read('CertAuth.syncUser');
$url = Configure::read('CertAuth.restApi.url');
if ($sync && $url) {
if (!self::getRestUser()) return false;
}
// find and fill user with model
$userModelKey = empty(Configure::read('CertAuth.userModelKey')) ? 'email' : Configure::read('CertAuth.userModelKey');
$userDefaults = Configure::read('CertAuth.userDefaults');
$this->User = ClassRegistry::init('User');
if (!empty(self::$user[$userModelKey])) {
$existingUser = $this->User->find('first', array(
'conditions' => array($userModelKey => self::$user[$userModelKey]),
'recursive' => false
));
}
if ($existingUser) {
if ($sync) {
if (!isset(self::$user['org_id']) && isset(self::$user['org'])) {
self::$user['org_id'] = $this->User->Organisation->createOrgFromName(self::$user['org'], $existingUser['User']['id'], true);
// reset user defaults in case it's a different org_id
if (self::$user['org_id'] && $existingUser['User']['org_id'] != self::$user['org_id']) {
if ($userDefaults && is_array($userDefaults)) {
self::$user = array_merge($userDefaults + self::$user);
}
}
unset(self::$user['org']);
}
$write = array();
foreach (self::$user as $k => $v) {
if (isset($existingUser['User'][$k]) && trim($existingUser['User'][$k]) != trim($v)) {
$write[] = $k;
$existingUser['User'][$k] = trim($v);
}
}
if (!empty($write) && !$this->User->save($existingUser['User'], true, $write)) {
CakeLog::write('alert', 'Could not update model at database with RestAPI data.');
}
}
self::$user = $this->User->getAuthUser($existingUser['User']['id']);
if (isset(self::$user['gpgkey'])) unset(self::$user['gpgkey']);
} else if ($sync && !empty(self::$user)) {
$org = isset(self::$client['org']) ? self::$client['org'] : null;
if ($org == null) return false;
if (!isset(self::$user['org_id']) && isset(self::$user['org'])) {
self::$user['org_id'] = $this->User->Organisation->createOrgFromName($org, 0, true);
unset(self::$user['org']);
}
if ($userDefaults && is_array($userDefaults)) {
self::$user = array_merge(self::$user, $userDefaults);
}
$this->User->create();
if ($this->User->save(self::$user)) {
$id = $this->User->id;
self::$user = $this->User->getAuthUser($id);
if (isset(self::$user['gpgkey'])) unset(self::$user['gpgkey']);
} else {
CakeLog::write('alert', 'Could not insert model at database from RestAPI data. Reason: ' . json_encode($this->User->validationErrors));
}
} else {
// No match -- User doesn't exist !!!
self::$user = false;
}
}
}
return self::$user;
}
// find and fill user with model
$userModelKey = empty(Configure::read('CertAuth.userModelKey')) ? 'email' : Configure::read('CertAuth.userModelKey');
$userDefaults = Configure::read('CertAuth.userDefaults');
$this->User = ClassRegistry::init('User');
if (!empty(self::$user[$userModelKey])) {
$existingUser = $this->User->find('first', array(
'conditions' => array($userModelKey => self::$user[$userModelKey]),
'recursive' => false
));
}
if ($existingUser) {
if ($sync) {
if (!isset(self::$user['org_id']) && isset(self::$user['org'])) {
self::$user['org_id'] = $this->User->Organisation->createOrgFromName(self::$user['org'], $existingUser['User']['id'], true);
// reset user defaults in case it's a different org_id
if (self::$user['org_id'] && $existingUser['User']['org_id'] != self::$user['org_id']) {
if ($userDefaults && is_array($userDefaults)) {
self::$user = array_merge($userDefaults + self::$user);
}
}
unset(self::$user['org']);
}
$write = array();
foreach (self::$user as $k => $v) {
if (isset($existingUser['User'][$k]) && trim($existingUser['User'][$k]) != trim($v)) {
$write[] = $k;
$existingUser['User'][$k] = trim($v);
}
}
if (!empty($write) && !$this->User->save($existingUser['User'], true, $write)) {
CakeLog::write('alert', 'Could not update model at database with RestAPI data.');
}
}
self::$user = $this->User->getAuthUser($existingUser['User']['id']);
if (isset(self::$user['gpgkey'])) unset(self::$user['gpgkey']);
} else if ($sync && !empty(self::$user)) {
$org = isset(self::$client['org']) ? self::$client['org'] : null;
if ($org == null) return false;
if (!isset(self::$user['org_id']) && isset(self::$user['org'])) {
self::$user['org_id'] = $this->User->Organisation->createOrgFromName($org, 0, true);
unset(self::$user['org']);
}
if ($userDefaults && is_array($userDefaults)) {
self::$user = array_merge(self::$user, $userDefaults);
}
$this->User->create();
if ($this->User->save(self::$user)) {
$id = $this->User->id;
self::$user = $this->User->getAuthUser($id);
if (isset(self::$user['gpgkey'])) unset(self::$user['gpgkey']);
} else {
CakeLog::write('alert', 'Could not insert model at database from RestAPI data. Reason: ' . json_encode($this->User->validationErrors));
}
} else {
// No match -- User doesn't exist !!!
self::$user = false;
}
}
}
return self::$user;
}
// to enable stateless authentication
public function authenticate(CakeRequest $request, CakeResponse $response)
{
return self::getUser($request);
}
// to enable stateless authentication
public function authenticate(CakeRequest $request, CakeResponse $response)
{
return self::getUser($request);
}
/**
* Fetches user information from external REST API
*
* Valid options (should be configured under CertAuth.restApi):
*
* @param (optional) array $options API configuration
* url (string) Where to fetch information from
* headers (array) list of additional headers to be used, reserved for authentication tokens
* params (array) mapping of additional params to be included at the url, uses $user values
* map (array) mapping of the return values to be added to the self::$user
* @return array updated user object
*/
public function getRestUser($options=null, $user=null)
{
if (is_null($options)) {
$options = Configure::read('CertAuth.restApi');
}
if (!is_null($user)) {
self::$user = $user;
}
/**
* Fetches user information from external REST API
*
* Valid options (should be configured under CertAuth.restApi):
*
* @param (optional) array $options API configuration
* url (string) Where to fetch information from
* headers (array) list of additional headers to be used, reserved for authentication tokens
* params (array) mapping of additional params to be included at the url, uses $user values
* map (array) mapping of the return values to be added to the self::$user
* @return array updated user object
*/
public function getRestUser($options=null, $user=null)
{
if (is_null($options)) {
$options = Configure::read('CertAuth.restApi');
}
if (!is_null($user)) {
self::$user = $user;
}
if (!isset($options['url'])) {
return null;
}
if (!isset($options['url'])) {
return null;
}
// Create a stream
$req = array(
'http'=>array(
'method'=>'GET',
'header'=>"Accept: application/json\r\n"
),
);
if (isset($options['headers'])) {
foreach ($options['headers'] as $k=>$v) {
if (is_int($k)) {
$req['header'] .= "{$v}\r\n";
} else {
$req['header'] .= "{$k}: {$v}\r\n";
}
unset($k, $v);
}
}
// Create a stream
$req = array(
'http'=>array(
'method'=>'GET',
'header'=>"Accept: application/json\r\n"
),
);
if (isset($options['headers'])) {
foreach ($options['headers'] as $k=>$v) {
if (is_int($k)) {
$req['header'] .= "{$v}\r\n";
} else {
$req['header'] .= "{$k}: {$v}\r\n";
}
unset($k, $v);
}
}
$url = $options['url'];
if (isset($options['param'])) {
foreach ($options['param'] as $k=>$v) {
if (isset(self::$user[$v])) {
$url .= ((strpos($url, '?'))?('&'):('?'))
. $k . '=' . urlencode(self::$user[$v]);
}
unset($k, $v);
}
}
$ctx = stream_context_create($req);
$a = file_get_contents($url, false, $ctx);
if (!$a) return null;
$url = $options['url'];
if (isset($options['param'])) {
foreach ($options['param'] as $k=>$v) {
if (isset(self::$user[$v])) {
$url .= ((strpos($url, '?'))?('&'):('?'))
. $k . '=' . urlencode(self::$user[$v]);
}
unset($k, $v);
}
}
$ctx = stream_context_create($req);
$url = mb_ereg_replace("/phar\:\/\//i", '', $url);
$a = file_get_contents($url, false, $ctx);
if (!$a) return null;
$A = json_decode($a, true);
if (!isset($A['data'][0])) {
self::$user = false;
} else if (isset($options['map'])) {
foreach ($options['map'] as $k=>$v) {
if (isset($A['data'][0][$k])) {
self::$user[$v] = $A['data'][0][$k];
}
unset($k, $v);
}
}
$A = json_decode($a, true);
if (!isset($A['data'][0])) {
self::$user = false;
} else if (isset($options['map'])) {
foreach ($options['map'] as $k=>$v) {
if (isset($A['data'][0][$k])) {
self::$user[$v] = $A['data'][0][$k];
}
unset($k, $v);
}
}
return self::$user;
}
return self::$user;
}
protected static $instance;
protected static $instance;
public static function ca()
{
if (is_null(self::$ca)) new CertificateAuthenticate();
return self::$ca;
}
public static function ca()
{
if (is_null(self::$ca)) new CertificateAuthenticate();
return self::$ca;
}
public static function client()
{
if (is_null(self::$client)) new CertificateAuthenticate();
return self::$client;
}
public static function client()
{
if (is_null(self::$client)) new CertificateAuthenticate();
return self::$client;
}
}

View File

@ -123,6 +123,13 @@ class LinOTPAuthenticate extends BaseAuthenticate
CakeLog::debug("getUser email: ${email}");
$linOTP_enabled = Configure::read("LinOTPAuth.enabled");
if (is_null($linOTP_enabled)) {
$linOTP_enabled = TRUE;
}
if (!$linOTP_enabled) {
return false;
}
$linOTP_baseUrl = rtrim(Configure::read("LinOTPAuth.baseUrl"), "/");
$linOTP_realm = Configure::read("LinOTPAuth.realm");
$linOTP_verifyssl = Configure::read("LinOTPAuth.verifyssl");
@ -131,7 +138,7 @@ class LinOTPAuthenticate extends BaseAuthenticate
if (!$linOTP_baseUrl || $linOTP_baseUrl === "") {
CakeLog::error("LinOTP: Please configure baseUrl.");
if ($mixedauth) {
throw new CakeException(__d('cake_dev', 'LinOTP: Missing "baseUrl" configuration - access denied!', 'authenticate()'));
throw new ForbiddenException(__('LinOTP: Missing "baseUrl" configuration - access denied!'));
} else {
return false;
}
@ -150,7 +157,7 @@ class LinOTPAuthenticate extends BaseAuthenticate
} else {
// Enforce OTP token by Authentication Form
if (!$otp || $otp === "") {
throw new CakeException(__d('cake_dev', 'Missing OTP Token.', 'authenticate()'));
throw new ForbiddenException(__('Missing OTP Token.'));
}
$response = $this->_linotp_verify(
@ -200,7 +207,7 @@ class LinOTPAuthenticate extends BaseAuthenticate
// Don't fall back to FormAuthenticate in mixedauth mode.
// This enforces the second factor.
if ($mixedauth && !self::$user) {
throw new CakeException(__d('cake_dev', 'User could not be authenticated by LinOTP.', 'authenticate()'));
throw new UnauthorizedException(__('User could not be authenticated by LinOTP.'));
}
return self::$user;
}

View File

@ -10,7 +10,7 @@ to login with passwords stored in MISP.
```
cd app
php composer.phar require jumbojett/openid-connect-php
php composer.phar require jakub-onderka/openid-connect-php:1.0.0-rc1
```
2. Enable in `app/Config/config.php`

View File

@ -16,15 +16,9 @@
'type' => 'simple',
'fa-icon' => 'plus',
'text' => __('Add authentication key'),
'class' => 'btn btn-primary',
'onClick' => 'openGenericModal',
'onClickParams' => [
sprintf(
'%s/auth_keys/add%s',
$baseurl,
empty($user_id) ? '' : ('/' . $user_id)
)
]
'class' => 'btn-primary modal-open',
'url' => "$baseurl/auth_keys/add" . (empty($user_id) ? '' : ('/' . $user_id)),
'requirement' => $canCreateAuthkey
]
]
],
@ -101,11 +95,9 @@
'title' => 'Edit auth key',
],
[
'onclick' => sprintf(
'openGenericModal(\'%s/authKeys/delete/[onclick_params_data_path]\');',
$baseurl
),
'onclick_params_data_path' => 'AuthKey.id',
'class' => 'modal-open',
'url' => "$baseurl/authKeys/delete",
'url_params_data_paths' => ['AuthKey.id'],
'icon' => 'trash',
'title' => __('Delete auth key'),
]

View File

@ -68,7 +68,15 @@
<script type="text/javascript">
var showContext = false;
$(function () {
queryEventLock('<?= h($event['Event']['id']); ?>', <?= (int)$event['Event']['timestamp'] ?>);
<?php
if (!Configure::check('MISP.disable_event_locks') || !Configure::read('MISP.disable_event_locks')) {
echo sprintf(
"queryEventLock('%s', %s);",
h($event['Event']['id']),
(int)$event['Event']['timestamp']
);
}
?>
popoverStartup();
$(document.body).tooltip({

View File

@ -49,7 +49,7 @@
<tr id="event_<?= $eventId ?>">
<?php if ($isSiteAdmin || ($event['Event']['orgc_id'] == $me['org_id'])):?>
<td style="width:10px;">
<input class="select" type="checkbox" data-id="<?= $eventId ?>" />
<input class="select" type="checkbox" data-id="<?= $eventId ?>" data-uuid="<?= h($event['Event']['uuid']) ?>" />
</td>
<?php else: ?>
<td style="padding-left:0;padding-right:0;"></td>

View File

@ -1,4 +1,5 @@
<?php
$eventId = (int) $event['Event']['id'];
if (!empty($this->passedArgs['correlation'])) {
$attributeFilter = 'correlation';
}
@ -28,7 +29,7 @@
'text' => __('Proposal'),
'active' => $attributeFilter == 'proposal',
'onClick' => 'filterAttributes',
'onClickParams' => array('proposal', $event['Event']['id'])
'onClickParams' => array('proposal', $eventId)
);
$simple_filter_data[] = array(
'id' => 'filter_correlation',
@ -44,7 +45,7 @@
'text' => __('Warning'),
'active' => $attributeFilter == 'warning',
'onClick' => 'filterAttributes',
'onClickParams' => array('warning', $event['Event']['id'])
'onClickParams' => array('warning', $eventId)
);
$data = array(
'children' => array(
@ -54,9 +55,8 @@
'id' => 'create-button',
'title' => $possibleAction === 'attribute' ? __('Add attribute') : __('Add proposal'),
'fa-icon' => 'plus',
'class' => 'last',
'onClick' => 'openGenericModal',
'onClickParams' => array('/' . $possibleAction . 's/add/' . h($event['Event']['id']))
'class' => 'last modal-open',
'url' => $baseurl . '/' . $possibleAction . 's/add/' . $eventId,
),
array(
'id' => 'multi-edit-button',
@ -64,7 +64,7 @@
'class' => 'mass-select hidden',
'fa-icon' => 'edit',
'onClick' => 'editSelectedAttributes',
'onClickParams' => array($event['Event']['id'])
'onClickParams' => array($eventId)
),
array(
'id' => 'multi-tag-button',
@ -81,7 +81,7 @@
'fa-icon' => 'rebel',
'fa-source' => 'fab',
'onClick' => 'popoverPopup',
'onClickParams' => array('this', 'selected/attribute/eventid:' . h($event['Event']['id']), 'galaxies', 'selectGalaxyNamespace')
'onClickParams' => array('this', 'selected/attribute/eventid:' . $eventId, 'galaxies', 'selectGalaxyNamespace')
),
array(
'id' => 'group-into-object-button',
@ -90,7 +90,7 @@
'fa-icon' => 'object-group',
'fa-source' => 'fa',
'onClick' => 'proposeObjectsFromSelectedAttributes',
'onClickParams' => array('this', $event['Event']['id'])
'onClickParams' => array('this', $eventId)
),
array(
'id' => 'multi-delete-button',
@ -98,7 +98,7 @@
'class' => 'mass-select hidden',
'fa-icon' => 'trash',
'onClick' => 'multiSelectAction',
'onClickParams' => array($event['Event']['id'], 'deleteAttributes')
'onClickParams' => array($eventId, 'deleteAttributes')
),
array(
'id' => 'multi-accept-button',
@ -106,7 +106,7 @@
'class' => 'mass-proposal-select hidden',
'fa-icon' => 'check-circle',
'onClick' => 'multiSelectAction',
'onClickParams' => array($event['Event']['id'], 'acceptProposals')
'onClickParams' => array($eventId, 'acceptProposals')
),
array(
'id' => 'multi-discard-button',
@ -114,7 +114,7 @@
'class' => 'mass-proposal-select hidden',
'fa-icon' => 'times',
'onClick' => 'multiSelectAction',
'onClickParams' => array($event['Event']['id'], 'discardProposals')
'onClickParams' => array($eventId, 'discardProposals')
),
array(
'id' => 'multi-sighting-button',
@ -132,7 +132,7 @@
'title' => __('Populate using a template'),
'fa-icon' => 'list',
'onClick' => 'getPopup',
'onClickParams' => array($event['Event']['id'], 'templates', 'templateChoices'),
'onClickParams' => array($eventId, 'templates', 'templateChoices'),
'requirement' => $mayModify
),
array(
@ -140,14 +140,14 @@
'title' => __('Populate using the freetext import tool'),
'fa-icon' => 'align-left',
'onClick' => 'getPopup',
'onClickParams' => array($event['Event']['id'], 'events', 'freeTextImport')
'onClickParams' => array($eventId, 'events', 'freeTextImport')
),
array(
'id' => 'attribute-replace-button',
'title' => __('Replace all attributes of a category/type combination within the event'),
'fa-icon' => 'random',
'onClick' => 'getPopup',
'onClickParams' => array($event['Event']['id'], 'attributes', 'attributeReplace'),
'onClickParams' => array($eventId, 'attributes', 'attributeReplace'),
'requirement' => $mayModify
)
)
@ -236,7 +236,7 @@
'fa-icon' => 'times',
'title' => __('Remove filters'),
'onClick' => 'filterAttributes',
'onClickParams' => array('all', $event['Event']['id'])
'onClickParams' => array('all', $eventId)
)
)
)

View File

@ -114,13 +114,22 @@
$action['onclick']
);
}
$title = empty($action['title']) ? '' : h($action['title']);
$classes = [];
if (!empty($action['class'])) {
$classes[] = h($action['class']);
}
if (!empty($action['dbclickAction'])) {
$classes[] = 'dblclickActionElement';
}
echo sprintf(
'<a href="%s" title="%s" aria-label="%s" %s %s><i class="black %s"></i></a> ',
'<a href="%s" title="%s" aria-label="%s"%s%s><i class="black %s"></i></a> ',
$url,
empty($action['title']) ? '' : h($action['title']),
empty($action['title']) ? '' : h($action['title']),
empty($action['dbclickAction']) ? '' : 'class="dblclickActionElement"',
empty($action['onclick']) ? '' : sprintf('onclick="event.preventDefault();%s"', $action['onclick']),
$title,
$title,
empty($classes) ? '' : ' class="' . implode(' ', $classes) . '"',
empty($action['onclick']) ? '' : sprintf(' onclick="event.preventDefault();%s"', $action['onclick']),
$this->FontAwesome->getClass($action['icon'])
);
}

View File

@ -13,7 +13,7 @@
}
$onClickParams = implode(',', $onClickParams);
$onClick = sprintf(
'onClick="%s%s"',
'onclick="%s%s"',
(empty($data['url'])) ? 'event.preventDefault();' : '',
(!empty($data['onClick']) ? sprintf(
'%s(%s)',
@ -54,4 +54,3 @@
empty($data['badge']) ? '' : sprintf('<span class="badge badge-%s">%s</span>', empty($data['badge']['type']) ? 'info' : $data['badge']['type'], h($data['badge']['text']))
);
}
?>

View File

@ -27,26 +27,20 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'dashboardImport',
'text' => __('Import Config JSON'),
'onClick' => array(
'function' => 'openGenericModal',
'params' => array($baseurl . '/dashboards/import')
),
'url' => $baseurl . '/dashboards/import',
'link_class' => 'modal-open',
));
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'dashboardExport',
'text' => __('Export Config JSON'),
'onClick' => array(
'function' => 'openGenericModal',
'params' => array($baseurl . '/dashboards/export')
),
'url' => $baseurl . '/dashboards/export',
'link_class' => 'modal-open',
));
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'dashboardSave',
'text' => __('Save Dashboard Config'),
'onClick' => array(
'function' => 'openGenericModal',
'params' => array($baseurl . '/dashboards/saveTemplate')
),
'url' => $baseurl . '/dashboards/saveTemplate',
'link_class' => 'modal-open',
));
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'dashboardTemplateIndex',
@ -55,7 +49,7 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
));
break;
case 'event':
$eventId = intval($event['Event']['id']);
$eventId = (int)$event['Event']['id'];
echo '<div id="hiddenSideMenuData" class="hidden" data-event-id="' . $eventId . '"></div>';
if (in_array($menuItem, array('editEvent', 'addAttribute', 'addObject', 'addAttachment', 'addIOC', 'addThreatConnect', 'populateFromTemplate', 'merge'))) {
// we can safely assume that mayModify is true if coming from these actions, as they require it in the controller and the user has already passed that check

View File

@ -29,6 +29,9 @@ if (empty($url)) {
if (!empty($title)) {
$a .= ' title="' . h($title) . '"';
}
if (!empty($link_class)) {
$a .= ' class="' . h($link_class) . '"';
}
if (!empty($onClick)) {
$params = '';
foreach ($onClick['params'] as $param) {

View File

@ -12,6 +12,11 @@ if (!empty($field['url'])) {
}
foreach ($field['url_vars'] as $k => $path) {
$field['url'] = str_replace('{{' . $k . '}}', Hash::extract($data, $path)[0], $field['url']);
$temp = explode(':', $field['url']);
if (!in_array(strtolower($temp[0]), ['http', 'https'])) {
$field['url'] = '#';
$string = 'Malformed URL - invalid protocol (' . h($temp[0]) . ':)';
}
}
}
$string = sprintf(

View File

@ -3,12 +3,12 @@
$event = Hash::extract($data, $field['event_path']);
if ($event['protected']) {
echo sprintf(
'<span class="fas fa-lock"></span> %s %s %s <br />',
'<span class="fas fa-lock"></span> %s %s %s <br>',
__('Event is in protected mode. (Limited distribution)'),
!$field['owner'] ? '' : sprintf(
'<br /><a href="#" onClick="%s" title="%s"><i class="fas fa-unlock"></i> %s</a>',
'<br><a href="%s" class="modal-open" title="%s"><i class="fas fa-unlock"></i> %s</a>',
sprintf(
"openGenericModal('%s/events/unprotect/%s');",
'%s/events/unprotect/%s',
$baseurl,
h($event['id'])
),
@ -16,11 +16,10 @@
empty($field['text']) ? __('Switch to unprotected mode') : h($field['text'])
),
!$field['owner'] ? '' : sprintf(
'<br /><a href="#" onClick="%s"><i class="fas fa-key"></i>%s</a>',
'<br><a href="%s" class="modal-open"><i class="fas fa-key"></i> %s</a>',
sprintf(
"openGenericModal('%s/CryptographicKeys/add/%s/%s');",
"%s/CryptographicKeys/add/Event/%s",
$baseurl,
h('Event'),
h($event['id'])
),
empty($field['text']) ? __('Add signing key') : h($field['text'])
@ -33,7 +32,7 @@
$foundInstanceKey = true;
}
echo sprintf(
'%s<span class="bold">%s</span> (%s) <a href="#" onClick="%s" title="%s"><i class="fas fa-search"></i></a> %s<br />',
'%s<span class="bold">%s</span> (%s) <a href="%s" class="modal-open" title="%s"><i class="fas fa-search"></i></a> %s<br>',
!$isInstanceKey ? '' : sprintf(
'<i class="fas fa-home blue" title="%s"></i>&nbsp;',
__('This is the instance signing key. When synchronising the instance, this will be the key used to validate the event.')
@ -41,13 +40,13 @@
h($key['type']),
empty($key['fingerprint']) ? '#' . h($key['id']) : h($key['fingerprint']),
sprintf(
"openGenericModal('%s/cryptographicKeys/view/%s');",
"%s/cryptographicKeys/view/%s",
$baseurl,
h($key['id'])
),
__('Inspect key'),
!$field['owner'] ? '' : sprintf(
'<a href="#" onClick="openGenericModal(\'%s/cryptographicKeys/delete/%s\')" title="%s"><i class="fas fa-trash"></i></a>',
'<a href="%s/cryptographicKeys/delete/%s" class="modal-open" title="%s"><i class="fas fa-trash"></i></a>',
$baseurl,
h($key['id']),
__('Detach key from the event. This key will no longer be used to sign and validate this event.')
@ -64,12 +63,12 @@
}
} else {
echo sprintf(
'<span class="fas fa-unlock"></span> <span>%s</span> %s<br />',
'<span class="fas fa-unlock"></span> <span>%s</span> %s<br>',
__('Event is in unprotected mode.'),
!$field['owner'] ? '' : sprintf(
'<br /><a href="#" onClick="%s" title="%s"><i class="fas fa-lock"></i> %s</a>',
'<br><a href="%s" class="modal-open" title="%s"><i class="fas fa-lock"></i> %s</a>',
sprintf(
"openGenericModal('%s/events/protect/%s');",
"%s/events/protect/%s",
$baseurl,
h($event['id'])
),
@ -78,4 +77,3 @@
)
);
}
//echo ;

View File

@ -82,7 +82,7 @@
);
}
}
$ajaxLists = '<br/>';
$ajaxLists = '<br>';
if (!empty($children)) {
foreach ($children as $child) {
$ajaxLists .= $this->element(
@ -131,4 +131,4 @@
$appendHtml,
$ajax ? '' : $this->element('/genericElements/SideMenu/side_menu', $menuData)
);
?>

View File

@ -544,7 +544,7 @@
);
}
?>
<div id="topBar" class="navbar navbar-inverse <?= $debugMode ?>">
<div id="topBar" class="navbar navbar-inverse <?= isset($debugMode) ? $debugMode : 'debugOff' ?>">
<div class="navbar-inner">
<ul class="nav">
<?php

View File

@ -1,10 +1,13 @@
<?php
$showLinebreak = in_array($responseType, ['txt', 'xml']);
?>
<div class="index">
<div id="restSearchExportResult">
<?php
if (!empty($renderView)) {
echo $this->render('/Events/module_views/' . $renderView, false);
}
?>
<div id="restSearchExportResult" style="<?= $showLinebreak ? 'white-space: pre;' : '' ?>">
<?php
if (!empty($renderView)) {
echo $this->render('/Events/module_views/' . $renderView, false);
}
?>
</div>
</div>
<?php

View File

@ -70,7 +70,7 @@
[
'key' => __('Contributors'),
'type' => 'custom',
'function' => function ($data) use($contributors, $baseurl, $event) {
'function' => function ($data) use ($contributors, $baseurl, $event) {
$contributorsContent = [];
foreach ($contributors as $organisationId => $name) {
$org = ['Organisation' => ['id' => $organisationId, 'name' => $name]];
@ -243,7 +243,7 @@
'key' => __('Delegation request'),
'class' => 'background-red bold',
'type' => 'delegationRequest',
'delegationRequest' => $delegationRequest,
'delegationRequest' => isset($delegationRequest) ? $delegationRequest : null,
'requirement' => !empty($delegationRequest)
],
[
@ -254,7 +254,7 @@
return sprintf(
'%s%s',
$data['Event']['disable_correlation'] ? __('Disabled') : __('Enabled'),
(!$mayModify && !$isSiteAdmin) ? '' : sprintf(
(!$mayModify && !$isSiteAdmin) ? '' :
sprintf(
' (<a onClick="getPopup(%s);" style="%scursor:pointer;font-weight:normal;">%s</a>)',
sprintf(
@ -264,7 +264,6 @@
$data['Event']['disable_correlation'] ? 'color:white;' : '',
$data['Event']['disable_correlation'] ? __('enable') : __('disable')
)
)
);
},
'requirement' => (!Configure::read('MISP.completely_disable_correlation') && Configure::read('MISP.allow_disabling_correlation'))
@ -302,4 +301,3 @@
]
]
);
?>

View File

@ -56,7 +56,7 @@ $table_data[] = array('key' => __('Collection UUID'), 'value' => $cluster['Galax
$table_data[] = array(
'key' => __('Source'),
'html' => filter_var($cluster['GalaxyCluster']['source'], FILTER_VALIDATE_URL) ?
'<a href="' . $cluster['GalaxyCluster']['source'] . '" rel="noreferrer noopener">' . h($cluster['GalaxyCluster']['source']) :
'<a href="' . h($cluster['GalaxyCluster']['source']) . '" rel="noreferrer noopener">' . h($cluster['GalaxyCluster']['source']) :
h($cluster['GalaxyCluster']['source']),
);
$table_data[] = array('key' => __('Authors'), 'value' => !empty($cluster['GalaxyCluster']['authors']) ? implode(', ', $cluster['GalaxyCluster']['authors']) : __('N/A'));

View File

@ -50,7 +50,7 @@ echo $this->element('genericElements/Form/genericForm', [
'type' => 'file',
'field' => 'logo',
'error' => array('escape' => false),
'label' => __('Logo (48×48 PNG or SVG)'),
'label' => __('Logo (48×48 %s)', Configure::read('Security.enable_svg_logos')? 'PNG or SVG' : 'PNG'),
],
[
'field' => 'nationality',

View File

@ -84,18 +84,16 @@ if ($isSiteAdmin) {
$actions = [
[
'url' => $baseurl . '/admin/roles/edit',
'url_params_data_paths' => array(
'Role.id'
),
'url_params_data_paths' => ['Role.id'],
'icon' => 'edit',
'title' => __('Edit role'),
],
[
'onclick' => sprintf(
'openGenericModal(\'%s/admin/roles/delete/[onclick_params_data_path]\');',
$baseurl
),
'onclick_params_data_path' => 'Role.id',
'class' => 'modal-open',
'url' => "$baseurl/admin/roles/delete",
'url_params_data_paths' => ['Role.id'],
'icon' => 'trash',
'title' => __('Delete role'),
]
];
} else {
@ -116,14 +114,8 @@ echo $this->element('genericElements/IndexTable/scaffold', [
'type' => 'simple',
'text' => __('Add role'),
'fa-icon' => 'plus',
'class' => 'btn btn-primary',
'onClick' => 'openGenericModal',
'onClickParams' => [
sprintf(
'%s/admin/roles/add',
$baseurl
)
],
'class' => 'btn-primary modal-open',
'url' => "$baseurl/admin/roles/add",
'requirement' => $isSiteAdmin,
]
]

View File

@ -39,13 +39,13 @@
<?php
echo $this->Form->input('email', array('autocomplete' => 'off', 'autofocus'));
echo $this->Form->input('password', array('autocomplete' => 'off'));
if (!empty(Configure::read('LinOTPAuth'))) {
if (!empty(Configure::read('LinOTPAuth')) && Configure::read('LinOTPAuth.enabled')!== FALSE) {
echo $this->Form->input('otp', array('autocomplete' => 'off', 'type' => 'password', 'label' => 'OTP'));
echo "<div class=\"clear\">";
echo sprintf(
'%s <a href="%s/selfservice" title="LinOTP Selfservice">LinOTP Selfservice</a> %s',
__('Visit'),
Configure::read('LinOTPAuth.baseUrl'),
h(Configure::read('LinOTPAuth.baseUrl')),
__('for the One-Time-Password selfservice.')
);
}
@ -92,7 +92,10 @@ function submitLoginForm() {
var url = $form.attr('action')
var email = $form.find('#UserEmail').val()
var password = $form.find('#UserPassword').val()
if (!empty(Configure::read('LinOTPAuth'))) {
var LinOTPAuth = <?= empty(Configure::read('LinOTPAuth')) ? 'false' : 'true' ?>;
var LinOTPAuthEnabled = <?= empty(Configure::read('LinOTPAuth.enabled')) ? 'false' : 'true' ?>;
if (LinOTPAuth && LinOTPAuthEnabled) {
var otp = $form.find('#UserOtp').val()
}
if (!$form[0].checkValidity()) {
@ -107,7 +110,7 @@ function submitLoginForm() {
var $tmpForm = $('#temp form#UserLoginForm')
$tmpForm.find('#UserEmail').val(email)
$tmpForm.find('#UserPassword').val(password)
if (!empty(Configure::read('LinOTPAuth'))) {
if (LinOTPAuth && LinOTPAuthEnabled) {
$tmpForm.find('#UserOtp').val(otp)
}
$tmpForm.submit()

View File

@ -5,7 +5,8 @@
if (!Configure::read('MISP.terms_file')) {
$termsFile = APP ."View/Users/terms";
} else {
$termsFile = APP . 'files' . DS . 'terms' . DS . Configure::read('MISP.terms_file');
$customTermsFile = basename(realpath(Configure::read('MISP.terms_file')));
$termsFile = APP . 'files' . DS . 'terms' . DS . $customTermsFile;
}
if (!(file_exists($termsFile))) {
echo "<p>" . __("Terms and Conditions file not found.") . "</p>";

@ -1 +1 @@
Subproject commit f49b54281b7645b703663a99613f6909ebc7a989
Subproject commit 4242732af18c873e192a8450bac466bd59742b91

@ -1 +1 @@
Subproject commit 9515ae332e45d9ac306eaaaa802b605c1090cf5c
Subproject commit ea23d591851856098fe8a7333823f5ebeda545d4

@ -1 +1 @@
Subproject commit 6da2a75fc41afb401d5c16635dfcd48d43372a88
Subproject commit 92d74aa5fc76080ddfd6cbaa7bdce18e30d11634

@ -1 +1 @@
Subproject commit 653a035ae77fa46e9f6b1608b9f75bd1506bd1a6
Subproject commit a6da32235f04b4c26fe9d1478bf385410232c872

View File

@ -1,371 +1,371 @@
class ActionTable {
constructor(options) {
this.__globalCounter = 0;
this.options = options;
this.id = options.id;
this.container = options.container;
this.classes = options.classes;
this.table_title = options.title;
this.header = options.header;
this.onAddition = options.onAddition;
this.onRemove = options.onRemove;
this.header.push("Action");
this.row_num = this.header.length;
this.data = options.data === undefined ? [] : options.data;
this.tr_id_mapping = {};
this.control_items = options.control_items;
this.header_action_button = options.header_action_button === undefined ? {} : options.header_action_button;
if (options.header_action_button !== undefined) {
this.header_action_button_style = this.header_action_button.style === undefined ? {} : this.header_action_button.style;
this.additionEnabled = this.header_action_button.additionEnabled === undefined ? true : this.header_action_button.additionEnabled;
this.additionButtonDisabled = this.header_action_button.disabled === undefined ? false : this.header_action_button.disabled;
} else {
this.header_action_button_style = {};
this.additionEnabled = true;
this.additionButtonDisabled = false;
}
constructor(options) {
this.__globalCounter = 0;
this.options = options;
this.id = options.id;
this.container = options.container;
this.classes = options.classes;
this.table_title = options.title;
this.header = options.header;
this.onAddition = options.onAddition;
this.onRemove = options.onRemove;
this.header.push("Action");
this.row_num = this.header.length;
this.data = options.data === undefined ? [] : options.data;
this.tr_id_mapping = {};
this.control_items = options.control_items;
this.header_action_button = options.header_action_button === undefined ? {} : options.header_action_button;
if (options.header_action_button !== undefined) {
this.header_action_button_style = this.header_action_button.style === undefined ? {} : this.header_action_button.style;
this.additionEnabled = this.header_action_button.additionEnabled === undefined ? true : this.header_action_button.additionEnabled;
this.additionButtonDisabled = this.header_action_button.disabled === undefined ? false : this.header_action_button.disabled;
} else {
this.header_action_button_style = {};
this.additionEnabled = true;
this.additionButtonDisabled = false;
}
this.row_action_button = options.row_action_button === undefined ? {} : options.row_action_button;
if (options.row_action_button !== undefined) {
this.row_action_button_style = this.row_action_button.style === undefined ? {} : this.row_action_button.style;
this.removalEnabled = this.row_action_button.removalEnabled === undefined ? true : this.row_action_button.removalEnabled;
} else {
this.row_action_button_style = {};
this.removalEnabled = true;
}
this.row_action_button = options.row_action_button === undefined ? {} : options.row_action_button;
if (options.row_action_button !== undefined) {
this.row_action_button_style = this.row_action_button.style === undefined ? {} : this.row_action_button.style;
this.removalEnabled = this.row_action_button.removalEnabled === undefined ? true : this.row_action_button.removalEnabled;
} else {
this.row_action_button_style = {};
this.removalEnabled = true;
}
this.selects = {};
this.selects = {};
this.__create_table();
}
this.__create_table();
}
__get_uniq_index() {
this.__globalCounter++;
return this.__globalCounter-1;
}
__get_uniq_index() {
this.__globalCounter++;
return this.__globalCounter-1;
}
add_row(row) {
if (!this.__data_already_exists(row)) {
var id = this.__add_row(row);
this.tr_id_mapping[this.data.length] = id;
this.data.push(row);
}
}
add_row(row) {
if (!this.__data_already_exists(row)) {
var id = this.__add_row(row);
this.tr_id_mapping[this.data.length] = id;
this.data.push(row);
}
}
delete_row(row_id) {
var tr = document.getElementById(row_id);
var array = this.__get_array_from_DOM_row(tr);
var data_index = this.__find_array_index(array, this.data);
tr.outerHTML = "";
this.data.splice(data_index, 1);
}
delete_row(row_id) {
var tr = document.getElementById(row_id);
var array = this.__get_array_from_DOM_row(tr);
var data_index = this.__find_array_index(array, this.data);
tr.outerHTML = "";
this.data.splice(data_index, 1);
}
delete_row_index(row_pos) {
var tr = this.get_DOM_row(row_pos);
var array = this.__get_array_from_DOM_row(tr);
var data_index = this.__find_array_index(array, this.data);
tr.outerHTML = "";
this.data.splice(data_index, 1);
}
delete_row_index(row_pos) {
var tr = this.get_DOM_row(row_pos);
var array = this.__get_array_from_DOM_row(tr);
var data_index = this.__find_array_index(array, this.data);
tr.outerHTML = "";
this.data.splice(data_index, 1);
}
get_DOM_row(row_pos) {
var row_id = this.tr_id_mapping[row_pos];
var tr = document.getElementById(row_id);
return tr;
}
get_DOM_row(row_pos) {
var row_id = this.tr_id_mapping[row_pos];
var tr = document.getElementById(row_id);
return tr;
}
get_data() {
return this.data;
}
get_data() {
return this.data;
}
clear_table() {
var dataLength = this.data.length;
for (var i=0; i<dataLength; i++) {
this.delete_row_index(i);
}
}
clear_table() {
var dataLength = this.data.length;
for (var i=0; i<dataLength; i++) {
this.delete_row_index(i);
}
}
set_table_data(data) {
this.clear_table();
for (var i in data) {
this.add_row(data[i]);
}
}
set_table_data(data) {
this.clear_table();
for (var i in data) {
this.add_row(data[i]);
}
}
add_options(id, values) {
var select = this.selects[id];
var selected_value = select.value;
select.innerHTML = ""; // ensure uniqueness
this.__add_options_to_select(select, values);
select.value = selected_value;
}
add_options(id, values) {
var select = this.selects[id];
var selected_value = select.value;
select.innerHTML = ""; // ensure uniqueness
this.__add_options_to_select(select, values);
select.value = selected_value;
}
__get_array_from_DOM_row(tr) {
var children = tr.children;
var array = [];
for (var i = 0; i < children.length-1; i++) {
array.push(children[i].innerText);
}
return array;
}
__get_array_from_DOM_row(tr) {
var children = tr.children;
var array = [];
for (var i = 0; i < children.length-1; i++) {
array.push(children[i].innerText);
}
return array;
}
__data_already_exists(data) {
return this.__find_array_index(data, this.data) >= 0;
}
__data_already_exists(data) {
return this.__find_array_index(data, this.data) >= 0;
}
__find_array_index(value, array) {
for (var i in array) {
if (JSON.stringify(array[i]) === JSON.stringify(value)) { // compare array
return i;
}
}
return -1;
}
__find_array_index(value, array) {
for (var i in array) {
if (JSON.stringify(array[i]) === JSON.stringify(value)) { // compare array
return i;
}
}
return -1;
}
__create_table() {
if (this.table_title !== undefined) {
var label = document.createElement('label');
label.innerHTML = this.table_title;
this.container.appendChild(label);
}
this.form = document.createElement('form');
this.table = document.createElement('table');
this.table.classList.add("table", "table-bordered", "action-table");
if (this.classes !== undefined) {
for (var i in this.classes) {
this.table.classList.add(this.classes[i]);
}
}
this.thead = document.createElement('thead');
this.tbody = document.createElement('tbody');
var trHead = document.createElement('tr');
for (var col of this.header) {
var th = document.createElement('th');
th.innerHTML = col;
trHead.appendChild(th);
}
this.thead.appendChild(trHead);
__create_table() {
if (this.table_title !== undefined) {
var label = document.createElement('label');
label.innerHTML = this.table_title;
this.container.appendChild(label);
}
this.form = document.createElement('form');
this.table = document.createElement('table');
this.table.classList.add("table", "table-bordered", "action-table");
if (this.classes !== undefined) {
for (var i in this.classes) {
this.table.classList.add(this.classes[i]);
}
}
this.thead = document.createElement('thead');
this.tbody = document.createElement('tbody');
var trHead = document.createElement('tr');
for (var col of this.header) {
var th = document.createElement('th');
th.innerHTML = col;
trHead.appendChild(th);
}
this.thead.appendChild(trHead);
this.__add_control_row();
this.__add_control_row();
for (var row of this.data) {
this.__add_row(row);
}
this.table.appendChild(this.thead);
this.table.appendChild(this.tbody);
this.form.appendChild(this.table);
this.container.appendChild(this.form);
}
for (var row of this.data) {
this.__add_row(row);
}
this.table.appendChild(this.thead);
this.table.appendChild(this.tbody);
this.form.appendChild(this.table);
this.container.appendChild(this.form);
}
__add_row(row) {
var tr = document.createElement('tr');
tr.id = "tr_" + this.__uuidv4();
for (var col of row) {
var td = document.createElement('td');
td.innerHTML = col;
tr.appendChild(td);
}
this.__add_action_button(tr);
this.tbody.appendChild(tr);
return tr.id;
}
__add_row(row) {
var tr = document.createElement('tr');
tr.id = "tr_" + this.__uuidv4();
for (var col of row) {
var td = document.createElement('td');
td.innerHTML = col;
tr.appendChild(td);
}
this.__add_action_button(tr);
this.tbody.appendChild(tr);
return tr.id;
}
__add_control_row() {
var tr = document.createElement('tr');
for (var itemOption of this.control_items) {
var td = document.createElement('td');
var item = this.__add_control_item(itemOption);
if (itemOption.colspan !== undefined) {
td.colSpan = itemOption.colspan;
}
td.appendChild(item);
tr.appendChild(td);
}
var td = document.createElement('td');
__add_control_row() {
var tr = document.createElement('tr');
for (var itemOption of this.control_items) {
var td = document.createElement('td');
var item = this.__add_control_item(itemOption);
if (itemOption.colspan !== undefined) {
td.colSpan = itemOption.colspan;
}
td.appendChild(item);
tr.appendChild(td);
}
var td = document.createElement('td');
var btn = document.createElement('button');
var header_action_button_style = this.header_action_button.style === undefined ? {} : this.header_action_button.style;
if (header_action_button_style.type !== undefined) {
btn.classList.add("btn", "btn-"+header_action_button_style.type);
} else {
btn.classList.add("btn", "btn-primary");
}
if (header_action_button_style.tooltip !== undefined) {
btn.title = header_action_button_style.tooltip;
}
if (header_action_button_style.icon !== undefined) {
btn.innerHTML = '<span class="fa '+header_action_button_style.icon+'"></span>';
} else {
btn.innerHTML = '<span class="fa fa-plus-square"></span>';
}
btn.type = "button";
btn.disabled = this.additionButtonDisabled;
var btn = document.createElement('button');
var header_action_button_style = this.header_action_button.style === undefined ? {} : this.header_action_button.style;
if (header_action_button_style.type !== undefined) {
btn.classList.add("btn", "btn-"+header_action_button_style.type);
} else {
btn.classList.add("btn", "btn-primary");
}
if (header_action_button_style.tooltip !== undefined) {
btn.title = header_action_button_style.tooltip;
}
if (header_action_button_style.icon !== undefined) {
btn.innerHTML = '<span class="fa '+header_action_button_style.icon+'"></span>';
} else {
btn.innerHTML = '<span class="fa fa-plus-square"></span>';
}
btn.type = "button";
btn.disabled = this.additionButtonDisabled;
var that = this;
btn.addEventListener("click", function(evt) {
var data = [];
for (var elem of that.form.elements) {
if (elem.classList.contains('form-group')) {
data.push(elem.value);
}
}
if (that.additionEnabled) {
that.add_row(data);
}
if (that.onAddition !== undefined) {
that.onAddition(data, that);
}
});
var that = this;
btn.addEventListener("click", function(evt) {
var data = [];
for (var elem of that.form.elements) {
if (elem.classList.contains('form-group')) {
data.push(elem.value);
}
}
if (that.additionEnabled) {
that.add_row(data);
}
if (that.onAddition !== undefined) {
that.onAddition(data, that);
}
});
td.appendChild(btn);
td.appendChild(btn);
tr.appendChild(td);
this.thead.appendChild(tr);
}
tr.appendChild(td);
this.thead.appendChild(tr);
}
__add_control_item(options) {
var item;
switch(options.DOMType) {
case "select":
item = this.__create_select(options.item_options);
this.selects[item.id] = item;
break;
case "input":
item = this.__create_input(options.item_options);
break;
case "empty":
item = this.__create_empty(options.item_options);
break;
default:
break;
}
return item;
}
__add_control_item(options) {
var item;
switch(options.DOMType) {
case "select":
item = this.__create_select(options.item_options);
this.selects[item.id] = item;
break;
case "input":
item = this.__create_input(options.item_options);
break;
case "empty":
item = this.__create_empty(options.item_options);
break;
default:
break;
}
return item;
}
__add_action_button(tr) {
var that = this;
var td = document.createElement('td');
var btn = document.createElement('button');
btn.classList.add("btn", "btn-danger");
btn.innerHTML = '<span class="fa fa-trash"></span>';
btn.type = "button";
btn.setAttribute('rowID', tr.id);
if (that.row_action_button_style.tooltip !== undefined) {
btn.title = that.row_action_button_style.tooltip;
}
if (that.row_action_button_style.style !== undefined) {
btn.style = that.row_action_button_style.style;
}
var that = this;
btn.addEventListener("click", function(evt) {
if (that.onRemove !== undefined) {
var tr = document.getElementById(this.getAttribute('rowID'));
var data = that.__get_array_from_DOM_row(tr);
that.onRemove(data, that);
}
if (that.removalEnabled) {
that.delete_row(this.getAttribute('rowID'));
}
});
td.appendChild(btn);
__add_action_button(tr) {
var that = this;
var td = document.createElement('td');
var btn = document.createElement('button');
btn.classList.add("btn", "btn-danger");
btn.innerHTML = '<span class="fa fa-trash"></span>';
btn.type = "button";
btn.setAttribute('rowID', tr.id);
if (that.row_action_button_style.tooltip !== undefined) {
btn.title = that.row_action_button_style.tooltip;
}
if (that.row_action_button_style.style !== undefined) {
btn.style = that.row_action_button_style.style;
}
var that = this;
btn.addEventListener("click", function(evt) {
if (that.onRemove !== undefined) {
var tr = document.getElementById(this.getAttribute('rowID'));
var data = that.__get_array_from_DOM_row(tr);
that.onRemove(data, that);
}
if (that.removalEnabled) {
that.delete_row(this.getAttribute('rowID'));
}
});
td.appendChild(btn);
if (that.row_action_button.others !== undefined) {
for (var i in that.row_action_button.others) {
var newBtnOptions = that.row_action_button.others[i];
if (that.row_action_button.others !== undefined) {
for (var i in that.row_action_button.others) {
var newBtnOptions = that.row_action_button.others[i];
var btn_style = newBtnOptions.style !== undefined ? newBtnOptions.style : {};
var btn = document.createElement('button');
btn.type = "button";
if (btn_style.type !== undefined) {
btn.classList.add("btn", "btn-"+btn_style.type);
} else {
btn.classList.add("btn", "btn-primary");
}
if (btn_style.icon !== undefined) {
btn.innerHTML = '<span class="fa '+btn_style.icon+'"></span>';
} else {
btn.innerHTML = '<span class="fa fa-check"></span>';
}
if (btn_style.title !== undefined) {
btn.title = btn_style.title;
}
if (btn_style.style !== undefined) {
btn.style = btn_style.style+"margin-left: 3px";
} else {
btn.style = "margin-left: 3px";
}
btn.setAttribute('rowID', tr.id);
if (newBtnOptions.event !== undefined) {
btn.addEventListener("click", function(evt) {
var tr = document.getElementById(this.getAttribute('rowID'));
var data = that.__get_array_from_DOM_row(tr);
newBtnOptions.event(data, that);
});
}
td.appendChild(btn);
}
}
var btn_style = newBtnOptions.style !== undefined ? newBtnOptions.style : {};
var btn = document.createElement('button');
btn.type = "button";
if (btn_style.type !== undefined) {
btn.classList.add("btn", "btn-"+btn_style.type);
} else {
btn.classList.add("btn", "btn-primary");
}
if (btn_style.icon !== undefined) {
btn.innerHTML = '<span class="fa '+btn_style.icon+'"></span>';
} else {
btn.innerHTML = '<span class="fa fa-check"></span>';
}
if (btn_style.title !== undefined) {
btn.title = btn_style.title;
}
if (btn_style.style !== undefined) {
btn.style = btn_style.style+"margin-left: 3px";
} else {
btn.style = "margin-left: 3px";
}
btn.setAttribute('rowID', tr.id);
if (newBtnOptions.event !== undefined) {
btn.addEventListener("click", function(evt) {
var tr = document.getElementById(this.getAttribute('rowID'));
var data = that.__get_array_from_DOM_row(tr);
newBtnOptions.event(data, that);
});
}
td.appendChild(btn);
}
}
tr.appendChild(td);
}
tr.appendChild(td);
}
__create_empty(options) {
var empty = document.createElement('span');
empty.classList.add("form-group");
empty.id = options.id !== undefined ? options.id : 'actionTable_controlSelect_'+this.__get_uniq_index();
return empty;
}
__create_empty(options) {
var empty = document.createElement('span');
empty.classList.add("form-group");
empty.id = options.id !== undefined ? options.id : 'actionTable_controlSelect_'+this.__get_uniq_index();
return empty;
}
__create_input(options) {
var input = document.createElement('input');
input.classList.add("form-group");
input.id = options.id !== undefined ? options.id : 'actionTable_controlSelect_'+this.__get_uniq_index();
if (options.style !== undefined) {
input.style = options.style;
}
if (options.placeholder !== undefined) {
input.placeholder = options.placeholder;
}
if (options.disabled !== undefined) {
input.disabled = options.disabled;
}
if (options.typeahead !== undefined) {
var typeaheadOption = options.typeahead;
$('#'+input.id).typeahead(typeaheadOption);
}
return input;
}
__create_input(options) {
var input = document.createElement('input');
input.classList.add("form-group");
input.id = options.id !== undefined ? options.id : 'actionTable_controlSelect_'+this.__get_uniq_index();
if (options.style !== undefined) {
input.style = options.style;
}
if (options.placeholder !== undefined) {
input.placeholder = options.placeholder;
}
if (options.disabled !== undefined) {
input.disabled = options.disabled;
}
if (options.typeahead !== undefined) {
var typeaheadOption = options.typeahead;
$('#'+input.id).typeahead(typeaheadOption);
}
return input;
}
__create_select(select_options) {
var select = document.createElement('select');
select.classList.add("form-group");
select.id = select_options.id !== undefined ? select_options.id : 'actionTable_controlSelect_'+this.__get_uniq_index();
select.style.width = "100%";
this.__add_options_to_select(select, select_options.options);
if(select_options.default !== undefined) {
select.value = select_options.default;
}
return select;
}
__create_select(select_options) {
var select = document.createElement('select');
select.classList.add("form-group");
select.id = select_options.id !== undefined ? select_options.id : 'actionTable_controlSelect_'+this.__get_uniq_index();
select.style.width = "100%";
this.__add_options_to_select(select, select_options.options);
if(select_options.default !== undefined) {
select.value = select_options.default;
}
return select;
}
__add_options_to_select(select, options) {
for(var value of options) {
var option = document.createElement('option');
if (Array.isArray(value)) { // array of type [value, text]
option.value = value[1];
option.innerHTML = value[1];
} else { // only value, text=value
option.value = value;
option.innerHTML = value;
}
select.appendChild(option);
}
}
__add_options_to_select(select, options) {
for(var value of options) {
var option = document.createElement('option');
if (Array.isArray(value)) { // array of type [value, text]
option.value = value[1];
option.textContent = value[1];
} else { // only value, text=value
option.value = value;
option.textContent = value;
}
select.appendChild(option);
}
}
__uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
__uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
}

10874
app/webroot/js/jquery.js vendored

File diff suppressed because one or more lines are too long

View File

@ -909,7 +909,7 @@ function multiSelectExportEvents() {
var selected = [];
$(".select").each(function() {
if ($(this).is(":checked")) {
var temp = $(this).data("id");
var temp = $(this).data("uuid");
if (temp != null) {
selected.push(temp);
}
@ -1220,13 +1220,12 @@ function openGenericModal(url, modalData, callback) {
} else {
htmlData = data;
}
$('body').append(htmlData);
$(document.body).append(htmlData);
$('#genericModal').modal().on('shown', function() {
if (callback !== undefined) {
callback();
}
});
},
error: function (data, textStatus, errorThrown) {
showMessage('fail', textStatus + ": " + errorThrown);
@ -3969,7 +3968,7 @@ function flashErrorPopover() {
$("#gray_out").fadeIn();
}
$('body').on('click', function (e) {
$(document.body).on('click', function (e) {
$('[data-toggle=popover]').each(function () {
// hide any open popovers when the anywhere else in the body is clicked
if (typeof currentPopover !== 'undefined' && currentPopover !== '') {
@ -4766,7 +4765,7 @@ $(document.body).on('click', '.quickSelect', function() {
selection.addRange(range);
});
// Any link with data-paginator attribute will be treat as AJAX paginator
// Any link with data-paginator attribute will be treated as AJAX paginator
$(document.body).on('click', 'a[data-paginator]', function (e) {
e.preventDefault();
var paginatorTarget = $(this).attr('data-paginator');
@ -4782,6 +4781,12 @@ $(document.body).on('click', 'a[data-paginator]', function (e) {
});
});
// Any link with modal-open class will be treated as generic modal
$(document.body).on('click', 'a.modal-open', function (e) {
e.preventDefault();
openGenericModal($(this).attr('href'));
});
function queryEventLock(event_id, timestamp) {
if (!document.hidden) {
$.ajax({
@ -5303,7 +5308,7 @@ function redirectIdSelection(scope, action) {
}
}
$('body').on('click', '.hex-value-convert', function() {
$(document.body).on('click', '.hex-value-convert', function() {
var $hexValueSpan = $(this).parent().children(':first-child');
var val = $hexValueSpan.text().trim();
if (!$hexValueSpan.hasClass('binary-representation')) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long