Merge branch 'develop' into 2.4

pull/7420/head v2.4.143
iglocska 2021-05-14 13:00:30 +02:00
commit e4a00d067a
No known key found for this signature in database
GPG Key ID: BEA224F1FEF113AC
112 changed files with 3864 additions and 1232 deletions

2
PyMISP

@ -1 +1 @@
Subproject commit 357096f24c4f8d7dac87dfe0b9edad4f924f27a3
Subproject commit c2e9663765e83f1a4aa70099546bec653ed770e7

View File

@ -1 +1 @@
{"major":2, "minor":4, "hotfix":142}
{"major":2, "minor":4, "hotfix":143}

View File

@ -28,10 +28,11 @@
Router::connect('/', array('controller' => 'events', 'action' => 'index'));
// admin Paginator
Router::connect('/whitelists/admin_index/*', array('controller' => 'whitelists', 'action' => 'index', 'admin' => true));
Router::connect('/allowedlists/admin_index/*', array('controller' => 'allowedlists', 'action' => 'index', 'admin' => true));
Router::connect('/users/admin_index/*', array('controller' => 'users', 'action' => 'index', 'admin' => true));
Router::connect('/roles/admin_index/*', array('controller' => 'roles', 'action' => 'index', 'admin' => true));
Router::connect('/logs/admin_search/*', array('controller' => 'logs', 'action' => 'search', 'admin' => true));
Router::connect('/audit_logs/admin_index/*', array('controller' => 'audit_logs', 'action' => 'index', 'admin' => true));
Router::connect('/logs/admin_index/*', array('controller' => 'logs', 'action' => 'index', 'admin' => true));
Router::connect('/regexp/admin_index/*', array('controller' => 'regexp', 'action' => 'index', 'admin' => true));

View File

@ -445,6 +445,7 @@ class EventShell extends AppShell
$inputData = $tempFile->read();
$inputData = json_decode($inputData, true);
$tempFile->delete();
Configure::write('CurrentUserId', $inputData['user']['id']);
$this->Event->processFreeTextData(
$inputData['user'],
$inputData['attributes'],
@ -465,6 +466,7 @@ class EventShell extends AppShell
$tempFile = new File(APP . 'tmp/cache/ingest' . DS . $inputFile);
$inputData = json_decode($tempFile->read(), true);
$tempFile->delete();
Configure::write('CurrentUserId', $inputData['user']['id']);
$this->Event->processModuleResultsData(
$inputData['user'],
$inputData['misp_format'],
@ -530,6 +532,7 @@ class EventShell extends AppShell
if (empty($user)) {
$this->error("User with ID $userId does not exists.");
}
Configure::write('CurrentUserId', $user['id']); // for audit logging purposes
return $user;
}

View File

@ -0,0 +1,150 @@
<?php
/**
* @property Log $Log
* @property AuditLog $AuditLog
* @property Server $Server
*/
class LogShell extends AppShell
{
public $uses = ['Log', 'AuditLog', 'Server'];
public function getOptionParser()
{
$parser = parent::getOptionParser();
$parser->addSubcommand('auditStatistics', [
'help' => __('Show statistics from audit logs.'),
]);
$parser->addSubcommand('statistics', [
'help' => __('Show statistics from logs.'),
]);
$parser->addSubcommand('export', [
'help' => __('Export logs to compressed file in JSON Lines format (one JSON encoded line per entry).'),
'parser' => array(
'arguments' => array(
'file' => ['help' => __('Path to output file'), 'required' => true],
),
),
]);
return $parser;
}
public function export()
{
list($path) = $this->args;
if (file_exists($path)) {
$this->error("File $path already exists");
}
$file = gzopen($path, 'wb4'); // Compression level 4 is best compromise between time and size
if ($file === false) {
$this->error("Could not open $path for writing");
}
$rows = $this->Log->query("SELECT TABLE_ROWS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'logs';");
/** @var ProgressShellHelper $progress */
$progress = $this->helper('progress');
$progress->init([
'total' => $rows[0]['TABLES']['TABLE_ROWS'], // just estimate, but fast
'width' => 50,
]);
$lastId = 0;
while (true) {
$logs = $this->Log->find('all', [
'conditions' => ['id >' => $lastId], // much faster than offset
'recursive' => -1,
'limit' => 100000,
'order' => ['id ASC'],
]);
if (empty($logs)) {
break;
}
$lines = '';
foreach ($logs as $log) {
$log = $log['Log'];
foreach (['id', 'model_id', 'user_id'] as $field) {
$log[$field] = (int)$log[$field]; // Convert to int to save space
}
if (empty($log['description'])) {
unset($log['description']);
}
if (empty($log['ip'])) {
unset($log['ip']);
}
$log['created'] = strtotime($log['created']); // to save space
if ($log['id'] > $lastId) {
$lastId = $log['id'];
}
$lines .= json_encode($log, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . "\n";
}
if (gzwrite($file, $lines) === false) {
$this->error("Could not write data to $path");
}
$progress->increment(count($logs));
$progress->draw();
}
gzclose($file);
$this->out('Done');
}
public function statistics()
{
$count = $this->Log->find('count');
$first = $this->Log->find('first', [
'recursive' => -1,
'fields' => ['created'],
'order' => ['id ASC'],
]);
$last = $this->Log->find('first', [
'recursive' => -1,
'fields' => ['created'],
'order' => ['id DESC'],
]);
$this->out(str_pad(__('Count:'), 20) . $count);
$this->out(str_pad(__('First:'), 20) . $first['Log']['created']);
$this->out(str_pad(__('Last:'), 20) . $last['Log']['created']);
$usage = $this->Server->dbSpaceUsage()['logs'];
$this->out(str_pad(__('Data size:'), 20) . CakeNumber::toReadableSize($usage['data_in_bytes']));
$this->out(str_pad(__('Index size:'), 20) . CakeNumber::toReadableSize($usage['index_in_bytes']));
$this->out(str_pad(__('Reclaimable size:'), 20) . CakeNumber::toReadableSize($usage['reclaimable_in_bytes']), 2);
}
public function auditStatistics()
{
$count = $this->AuditLog->find('count');
$first = $this->AuditLog->find('first', [
'recursive' => -1,
'fields' => ['created'],
'order' => ['id ASC'],
]);
$last = $this->AuditLog->find('first', [
'recursive' => -1,
'fields' => ['created'],
'order' => ['id DESC'],
]);
$this->out(str_pad(__('Count:'), 20) . $count);
$this->out(str_pad(__('First:'), 20) . $first['AuditLog']['created']);
$this->out(str_pad(__('Last:'), 20) . $last['AuditLog']['created']);
$usage = $this->Server->dbSpaceUsage()['audit_logs'];
$this->out(str_pad(__('Data size:'), 20) . CakeNumber::toReadableSize($usage['data_in_bytes']));
$this->out(str_pad(__('Index size:'), 20) . CakeNumber::toReadableSize($usage['index_in_bytes']));
$this->out(str_pad(__('Reclaimable size:'), 20) . CakeNumber::toReadableSize($usage['reclaimable_in_bytes']), 2);
// Just to fetch compressionStats
$this->AuditLog->find('column', [
'fields' => ['change'],
]);
$this->out('Change field:');
$this->out('-------------');
$this->out(str_pad(__('Compressed items:'), 20) . $this->AuditLog->compressionStats['compressed']);
$this->out(str_pad(__('Uncompressed size:'), 20) . CakeNumber::toReadableSize($this->AuditLog->compressionStats['bytes_uncompressed']));
$this->out(str_pad(__('Compressed size:'), 20) . CakeNumber::toReadableSize($this->AuditLog->compressionStats['bytes_compressed']));
}
}

View File

@ -21,6 +21,7 @@ class AllowedlistsController extends AppController
if (!$this->userRole['perm_regexp_access']) {
$this->redirect(array('controller' => 'regexp', 'action' => 'index', 'admin' => false));
}
$this->set('action', 'add');
$this->AdminCrud->adminAdd();
}
@ -30,6 +31,7 @@ class AllowedlistsController extends AppController
$this->redirect(array('controller' => 'allowedlists', 'action' => 'index', 'admin' => false));
}
$this->AdminCrud->adminIndex();
$this->render('index');
}
public function admin_edit($id = null)
@ -38,6 +40,9 @@ class AllowedlistsController extends AppController
$this->redirect(array('controller' => 'allowedlists', 'action' => 'index', 'admin' => false));
}
$this->AdminCrud->adminEdit($id);
$this->set('action', 'edit');
$this->set('id', $id);
$this->render('admin_add');
}
public function admin_delete($id = null)

View File

@ -26,7 +26,7 @@ class AppController extends Controller
public $helpers = array('OrgImg', 'FontAwesome', 'UserName', 'DataPathCollector');
private $__queryVersion = '129';
public $pyMispVersion = '2.4.142';
public $pyMispVersion = '2.4.143';
public $phpmin = '7.2';
public $phprec = '7.4';
public $phptoonew = '8.0';

View File

@ -1257,7 +1257,7 @@ class AttributesController extends AppController
// tags to remove
$tags = $this->Attribute->AttributeTag->getAttributesTags($attributes);
$tagItemsRemove = array();
foreach ($tags as $k => $tag) {
foreach ($tags as $tag) {
$tagName = $tag['name'];
$tagItemsRemove[] = array(
'name' => $tagName,
@ -1275,9 +1275,9 @@ class AttributesController extends AppController
unset($tags);
// clusters to remove
$clusters = $this->Attribute->AttributeTag->getAttributesClusters($attributes);
$clusters = $this->Attribute->AttributeTag->getAttributesClusters($this->Auth->user(), $attributes);
$clusterItemsRemove = array();
foreach ($clusters as $k => $cluster) {
foreach ($clusters as $cluster) {
$name = $cluster['value'];
$optionName = $cluster['value'];
$synom = $cluster['synonyms_string'] !== '' ? " ({$cluster['synonyms_string']})" : '';
@ -1304,7 +1304,7 @@ class AttributesController extends AppController
'conditions' => array('published' => true)
));
$clusterItemsAdd = array();
foreach ($clusters as $k => $cluster) {
foreach ($clusters as $cluster) {
$clusterItemsAdd[] = array(
'name' => $cluster['GalaxyCluster']['value'],
'value' => $cluster['GalaxyCluster']['id']
@ -1346,6 +1346,7 @@ class AttributesController extends AppController
$this->set('clusterItemsRemove', $clusterItemsRemove);
$this->set('options', array( // set chosen (select picker) options
'multiple' => -1,
'autofocus' => false,
'disabledSubmitButton' => true,
'flag_redraw_chosen' => true,
'select_options' => array(
@ -2963,7 +2964,7 @@ class AttributesController extends AppController
if ($attribute['Attribute']['disable_correlation']) {
$attribute['Attribute']['disable_correlation'] = 0;
$this->Attribute->save($attribute);
$this->Attribute->__afterSaveCorrelation($attribute['Attribute'], false, $attribute);
ClassRegistry::init('Correlation')->afterSaveCorrelation($attribute['Attribute'], false, $attribute);
} else {
$attribute['Attribute']['disable_correlation'] = 1;
$this->Attribute->save($attribute);

View File

@ -0,0 +1,586 @@
<?php
App::uses('AppController', 'Controller');
App::uses('AuditLog', 'Model');
/**
* @property AuditLog $AuditLog
*/
class AuditLogsController extends AppController
{
public $components = [
'Security',
'RequestHandler',
];
/** @var array */
private $actions;
/** @var string[] */
private $models = [
'AdminSetting',
'Attribute',
'Allowedlist',
'AuthKey',
'Cerebrate',
'CorrelationExclusion',
'Event',
'EventBlocklist',
'Feed',
'DecayingModel',
'Object',
'ObjectTemplate',
'Organisation',
'OrgBlocklist',
'Post',
'Regexp',
'Role',
'Server',
'ShadowAttribute',
'SharingGroup',
'Tag',
'TagCollection',
'TagCollectionTag',
'Task',
'Taxonomy',
'Template',
'Thread',
'User',
'UserSetting',
'Galaxy',
'GalaxyCluster',
'GalaxyClusterBlocklist',
'GalaxyClusterRelation',
'News',
'Warninglist',
];
public $paginate = [
'recursive' => -1,
'limit' => 60,
'fields' => ['id', 'created', 'user_id', 'org_id', 'action', 'model', 'model_id', 'model_title', 'event_id', 'change'],
'contain' => [
'User' => ['fields' => ['id', 'email', 'org_id']],
'Organisation' => ['fields' => ['id', 'name', 'uuid']],
],
'order' => [
'AuditLog.id' => 'DESC'
],
];
public function __construct($id = false, $table = null, $ds = null)
{
parent::__construct($id, $table, $ds);
$this->actions = [
AuditLog::ACTION_ADD => __('Add'),
AuditLog::ACTION_EDIT => __('Edit'),
AuditLog::ACTION_SOFT_DELETE => __('Soft delete'),
AuditLog::ACTION_DELETE => __('Delete'),
AuditLog::ACTION_UNDELETE => __('Undelete'),
AuditLog::ACTION_TAG => __('Tag'),
AuditLog::ACTION_TAG_LOCAL => __('Tag'),
AuditLog::ACTION_REMOVE_TAG => __('Remove tag'),
AuditLog::ACTION_REMOVE_TAG_LOCAL => __('Remove tag'),
AuditLog::ACTION_GALAXY => __('Galaxy cluster'),
AuditLog::ACTION_GALAXY_LOCAL => __('Galaxy cluster'),
AuditLog::ACTION_REMOVE_GALAXY => __('Remove galaxy cluster'),
AuditLog::ACTION_REMOVE_GALAXY_LOCAL => __('Remove galaxy cluster'),
AuditLog::ACTION_PUBLISH => __('Publish'),
AuditLog::ACTION_PUBLISH_SIGHTINGS => __('Publish sightings'),
];
}
public function admin_index()
{
$this->paginate['fields'][] = 'ip';
$this->paginate['fields'][] = 'request_type';
$this->paginate['fields'][] = 'authkey_id';
if ($this->_isRest()) {
$this->paginate['fields'][] = 'request_id';
}
$this->paginate['conditions'] = $this->__searchConditions();
$list = $this->paginate();
if ($this->_isRest()) {
return $this->RestResponse->viewData($list, 'json');
}
$list = $this->__appendModelLinks($list);
foreach ($list as $k => $item) {
$list[$k]['AuditLog']['action_human'] = $this->actions[$item['AuditLog']['action']];
}
$this->set('list', $list);
$this->set('actions', [
AuditLog::ACTION_ADD => __('Add'),
AuditLog::ACTION_EDIT => __('Edit'),
AuditLog::ACTION_SOFT_DELETE => __('Soft delete'),
AuditLog::ACTION_DELETE => __('Delete'),
AuditLog::ACTION_UNDELETE => __('Undelete'),
AuditLog::ACTION_TAG . '||' . AuditLog::ACTION_TAG_LOCAL => __('Tag'),
AuditLog::ACTION_REMOVE_TAG . '||' . AuditLog::ACTION_REMOVE_TAG_LOCAL => __('Remove tag'),
AuditLog::ACTION_GALAXY . '||' . AuditLog::ACTION_GALAXY_LOCAL => __('Galaxy cluster'),
AuditLog::ACTION_REMOVE_GALAXY . '||' . AuditLog::ACTION_REMOVE_GALAXY_LOCAL => __('Remove galaxy cluster'),
AuditLog::ACTION_PUBLISH => __('Publish'),
AuditLog::ACTION_PUBLISH_SIGHTINGS => $this->actions[AuditLog::ACTION_PUBLISH_SIGHTINGS],
]);
$models = $this->models;
sort($models);
$this->set('models', $models);
$this->set('title_for_layout', __('Audit logs'));
}
public function eventIndex($eventId, $org = null)
{
$this->loadModel('Event');
$event = $this->Event->fetchSimpleEvent($this->Auth->user(), $eventId);
if (empty($event)) {
throw new NotFoundException('Invalid event.');
}
$this->paginate['conditions'] = $this->__createEventIndexConditions($event);
if ($org) {
$org = $this->AuditLog->Organisation->fetchOrg($org);
if ($org) {
$this->paginate['conditions']['AND']['org_id'] = $org['id'];
} else {
$this->paginate['conditions']['AND']['org_id'] = -1;
}
}
$list = $this->paginate();
if (!$this->_isSiteAdmin()) {
// Remove all user info about users from different org
$this->loadModel('User');
$orgUserIds = $this->User->find('column', array(
'conditions' => ['User.org_id' => $this->Auth->user('org_id')],
'fields' => ['User.id'],
));
foreach ($list as $k => $item) {
if ($item['AuditLog']['user_id'] == 0) {
continue;
}
if (!in_array($item['User']['id'], $orgUserIds)) {
unset($list[$k]['User']);
unset($list[$k]['AuditLog']['user_id']);
}
}
}
if ($this->_isRest()) {
return $this->RestResponse->viewData($list, 'json');
}
foreach ($list as $k => $item) {
$list[$k]['AuditLog']['action_human'] = $this->actions[$item['AuditLog']['action']];
}
$this->set('list', $list);
$this->set('event', $event);
$this->set('mayModify', $this->__canModifyEvent($event));
$this->set('title_for_layout', __('Audit logs for event #%s', $event['Event']['id']));
}
public function fullChange($id)
{
$log = $this->AuditLog->find('first', [
'conditions' => ['id' => $id],
'recursive' => -1,
'fields' => ['change', 'action'],
]);
if (empty($log)) {
throw new Exception('Log not found.');
}
$this->set('log', $log);
}
public function returnDates($org = 'all')
{
if (!$this->Auth->user('Role')['perm_sharing_group'] && !empty(Configure::read('Security.hide_organisation_index_from_users'))) {
if ($org !== 'all' && $org !== $this->Auth->user('Organisation')['name']) {
throw new MethodNotAllowedException('Invalid organisation.');
}
}
// Fetching dates can be slow, so to allow concurrent requests, we can close sessions to release session lock
session_write_close();
$data = $this->AuditLog->returnDates($org);
return $this->RestResponse->viewData($data, $this->response->type());
}
/**
* @return array
*/
private function __searchConditions()
{
$params = $this->IndexFilter->harvestParameters([
'ip',
'user',
'request_id',
'authkey_id',
'model',
'model_id',
'event_id',
'model_title',
'action',
'org',
'created',
'request_type',
]);
$qbRules = [];
foreach ($params as $key => $value) {
if ($key === 'model' && strpos($value, ':') !== false) {
$parts = explode(':', $value);
$qbRules[] = [
'id' => 'model',
'value' => $parts[0],
];
$qbRules[] = [
'id' => 'model_id',
'value' => $parts[1],
];
} elseif ($key === 'created') {
$qbRules[] = [
'id' => $key,
'operator' => is_array($value) ? 'between' : 'greater_or_equal',
'value' => $value,
];
} else {
if (is_array($value)) {
$value = implode('||', $value);
}
$qbRules[] = [
'id' => $key,
'value' => $value,
];
}
}
$this->set('qbRules', $qbRules);
$conditions = [];
if (isset($params['user'])) {
if (strtoupper($params['user']) === 'SYSTEM') {
$conditions['AuditLog.user_id'] = 0;
} else if (is_numeric($params['user'])) {
$conditions['AuditLog.user_id'] = $params['user'];
} else {
$user = $this->User->find('first', [
'conditions' => ['User.email' => $params['user']],
'fields' => ['id'],
]);
if (!empty($user)) {
$conditions['AuditLog.user_id'] = $user['User']['id'];
} else {
$conditions['AuditLog.user_id'] = -1;
}
}
}
if (isset($params['ip'])) {
$conditions['AuditLog.ip'] = inet_pton($params['ip']);
}
if (isset($params['authkey_id'])) {
$conditions['AuditLog.authkey_id'] = $params['authkey_id'];
}
if (isset($params['request_id'])) {
$conditions['AuditLog.request_id'] = $params['request_id'];
}
if (isset($params['request_type'])) {
$conditions['AuditLog.request_type'] = $params['request_type'];
}
if (isset($params['model'])) {
$conditions['AuditLog.model'] = $params['model'];
}
if (isset($params['model_id'])) {
$conditions['AuditLog.model_id'] = $params['model_id'];
}
if (isset($params['event_id'])) {
$conditions['AuditLog.event_id'] = $params['event_id'];
}
if (isset($params['model_title'])) {
$conditions['AuditLog.model_title LIKE'] = '%' . $params['model_title'] . '%';
}
if (isset($params['action'])) {
$conditions['AuditLog.action'] = $params['action'];
}
if (isset($params['org'])) {
if (is_numeric($params['org'])) {
$conditions['AuditLog.org_id'] = $params['org'];
} else {
$org = $this->AuditLog->Organisation->fetchOrg($params['org']);
if ($org) {
$conditions['AuditLog.org_id'] = $org['id'];
} else {
$conditions['AuditLog.org_id'] = -1;
}
}
}
if (isset($params['created'])) {
$tempData = is_array($params['created']) ? $params['created'] : [$params['created']];
foreach ($tempData as $k => $v) {
$tempData[$k] = $this->AuditLog->resolveTimeDelta($v);
}
if (count($tempData) === 1) {
$conditions['AuditLog.created >='] = date("Y-m-d H:i:s", $tempData[0]);
} else {
if ($tempData[0] < $tempData[1]) {
$temp = $tempData[1];
$tempData[1] = $tempData[0];
$tempData[0] = $temp;
}
$conditions['AND'][] = ['AuditLog.created <=' => date("Y-m-d H:i:s", $tempData[0])];
$conditions['AND'][] = ['AuditLog.created >=' => date("Y-m-d H:i:s", $tempData[1])];
}
}
return $conditions;
}
/**
* Create conditions that will include just events parts that user can see.
* @param array $event
* @return array
*/
private function __createEventIndexConditions(array $event)
{
if ($this->_isSiteAdmin() || $event['Event']['orgc_id'] == $this->Auth->user('org_id')) {
// Site admins and event owners can see all changes
return ['event_id' => $event['Event']['id']];
}
$event = $this->Event->fetchEvent($this->Auth->user(), [
'eventid' => $event['Event']['id'],
'sgReferenceOnly' => 1,
'deleted' => [0, 1],
'deleted_proposals' => 1,
'noSightings' => true,
'includeEventCorrelations' => false,
'excludeGalaxy' => true,
])[0];
$attributeIds = [];
$objectIds = [];
$proposalIds = array_column($event['ShadowAttribute'], 'id');
$objectReferenceId = [];
foreach ($event['Attribute'] as $aa) {
$attributeIds[] = $aa['id'];
if (!empty($aa['ShadowAttribute'])) {
foreach ($aa['ShadowAttribute'] as $sa) {
$proposalIds[] = $sa['id'];
}
}
}
unset($event['Attribute']);
foreach ($event['Object'] as $ob) {
foreach ($ob['Attribute'] as $aa) {
$attributeIds[] = $aa['id'];
if (!empty($aa['ShadowAttribute'])) {
foreach ($aa['ShadowAttribute'] as $sa) {
$proposalIds[] = $sa['id'];
}
}
}
foreach ($ob['ObjectReference'] as $or) {
$objectReferenceId[] = $or['id'];
}
$objectIds[] = $ob['id'];
}
unset($event['Object']);
$conditions = [];
$conditions['AND']['event_id'] = $event['Event']['id'];
$conditions['AND']['OR'][] = ['model' => 'Event'];
$parts = [
'Attribute' => $attributeIds,
'ShadowAttribute' => $proposalIds,
'Object' => $objectIds,
'ObjectReference' => $objectReferenceId,
'EventReport' => array_column($event['EventReport'], 'id'),
];
foreach ($parts as $model => $modelIds) {
if (!empty($modelIds)) {
$conditions['AND']['OR'][] = [
'AND' => [
'model' => $model,
'model_id' => $modelIds,
],
];
}
}
return $conditions;
}
/**
* Generate link to model view if exists and use has permission to access it.
* @param array $auditLogs
* @return array
*/
private function __appendModelLinks(array $auditLogs)
{
$models = [];
foreach ($auditLogs as $auditLog) {
if (isset($models[$auditLog['AuditLog']['model']])) {
$models[$auditLog['AuditLog']['model']][] = $auditLog['AuditLog']['model_id'];
} else {
$models[$auditLog['AuditLog']['model']] = [$auditLog['AuditLog']['model_id']];
}
}
$eventIds = isset($models['Event']) ? $models['Event'] : [];
if (isset($models['ObjectReference'])) {
$this->loadModel('ObjectReference');
$objectReferences = $this->ObjectReference->find('list', [
'conditions' => ['ObjectReference.id' => array_unique($models['ObjectReference'])],
'fields' => ['ObjectReference.id', 'ObjectReference.object_id'],
]);
}
if (isset($models['Object']) || isset($objectReferences)) {
$objectIds = array_unique(array_merge(
isset($models['Object']) ? $models['Object'] : [],
isset($objectReferences) ? array_values($objectReferences) : []
));
$this->loadModel('MispObject');
$conditions = $this->MispObject->buildConditions($this->Auth->user());
$conditions['Object.id'] = $objectIds;
$objects = $this->MispObject->find('all', [
'conditions' => $conditions,
'contain' => ['Event'],
'fields' => ['Object.id', 'Object.event_id', 'Object.uuid', 'Object.deleted'],
]);
$objects = array_column(array_column($objects, 'Object'), null, 'id');
$eventIds = array_merge($eventIds, array_column($objects, 'event_id'));
}
if (isset($models['Attribute'])) {
$this->loadModel('Attribute');
$attributes = $this->Attribute->fetchAttributesSimple($this->Auth->user(), [
'conditions' => ['Attribute.id' => array_unique($models['Attribute'])],
'fields' => ['Attribute.id', 'Attribute.event_id', 'Attribute.uuid', 'Attribute.deleted'],
]);
$attributes = array_column(array_column($attributes, 'Attribute'), null, 'id');
$eventIds = array_merge($eventIds, array_column($attributes, 'event_id'));
}
if (isset($models['ShadowAttribute'])) {
$this->loadModel('ShadowAttribute');
$conditions = $this->ShadowAttribute->buildConditions($this->Auth->user());
$conditions['AND'][] = ['ShadowAttribute.id' => array_unique($models['ShadowAttribute'])];
$shadowAttributes = $this->ShadowAttribute->find('all', [
'conditions' => $conditions,
'fields' => ['ShadowAttribute.id', 'ShadowAttribute.event_id', 'ShadowAttribute.uuid', 'ShadowAttribute.deleted'],
'contain' => ['Event', 'Attribute'],
]);
$shadowAttributes = array_column(array_column($shadowAttributes, 'ShadowAttribute'), null, 'id');
$eventIds = array_merge($eventIds, array_column($shadowAttributes, 'event_id'));
}
if (!empty($eventIds)) {
$this->loadModel('Event');
$events = $this->Event->fetchSimpleEvents($this->Auth->user(), [
'conditions' => ['Event.id' => array_unique($eventIds)],
]);
$events = array_column(array_column($events, 'Event'), null, 'id');
}
$existingObjects = [];
foreach (['User', 'Organisation', 'Galaxy', 'GalaxyCluster', 'Warninglist', 'AuthKey', 'ObjectTemplate', 'Role'] as $modelName) {
if (isset($models[$modelName])) {
$this->loadModel($modelName);
$data = $this->{$modelName}->find('column', [
'conditions' => ['id' => array_unique($models[$modelName])],
'fields' => ['id'],
]);
$existingObjects[$modelName] = array_flip($data);
}
}
$links = [
'ObjectTemplate' => 'objectTemplates',
'AuthKey' => 'auth_keys',
'GalaxyCluster' => 'galaxy_clusters',
'Galaxy' => 'galaxies',
'Organisation' => 'organisation',
'Warninglist' => 'warninglists',
'User' => 'admin/user',
'Role' => 'roles',
];
foreach ($auditLogs as $k => $auditLog) {
$auditLog = $auditLog['AuditLog'];
$modelId = (int)$auditLog['model_id'];
$url = null;
$eventInfo = null;
switch ($auditLog['model']) {
case 'Event':
if (isset($events[$modelId])) {
$url = '/events/view/' . $modelId;
$eventInfo = $events[$modelId]['info'];
}
break;
case 'ObjectReference':
if (isset($objectReferences[$modelId]) && isset($objects[$objectReferences[$modelId]])) {
$url = '/events/view/' . $objects[$objectReferences[$modelId]]['event_id'] . '/focus:' . $objects[$objectReferences[$modelId]]['uuid'];
if ($objects[$objectReferences[$modelId]]['deleted']) {
$url .= '/deleted:2';
}
if (isset($events[$objects[$objectReferences[$modelId]]['event_id']])) {
$eventInfo = $events[$objects[$objectReferences[$modelId]]['event_id']]['info'];
}
}
break;
case 'Object':
if (isset($objects[$modelId])) {
$url = '/events/view/' . $objects[$modelId]['event_id'] . '/focus:' . $objects[$modelId]['uuid'];
if ($objects[$modelId]['deleted']) {
$url .= '/deleted:2';
}
if (isset($events[$objects[$modelId]['event_id']])) {
$eventInfo = $events[$objects[$modelId]['event_id']]['info'];
}
}
break;
case 'Attribute':
if (isset($attributes[$modelId])) {
$url = '/events/view/' . $attributes[$modelId]['event_id'] . '/focus:' . $attributes[$modelId]['uuid'];
if ($attributes[$modelId]['deleted']) {
$url .= '/deleted:2';
}
if (isset($events[$attributes[$modelId]['event_id']])) {
$eventInfo = $events[$attributes[$modelId]['event_id']]['info'];
}
}
break;
case 'ShadowAttribute':
if (isset($shadowAttributes[$modelId])) {
$url = '/events/view/' . $shadowAttributes[$modelId]['event_id'] . '/focus:' . $shadowAttributes[$modelId]['uuid'];
if (isset($events[$shadowAttributes[$modelId]['event_id']])) {
$eventInfo = $events[$shadowAttributes[$modelId]['event_id']]['info'];
}
}
break;
default:
if (isset($existingObjects[$auditLog['model']][$modelId])) {
$url = '/' . $links[$auditLog['model']] . '/view/' . $modelId;
} else {
continue 2;
}
}
if ($url) {
$auditLogs[$k]['AuditLog']['model_link'] = $this->baseurl . $url;
}
if ($eventInfo) {
$auditLogs[$k]['AuditLog']['event_info'] = $eventInfo;
}
}
return $auditLogs;
}
}

View File

@ -377,6 +377,12 @@ class ACLComponent extends Component
'testForStolenAttributes' => array(),
'pruneUpdateLogs' => array()
),
'auditLogs' => [
'admin_index' => ['perm_audit'],
'fullChange' => ['perm_audit'],
'eventIndex' => ['*'],
'returnDates' => ['*'],
],
'modules' => array(
'index' => array('perm_auth'),
'queryEnrichment' => array('perm_auth'),
@ -677,6 +683,7 @@ class ACLComponent extends Component
'admin_email' => array('perm_admin'),
'admin_filterUserIndex' => array('perm_admin'),
'admin_index' => array('perm_admin'),
'admin_massToggleField' => array('perm_admin'),
'admin_monitor' => array('perm_site_admin'),
'admin_quickEmail' => array('perm_admin'),
'admin_view' => array('perm_admin'),

View File

@ -610,11 +610,14 @@ class RestResponseComponent extends Component
}
/**
* Detect if request comes from automatic tool, like other MISP instance or PyMISP
* Detect if request comes from automatic tool (like other MISP instance or PyMISP) or AJAX
* @return bool
*/
public function isAutomaticTool()
{
if ($this->Controller->request->is('ajax')) {
return true;
}
$userAgent = CakeRequest::header('User-Agent');
return $userAgent && (substr($userAgent, 0, 6) === 'PyMISP' || substr($userAgent, 0, 4) === 'MISP');
}

View File

@ -863,6 +863,10 @@ class EventsController extends AppController
$possibleColumns[] = 'proposals';
}
if (Configure::read('MISP.showEventReportCountOnIndex')) {
$possibleColumns[] = 'report_count';
}
if (Configure::read('MISP.showDiscussionsCountOnIndex')) {
$possibleColumns[] = 'discussion';
}
@ -906,6 +910,10 @@ class EventsController extends AppController
$events = $this->Event->attachDiscussionsCountToEvents($user, $events);
}
if (in_array('report_count', $columns, true)) {
$events = $this->Event->EventReport->attachReportCountsToEvents($user, $events);
}
return $events;
}
@ -1151,7 +1159,17 @@ class EventsController extends AppController
$this->set('includeDecayScore', 0);
}
$results = $this->Event->fetchEvent($this->Auth->user(), $conditions);
// Site admin can view event as different user
if ($this->_isSiteAdmin() && isset($this->params['named']['viewAs'])) {
$user = $this->User->getAuthUser($this->params['named']['viewAs']);
if (empty($user)) {
throw new NotFoundException(__("User not found"));
}
} else {
$user = $this->Auth->user();
}
$results = $this->Event->fetchEvent($user, $conditions);
if (empty($results)) {
throw new NotFoundException(__('Invalid event'));
}
@ -1300,11 +1318,12 @@ class EventsController extends AppController
}
/**
* @param array $user
* @param array $event
* @param bool $continue
* @param int $fromEvent
*/
private function __viewUI($event, $continue, $fromEvent)
private function __viewUI(array $user, $event, $continue, $fromEvent)
{
$this->loadModel('Taxonomy');
$filterData = array(
@ -1338,7 +1357,7 @@ class EventsController extends AppController
// set the data for the contributors / history field
$contributors = $this->Event->ShadowAttribute->getEventContributors($event['Event']['id']);
$this->set('contributors', $contributors);
if ($this->userRole['perm_publish'] && $event['Event']['orgc_id'] == $this->Auth->user('org_id')) {
if ($user['Role']['perm_publish'] && $event['Event']['orgc_id'] == $user['org_id']) {
$proposalStatus = false;
if (isset($event['ShadowAttribute']) && !empty($event['ShadowAttribute'])) {
$proposalStatus = true;
@ -1482,12 +1501,12 @@ class EventsController extends AppController
}
unset($modificationMap);
$this->loadModel('Sighting');
$sightingsData = $this->Sighting->eventsStatistic([$event], $this->Auth->user());
$sightingsData = $this->Sighting->eventsStatistic([$event], $user);
$this->set('sightingsData', $sightingsData);
$params = $this->Event->rearrangeEventForView($event, $filters, false, $sightingsData);
if (!empty($filters['includeSightingdb']) && Configure::read('Plugin.Sightings_sighting_db_enable')) {
$this->loadModel('Sightingdb');
$event = $this->Sightingdb->attachToEvent($event, $this->Auth->user());
$event = $this->Sightingdb->attachToEvent($event, $user);
}
$this->params->params['paging'] = array($this->modelClass => $params);
$this->set('event', $event);
@ -1496,10 +1515,10 @@ class EventsController extends AppController
'Event.extends_uuid' => $event['Event']['uuid']
)
);
$extensions = $this->Event->fetchSimpleEvents($this->Auth->user(), $extensionParams);
$extensions = $this->Event->fetchSimpleEvents($user, $extensionParams);
$this->set('extensions', $extensions);
if (!empty($event['Event']['extends_uuid'])) {
$extendedEvent = $this->Event->fetchSimpleEvents($this->Auth->user(), array('conditions' => array('Event.uuid' => $event['Event']['extends_uuid'])));
$extendedEvent = $this->Event->fetchSimpleEvents($user, array('conditions' => array('Event.uuid' => $event['Event']['extends_uuid'])));
if (empty($extendedEvent)) {
$extendedEvent = $event['Event']['extends_uuid'];
}
@ -1509,8 +1528,8 @@ class EventsController extends AppController
$this->loadModel('EventDelegation');
$delegationConditions = array('EventDelegation.event_id' => $event['Event']['id']);
if (!$this->_isSiteAdmin() && $this->userRole['perm_publish']) {
$delegationConditions['OR'] = array('EventDelegation.org_id' => $this->Auth->user('org_id'),
'EventDelegation.requester_org_id' => $this->Auth->user('org_id'));
$delegationConditions['OR'] = array('EventDelegation.org_id' => $user['org_id'],
'EventDelegation.requester_org_id' => $user['org_id']);
}
$this->set('delegationRequest', $this->EventDelegation->find('first', array(
'conditions' => $delegationConditions,
@ -1520,11 +1539,11 @@ class EventsController extends AppController
}
if (Configure::read('Plugin.Enrichment_services_enable')) {
$this->loadModel('Module');
$modules = $this->Module->getEnabledModules($this->Auth->user());
$modules = $this->Module->getEnabledModules($user);
if (is_array($modules)) {
foreach ($modules as $k => $v) {
if (isset($v['restrict'])) {
if ($this->_isSiteAdmin() && $v['restrict'] != $this->Auth->user('org_id')) {
if ($this->_isSiteAdmin() && $v['restrict'] != $user['org_id']) {
unset($modules[$k]);
}
}
@ -1534,7 +1553,7 @@ class EventsController extends AppController
}
if (Configure::read('Plugin.Cortex_services_enable')) {
$this->loadModel('Module');
$cortex_modules = $this->Module->getEnabledModules($this->Auth->user(), false, 'Cortex');
$cortex_modules = $this->Module->getEnabledModules($user, false, 'Cortex');
$this->set('cortex_modules', $cortex_modules);
}
$this->set('typeGroups', array_keys($this->Event->Attribute->typeGroupings));
@ -1554,7 +1573,7 @@ class EventsController extends AppController
'fields' => array('Orgc.id', 'Orgc.name')
));
if (!empty($filters['includeSightingdb']) && Configure::read('Plugin.Sightings_sighting_db_enable')) {
$this->set('sightingdbs', $this->Sightingdb->getSightingdbList($this->Auth->user()));
$this->set('sightingdbs', $this->Sightingdb->getSightingdbList($user));
}
$this->set('includeSightingdb', (!empty($filters['includeSightingdb']) && Configure::read('Plugin.Sightings_sighting_db_enable')));
$this->set('relatedEventCorrelationCount', $relatedEventCorrelationCount);
@ -1655,7 +1674,19 @@ class EventsController extends AppController
} else {
$conditions['includeServerCorrelations'] = $this->params['named']['includeServerCorrelations'];
}
$results = $this->Event->fetchEvent($this->Auth->user(), $conditions);
// Site admin can view event as different user
if ($this->_isSiteAdmin() && isset($this->params['named']['viewAs'])) {
$user = $this->User->getAuthUser($this->params['named']['viewAs']);
if (empty($user)) {
throw new NotFoundException(__("User not found"));
}
$this->Flash->info(__('Viewing event as %s from %s', h($user['email']), h($user['Organisation']['name'])));
} else {
$user = $this->Auth->user();
}
$results = $this->Event->fetchEvent($user, $conditions);
if (empty($results)) {
throw new NotFoundException(__('Invalid event'));
}
@ -1703,7 +1734,7 @@ class EventsController extends AppController
if ($this->_isSiteAdmin() && $event['Event']['orgc_id'] !== $this->Auth->user('org_id')) {
$this->Flash->info(__('You are currently logged in as a site administrator and about to edit an event not belonging to your organisation. This goes against the sharing model of MISP. Use a normal user account for day to day work.'));
}
$this->__viewUI($event, $continue, $fromEvent);
$this->__viewUI($user, $event, $continue, $fromEvent);
}
/**
@ -3280,41 +3311,40 @@ class EventsController extends AppController
public function proposalEventIndex()
{
$this->loadModel('ShadowAttribute');
$this->ShadowAttribute->recursive = -1;
$conditions = array('ShadowAttribute.deleted' => 0);
if (!$this->_isSiteAdmin()) {
$conditions[] = array('ShadowAttribute.event_org_id' => $this->Auth->user('org_id'));
}
$result = $this->ShadowAttribute->find('all', array(
'fields' => array('event_id'),
'group' => array('event_id', 'id'),
'conditions' => $conditions
));
$result = $this->ShadowAttribute->find('column', [
'fields' => ['event_id'],
'conditions' => $conditions,
'unique' => true,
]);
$this->Event->recursive = -1;
$conditions = array();
foreach ($result as $eventId) {
$conditions['OR'][] = array('Event.id =' => $eventId['ShadowAttribute']['event_id']);
}
if (empty($result)) {
$conditions['OR'][] = array('Event.id =' => -1);
$conditions = array('Event.id' => -1);
} else {
$conditions = array('Event.id' => $result);
}
$this->paginate = array(
'fields' => array('Event.id', 'Event.org_id', 'Event.orgc_id', 'Event.publish_timestamp', 'Event.distribution', 'Event.info', 'Event.date', 'Event.published'),
'conditions' => $conditions,
'contain' => array(
'User' => array(
'fields' => array(
'User.email'
)),
'ShadowAttribute'=> array(
'fields' => array(
'ShadowAttribute.id', 'ShadowAttribute.org_id', 'ShadowAttribute.event_id'
),
'conditions' => array(
'ShadowAttribute.deleted' => 0
),
'fields' => array('Event.id', 'Event.org_id', 'Event.orgc_id', 'Event.publish_timestamp', 'Event.distribution', 'Event.info', 'Event.date', 'Event.published'),
'conditions' => $conditions,
'contain' => array(
'User' => array(
'fields' => array(
'User.email'
)),
'ShadowAttribute'=> array(
'fields' => array(
'ShadowAttribute.id', 'ShadowAttribute.org_id', 'ShadowAttribute.event_id'
),
));
'conditions' => array(
'ShadowAttribute.deleted' => 0
),
),
)
);
$events = $this->paginate();
$orgIds = array();
foreach ($events as $k => $event) {
@ -5455,10 +5485,10 @@ class EventsController extends AppController
$editors = array_unique($editors);
if ($event['Event']['timestamp'] > $timestamp && empty($editors)) {
$message = __('<b>Warning<b>: This event view is outdated. Please reload page to see latest changes.');
$message = __('<b>Warning</b>: This event view is outdated. Please reload page to see latest changes.');
$this->set('class', 'alert');
} else if ($event['Event']['timestamp'] > $timestamp) {
$message = __('<b>Warning<b>: This event view is outdated, because is currently being edited by: %s. Please reload page to see latest changes.', h(implode(', ', $editors)));
$message = __('<b>Warning</b>: This event view is outdated, because is currently being edited by: %s. Please reload page to see latest changes.', h(implode(', ', $editors)));
$this->set('class', 'alert');
} else if (empty($editors)) {
return new CakeResponse(['status' => 204]);

View File

@ -118,6 +118,8 @@ class LogsController extends AppController
'deleted_proposals' => 1,
'noSightings' => true,
'noEventReports' => true,
'includeEventCorrelations' => false,
'excludeGalaxy' => true,
));
if (empty($event)) {
throw new NotFoundException('Invalid event.');
@ -125,7 +127,7 @@ class LogsController extends AppController
$event = $event[0];
$attribute_ids = array();
$object_ids = array();
$proposal_ids = array();
$proposal_ids = array_column($event['ShadowAttribute'], 'id');;
if (!empty($event['Attribute'])) {
foreach ($event['Attribute'] as $aa) {
$attribute_ids[] = $aa['id'];

View File

@ -159,6 +159,7 @@ class OrganisationsController extends AppController
}
$countries = array_merge(['' => __('Not specified')], $this->_arrayToValuesIndexArray($this->Organisation->getCountries()));
$this->set('countries', $countries);
$this->set('action', 'add');
}
public function admin_edit($id)
@ -183,7 +184,7 @@ class OrganisationsController extends AppController
$this->request->data['Organisation'] = $this->request->data;
}
$existingOrg = $this->Organisation->find('first', array('conditions' => array('Organisation.id' => $id)));
$changeFields = array('name', 'type', 'nationality', 'sector', 'contacts', 'description', 'local', 'uuid');
$changeFields = array('name', 'type', 'nationality', 'sector', 'contacts', 'description', 'local', 'uuid', 'restricted_to_domain');
$temp = array('Organisation' => array());
foreach ($changeFields as $field) {
if (isset($this->request->data['Organisation'][$field])) {
@ -245,6 +246,8 @@ class OrganisationsController extends AppController
$this->request->data['Organisation']['restricted_to_domain'] = implode("\n", $this->request->data['Organisation']['restricted_to_domain']);
}
$this->set('id', $id);
$this->set('action', 'edit');
$this->render('admin_add');
}
public function admin_delete($id)

View File

@ -455,14 +455,14 @@ class ServersController extends AppController
$this->redirect(array('controller' => 'servers', 'action' => 'index'));
}
if ($this->request->is('post') || $this->request->is('put')) {
if (empty(Configure::read('MISP.host_org_id'))) {
$this->request->data['Server']['internal'] = 0;
}
if ($this->_isRest()) {
if (!isset($this->request->data['Server'])) {
$this->request->data = array('Server' => $this->request->data);
}
}
if (empty(Configure::read('MISP.host_org_id'))) {
$this->request->data['Server']['internal'] = 0;
}
if (isset($this->request->data['Server']['json'])) {
$json = json_decode($this->request->data['Server']['json'], true);
} else {

View File

@ -28,6 +28,8 @@ class UsersController extends AppController
public $helpers = array('Js' => array('Jquery'));
public $toggleableFields = ['disabled', 'autoalert'];
public function beforeFilter()
{
parent::beforeFilter();
@ -473,11 +475,6 @@ class UsersController extends AppController
}
$this->set('users', $users);
}
if ($this->request->is('ajax')) {
$this->autoRender = false;
$this->layout = false;
$this->render('ajax/admin_index');
}
}
}
@ -1095,6 +1092,54 @@ class UsersController extends AppController
$this->redirect(array('action' => 'index'));
}
public function admin_massToggleField($fieldName, $enabled)
{
if (!in_array($fieldName, $this->toggleableFields)) {
throw new MethodNotAllowedException(__('The field `%s` cannot be toggled', $fieldName));
}
if (!$this->_isAdmin()) {
throw new UnauthorizedException(__('Administrators only'));
}
if ($this->request->is('post') || $this->request->is('put')) {
$jsonIds = $this->request->data['User']['user_ids'];
$ids = $this->User->jsonDecode($jsonIds);
$conditions = ['User.id' => $ids];
if (!$this->_isSiteAdmin()) {
$conditions['User.org_id'] = $this->Auth->user('org_id');
}
$users = $this->User->find('all', [
'conditions' => $conditions,
'recursive' => -1
]);
if (empty($users)) {
throw new NotFoundException(__('Invalid users'));
}
$count = 0;
foreach ($users as $user) {
if ($user['User'][$fieldName] != $enabled) {
$this->User->id = $user['User']['id'];
$this->User->saveField($fieldName, $enabled);
$count++;
}
}
if ($count > 0) {
$message = __('%s users got their field `%s` %s', $count, $fieldName, $enabled ? __('enabled') : __('disabled'));
} else {
$message = __('All users have already their field `%s` %s', $fieldName, $enabled ? __('enabled') : __('disabled'));
}
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('User', 'admin_massToggleField', 'selected', $this->response->type(), $message);
} else {
if ($count > 0) {
$this->Flash->success($message);
} else {
$this->Flash->info($message);
}
$this->redirect('/admin/users/index');
}
}
}
public function updateLoginTime()
{
if (!$this->request->is('post')) {
@ -1245,9 +1290,9 @@ class UsersController extends AppController
),
'recursive' => -1
));
$lastUserLogin = $user['User']['last_login'];
unset($user['User']['password']);
$this->User->updateLoginTimes($user['User']);
$lastUserLogin = $user['User']['last_login'];
$this->User->Behaviors->enable('SysLogLogable.SysLogLogable');
if ($lastUserLogin) {
$readableDatetime = (new DateTime())->setTimestamp($lastUserLogin)->format('D, d M y H:i:s O'); // RFC822
@ -1813,14 +1858,14 @@ class UsersController extends AppController
{
// set all of the data up for the heatmaps
$params = array(
'fields' => array('name'),
'fields' => array('id', 'name'),
'recursive' => -1,
'conditions' => array()
);
if (!$this->_isSiteAdmin() && !empty(Configure::read('Security.hide_organisation_index_from_users'))) {
$params['conditions'] = array('Organisation.id' => $this->Auth->user('org_id'));
}
$orgs = $this->User->Organisation->find('all', $params);
$orgs = $this->User->Organisation->find('list', $params);
$local_orgs_params = $params;
$local_orgs_params['conditions']['Organisation.local'] = 1;
@ -1848,7 +1893,7 @@ class UsersController extends AppController
$stats['correlation_count'] = $this->Correlation->find('count', array('recursive' => -1));
$stats['correlation_count'] = $stats['correlation_count'] / 2;
$stats['proposal_count'] = $this->User->Event->ShadowAttribute->find('count', array('recursive' => -1));
$stats['proposal_count'] = $this->User->Event->ShadowAttribute->find('count', array('recursive' => -1, 'conditions' => array('deleted' => 0)));
$stats['user_count'] = $this->User->find('count', array('recursive' => -1));
$stats['user_count_pgp'] = $this->User->find('count', array('recursive' => -1, 'conditions' => array('User.gpgkey !=' => '')));
@ -1869,16 +1914,17 @@ class UsersController extends AppController
'stats' => $stats
);
return $this->RestResponse->viewData($data, $this->response->type());
} else {
$this->set('stats', $stats);
$this->set('orgs', $orgs);
$this->set('start', strtotime(date('Y-m-d H:i:s') . ' -5 months'));
$this->set('end', strtotime(date('Y-m-d H:i:s')));
$this->set('startDateCal', $year . ', ' . $month . ', 01');
$range = '[5, 10, 50, 100]';
$this->set('range', $range);
$this->render('statistics_data');
}
$this->set('stats', $stats);
$this->set('orgs', $orgs);
$this->set('start', strtotime(date('Y-m-d H:i:s') . ' -5 months'));
$this->set('end', strtotime(date('Y-m-d H:i:s')));
$this->set('startDateCal', $year . ', ' . $month . ', 01');
$range = '[5, 10, 50, 100]';
$this->set('range', $range);
$this->set('activityUrl', $this->baseurl . (Configure::read('MISP.log_new_audit') ? '/audit_logs' : '/logs') . '/returnDates');
$this->render('statistics_data');
}
private function __statisticsSightings($params = array())

View File

@ -30,7 +30,7 @@ class UsageDataWidget
$this->Correlation = ClassRegistry::init('Correlation');
$correlationsCount = $this->Correlation->find('count', array('recursive' => -1)) / 2;
$proposalsCount = $this->Event->ShadowAttribute->find('count', array('recursive' => -1));
$proposalsCount = $this->Event->ShadowAttribute->find('count', array('recursive' => -1, 'conditions' => array('deleted' => 0)));
$usersCount = $this->User->find('count', array('recursive' => -1));
$usersCountPgp = $this->User->find('count', array('recursive' => -1, 'conditions' => array('User.gpgkey !=' => '')));

View File

@ -1,9 +1,7 @@
<?php
App::uses('JsonExport', 'Export');
App::uses('AppModel', 'Model');
class YaraExport
{
private $__script_path = APP . 'files/scripts/yara/yaraexport.py';
@ -13,6 +11,7 @@ class YaraExport
private $__MAX_n_attributes = 15000;
private $__yara_file_gen = null;
private $__yara_file_asis = null;
/** @var null|File */
private $__curr_input_file = null;
private $__scope = false;
private $__curr_input_is_empty = true;
@ -73,7 +72,13 @@ class YaraExport
$this->separator(); // calling separator since returning '' will prevent it
}
$jsonData = $this->__JsonExporter->handler($data, $options);
$this->__curr_input_file->append($jsonData);
if ($jsonData instanceof Generator) {
foreach ($jsonData as $part) {
$this->__curr_input_file->append($part);
}
} else {
$this->__curr_input_file->append($jsonData);
}
$this->__curr_input_is_empty = false;
}
$this->__n_attributes += $attr_count;

View File

@ -125,6 +125,12 @@ class SecurityAudit
__('Passing user information to response headers is disabled. This can be useful for logging user info at the reverse proxy level. You can enable it by setting `Security.username_in_response_header` to `true`.'),
];
}
if (!Configure::read('MISP.log_new_audit')) {
$output['Logging'][] = [
'hint',
__('New audit log stores more information, like used authkey ID or request ID that can help when analysing or correlating audit logs.'),
];
}
if (empty(Configure::read('MISP.attachment_scan_module'))) {
$output['Attachment scanning'][] = ['hint', __('No module for scanning attachments for viruses is currently defined.')];

View File

@ -6,6 +6,7 @@ class AdminSetting extends AppModel
public $useTable = 'admin_settings';
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array(
'userModel' => 'User',
'userKey' => 'user_id',

View File

@ -9,6 +9,7 @@ class Allowedlist extends AppModel
public $displayField = 'name';
public $actsAs = array(
'AuditLog',
'Trim',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'roleModel' => 'Role',

View File

@ -89,7 +89,8 @@ class AppModel extends Model
45 => false, 46 => false, 47 => false, 48 => false, 49 => false, 50 => false,
51 => false, 52 => false, 53 => false, 54 => false, 55 => false, 56 => false,
57 => false, 58 => false, 59 => false, 60 => false, 61 => false, 62 => false,
63 => true, 64 => false, 65 => false, 66 => false, 67 => false, 68 => false
63 => true, 64 => false, 65 => false, 66 => false, 67 => false, 68 => false,
69 => false,
);
public $advanced_updates_description = array(
@ -1578,6 +1579,27 @@ class AppModel extends Model
case 68:
$sqlArray[] = "ALTER TABLE `correlation_exclusions` ADD `comment` text DEFAULT NULL;";
break;
case 69:
$sqlArray[] = "CREATE TABLE IF NOT EXISTS `audit_logs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`created` datetime NOT NULL,
`user_id` int(11) NOT NULL,
`org_id` int(11) NOT NULL,
`authkey_id` int(11) DEFAULT NULL,
`ip` varbinary(16) DEFAULT NULL,
`request_type` tinyint NOT NULL,
`request_id` varchar(255) DEFAULT NULL,
`action` varchar(20) NOT NULL,
`model` varchar(80) NOT NULL,
`model_id` int(11) NOT NULL,
`model_title` text DEFAULT NULL,
`event_id` int(11) NULL,
`change` blob,
PRIMARY KEY (`id`),
INDEX `event_id` (`event_id`),
INDEX `model_id` (`model_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
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

@ -23,6 +23,7 @@ class Attribute extends AppModel
public $name = 'Attribute'; // TODO general
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'userModel' => 'User',
'userKey' => 'user_id',

View File

@ -7,7 +7,7 @@ App::uses('AppModel', 'Model');
*/
class AttributeTag extends AppModel
{
public $actsAs = array('Containable');
public $actsAs = array('AuditLog', 'Containable');
public $validate = array(
'attribute_id' => array(
@ -296,8 +296,13 @@ class AttributeTag extends AppModel
return $allTags;
}
// find all galaxies that belong to a list of attributes (contains in the same event)
public function getAttributesClusters(array $attributes)
/**
* Find all galaxies that belong to a list of attributes (contains in the same event)
* @param array $user
* @param array $attributes
* @return array
*/
public function getAttributesClusters(array $user, array $attributes)
{
if (empty($attributes)) {
return array();

366
app/Model/AuditLog.php Normal file
View File

@ -0,0 +1,366 @@
<?php
App::uses('AppModel', 'Model');
/**
* @property Event $Event
* @property User $User
* @property Organisation $Organisation
*/
class AuditLog extends AppModel
{
const BROTLI_HEADER = "\xce\xb2\xcf\x81";
const BROTLI_MIN_LENGTH = 200;
const ACTION_ADD = 'add',
ACTION_EDIT = 'edit',
ACTION_SOFT_DELETE = 'soft_delete',
ACTION_DELETE = 'delete',
ACTION_UNDELETE = 'undelete',
ACTION_TAG = 'tag',
ACTION_TAG_LOCAL = 'tag_local',
ACTION_REMOVE_TAG = 'remove_tag',
ACTION_REMOVE_TAG_LOCAL = 'remove_local_tag',
ACTION_GALAXY = 'galaxy',
ACTION_GALAXY_LOCAL = 'galaxy_local',
ACTION_REMOVE_GALAXY = 'remove_galaxy',
ACTION_REMOVE_GALAXY_LOCAL = 'remove_local_galaxy',
ACTION_PUBLISH = 'publish',
ACTION_PUBLISH_SIGHTINGS = 'publish_sightings';
const REQUEST_TYPE_DEFAULT = 0,
REQUEST_TYPE_API = 1,
REQUEST_TYPE_CLI = 2;
public $actsAs = [
'Containable',
];
/** @var array|null */
private $user = null;
/** @var bool */
private $compressionEnabled;
/**
* Null when not defined, false when not enabled
* @var Syslog|null|false
*/
private $syslog;
public $compressionStats = [
'compressed' => 0,
'bytes_compressed' => 0,
'bytes_uncompressed' => 0,
];
public $belongsTo = [
'User' => [
'className' => 'User',
'foreignKey' => 'user_id',
],
'Event' => [
'className' => 'Event',
'foreignKey' => 'event_id',
],
'Organisation' => [
'className' => 'Organisation',
'foreignKey' => 'org_id',
],
];
public function __construct($id = false, $table = null, $ds = null)
{
parent::__construct($id, $table, $ds);
$this->compressionEnabled = Configure::read('MISP.log_new_audit_compress') && function_exists('brotli_compress');
}
public function afterFind($results, $primary = false)
{
foreach ($results as $key => $result) {
if (isset($result['AuditLog']['ip'])) {
$results[$key]['AuditLog']['ip'] = inet_ntop($result['AuditLog']['ip']);
}
if (isset($result['AuditLog']['change']) && $result['AuditLog']['change']) {
$results[$key]['AuditLog']['change'] = $this->decodeChange($result['AuditLog']['change']);
}
if (isset($result['AuditLog']['action']) && isset($result['AuditLog']['model']) && isset($result['AuditLog']['model_id'])) {
$results[$key]['AuditLog']['title'] = $this->generateUserFriendlyTitle($result['AuditLog']);
}
}
return $results;
}
/**
* @param array $auditLog
* @return string
*/
private function generateUserFriendlyTitle(array $auditLog)
{
if (in_array($auditLog['action'], [self::ACTION_TAG, self::ACTION_TAG_LOCAL, self::ACTION_REMOVE_TAG, self::ACTION_REMOVE_TAG_LOCAL], true)) {
$attached = ($auditLog['action'] === self::ACTION_TAG || $auditLog['action'] === self::ACTION_TAG_LOCAL);
$local = ($auditLog['action'] === self::ACTION_TAG_LOCAL || $auditLog['action'] === self::ACTION_REMOVE_TAG_LOCAL) ? 'local' : 'global';
if ($attached) {
return __('Attached %s tag "%s" to %s #%s', $local, $auditLog['model_title'], strtolower($auditLog['model']), $auditLog['model_id']);
} else {
return __('Detached %s tag "%s" from %s #%s', $local, $auditLog['model_title'], strtolower($auditLog['model']), $auditLog['model_id']);
}
}
if (in_array($auditLog['action'], [self::ACTION_GALAXY, self::ACTION_GALAXY_LOCAL, self::ACTION_REMOVE_GALAXY, self::ACTION_REMOVE_GALAXY_LOCAL], true)) {
$attached = ($auditLog['action'] === self::ACTION_GALAXY || $auditLog['action'] === self::ACTION_GALAXY_LOCAL);
$local = ($auditLog['action'] === self::ACTION_GALAXY_LOCAL || $auditLog['action'] === self::ACTION_REMOVE_GALAXY_LOCAL) ? 'local' : 'global';
if ($attached) {
return __('Attached %s galaxy cluster "%s" to %s #%s', $local, $auditLog['model_title'], strtolower($auditLog['model']), $auditLog['model_id']);
} else {
return __('Detached %s galaxy cluster "%s" from %s #%s', $local, $auditLog['model_title'], strtolower($auditLog['model']), $auditLog['model_id']);
}
}
if (in_array($auditLog['model'], ['Attribute', 'Object', 'ShadowAttribute'], true)) {
$modelName = $auditLog['model'] === 'ShadowAttribute' ? 'Proposal' : $auditLog['model'];
$title = __('%s from Event #%s', $modelName, $auditLog['event_id']);
} else {
$title = "{$auditLog['model']} #{$auditLog['model_id']}";
}
if (isset($auditLog['model_title']) && $auditLog['model_title']) {
$title .= ": {$auditLog['model_title']}";
}
return $title;
}
/**
* @param string $change
* @return array|string
* @throws JsonException
*/
private function decodeChange($change)
{
if (substr($change, 0, 4) === self::BROTLI_HEADER) {
$this->compressionStats['compressed']++;
if (function_exists('brotli_uncompress')) {
$this->compressionStats['bytes_compressed'] += strlen($change);
$change = brotli_uncompress(substr($change, 4));
$this->compressionStats['bytes_uncompressed'] += strlen($change);
if ($change === false) {
return 'Compressed';
}
} else {
return 'Compressed';
}
}
return $this->jsonDecode($change);
}
public function beforeValidate($options = array())
{
if (isset($this->data['AuditLog']['change']) && !is_array($this->data['AuditLog']['change'])) {
$this->invalidate('change', 'Change field must be array');
}
}
public function beforeSave($options = array())
{
if (!isset($this->data['AuditLog']['ip']) && Configure::read('MISP.log_client_ip')) {
$ipHeader = Configure::read('MISP.log_client_ip_header') ?: 'REMOTE_ADDR';
if (isset($_SERVER[$ipHeader])) {
$this->data['AuditLog']['ip'] = inet_pton($_SERVER[$ipHeader]);
}
}
if (!isset($this->data['AuditLog']['user_id'])) {
$this->data['AuditLog']['user_id'] = $this->userInfo()['id'];
}
if (!isset($this->data['AuditLog']['org_id'])) {
$this->data['AuditLog']['org_id'] = $this->userInfo()['org_id'];
}
if (!isset($this->data['AuditLog']['request_type'])) {
$this->data['AuditLog']['request_type'] = $this->userInfo()['request_type'];
}
if (!isset($this->data['AuditLog']['authkey_id'])) {
$this->data['AuditLog']['authkey_id'] = $this->userInfo()['authkey_id'];
}
if (!isset($this->data['AuditLog']['request_id'] ) && isset($_SERVER['HTTP_X_REQUEST_ID'])) {
$this->data['AuditLog']['request_id'] = $_SERVER['HTTP_X_REQUEST_ID'];
}
// Truncate request_id
if (isset($this->data['AuditLog']['request_id']) && strlen($this->data['AuditLog']['request_id']) > 255) {
$this->data['AuditLog']['request_id'] = substr($this->data['AuditLog']['request_id'], 0, 255);
}
// Truncate model title
if (isset($this->data['AuditLog']['model_title']) && mb_strlen($this->data['AuditLog']['model_title']) > 255) {
$this->data['AuditLog']['model_title'] = mb_substr($this->data['AuditLog']['model_title'], 0, 252) . '...';
}
$this->logData($this->data);
if (isset($this->data['AuditLog']['change'])) {
$change = json_encode($this->data['AuditLog']['change'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if ($this->compressionEnabled && strlen($change) >= self::BROTLI_MIN_LENGTH) {
$change = self::BROTLI_HEADER . brotli_compress($change, 4, BROTLI_TEXT);
}
$this->data['AuditLog']['change'] = $change;
}
}
/**
* @param array $data
* @return bool
*/
private function logData(array $data)
{
if (Configure::read('Plugin.ZeroMQ_enable') && Configure::read('Plugin.ZeroMQ_audit_notifications_enable')) {
$pubSubTool = $this->getPubSubTool();
$pubSubTool->publish($data, 'audit', 'log');
}
$this->publishKafkaNotification('audit', $data, 'log');
if (Configure::read('Plugin.ElasticSearch_logging_enable')) {
// send off our logs to distributed /dev/null
$logIndex = Configure::read("Plugin.ElasticSearch_log_index");
$elasticSearchClient = $this->getElasticSearchTool();
$elasticSearchClient->pushDocument($logIndex, "log", $data);
}
// write to syslogd as well if enabled
if ($this->syslog === null) {
if (Configure::read('Security.syslog')) {
$options = [];
$syslogToStdErr = Configure::read('Security.syslog_to_stderr');
if ($syslogToStdErr !== null) {
$options['to_stderr'] = $syslogToStdErr;
}
$syslogIdent = Configure::read('Security.syslog_ident');
if ($syslogIdent) {
$options['ident'] = $syslogIdent;
}
$this->syslog = new SysLog($options);
} else {
$this->syslog = false;
}
}
if ($this->syslog) {
$entry = $data['AuditLog']['action'];
$title = $this->generateUserFriendlyTitle($data['AuditLog']);
if ($title) {
$entry .= " -- $title";
}
$this->syslog->write('info', $entry);
}
return true;
}
/**
* @return array
*/
private function userInfo()
{
if ($this->user !== null) {
return $this->user;
}
$this->user = ['id' => 0, 'org_id' => 0, 'authkey_id' => 0, 'request_type' => self::REQUEST_TYPE_DEFAULT];
$isShell = defined('CAKEPHP_SHELL') && CAKEPHP_SHELL;
if ($isShell) {
// do not start session for shell commands and fetch user info from configuration
$this->user['request_type'] = self::REQUEST_TYPE_CLI;
$currentUserId = Configure::read('CurrentUserId');
if (!empty($currentUserId)) {
$this->user['id'] = $currentUserId;
$userFromDb = $this->User->find('first', [
'conditions' => ['User.id' => $currentUserId],
'fields' => ['User.org_id'],
]);
$this->user['org_id'] = $userFromDb['User']['org_id'];
}
} else {
App::uses('AuthComponent', 'Controller/Component');
$authUser = AuthComponent::user();
if (!empty($authUser)) {
$this->user['id'] = $authUser['id'];
$this->user['org_id'] = $authUser['org_id'];
if (isset($authUser['logged_by_authkey']) && $authUser['logged_by_authkey']) {
$this->user['request_type'] = self::REQUEST_TYPE_API;
}
if (isset($authUser['authkey_id'])) {
$this->user['authkey_id'] = $authUser['authkey_id'];
}
}
}
return $this->user;
}
public function insert(array $data)
{
try {
$this->create();
} catch (Exception $e) {
return; // Table is missing when updating, so this is intentional
}
if ($this->save($data) === false) {
throw new Exception($this->validationErrors);
}
}
public function recompress()
{
$changes = $this->find('all', [
'fields' => ['AuditLog.id', 'AuditLog.change'],
'recursive' => -1,
'conditions' => ['length(AuditLog.change) >=' => self::BROTLI_MIN_LENGTH],
]);
foreach ($changes as $change) {
$this->save($change, true, ['id', 'change']);
}
}
/**
* @param string|int $org
* @return array
*/
public function returnDates($org = 'all')
{
$conditions = [];
if ($org !== 'all') {
$org = $this->Organisation->fetchOrg($org);
if (empty($org)) {
throw new NotFoundException('Invalid organisation.');
}
$conditions['org_id'] = $org['id'];
}
$dataSource = ConnectionManager::getDataSource('default')->config['datasource'];
if ($dataSource === 'Database/Mysql' || $dataSource === 'Database/MysqlObserver') {
$validDates = $this->find('all', [
'recursive' => -1,
'fields' => ['DISTINCT UNIX_TIMESTAMP(DATE(created)) AS Date', 'count(id) AS count'],
'conditions' => $conditions,
'group' => ['Date'],
'order' => ['Date'],
]);
} elseif ($dataSource === 'Database/Postgres') {
if (!empty($conditions['org_id'])) {
$condOrg = 'WHERE org_id = "' . $conditions['org_id'] . '"';
} else {
$condOrg = '';
}
$sql = 'SELECT DISTINCT EXTRACT(EPOCH FROM CAST(created AS DATE)) AS "Date", COUNT(id) AS count
FROM audit_logs
' . $condOrg . '
GROUP BY "Date" ORDER BY "Date"';
$validDates = $this->query($sql);
}
$data = [];
foreach ($validDates as $k => $date) {
$data[(int)$date[0]['Date']] = (int)$date[0]['count'];
}
return $data;
}
}

View File

@ -11,6 +11,7 @@ class AuthKey extends AppModel
public $recursive = -1;
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array(
'userModel' => 'User',
'userKey' => 'user_id',

View File

@ -0,0 +1,357 @@
<?php
App::uses('AuditLog', 'Model');
class AuditLogBehavior extends ModelBehavior
{
/** @var array */
private $config;
/** @var array|null */
private $old;
/** @var AuditLog|null */
private $AuditLog;
/** @var bool */
private $enabled;
private $skipFields = [
'id' => true,
'lastpushedid' => true,
'timestamp' => true,
'revision' => true,
'modified' => true,
'date_modified' => true, // User
'current_login' => true, // User
'last_login' => true, // User
'newsread' => true, // User
'proposal_email_lock' => true, // Event
'warninglist_entry_count' => true, // Warninglist
];
private $modelInfo = [
'Event' => 'info',
'User' => 'email',
'Object' => 'name',
'EventReport' => 'name',
'Server' => 'name',
'Feed' => 'name',
'Role' => 'name',
'SharingGroup' => 'name',
'Tag' => 'name',
'TagCollection' => 'name',
'Taxonomy' => 'namespace',
'Organisation' => 'name',
'AdminSetting' => 'setting',
'UserSetting' => 'setting',
'Galaxy' => 'name',
'GalaxyCluster' => 'value',
'Warninglist' => 'name',
];
public function __construct()
{
parent::__construct();
$this->enabled = Configure::read('MISP.log_new_audit');
}
public function setup(Model $model, $config = [])
{
$this->config = $config;
// Generate model info for attribute and proposals
$attributeInfo = function (array $new, array $old) {
$category = isset($new['category']) ? $new['category'] : $old['category'];
$type = isset($new['type']) ? $new['type'] : $old['type'];
$value1 = trim(isset($new['value1']) ? $new['value1'] : $old['value1']);
$value2 = trim(isset($new['value2']) ? $new['value2'] : $old['value2']);
$value = $value1 . (empty($value2) ? '' : '|' . $value2);
return "$category/$type $value";
};
$this->modelInfo['Attribute'] = $attributeInfo;
$this->modelInfo['ShadowAttribute'] = $attributeInfo;
$this->modelInfo['AuthKey'] = function (array $new, array $old) {
$start = isset($new['authkey_start']) ? $new['authkey_start'] : $old['authkey_start'];
$end = isset($new['authkey_end']) ? $new['authkey_end'] : $old['authkey_end'];
return "$start********************************$end";
};
}
public function beforeSave(Model $model, $options = [])
{
if (!$this->enabled) {
return true;
}
if ($model->id) {
$this->old = $model->find('first', [
'conditions' => [$model->alias . '.' . $model->primaryKey => $model->id],
'recursive' => -1,
'callbacks' => false,
]);
} else {
$this->old = null;
}
return true;
}
public function afterSave(Model $model, $created, $options = [])
{
if (!$this->enabled) {
return;
}
if ($model->id) {
$id = $model->id;
} else if ($model->insertId) {
$id = $model->insertId;
} else {
$id = null;
}
if ($created) {
$action = AuditLog::ACTION_ADD;
} else {
$action = AuditLog::ACTION_EDIT;
if (isset($model->data[$model->alias]['deleted'])) {
if ($model->data[$model->alias]['deleted']) {
$action = AuditLog::ACTION_SOFT_DELETE;
} else if (!$model->data[$model->alias]['deleted'] && $this->old[$model->alias]['deleted']) {
$action = AuditLog::ACTION_UNDELETE;
}
}
}
$changedFields = $this->changedFields($model, isset($options['fieldList']) ? $options['fieldList'] : null);
if (empty($changedFields)) {
return;
}
if ($model->name === 'Event') {
$eventId = $id;
} else if (isset($model->data[$model->alias]['event_id'])) {
$eventId = $model->data[$model->alias]['event_id'];
} else if (isset($this->old[$model->alias]['event_id'])) {
$eventId = $this->old[$model->alias]['event_id'];
} else {
$eventId = null;
}
$modelTitle = null;
if (isset($this->modelInfo[$model->name])) {
$modelTitleField = $this->modelInfo[$model->name];
if (is_callable($modelTitleField)) {
$modelTitle = $modelTitleField($model->data[$model->alias], isset($this->old[$model->alias]) ? $this->old[$model->alias] : []);
} else if (isset($model->data[$model->alias][$modelTitleField])) {
$modelTitle = $model->data[$model->alias][$modelTitleField];
} else if ($this->old[$model->alias][$modelTitleField]) {
$modelTitle = $this->old[$model->alias][$modelTitleField];
}
}
$modelName = $model->name === 'MispObject' ? 'Object' : $model->name;
if ($modelName === 'AttributeTag' || $modelName === 'EventTag') {
$action = $model->data[$model->alias]['local'] ? AuditLog::ACTION_TAG_LOCAL : AuditLog::ACTION_TAG;
$tagInfo = $this->getTagInfo($model, $model->data[$model->alias]['tag_id']);
if ($tagInfo) {
$modelTitle = $tagInfo['tag_name'];
if ($tagInfo['is_galaxy']) {
$action = $model->data[$model->alias]['local'] ? AuditLog::ACTION_GALAXY_LOCAL : AuditLog::ACTION_GALAXY;
if ($tagInfo['galaxy_cluster_name']) {
$modelTitle = $tagInfo['galaxy_cluster_name'];
}
}
}
$id = $modelName === 'AttributeTag' ? $model->data[$model->alias]['attribute_id'] : $model->data[$model->alias]['event_id'];
$modelName = $modelName === 'AttributeTag' ? 'Attribute' : 'Event';
}
if ($modelName === 'Event') {
if (isset($changedFields['published'][1]) && $changedFields['published'][1]) {
$action = AuditLog::ACTION_PUBLISH;
} else if (isset($changedFields['sighting_timestamp'][1]) && $changedFields['sighting_timestamp'][1]) {
$action = AuditLog::ACTION_PUBLISH_SIGHTINGS;
}
}
$this->auditLog()->insert([
'action' => $action,
'model' => $modelName,
'model_id' => $id,
'model_title' => $modelTitle,
'event_id' => $eventId,
'change' => $changedFields,
]);
}
public function beforeDelete(Model $model, $cascade = true)
{
if (!$this->enabled) {
return true;
}
$model->recursive = -1;
$model->read();
return true;
}
public function afterDelete(Model $model)
{
if (!$this->enabled) {
return;
}
if ($model->name === 'Event') {
$eventId = $model->id;
} else {
$eventId = isset($model->data[$model->alias]['event_id']) ? $model->data[$model->alias]['event_id'] : null;
}
$modelTitle = null;
if (isset($this->modelInfo[$model->name])) {
$modelTitleField = $this->modelInfo[$model->name];
if (is_callable($modelTitleField)) {
$modelTitle = $modelTitleField($model->data[$model->alias], []);
} else if (isset($model->data[$model->alias][$modelTitleField])) {
$modelTitle = $model->data[$model->alias][$modelTitleField];
}
}
$modelName = $model->name === 'MispObject' ? 'Object' : $model->name;
$action = AuditLog::ACTION_DELETE;
$id = $model->id;
if ($modelName === 'AttributeTag' || $modelName === 'EventTag') {
$action = $model->data[$model->alias]['local'] ? AuditLog::ACTION_REMOVE_TAG_LOCAL : AuditLog::ACTION_REMOVE_TAG;
$tagInfo = $this->getTagInfo($model, $model->data[$model->alias]['tag_id']);
if ($tagInfo) {
$modelTitle = $tagInfo['tag_name'];
if ($tagInfo['is_galaxy']) {
$action = $model->data[$model->alias]['local'] ? AuditLog::ACTION_REMOVE_GALAXY_LOCAL : AuditLog::ACTION_REMOVE_GALAXY;
if ($tagInfo['galaxy_cluster_name']) {
$modelTitle = $tagInfo['galaxy_cluster_name'];
}
}
}
$id = $modelName === 'AttributeTag' ? $model->data[$model->alias]['attribute_id'] : $model->data[$model->alias]['event_id'];
$modelName = $modelName === 'AttributeTag' ? 'Attribute' : 'Event';
}
$this->auditLog()->insert([
'action' => $action,
'model' => $modelName,
'model_id' => $id,
'model_title' => $modelTitle,
'event_id' => $eventId,
'change' => $this->changedFields($model),
]);
}
/**
* @param Model $model
* @param int $tagId
* @return array|null
*/
private function getTagInfo(Model $model, $tagId)
{
$tag = $model->Tag->find('first', [
'conditions' => ['Tag.id' => $tagId],
'recursive' => -1,
'fields' => ['Tag.name', 'Tag.is_galaxy'],
]);
if (empty($tag)) {
return null;
}
$galaxyClusterName = null;
if ($tag['Tag']['is_galaxy']) {
if (!isset($this->GalaxyCluster)) {
$this->GalaxyCluster = ClassRegistry::init('GalaxyCluster');
}
$galaxyCluster = $this->GalaxyCluster->find('first', [
'conditions' => ['GalaxyCluster.tag_name' => $tag['Tag']['name']],
'recursive' => -1,
'fields' => ['GalaxyCluster.value'],
]);
if (!empty($galaxyCluster)) {
$galaxyClusterName = $galaxyCluster['GalaxyCluster']['value'];
}
}
return [
'tag_name' => $tag['Tag']['name'],
'is_galaxy' => $tag['Tag']['is_galaxy'],
'galaxy_cluster_name' => $galaxyClusterName,
];
}
/**
* @param Model $model
* @param array|null $fieldsToSave
* @return array
*/
private function changedFields(Model $model, $fieldsToSave = null)
{
$dbFields = $model->schema();
$changedFields = [];
foreach ($model->data[$model->alias] as $key => $value) {
if (isset($this->skipFields[$key])) {
continue;
}
if (!isset($dbFields[$key])) {
continue;
}
if ($fieldsToSave && !in_array($key, $fieldsToSave, true)) {
continue;
}
if (isset($model->data[$model->alias][$model->primaryKey]) && isset($this->old[$model->alias][$key])) {
$old = $this->old[$model->alias][$key];
} else {
$old = null;
}
// Normalize
if (is_bool($old)) {
$old = $old ? 1 : 0;
}
if (is_bool($value)) {
$value = $value ? 1 : 0;
}
$dbType = $dbFields[$key]['type'];
if ($dbType === 'integer' || $dbType === 'tinyinteger' || $dbType === 'biginteger' || $dbType === 'boolean') {
$value = (int)$value;
if ($old !== null) {
$old = (int)$old;
}
}
if ($value == $old) {
continue;
}
if ($key === 'password' || $key === 'authkey') {
$value = '*****';
if ($old !== null) {
$old = $value;
}
}
if ($old === null) {
$changedFields[$key] = $value;
} else {
$changedFields[$key] = [$old, $value];
}
}
return $changedFields;
}
/**
* @return AuditLog
*/
private function auditLog()
{
if ($this->AuditLog === null) {
$this->AuditLog = ClassRegistry::init('AuditLog');
}
return $this->AuditLog;
}
}

View File

@ -5,6 +5,7 @@ App::uses('AppModel', 'Model');
class Cerebrate extends AppModel
{
public $actsAs = [
'AuditLog',
'SysLogLogable.SysLogLogable' => [
'roleModel' => 'Role',
'roleKey' => 'role_id',

View File

@ -2,11 +2,13 @@
App::uses('AppModel', 'Model');
App::uses('RandomTool', 'Tools');
/**
* @property Attribute $Attribute
*/
class Correlation extends AppModel
{
public $cache_name = 'misp:top_correlations';
public $cache_age = 'misp:top_correlations_age';
const CACHE_NAME = 'misp:top_correlations',
CACHE_AGE = 'misp:top_correlations_age';
public $belongsTo = array(
'Attribute' => [
@ -19,6 +21,8 @@ class Correlation extends AppModel
)
);
private $exclusions = [];
public function correlateValueRouter($value)
{
if (Configure::read('MISP.background_jobs')) {
@ -57,7 +61,7 @@ class Correlation extends AppModel
$a = $a['Attribute'];
}
$extraConditions = null;
if (in_array($a['type'], array('ip-src', 'ip-dst', 'ip-src|port', 'ip-dst|port'))) {
if (in_array($a['type'], ['ip-src', 'ip-dst', 'ip-src|port', 'ip-dst|port'], true)) {
$extraConditions = $this->cidrCorrelation($a);
} else if ($a['type'] === 'ssdeep' && function_exists('ssdeep_fuzzy_compare')) {
$extraConditions = $this->ssdeepCorrelation($a);
@ -199,21 +203,18 @@ class Correlation extends AppModel
}
}
foreach ($correlatingAttributes as $k => $correlatingAttribute) {
foreach ($correlatingAttributes as $k2 => $correlatingAttribute2) {
foreach ($correlatingAttributes as $correlatingAttribute2) {
$correlations = $this->__addCorrelationEntry($value, $correlatingAttribute, $correlatingAttribute2, $correlations);
}
$extraCorrelations = $this->__addAdvancedCorrelations($correlatingAttribute);
if (!empty($extraCorrelations)) {
foreach ($extraCorrelations as $k3 => $extraCorrelation) {
foreach ($extraCorrelations as $extraCorrelation) {
$correlations = $this->__addCorrelationEntry($value, $correlatingAttribute, $extraCorrelation, $correlations);
//$correlations = $this->__addCorrelationEntry($value, $extraCorrelation, $correlatingAttribute, $correlations);
}
}
if ($jobId && $k % 100 === 0) {
$job['Job']['progress'] = floor(100 * $k / $count);
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
$job['Job']['message'] = __('Correlating Attributes based on value. %s attributes correlated out of %s.', $k, $count);
$this->Job->save($job);
$this->Job->saveProgress($jobId, __('Correlating Attributes based on value. %s attributes correlated out of %s.', $k, $count), floor(100 * $k / $count));
}
}
return $this->__saveCorrelations($correlations);
@ -289,7 +290,7 @@ class Correlation extends AppModel
// generate additional correlating attribute list based on the advanced correlations
$extraConditions = $this->__buildAdvancedCorrelationConditions($a);
$correlatingValues = array($a['value1']);
if (!empty($a['value2']) && !in_array($a['type'], $this->Attribute->primaryOnlyCorrelatingTypes)) {
if (!empty($a['value2']) && !in_array($a['type'], $this->Attribute->primaryOnlyCorrelatingTypes, true)) {
$correlatingValues[] = $a['value2'];
}
@ -356,13 +357,10 @@ class Correlation extends AppModel
}
if (empty($this->exclusions)) {
try {
$this->redis = $this->setupRedisWithException();
$redis = $this->setupRedisWithException();
$this->exclusions = $redis->sMembers('misp:correlation_exclusions');
} catch (Exception $e) {
$redisFail = true;
}
if (empty($redisFail)) {
$this->Correlation = ClassRegistry::init('Correlation');
$this->exclusions = $this->redis->sMembers('misp:correlation_exclusions');
return false;
}
}
foreach ($this->exclusions as $exclusion) {
@ -394,7 +392,7 @@ class Correlation extends AppModel
return false;
}
public function ssdeepCorrelation($a)
private function ssdeepCorrelation($a)
{
if (empty($this->FuzzyCorrelateSsdeep)) {
$this->FuzzyCorrelateSsdeep = ClassRegistry::init('FuzzyCorrelateSsdeep');
@ -422,7 +420,7 @@ class Correlation extends AppModel
return false;
}
public function cidrCorrelation($a)
private function cidrCorrelation($a)
{
$ipValues = array();
$ip = $a['value1'];
@ -437,7 +435,7 @@ class Correlation extends AppModel
'deleted' => 0,
);
if (in_array($this->getDataSource()->config['datasource'], array('Database/Mysql', 'Database/MysqlObserver'))) {
if (in_array($this->getDataSource()->config['datasource'], ['Database/Mysql', 'Database/MysqlObserver'])) {
// Massive speed up for CIDR correlation. Instead of testing all in PHP, database can do that work much
// faster. But these methods are just supported by MySQL.
if ($ip_version === 4) {
@ -446,31 +444,6 @@ class Correlation extends AppModel
// Just fetch IP address that fit in CIDR range.
$conditions['INET_ATON(value1) BETWEEN ? AND ?'] = array($startIp, $endIp);
// Just fetch IPv4 address that starts with given prefix. This is fast, because value1 is indexed.
// This optimisation is possible just to mask bigger than 8 bites.
if ($mask >= 8) {
$ipv4Parts = explode('.', $networkIp);
$ipv4Parts = array_slice($ipv4Parts, 0, intval($mask / 8));
$prefix = implode('.', $ipv4Parts);
$conditions['value1 LIKE'] = $prefix . '%';
}
} else {
$conditions[] = 'IS_IPV6(value1)';
// Just fetch IPv6 address that starts with given prefix. This is fast, because value1 is indexed.
if ($mask >= 16) {
$ipv6Parts = explode(':', rtrim($networkIp, ':'));
$ipv6Parts = array_slice($ipv6Parts, 0, intval($mask / 16));
$prefix = implode(':', $ipv6Parts);
$conditions['value1 LIKE'] = $prefix . '%';
}
}// Massive speed up for CIDR correlation. Instead of testing all in PHP, database can do that work much
// faster. But these methods are just supported by MySQL.
if ($ip_version === 4) {
$startIp = ip2long($networkIp) & ((-1 << (32 - $mask)));
$endIp = $startIp + pow(2, (32 - $mask)) - 1;
// Just fetch IP address that fit in CIDR range.
$conditions['INET_ATON(value1) BETWEEN ? AND ?'] = array($startIp, $endIp);
// Just fetch IPv4 address that starts with given prefix. This is fast, because value1 is indexed.
// This optimisation is possible just to mask bigger than 8 bites.
if ($mask >= 8) {
@ -580,6 +553,10 @@ class Correlation extends AppModel
return true;
}
/**
* @return int|bool
* @throws Exception
*/
public function generateTopCorrelationsRouter()
{
if (Configure::read('MISP.background_jobs')) {
@ -606,7 +583,7 @@ class Correlation extends AppModel
true
);
$this->Job->saveField('process_id', $process_id);
return true;
return $jobId;
} else {
return $this->generateTopCorrelations();
}
@ -615,11 +592,10 @@ class Correlation extends AppModel
public function generateTopCorrelations($jobId = false)
{
try {
$this->redis = $this->setupRedisWithException();
$redis = $this->setupRedisWithException();
} catch (Exception $e) {
throw new NotFoundException(__('No redis connection found.'));
}
$mem_initial = memory_get_usage();
$max_id = $this->find('first', [
'fields' => ['MAX(id) AS max_id'],
'recursive' => -1
@ -641,54 +617,50 @@ class Correlation extends AppModel
}
$max_id = $max_id[0]['max_id'];
$this->redis->del($this->cache_name);
$this->redis->set($this->cache_age, time());
$redis->del(self::CACHE_NAME);
$redis->set(self::CACHE_AGE, time());
$chunk_size = 1000000;
$max = ceil($max_id / $chunk_size);
for ($i = 0; $i < $max; $i++) {
$correlations = $this->find('column', [
'recursive' => -1,
'fields' => ['value'],
'conditions' => [
'id >' => ($i * $chunk_size),
'id >' => $i * $chunk_size,
'id <=' => (($i + 1) * $chunk_size)
]
]);
$newElements = count($correlations);
$correlations = array_count_values($correlations);
$pipeline = $this->redis->pipeline();
$pipeline = $redis->pipeline();
foreach ($correlations as $correlation => $count) {
$pipeline->zadd($this->cache_name, ['INCR'], $count, $correlation);
$pipeline->zadd(self::CACHE_NAME, ['INCR'], $count, $correlation);
}
$pipeline->exec();
if ($jobId) {
$job['Job']['progress'] = floor(100 * $i / $max);
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
$job['Job']['message'] = __('Generating top correlations. Processed %s IDs.', ($i * $chunk_size) + $newElements);
$this->Job->save($job);
$this->Job->saveProgress($jobId, __('Generating top correlations. Processed %s IDs.', ($i * $chunk_size) + $newElements), floor(100 * $i / $max));
return $jobId;
}
}
return true;
}
public function findTop($query)
public function findTop(array $query)
{
try {
$this->redis = $this->setupRedisWithException();
$redis = $this->setupRedisWithException();
} catch (Exception $e) {
return false;
}
$start = $query['limit'] * ($query['page'] -1);
$end = $query['limit'] * ($query['page']);
$list = $this->redis->zRevRange($this->cache_name, $start, $end, true);
$end = $query['limit'] * $query['page'];
$list = $redis->zRevRange(self::CACHE_NAME, $start, $end, true);
$results = [];
foreach ($list as $value => $count) {
$results[] = [
'Correlation' => [
'value' => $value,
'count' => $count,
'excluded' => $this->redis->sismember('misp:correlation_exclusions', $value)
'excluded' => $this->__preventExcludedCorrelations(['value1' => $value]),
]
];
}
@ -698,10 +670,10 @@ class Correlation extends AppModel
public function getTopTime()
{
try {
$this->redis = $this->setupRedisWithException();
$redis = $this->setupRedisWithException();
} catch (Exception $e) {
return false;
}
return $this->redis->get($this->cache_age);
return $redis->get(self::CACHE_AGE);
}
}

View File

@ -9,6 +9,7 @@ class CorrelationExclusion extends AppModel
public $key = 'misp:correlation_exclusions';
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array(
'userModel' => 'User',
'userKey' => 'user_id',

View File

@ -17,6 +17,7 @@ App::uses('SendEmailTemplate', 'Tools');
class Event extends AppModel
{
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array(
'userModel' => 'User',
'userKey' => 'user_id',

View File

@ -8,6 +8,7 @@ class EventBlocklist extends AppModel
public $recursive = -1;
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'userModel' => 'User',
'userKey' => 'user_id',

View File

@ -3,7 +3,7 @@ App::uses('AppModel', 'Model');
class EventDelegation extends AppModel
{
public $actsAs = array('Containable');
public $actsAs = array('AuditLog', 'Containable');
public $validate = array(
'event_id' => array(

View File

@ -7,6 +7,7 @@ App::uses('AppModel', 'Model');
class EventReport extends AppModel
{
public $actsAs = array(
'AuditLog',
'Containable',
'SysLogLogable.SysLogLogable' => array(
'userModel' => 'User',
@ -271,6 +272,44 @@ class EventReport extends AppModel
return $conditions;
}
/**
* buildACLConditions Generate ACL conditions for viewing the report
*
* @param array $user
* @param array $events
* @return array
*/
public function attachReportCountsToEvents(array $user, $events)
{
$conditions = array();
if (!$user['Role']['perm_site_admin']) {
$sgids = $this->Event->cacheSgids($user, true);
}
foreach ($events as $k => $event) {
$conditions = [
'AND' => [
[
'Event.id' => $event['Event']['id']
]
]
];
if (!$user['Role']['perm_site_admin'] && $event['Event']['org_id'] != $user['org_id']) {
$conditions['AND'][] = [
'EventReport.distribution' => [1, 2, 3, 5],
'AND '=> [
'EventReport.distribution' => 4,
'EventReport.sharing_group_id' => $sgids,
]
];
}
$events[$k]['Event']['report_count'] = $this->find('count', [
'conditions' => $conditions
]);
}
return $events;
}
/**
* fetchById Simple ACL-aware method to fetch a report by Id or UUID
*

View File

@ -6,7 +6,7 @@ App::uses('AppModel', 'Model');
*/
class EventTag extends AppModel
{
public $actsAs = array('Containable');
public $actsAs = array('AuditLog', 'Containable');
public $validate = array(
'event_id' => array(

View File

@ -5,7 +5,9 @@ App::uses('TmpFileTool', 'Tools');
class Feed extends AppModel
{
public $actsAs = array('SysLogLogable.SysLogLogable' => array(
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array(
'change' => 'full'
),
'Trim',
@ -548,7 +550,16 @@ class Feed extends AppModel
return $sources;
}
public function downloadFromFeed($actions, $feed, HttpSocket $HttpSocket = null, $user, $jobId = false)
/**
* @param array $actions
* @param array $feed
* @param HttpSocket|null $HttpSocket
* @param array $user
* @param int|false $jobId
* @return array
* @throws Exception
*/
private function downloadFromFeed(array $actions, array $feed, HttpSocket $HttpSocket = null, array $user, $jobId = false)
{
$total = count($actions['add']) + count($actions['edit']);
$currentItem = 0;
@ -559,10 +570,12 @@ class Feed extends AppModel
foreach ($actions['add'] as $uuid) {
try {
$result = $this->__addEventFromFeed($HttpSocket, $feed, $uuid, $user, $filterRules);
if ($result !== 'blocked') {
if ($result === true) {
$results['add']['success'] = $uuid;
} else if ($result !== 'blocked') {
$results['add']['fail'] = ['uuid' => $uuid, 'reason' => $result];
$this->log("Could not add event '$uuid' from feed {$feed['Feed']['id']}: $result", LOG_WARNING);
}
} catch (Exception $e) {
$this->logException("Could not add event '$uuid' from feed {$feed['Feed']['id']}.", $e);
$results['add']['fail'] = array('uuid' => $uuid, 'reason' => $e->getMessage());
@ -577,8 +590,11 @@ class Feed extends AppModel
$uuid = $editTarget['uuid'];
try {
$result = $this->__updateEventFromFeed($HttpSocket, $feed, $uuid, $editTarget['id'], $user, $filterRules);
if ($result !== 'blocked') {
$results['edit']['success'] = $uuid;
if ($result === true) {
$results['add']['success'] = $uuid;
} else if ($result !== 'blocked') {
$results['add']['fail'] = ['uuid' => $uuid, 'reason' => $result];
$this->log("Could not edit event '$uuid' from feed {$feed['Feed']['id']}: $result", LOG_WARNING);
}
} catch (Exception $e) {
$this->logException("Could not edit event '$uuid' from feed {$feed['Feed']['id']}.", $e);
@ -586,7 +602,7 @@ class Feed extends AppModel
}
$this->__cleanupFile($feed, '/' . $uuid . '.json');
if ($currentItem % 10 == 0) {
if ($currentItem % 10 === 0) {
$this->jobProgress($jobId, null, 100 * (($currentItem + 1) / $total));
}
$currentItem++;
@ -869,7 +885,7 @@ class Feed extends AppModel
* @param HttpSocket|null $HttpSocket
* @param array $feed
* @param string $uuid
* @param $user
* @param array $user
* @param array|bool $filterRules
* @return array|bool|string
* @throws Exception
@ -879,7 +895,6 @@ class Feed extends AppModel
$event = $this->downloadAndParseEventFromFeed($feed, $uuid, $HttpSocket);
$event = $this->__prepareEvent($event, $feed, $filterRules);
if (is_array($event)) {
$this->Event = ClassRegistry::init('Event');
return $this->Event->_add($event, true, $user);
} else {
return $event;
@ -937,13 +952,13 @@ class Feed extends AppModel
}
$HttpSocket = $this->isFeedLocal($this->data) ? null : $this->__setupHttpSocket($this->data);
if ($this->data['Feed']['source_format'] == 'misp') {
if ($this->data['Feed']['source_format'] === 'misp') {
$this->jobProgress($jobId, 'Fetching event manifest.');
try {
$actions = $this->getNewEventUuids($this->data, $HttpSocket);
} catch (Exception $e) {
$this->logException("Could not get new event uuids for feed $feedId.", $e);
$this->jobProgress($jobId, 'Could not fetch event manifest. See log for more details.');
$this->jobProgress($jobId, 'Could not fetch event manifest. See error log for more details.');
return false;
}
@ -952,7 +967,7 @@ class Feed extends AppModel
}
$total = count($actions['add']) + count($actions['edit']);
$this->jobProgress($jobId, "Fetching $total events.");
$this->jobProgress($jobId, __("Fetching %s events.", $total));
$result = $this->downloadFromFeed($actions, $this->data, $HttpSocket, $user, $jobId);
$this->__cleanupFile($this->data, '/manifest.json');
} else {

View File

@ -7,6 +7,7 @@ class Galaxy extends AppModel
public $recursive = -1;
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'userModel' => 'User',
'userKey' => 'user_id',

View File

@ -13,6 +13,7 @@ class GalaxyCluster extends AppModel
public $recursive = -1;
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'userModel' => 'User',
'userKey' => 'user_id',

View File

@ -7,6 +7,7 @@ class GalaxyClusterBlocklist extends AppModel
public $recursive = -1;
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'userModel' => 'User',
'userKey' => 'user_id',

View File

@ -8,6 +8,7 @@ class GalaxyClusterRelation extends AppModel
public $recursive = -1;
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'userModel' => 'User',
'userKey' => 'user_id',

View File

@ -4,7 +4,7 @@ App::uses('AppModel', 'Model');
class GalaxyClusterRelationTag extends AppModel
{
public $useTable = 'galaxy_cluster_relation_tags';
public $actsAs = array('Containable');
public $actsAs = array('AuditLog', 'Containable');
public $validate = array(
'galaxy_cluster_relation_id' => array(

View File

@ -7,6 +7,7 @@ class GalaxyElement extends AppModel
public $recursive = -1;
public $actsAs = array(
'AuditLog',
'Containable',
);

View File

@ -155,11 +155,11 @@ class Log extends AppModel
$conditions = array();
$this->Organisation = ClassRegistry::init('Organisation');
if ($org !== 'all') {
$org = $this->Organisation->find('first', array('fields' => array('name'), 'recursive' => -1, 'conditions' => array('UPPER(Organisation.name) LIKE' => strtoupper($org))));
$org = $this->Organisation->fetchOrg($org);
if (empty($org)) {
return MethodNotAllowedException('Invalid organisation.');
throw new MethodNotAllowedException('Invalid organisation.');
}
$conditions['org'] = $org['Organisation']['name'];
$conditions['org'] = $org['name'];
}
$conditions['AND']['NOT'] = array('action' => array('login', 'logout', 'changepw'));
if ($dataSource == 'Database/Mysql' || $dataSource == 'Database/MysqlObserver') {
@ -206,6 +206,9 @@ class Log extends AppModel
*/
public function createLogEntry($user, $action, $model, $modelId = 0, $title = '', $change = '')
{
if (in_array($action, ['tag', 'galaxy', 'publish', 'publish_sightings'], true) && Configure::read('MISP.log_new_audit')) {
return; // Do not store tag changes when new audit is enabled
}
if ($user === 'SYSTEM') {
$user = array('Organisation' => array('name' => 'SYSTEM'), 'email' => 'SYSTEM', 'id' => 0);
} else if (!is_array($user)) {

View File

@ -15,6 +15,7 @@ class MispObject extends AppModel
public $useTable = 'objects';
public $actsAs = array(
'AuditLog',
'Containable',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'userModel' => 'User',
@ -85,7 +86,33 @@ class MispObject extends AppModel
'required' => false,
'message' => array('Last seen value should be greater than first seen value')
),
)
),
'name' => array(
'stringNotEmpty' => array(
'rule' => array('stringNotEmpty')
),
),
'meta-category' => array(
'stringNotEmpty' => array(
'rule' => array('stringNotEmpty')
),
),
'description' => array(
'stringNotEmpty' => array(
'rule' => array('stringNotEmpty')
),
),
'template_uuid' => array(
'uuid' => array(
'rule' => 'uuid',
'message' => 'Please provide a valid RFC 4122 UUID'
),
),
'template_version' => array(
'numeric' => array(
'rule' => 'naturalNumber',
)
),
);
private $__objectDuplicationCheckCache = [];

View File

@ -3,7 +3,7 @@ App::uses('AppModel', 'Model');
class News extends AppModel
{
public $actsAs = array('Containable');
public $actsAs = array('AuditLog', 'Containable');
public $validate = array(
'message' => array(

View File

@ -5,6 +5,7 @@ App::uses('AppModel', 'Model');
class ObjectReference extends AppModel
{
public $actsAs = array(
'AuditLog',
'Containable',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'userModel' => 'User',

View File

@ -5,6 +5,7 @@ App::uses('AppModel', 'Model');
class ObjectRelationship extends AppModel
{
public $actsAs = array(
'AuditLog',
'Containable',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'userModel' => 'User',

View File

@ -5,6 +5,7 @@ App::uses('AppModel', 'Model');
class ObjectTemplate extends AppModel
{
public $actsAs = array(
'AuditLog',
'Containable',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'userModel' => 'User',

View File

@ -7,6 +7,7 @@ class OrgBlocklist extends AppModel
public $recursive = -1;
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'userModel' => 'User',
'userKey' => 'user_id',

View File

@ -12,6 +12,7 @@ class Organisation extends AppModel
public $recursive = -1;
public $actsAs = array(
'AuditLog',
'Containable',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'roleModel' => 'Organisation',
@ -97,7 +98,7 @@ class Organisation extends AppModel
'uuid' => '0',
'contacts' => '',
'local' => true,
'restricted_to_domain' => '',
'restricted_to_domain' => '[]',
'landingpage' => null
);
@ -110,15 +111,19 @@ class Organisation extends AppModel
$this->data['Organisation']['uuid'] = strtolower(trim($this->data['Organisation']['uuid']));
}
$date = date('Y-m-d H:i:s');
if (!empty($this->data['Organisation']['restricted_to_domain'])) {
$this->data['Organisation']['restricted_to_domain'] = str_replace("\r", '', $this->data['Organisation']['restricted_to_domain']);
$this->data['Organisation']['restricted_to_domain'] = explode("\n", $this->data['Organisation']['restricted_to_domain']);
foreach ($this->data['Organisation']['restricted_to_domain'] as $k => $v) {
$this->data['Organisation']['restricted_to_domain'][$k] = trim($v);
if (array_key_exists('restricted_to_domain', $this->data['Organisation'])) {
if (!is_array($this->data['Organisation']['restricted_to_domain'])) {
$this->data['Organisation']['restricted_to_domain'] = str_replace('\r', '', $this->data['Organisation']['restricted_to_domain']);
$this->data['Organisation']['restricted_to_domain'] = explode('\n', $this->data['Organisation']['restricted_to_domain']);
}
$this->data['Organisation']['restricted_to_domain'] = array_values(
array_filter(
array_map('trim', $this->data['Organisation']['restricted_to_domain'])
)
);
$this->data['Organisation']['restricted_to_domain'] = json_encode($this->data['Organisation']['restricted_to_domain']);
} else {
$this->data['Organisation']['restricted_to_domain'] = '';
}
if (!isset($this->data['Organisation']['id'])) {
$this->data['Organisation']['date_created'] = $date;

View File

@ -8,6 +8,7 @@ App::uses('AppModel', 'Model');
class Post extends AppModel
{
public $actsAs = array(
'AuditLog',
'Containable',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'roleModel' => 'Post',

View File

@ -9,6 +9,7 @@ App::uses('AppModel', 'Model');
class Regexp extends AppModel
{
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'roleModel' => 'Role',
'roleKey' => 'role_id',

View File

@ -39,6 +39,7 @@ class Role extends AppModel
);
public $actsAs = array(
'AuditLog',
'Trim',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'roleModel' => 'Role',

View File

@ -14,7 +14,9 @@ class Server extends AppModel
public $name = 'Server';
public $actsAs = array('SysLogLogable.SysLogLogable' => array(
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array(
'userModel' => 'User',
'userKey' => 'user_id',
'change' => 'full'
@ -469,6 +471,7 @@ class Server extends AppModel
public function pull($user, $id = null, $technique=false, $server, $jobId = false, $force = false)
{
if ($jobId) {
Configure::write('CurrentUserId', $user['id']);
$job = ClassRegistry::init('Job');
$job->read(null, $jobId);
$email = "Scheduled job";
@ -2628,24 +2631,32 @@ class Server extends AppModel
public function dbSpaceUsage()
{
$inMb = function ($value) {
return round($value / 1024 / 1024, 2) . " MB";
};
$result = [];
$dataSource = $this->getDataSource()->config['datasource'];
if ($dataSource == 'Database/Mysql' || $dataSource == 'Database/MysqlObserver') {
if ($dataSource === 'Database/Mysql' || $dataSource === 'Database/MysqlObserver') {
$sql = sprintf(
'select TABLE_NAME, sum((DATA_LENGTH+INDEX_LENGTH)/1024/1024) AS used, sum(DATA_FREE)/1024/1024 AS reclaimable from information_schema.tables where table_schema = %s group by TABLE_NAME;',
'select TABLE_NAME, DATA_LENGTH, INDEX_LENGTH, DATA_FREE from information_schema.tables where table_schema = %s group by TABLE_NAME;',
"'" . $this->getDataSource()->config['database'] . "'"
);
$sqlResult = $this->query($sql);
$result = array();
foreach ($sqlResult as $temp) {
foreach ($temp[0] as $k => $v) {
$temp[0][$k] = round($v, 2) . 'MB';
}
$temp[0]['table'] = $temp['tables']['TABLE_NAME'];
$result[] = $temp[0];
$result[$temp['tables']['TABLE_NAME']] = [
'table' => $temp['tables']['TABLE_NAME'],
'used' => $inMb($temp['tables']['DATA_LENGTH'] + $temp['tables']['INDEX_LENGTH']),
'reclaimable' => $inMb($temp['tables']['DATA_FREE']),
'data_in_bytes' => (int) $temp['tables']['DATA_LENGTH'],
'index_in_bytes' => (int) $temp['tables']['INDEX_LENGTH'],
'reclaimable_in_bytes' => (int) $temp['tables']['DATA_FREE'],
];
}
return $result;
}
else if ($dataSource == 'Database/Postgres') {
else if ($dataSource === 'Database/Postgres') {
$sql = sprintf(
'select TABLE_NAME as table, pg_total_relation_size(%s||%s||TABLE_NAME) as used from information_schema.tables where table_schema = %s group by TABLE_NAME;',
"'" . $this->getDataSource()->config['database'] . "'",
@ -2653,19 +2664,18 @@ class Server extends AppModel
"'" . $this->getDataSource()->config['database'] . "'"
);
$sqlResult = $this->query($sql);
$result = array();
foreach ($sqlResult as $temp) {
foreach ($temp[0] as $k => $v) {
if ($k == "table") {
continue;
}
$temp[0][$k] = round($v / 1024 / 1024, 2) . 'MB';
$temp[0][$k] = $inMb($v);
}
$temp[0]['reclaimable'] = '0MB';
$temp[0]['reclaimable'] = '0 MB';
$result[] = $temp[0];
}
return $result;
}
return $result;
}
public function redisInfo()
@ -4460,19 +4470,28 @@ class Server extends AppModel
public function queryAvailableSyncFilteringRules($server)
{
$syncFilteringRules = [
'error' => '',
'data' => []
];
$HttpSocket = $this->setupHttpSocket($server, null);
$uri = $server['Server']['url'] . '/servers/getAvailableSyncFilteringRules';
$request = $this->setupSyncRequest($server);
$response = $HttpSocket->get($uri, false, $request);
if ($response === false) {
throw new Exception(__('Connection failed for unknown reason.'));
try {
$response = $HttpSocket->get($uri, false, $request);
if ($response === false) {
$syncFilteringRules['error'] = __('Connection failed for unknown reason.');
return $syncFilteringRules;
}
} catch (SocketException $e) {
$syncFilteringRules['error'] = __('Connection failed for unknown reason. Error returned: %s', $e->getMessage());
return $syncFilteringRules;
}
$syncFilteringRules = [];
if ($response->isOk()) {
$syncFilteringRules = $this->jsonDecode($response->body());
$syncFilteringRules['data'] = $this->jsonDecode($response->body());
} else {
throw new Exception(__('Reponse was not OK. (HTTP code: %s)', $response->code));
$syncFilteringRules['error'] = __('Reponse was not OK. (HTTP code: %s)', $response->code);
}
return $syncFilteringRules;
}
@ -5217,6 +5236,24 @@ class Server extends AppModel
'type' => 'boolean',
'null' => true
],
'log_new_audit' => [
'level' => self::SETTING_RECOMMENDED,
'description' => __('Enable new audit log system.'),
'value' => false,
'errorMessage' => '',
'test' => 'testBool',
'type' => 'boolean',
'null' => true
],
'log_new_audit_compress' => [
'level' => self::SETTING_OPTIONAL,
'description' => __('Compress log changes by brotli algorithm. This will reduce log database size.'),
'value' => false,
'errorMessage' => '',
'test' => 'testBool',
'type' => 'boolean',
'null' => true
],
'delegation' => array(
'level' => 1,
'description' => __('This feature allows users to create org only events and ask another organisation to take ownership of the event. This allows organisations to remain anonymous by asking a partner to publish an event for them.'),
@ -5262,6 +5299,15 @@ class Server extends AppModel
'type' => 'boolean',
'null' => true
),
'showEventReportCountOnIndex' => array(
'level' => 1,
'description' => __('When enabled, the aggregate number of event reports for the event becomes visible to the currently logged in user on the event index UI.'),
'value' => false,
'errorMessage' => '',
'test' => 'testBool',
'type' => 'boolean',
'null' => true
),
'disableUserSelfManagement' => array(
'level' => 1,
'description' => __('When enabled only Org and Site admins can edit a user\'s profile.'),

View File

@ -20,6 +20,7 @@ class ShadowAttribute extends AppModel
public $recursive = -1;
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'userModel' => 'User',
'userKey' => 'user_id',

View File

@ -9,6 +9,7 @@ App::uses('AppModel', 'Model');
class SharingGroup extends AppModel
{
public $actsAs = array(
'AuditLog',
'Containable',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'roleModel' => 'SharingGroup',

View File

@ -2,7 +2,7 @@
App::uses('AppModel', 'Model');
class SharingGroupOrg extends AppModel
{
public $actsAs = array('Containable');
public $actsAs = array('AuditLog', 'Containable');
public $belongsTo = array(
'SharingGroup' => array(

View File

@ -3,7 +3,7 @@ App::uses('AppModel', 'Model');
class SharingGroupServer extends AppModel
{
public $actsAs = array('Containable');
public $actsAs = array('AuditLog', 'Containable');
public $belongsTo = array(
'SharingGroup' => array(

View File

@ -15,6 +15,7 @@ class Sightingdb extends AppModel
);
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array(
'userModel' => 'User',
'userKey' => 'user_id',

View File

@ -17,6 +17,7 @@ class SightingdbOrg extends AppModel
);
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array(
'userModel' => 'User',
'userKey' => 'user_id',

View File

@ -12,6 +12,7 @@ class Tag extends AppModel
public $displayField = 'name';
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'roleModel' => 'Tag',
'roleKey' => 'tag_id',

View File

@ -9,6 +9,7 @@ class TagCollection extends AppModel
public $displayField = 'name';
public $actsAs = array(
'AuditLog',
'Trim',
'SysLogLogable.SysLogLogable' => array(
'roleModel' => 'Role',

View File

@ -7,6 +7,7 @@ class TagCollectionTag extends AppModel
public $useTable = 'tag_collection_tags';
public $actsAs = array(
'AuditLog',
'Trim',
'SysLogLogable.SysLogLogable' => array(
'roleModel' => 'Role',

View File

@ -11,6 +11,7 @@ class Taxonomy extends AppModel
public $recursive = -1;
public $actsAs = array(
'AuditLog',
'Containable',
);

View File

@ -5,6 +5,7 @@ App::uses('AppModel', 'Model');
class Thread extends AppModel
{
public $actsAs = array(
'AuditLog',
'Containable',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'roleModel' => 'Thread',

View File

@ -216,6 +216,7 @@ class User extends AppModel
);
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array(
'userModel' => 'User',
'userKey' => 'user_id',

View File

@ -7,6 +7,7 @@ class UserSetting extends AppModel
public $recursive = -1;
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array(
'userModel' => 'User',
'userKey' => 'user_id',

View File

@ -13,6 +13,7 @@ class Warninglist extends AppModel
public $recursive = -1;
public $actsAs = array(
'AuditLog',
'Containable',
);

View File

@ -2,9 +2,15 @@
App::import('Lib', 'SysLog.SysLog'); // Audit, syslogd, extra
class SysLogLogableBehavior extends LogableBehavior {
class SysLogLogableBehavior extends LogableBehavior
{
public function __construct()
{
parent::__construct();
$this->defaults['enabled'] = !Configure::read('MISP.log_new_audit');
}
function afterSave(Model $Model, $created, $options = array()) {
function afterSave(Model $Model, $created, $options = array()) {
if (!$this->settings[$Model->alias]['enabled']) {
return true;
}
@ -264,6 +270,10 @@ class SysLogLogableBehavior extends LogableBehavior {
$this->settings[$Model->alias] = array_merge($this->defaults, $config);
$this->settings[$Model->alias]['ignore'][] = $Model->primaryKey;
if (!$this->settings[$Model->alias]['enabled']) {
return;
}
$this->Log = ClassRegistry::init('Log');
if ($this->settings[$Model->alias]['userModel'] != $Model->alias) {
$this->UserModel = ClassRegistry::init($this->settings[$Model->alias]['userModel']);

View File

@ -1,19 +1,24 @@
<div class="allowedlist form">
<?php echo $this->Form->create('Allowedlist');?>
<fieldset>
<legend><?php echo __('Add Signature Allowedlist');?></legend>
<?php
echo $this->Form->input('name', array(
'class' => 'input-xxlarge'
));
?>
</fieldset>
<?php
echo $this->Form->button(__('Add'), array('class' => 'btn btn-primary'));
echo $this->Form->end();
$modelForForm = 'Allowedlist';
echo $this->element('genericElements/Form/genericForm', [
'form' => $this->Form,
'data' => [
'title' => $action == 'add' ? __('Add Signature Allowedlist') : __('Edit Signature Allowedlist'),
'model' => $modelForForm,
'fields' => [
[
'field' => 'name',
'class' => 'input-xxlarge',
],
],
'submit' => [
'action' => $this->request->params['action'],
],
]
]);
?>
</div>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'allowedlist', 'menuItem' => 'add'));
?>
if (empty($ajax)) {
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'allowedlist', 'menuItem' => $action));
}
?>

View File

@ -1,19 +0,0 @@
<div class="allowedlist form">
<?php echo $this->Form->create('Allowedlist');?>
<fieldset>
<legend><?php echo __('Edit Signature Allowedlist');?></legend>
<?php
echo $this->Form->input('id');
echo $this->Form->input('name', array(
'class' => 'input-xxlarge'
));
?>
</fieldset>
<?php
echo $this->Form->button(__('Submit'), array('class' => 'btn btn-primary'));
echo $this->Form->end();
?>
</div>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'allowedlist', 'menuItem' => 'edit', 'id' => $this->Form->value('Allowedlist.id')));
?>

View File

@ -1,48 +0,0 @@
<div class="allowedlist index">
<h2><?php echo __('Signature Allowedlist');?></h2>
<p><?php echo __('Regex entries (in the standard php regex /{regex}/{modifier} format) entered below will restrict matching attributes from being included and data sets retrieved through restSearch.');?></p>
<div class="pagination">
<ul>
<?php
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
</ul>
</div>
<table class="table table-striped table-hover table-condensed">
<tr>
<th><?php echo $this->Paginator->sort('id');?></th>
<th><?php echo $this->Paginator->sort('name');?></th>
<th class="actions"><?php echo __('Actions');?></th>
</tr><?php
foreach ($list as $item):?>
<tr>
<td class="short"><?php echo h($item['Allowedlist']['id']);?>&nbsp;</td>
<td><?php echo h($item['Allowedlist']['name']);?>&nbsp;</td>
<td class="short action-links">
<?php echo $this->Html->link('', array('admin' => true, 'action' => 'edit', $item['Allowedlist']['id']), array('class' => 'fa fa-edit', 'title' => __('Edit'), 'aria-label' => __('Edit')));?>
<?php echo $this->Form->postLink('', array('admin' => true, 'action' => 'delete', $item['Allowedlist']['id']), array('class' => 'fa fa-trash', 'title' => __('Delete'), 'aria-label' => __('Delete')), __('Are you sure you want to delete "%s"?', $item['Allowedlist']['name']));?>
</td>
</tr><?php
endforeach;?>
</table>
<p>
<?php
echo $this->Paginator->counter(array(
'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}')
));
?>
</p>
<div class="pagination">
<ul>
<?php
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
</ul>
</div>
</div>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'allowedlist', 'menuItem' => 'index'));

View File

@ -1,43 +1,48 @@
<div class="allowedlist index">
<h2><?php echo __('Signature Allowedlist');?></h2>
<p><?php echo __('Regex entries (in the standard php regex /{regex}/{modifier} format) entered below will restrict matching attributes from being included in the IDS flag sensitive exports (such as NIDS exports).');?></p>
<div class="pagination">
<ul>
<?php
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
</ul>
</div>
<table class="table table-striped table-hover table-condensed">
<tr>
<th><?php echo $this->Paginator->sort('id');?></th>
<th><?php echo $this->Paginator->sort('name');?></th>
</tr><?php
foreach ($list as $item):?>
<tr>
<td class="short"><?php echo h($item['Allowedlist']['id']);?>&nbsp;</td>
<td><?php echo h($item['Allowedlist']['name']);?>&nbsp;</td>
</tr><?php
endforeach;?>
</table>
<p>
<?php
echo $this->Paginator->counter(array(
'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}')
<?php
echo $this->element('/genericElements/IndexTable/index_table', array(
'data' => array(
'data' => $list,
'title' =>__('Signature Allowedlist'),
'description' => __('Regex entries (in the standard php regex /{regex}/{modifier} format) entered below will restrict matching attributes from being included in the IDS flag sensitive exports (such as NIDS exports).'),
'primary_id_path' => 'Allowedlist.id',
'fields' => array(
array(
'name' => __('ID'),
'sort' => 'id',
'class' => 'short',
'data_path' => 'Allowedlist.id',
'element' => 'links',
'url' => $baseurl . '/allowedlists/view/%s'
),
array(
'name' => __('Name'),
'sort' => 'name',
'data_path' => 'Allowedlist.name',
),
),
'actions' => array(
array(
'url' => $baseurl . '/admin/allowedlists/edit',
'url_params_data_paths' => array(
'Allowedlist.id'
),
'icon' => 'edit'
),
array(
'title' => __('Delete'),
'url' => $baseurl . '/admin/allowedlists/delete',
'url_params_data_paths' => array(
'Allowedlist.id'
),
'postLink' => true,
'postLinkConfirm' => __('Are you sure you want to delete the entry?'),
'icon' => 'trash',
'requirements' => $isSiteAdmin,
),
)
)
));
?>
</p>
<div class="pagination">
<ul>
<?php
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
</ul>
</div>
</div>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'allowedlist', 'menuItem' => 'index'));
<?= $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'allowedlist', 'menuItem' => 'index')); ?>

View File

@ -139,8 +139,7 @@ function syncMassEditFormAndSubmit(btn) {
submitPopoverForm('<?php echo $id;?>', 'massEdit');
}
$(document).ready(function() {
$(function() {
$('#AttributeDistribution').change(function() {
if ($('#AttributeDistribution').val() == 4) $('#SGContainer').show();
else $('#SGContainer').hide();
@ -162,17 +161,18 @@ $(document).ready(function() {
});
$("input, label").on('mouseleave', function(e) {
$('#'+e.currentTarget.id).popover('destroy');
});
$("input, label").on('mouseover', function(e) {
var $e = $(e.target);
$('#'+e.currentTarget.id).popover('destroy');
$('#'+e.currentTarget.id).popover({
trigger: 'focus',
placement: 'right',
container: 'body',
}).popover('show');
if (e.currentTarget.id) {
$('#' + e.currentTarget.id).popover('destroy');
}
}).on('mouseover', function(e) {
if (e.currentTarget.id) {
$('#' + e.currentTarget.id).popover('destroy');
$('#' + e.currentTarget.id).popover({
trigger: 'focus',
placement: 'right',
container: 'body',
}).popover('show');
}
});
// workaround for browsers like IE and Chrome that do now have an onmouseover on the 'options' of a select.

View File

@ -0,0 +1,357 @@
<div class="logs index">
<h2><?= __('Audit logs') ?></h2>
<div>
<div id="builder"></div>
<div style="display: flex; justify-content: flex-end; margin-top: 5px;">
<button id="qbSubmit" type="button" class="btn btn-success" style="margin-right: 5px;"> <i class="fa fa-filter"></i> <?= __('Filter'); ?></button>
<button id="qbClear" type="button" class="btn btn-xs btn-danger" title="<?= __('Clear filtering rules'); ?>"> <i class="fa fa-times"></i> <?= __('Clear'); ?></button>
</div>
</div>
<?php
echo $this->Html->script('moment-with-locales');
echo $this->Html->script('doT');
echo $this->Html->script('extendext');
echo $this->Html->css('query-builder.default');
echo $this->Html->script('query-builder');
?>
<script type="text/javascript">
var qbOptions = {
plugins: {
'unique-filter': null,
'filter-description' : {
mode: 'inline'
},
},
conditions: ['AND'],
allow_empty: true,
filters: [
{
id: 'created',
label: 'Created',
type: 'date',
operators: ['greater_or_equal', 'between'],
validation: {
format: 'YYYY-MM-DD'
},
plugin: 'datepicker',
plugin_config: {
format: 'yyyy-mm-dd',
todayBtn: 'linked',
todayHighlight: true,
autoclose: true
}
},
{
input: "select",
type: "string",
operators: [
"equal",
],
unique: true,
id: "action",
label: "Action",
values: <?= json_encode($actions) ?>
},
{
input: "select",
type: "string",
operators: [
"equal",
],
unique: true,
id: "model",
label: "Model type",
values: <?= json_encode($models) ?>
},
{
input: "text",
type: "integer",
operators: [
"equal",
],
unique: true,
id: "model_id",
label: "Model ID",
},
{
input: "text",
type: "integer",
operators: [
"equal",
],
unique: true,
id: "event_id",
label: "Belongs to event with ID",
},
{
input: "text",
type: "string",
operators: [
"contains",
],
unique: true,
id: "model_title",
label: "Model title",
},
{
input: "text",
type: "string",
operators: [
"equal",
],
unique: true,
id: "ip",
label: "IP",
},
{
input: "text",
type: "string",
operators: [
"equal",
],
unique: true,
id: "user",
label: "User",
description: "User ID or mail",
},
{
input: "text",
type: "integer",
operators: [
"equal",
],
unique: true,
id: "authkey_id",
label: "Authentication key ID",
},
{
input: "select",
type: "integer",
operators: [
"equal",
],
unique: true,
id: "request_type",
label: "Request type",
values: {0: "Browser", 1: "API", 2: "CLI or background job"}
},
{
input: "text",
type: "string",
operators: [
"equal",
],
unique: true,
id: "request_id",
label: "Request ID",
description: "Request ID from X-Request-ID HTTP header",
},
{
input: "text",
type: "string",
operators: [
"equal",
],
unique: true,
id: "org",
label: "Organisation",
description: "Organisation ID, UUID or name",
}
],
rules: {
condition: 'AND',
not: false,
rules: <?= json_encode($qbRules) ?>,
flags: {
no_add_group: true,
condition_readonly: true,
}
},
icons: {
add_group: 'fa fa-plus-square',
add_rule: 'fa fa-plus-circle',
remove_group: 'fa fa-minus-square',
remove_rule: 'fa fa-minus-circle',
error: 'fa fa-exclamation-triangle'
}
};
$(function() {
var $builder = $('#builder');
// Fix for Bootstrap Datepicker
$builder.on('afterUpdateRuleValue.queryBuilder', function (e, rule) {
if (rule.filter.plugin === 'datepicker') {
rule.$el.find('.rule-value-container input').datepicker('update');
}
});
var queryBuilder = $builder.queryBuilder(qbOptions);
queryBuilder = queryBuilder[0].queryBuilder;
$('#qbClear').off('click').on('click', function () {
queryBuilder.reset();
});
// Submit on enter
$builder.on('keyup', 'input[type=text], select', function (event) {
if (event.keyCode === 13) {
$('#qbSubmit').click();
}
});
$('#qbSubmit').off('click').on('click', function () {
var rules = queryBuilder.getRules({skip_empty: true});
passedArgs = [];
for (var key in rules.rules) {
var rule = rules.rules[key];
var k = rule.id;
var v = rule.value;
if (Array.isArray(v)) {
v = v.join('||');
}
passedArgs[k] = v;
}
var url = here;
for (var key in passedArgs) {
if (typeof key === 'number') {
url += "/" + passedArgs[key];
} else if (key !== 'page') {
url += "/" + key + ":" + encodeURIComponent(passedArgs[key]);
}
}
window.location.href = url;
});
});
</script>
<div class="pagination">
<ul>
<?php
$paginator = $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
$paginator .= $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
$paginator .= $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $paginator;
?>
</ul>
</div>
<table class="table table-striped table-hover table-condensed">
<tr>
<th><?= $this->Paginator->sort('created') ?></th>
<th><?= $this->Paginator->sort('user_id', __('User')) ?></th>
<th><?= $this->Paginator->sort('ip', __('IP')) ?></th>
<th><?= $this->Paginator->sort('org_id', __('Org')) ?></th>
<th><?= $this->Paginator->sort('action') ?></th>
<th><?= __('Model') ?></th>
<th><?= __('Title') ?></th>
<th><?= __('Change') ?></th>
</tr>
<?php foreach ($list as $item): ?>
<tr>
<td class="short"><?= $this->Time->time($item['AuditLog']['created']); ?></td>
<td class="short" data-search="user" data-search-value="<?= h($item['AuditLog']['user_id']) ?>"><?php
if (isset($item['AuditLog']['user_id']) && $item['AuditLog']['user_id'] == 0) {
echo __('SYSTEM');
} else if (isset($item['User']['email'])) {
echo '<a href="' . $baseurl . '/admin/users/view/' . h($item['User']['id']) . '">' . h($item['User']['email']) . '</a>';
} else {
echo __('<i>Deleted user #%s</i>', h($item['AuditLog']['user_id']));
}
if ($item['AuditLog']['request_type'] == AuditLog::REQUEST_TYPE_CLI) {
echo ' <i class="fas fa-terminal" title="' . __('Action done by CLI or background job') .'"></i>';
} else if ($item['AuditLog']['request_type'] == AuditLog::REQUEST_TYPE_API) {
$key = $item['AuditLog']['authkey_id'] ? ' ' . __('by auth key #%s', h($item['AuditLog']['authkey_id'])) : '';
echo ' <i class="fas fa-cogs" title="' . __('Action done trough API') . $key . '"></i>';
}
?></td>
<td class="short" data-search="ip" data-search-value="<?= h($item['AuditLog']['ip']) ?>"><?= h($item['AuditLog']['ip']) ?></td>
<td class="short" data-search="org" data-search-value="<?= h($item['AuditLog']['org_id']) ?>">
<?php if (isset($item['Organisation']) && $item['Organisation']['id']) {
echo $this->OrgImg->getOrgLogo($item, 24);
} else if ($item['AuditLog']['org_id'] != 0) {
echo __('<i>Deleted org #%s</i>', h($item['AuditLog']['org_id']));
}
?>
</td>
<td class="short" data-search="action" data-search-value="<?= h($item['AuditLog']['action']) ?>"><?= h($item['AuditLog']['action_human']) ?></td>
<td class="short" data-search="model" data-search-value="<?= h($item['AuditLog']['model']) . ':' . h($item['AuditLog']['model_id']) ?>">
<?php $title = isset($item['AuditLog']['event_info']) ? ' title="' . __('Event #%s: %s', $item['AuditLog']['event_id'], h($item['AuditLog']['event_info'])) . '"' : '' ?>
<?= isset($item['AuditLog']['model_link']) ? '<a href="' . h($item['AuditLog']['model_link']) . '"' . $title . '>' : '' ?>
<?= h($item['AuditLog']['model']) . ' #' . h($item['AuditLog']['model_id']) ?>
<?= isset($item['AuditLog']['model_link']) ? '</a>' : '' ?>
</td>
<td class="limitedWidth"><?= h($item['AuditLog']['title']) ?></td>
<td ondblclick="showFullChange(<?= h($item['AuditLog']['id']) ?>)"><?= $this->element('AuditLog/change', ['item' => $item]) ?></td>
</tr>
<?php endforeach; ?>
</table>
<p>
<?= $this->Paginator->counter(array(
'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}')
));
?>
</p>
<div class="pagination">
<ul>
<?= $paginator ?>
</ul>
</div>
</div>
<script type="text/javascript">
var passedArgs = <?= $passedArgs ?>;
function showFullChange(id) {
$.get(baseurl + "/audit_logs/fullChange/" + id, function(data) {
var $popoverFormLarge = $('#popover_form_large');
$popoverFormLarge.html(data);
$popoverFormLarge.find("span.json").each(function () {
$(this).html(syntaxHighlightJson($(this).text()));
});
openPopup($popoverFormLarge);
});
return false;
}
$('td[data-search]').mouseenter(function() {
var $td = $(this);
if ($td.data('search-value').length === 0) {
return;
}
$td.find('#quickEditButton').remove(); // clean all similar if exist
var $div = $('<div id="quickEditButton"></div>');
$div.addClass('quick-edit-row-div');
var $span = $('<span></span>');
$span.addClass('fa-as-icon fa fa-search-plus');
$span.css('font-size', '12px');
$div.append($span);
$td.append($div);
$span.click(function() {
if ($td.data('search') === 'model') {
var val = $td.data('search-value').split(":");
passedArgs['model'] = encodeURIComponent(val[0]);
passedArgs['model_id'] = encodeURIComponent(val[1]);
} else {
passedArgs[$td.data('search')] = encodeURIComponent($td.data('search-value'));
}
var url = here;
for (var key in passedArgs) {
if (typeof key === 'number') {
url += "/" + passedArgs[key];
} else if (key !== 'page') {
url += "/" + key + ":" + passedArgs[key];
}
}
window.location.href = url;
});
$td.off('mouseleave').on('mouseleave', function() {
$div.remove();
});
});
</script>
<?= $this->element('/genericElements/SideMenu/side_menu', ['menuList' => 'logs', 'menuItem' => 'listAuditLogs']);

View File

@ -0,0 +1,55 @@
<div class="logs index">
<h2><?= __('Audit logs for event #%s', $event['Event']['id']) ?></h2>
<div class="pagination">
<ul>
<?php
$paginator = $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
$paginator .= $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
$paginator .= $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $paginator;
?>
<li><a href="<?= $baseurl . '/logs/event_index/' . h($event['Event']['id']) ?>"><?= __('Older logs') ?></a></li>
</ul>
</div>
<table class="table table-striped table-hover table-condensed">
<tr>
<th><?= $this->Paginator->sort('created') ?></th>
<th><?= $this->Paginator->sort('user_id', __('User')) ?></th>
<th><?= $this->Paginator->sort('org_id', __('Org')) ?></th>
<th><?= $this->Paginator->sort('action') ?></th>
<th><?= __('Model') ?></th>
<th><?= __('Title') ?></th>
<th><?= __('Change') ?></th>
</tr>
<?php foreach ($list as $item): ?>
<tr>
<td class="short"><?= $this->Time->time($item['AuditLog']['created']); ?></td>
<td class="short"><?php
if (isset($item['AuditLog']['user_id']) && $item['AuditLog']['user_id'] == 0) {
echo __('SYSTEM');
} else if (isset($item['User']['email'])) {
echo h($item['User']['email']);
} ?></td>
<td class="short"><?= isset($item['Organisation']) ? $this->OrgImg->getOrgLogo($item, 24) : '' ?></td>
<td class="short"><?= h($item['AuditLog']['action_human']) ?></td>
<td class="short"><?= h($item['AuditLog']['model']) . ' #' . h($item['AuditLog']['model_id']) ?></td>
<td class="limitedWidth"><?= h($item['AuditLog']['title']) ?></td>
<td><?= $this->element('AuditLog/change', ['item' => $item]) ?></td>
</tr>
<?php endforeach; ?>
</table>
<p>
<?= $this->Paginator->counter(array(
'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}')
));
?>
</p>
<div class="pagination">
<ul>
<?= $paginator ?>
<li><a href="<?= $baseurl . '/logs/event_index/' . h($event['Event']['id']) ?>"><?= __('Older logs') ?></a></li>
</ul>
</div>
</div>
<?= $this->element('/genericElements/SideMenu/side_menu', ['menuList' => 'event', 'menuItem' => 'eventLog', 'event' => $event, 'mayModify' => $mayModify]);

View File

@ -0,0 +1,3 @@
<div style="padding: 1em; background: white; word-wrap: break-word;">
<?= $this->element('AuditLog/change', ['item' => $log, 'full' => true]) ?>
</div>

View File

@ -0,0 +1,52 @@
<?php
$removeActions = [
AuditLog::ACTION_DELETE => true,
AuditLog::ACTION_REMOVE_GALAXY_LOCAL => true,
AuditLog::ACTION_REMOVE_GALAXY => true,
AuditLog::ACTION_REMOVE_TAG => true,
AuditLog::ACTION_REMOVE_TAG_LOCAL => true,
];
$full = isset($full) ? $full : false;
$formatValue = function($field, $value) use ($full) {
if ((strpos($field, 'timestamp') !== false || in_array($field, ['expiration', 'created', 'date_created'], true)) && is_numeric($value)) {
$date = date('Y-m-d H:i:s', $value);
if ($date !== false) {
return '<span title="Original value: ' . h($value) . '">' . h($date) . '</span>';
}
} else if ($field === 'last_seen' || $field === 'first_seen') {
$ls_sec = intval($value / 1000000); // $ls is in micro (10^6)
$ls_micro = $value % 1000000;
$ls_micro = str_pad($ls_micro, 6, "0", STR_PAD_LEFT);
$ls = $ls_sec . '.' . $ls_micro;
$date = DateTime::createFromFormat('U.u', $ls)->format('Y-m-d\TH:i:s.u');
return '<span title="Original value: ' . h($value) . '">' . h($date) . '</span>';
}
if ($full && is_string($value) && !empty($value) && ($value[0] === '{' || $value[0] === '[') && json_decode($value) !== null) {
return '<span class="json">' . h($value) . '</span>';
}
if (!$full && mb_strlen($value) > 64) {
$value = mb_substr($value, 0, 64) . '...';
}
return h(json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
};
if (is_array($item['AuditLog']['change'])) {
foreach ($item['AuditLog']['change'] as $field => $values) {
echo '<span class="json_key">' . h($field) . ':</span> ';
if (isset($removeActions[$item['AuditLog']['action']])) {
echo '<span class="json_string">' . $formatValue($field, $values) . '</span> <i class="fas fa-arrow-right json_null"></i> <i class="fas fa-times json_string"></i><br>';
} else {
if (is_array($values)) {
echo '<span class="json_string">' . $formatValue($field, $values[0]) . '</span> ';
$value = $values[1];
} else {
$value = $values;
}
echo '<i class="fas fa-arrow-right json_null"></i> <span class="json_string">' . $formatValue($field, $value) . '</span><br>';
}
}
}

View File

@ -31,6 +31,7 @@
<?php if (in_array('tags', $columns, true)): ?><th><?= __('Tags') ?></th><?php endif; ?>
<?php if (in_array('attribute_count', $columns, true)): ?><th title="<?= __('Attribute Count') ?>"><?= $this->Paginator->sort('attribute_count', __('#Attr.')) ?></th><?php endif; ?>
<?php if (in_array('correlations', $columns, true)): ?><th title="<?= __('Correlation Count') ?>"><?= __('#Corr.') ?></th><?php endif; ?>
<?php if (in_array('report_count', $columns, true)): ?><th title="<?= __('Report Count') ?>"><?= $this->Paginator->sort('report_count', __('#Reports')) ?></th><?php endif; ?>
<?php if (in_array('sightings', $columns, true)): ?><th title="<?= __('Sighting Count')?>"><?= __('#Sightings') ?></th><?php endif; ?>
<?php if (in_array('proposals', $columns, true)): ?><th title="<?= __('Proposal Count') ?>"><?= __('#Prop') ?></th><?php endif; ?>
<?php if (in_array('discussion', $columns, true)): ?><th title="<?= __('Post Count') ?>"><?= __('#Posts') ?></th><?php endif; ?>
@ -124,6 +125,11 @@
<?php endif; ?>
</td>
<?php endif; ?>
<?php if (in_array('report_count', $columns, true)): ?>
<td class="bold" style="width:30px;">
<?= $event['Event']['report_count']; ?>
</td>
<?php endif; ?>
<?php if (in_array('sightings', $columns, true)): ?>
<td class="bold" style="width:30px;">
<?php if (!empty($event['Event']['sightings_count'])): ?>

View File

@ -1,104 +0,0 @@
<table class="table table-striped table-hover table-condensed">
<tr>
<th><?php echo $this->Paginator->sort('id', __('ID'));?></th>
<th><?php echo $this->Paginator->sort('org_ci', __('Org'));?></th>
<th><?php echo $this->Paginator->sort('role_id', __('Role'));?></th>
<th><?php echo $this->Paginator->sort('email');?></th>
<?php if (empty(Configure::read('Security.advanced_authkeys'))): ?>
<th><?php echo $this->Paginator->sort('authkey');?></th>
<?php endif; ?>
<th><?php echo $this->Paginator->sort('autoalert', __('Event alert'));?></th>
<th><?php echo $this->Paginator->sort('contactalert', __('Contact alert'));?></th>
<th><?php echo $this->Paginator->sort('gpgkey', __('PGP Key'));?></th>
<?php if (Configure::read('SMIME.enabled')): ?>
<th><?php echo $this->Paginator->sort('certif_public', 'S/MIME');?></th>
<?php endif; ?>
<th><?php echo $this->Paginator->sort('nids_sid', __('NIDS SID'));?></th>
<th><?php echo $this->Paginator->sort('termsaccepted', __('Terms accepted'));?></th>
<th><?php echo $this->Paginator->sort('current_login', __('Last login'));?></th>
<th><?php echo $this->Paginator->sort('date_created', __('Created'));?></th>
<?php
if (Configure::read('Plugin.CustomAuth_enable') && !Configure::read('Plugin.CustomAuth_required')):
?>
<th><?php echo $this->Paginator->sort('external_auth_required', Configure::read('Plugin.CustomAuth_name') ? Configure::read('Plugin.CustomAuth_name') : 'External authentication');?></th>
<?php
endif;
?>
<th><?php echo $this->Paginator->sort('disabled');?></th>
<th class="actions"><?php echo __('Actions');?></th>
</tr>
<?php
foreach ($users as $user): ?>
<tr <?php echo $user['User']['disabled'] ? 'class="deleted_row"' : '';?>>
<td class="short" ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';">
<?php echo h($user['User']['id']); ?>&nbsp;
</td>
<td class="short" ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';">
<a href="<?php echo $baseurl; ?>/organisations/view/<?php echo $user['Organisation']['id'];?>"><?php echo h($user['Organisation']['name']); ?>&nbsp;</a>
</td>
<td class="short" ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';">
<?php echo $this->Html->link($user['Role']['name'], array('controller' => 'roles', 'action' => 'view', $user['Role']['id'])); ?>
</td>
<td ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';">
<?php echo h($user['User']['email']); ?>&nbsp;
</td>
<?php if (empty(Configure::read('Security.advanced_authkeys'))): ?>
<td class="bold<?= $user['Role']['perm_auth'] ? '' : ' grey'; ?>">
<span class="privacy-value quickSelect" data-hidden-value="<?= h($user['User']['authkey']) ?>">****************************************</span>&nbsp;<i class="privacy-toggle fas fa-eye useCursorPointer" title="<?= __('Reveal hidden value') ?>"></i>
</td>
<?php endif; ?>
<td class="short" ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';">
<?php echo $user['User']['autoalert']? __('Yes') : __('No'); ?>
</td>
<td class="short" ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';">
<?php echo $user['User']['contactalert']? __('Yes') : __('No'); ?>
</td>
<td class="short" ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';">
<?php echo $user['User']['gpgkey']? 'Yes' : 'No'; ?>
</td>
<?php if (Configure::read('SMIME.enabled')): ?>
<td class="short" ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';">
<?php echo $user['User']['certif_public']? __('Yes') : __('No'); ?>
</td>
<?php endif; ?>
<td class="short" ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';">
<?php echo h($user['User']['nids_sid']); ?>&nbsp;
</td>
<td class="short" ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';">
<?php echo ($user['User']['termsaccepted'] == 1) ? __("Yes") : __("No"); ?>
</td>
<td class="short" ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';" title="<?php echo !$user['User']['current_login'] ? __('N/A') : h(date("Y-m-d H:i:s",$user['User']['current_login']));?>">
<?php echo !$user['User']['current_login'] ? __('N/A') : h(date("Y-m-d", $user['User']['current_login'])); ?>&nbsp;
</td>
<td class="short" ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';" title="<?php echo !$user['User']['current_login'] ? 'N/A' : h(date("Y-m-d H:i:s",$user['User']['current_login']));?>">
<?php echo !$user['User']['date_created'] ? __('N/A') : h(date("Y-m-d", $user['User']['date_created'])); ?>&nbsp;
</td>
<?php
if (Configure::read('Plugin.CustomAuth_enable') && !Configure::read('Plugin.CustomAuth_required')):
?>
<td class="short" ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';" title="">
<?php echo ($user['User']['external_auth_required'] ? __('Yes') : __('No')); ?>
</td>
<?php
endif;
?>
<td class="short <?php if ($user['User']['disabled']) echo 'red bold';?>" ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';">
<?php echo ($user['User']['disabled'] ? __('Yes') : __('No')); ?>
</td>
<td class="short action-links">
<?php
if (($isAclAdmin && (($user['User']['org_id'] == $me['org_id'])) || ('1' == $me['id'])) || ($isSiteAdmin)):
?>
<span role="button" tabindex="0" class="fa fa-sync useCursorPointer" onClick="initiatePasswordReset('<?php echo $user['User']['id']; ?>');" title="<?php echo __('Create new credentials and inform user');?>" aria-label="<?php echo __('Create new credentials and inform user');?>"></span>
<?php
echo $this->Html->link('', array('admin' => true, 'action' => 'edit', $user['User']['id']), array('class' => 'fa fa-edit', 'title' => __('Edit'), 'aria-label' => __('Edit')));
echo $this->Form->postLink('', array('admin' => true, 'action' => 'delete', $user['User']['id']), array('class' => 'fa fa-trash', 'title' => __('Delete'), 'aria-label' => __('Delete')), __('Are you sure you want to delete # %s? It is highly recommended to never delete users but to disable them instead.', $user['User']['id']));
endif;
?>
<?php echo $this->Html->link('', array('admin' => true, 'action' => 'view', $user['User']['id']), array('class' => 'fa fa-eye', 'title' => __('View'), 'aria-label' => __('View'))); ?>
</td>
</tr>
<?php
endforeach;
?>
</table>

View File

@ -69,7 +69,16 @@
if (!empty($params['type']) && $params['type'] === 'dropdown') {
$params['type'] = 'select';
}
if (!empty($fieldData['description'])) {
if (!isset($params['class'])) {
$params['class'] = '';
}
$params['class'] .= ' input-with-description';
}
$temp = $this->Form->input($fieldData['field'], $params);
if (!empty($fieldData['description'])) {
$temp .= sprintf('<small class="clear form-field-description apply_css_arrow">%s</small>', h($fieldData['description']));
}
$fieldsArrayForPersistence []= $modelForForm . Inflector::camelize($fieldData['field']);
if (!empty($fieldData['hidden'])) {
$temp = '<span class="hidden">' . $temp . '</span>';

View File

@ -0,0 +1,10 @@
<?php
$data = Hash::extract($row, $field['data_path']);
$html = '';
if (isset($data['country_code'])) {
$html .= $this->Icon->countryFlag($data['country_code']) . '&nbsp;';
}
if ($data['nationality'] !== 'Not specified') {
$html .= h($data['nationality']);
}
echo $html;

View File

@ -2,7 +2,10 @@
$data = Hash::extract($row, $field['data_path']);
if (is_array($data)) {
if (count($data) > 1) {
$data = implode(', ', $data);
$implodeGlue = isset($field['array_implode_glue']) ? $field['array_implode_glue'] : ', ';
$data = implode($implodeGlue, array_map(function($entry) {
return h($entry);
}, $data));
} else {
if (count($data) > 0) {
$data = $data[0];
@ -10,8 +13,7 @@
$data = '';
}
}
}
if (is_bool($data)) {
} else if (is_bool($data)) {
$data = sprintf(
'<i class="black fa fa-%s"></i>',
$data ? 'check' : 'times'

View File

@ -13,7 +13,7 @@
}
}
echo sprintf(
'<input class="select_attribute select" type="checkbox" data-rowid="%s" %s>',
'<input class="select_attribute select" ondblclick="event.stopPropagation();" type="checkbox" data-rowid="%s" %s>',
h($k),
empty($data) ? '' : implode(' ', $data)
);

View File

@ -94,7 +94,7 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
));
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'eventLog',
'url' => $baseurl . '/logs/event_index/' . $eventId,
'url' => $baseurl . (Configure::read('MISP.log_new_audit') ? '/audit_logs/eventIndex/' : '/logs/event_index/') . $eventId,
'text' => __('View Event History')
));
echo $divider;
@ -566,7 +566,8 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
if ($menuItem == 'edit') {
echo $divider;
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'url' => $baseurl . '/admin/allowedlists/edit' . h($id),
'url' => $baseurl . '/admin/allowedlists/edit/' . h($id),
'element_id' => 'edit',
'text' => __('Edit Allowedlist')
));
echo $this->element('/genericElements/SideMenu/side_menu_post_link', array(
@ -1030,6 +1031,12 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
'url' => $baseurl . '/admin/logs/index',
'text' => __('List Logs')
));
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'listAuditLogs',
'url' => $baseurl . '/admin/audit_logs/index',
'text' => __('List Audit Logs'),
'requirement' => Configure::read('MISP.log_new_audit'),
));
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'url' => $baseurl . '/admin/logs/search',
'text' => __('Search Logs')

View File

@ -24,6 +24,7 @@
'disabledSubmitButton' => false, // wether to not draw the submit button
'flag_redraw_chosen' => false, // should chosen picker be redraw at drawing time
'redraw_debounce_time' => 200,
'autofocus' => true,
);
/**
* Supported default option in <Option> fields:
@ -252,7 +253,7 @@ function submitFunction(clicked, callback) {
$flag_addPills = false;
?>
<?php if ($use_select): ?>
<select id="<?php echo $select_id; ?>" autofocus style="height: 100px; margin-bottom: 0px;" <?= $this->GenericPicker->add_select_params($defaults); ?>>
<select id="<?php echo $select_id; ?>"<?= $defaults['autofocus'] ? ' autofocus' : '' ?> style="height: 100px; margin-bottom: 0px;" <?= $this->GenericPicker->add_select_params($defaults); ?>>
<option></option>
<?php
foreach ($items as $param) {
@ -301,7 +302,7 @@ function submitFunction(clicked, callback) {
<?php elseif (count($items) > 0): ?>
<ul class="nav nav-pills">
<select id="<?php echo $select_id; ?>" autofocus style="display: none;" <?php echo h($this->GenericPicker->add_select_params($defaults)); ?>></select>
<select id="<?php echo $select_id; ?>"<?= $defaults['autofocus'] ? ' autofocus' : '' ?> style="display: none;" <?php echo h($this->GenericPicker->add_select_params($defaults)); ?>></select>
<?php
foreach ($items as $param) {
echo $this->GenericPicker->add_pill($param, $defaults);

View File

@ -459,13 +459,18 @@
),
array(
'type' => 'root',
'text' => __('Audit'),
'text' => __('Logs'),
'requirement' => $isAclAudit,
'children' => array(
array(
'text' => __('List Logs'),
'url' => $baseurl . '/admin/logs/index'
),
array(
'text' => __('List Audit Logs'),
'url' => $baseurl . '/admin/audit_logs/index',
'requirement' => Configure::read('MISP.log_new_audit'),
),
array(
'text' => __('Search Logs'),
'url' => $baseurl . '/admin/logs/search'

View File

@ -118,7 +118,6 @@ $(function() {
$('div.notice-pull-rule-fetched.alert-success').show()
},
function(errorMessage) {
showMessage('fail', '<?= __('Could not fetch remote sync filtering rules.') ?>');
var regex = /Reponse was not OK\. \(HTTP code: (?<code>\d+)\)/m
var matches = errorMessage.match(regex)
if (matches !== null) {
@ -129,6 +128,10 @@ $(function() {
$('div.notice-pull-rule-fetched.alert-warning').show().find('.reason').text(errorMessage)
$pickerTags.parent().remove()
$pickerOrgs.parent().remove()
$rootContainer.find('.freetext-button-toggle-tag').collapse('show').remove()
$rootContainer.find('.freetext-button-toggle-org').collapse('show').remove()
$rootContainer.find('.collapse-freetext-tag').removeClass('collapse')
$rootContainer.find('.collapse-freetext-org').removeClass('collapse')
},
function() {
$('div.notice-pull-rule-fetched.alert-primary').hide()
@ -146,10 +149,11 @@ $(function() {
function getPullFilteringRules(callback, failCallback, alwaysCallback) {
$.getJSON('/servers/queryAvailableSyncFilteringRules/' + serverID, function(availableRules) {
callback(availableRules)
})
.fail(function(jqxhr, textStatus, error) {
failCallback(jqxhr.responseJSON.message !== undefined ? jqxhr.responseJSON.message : textStatus)
if (availableRules.error.length == 0) {
callback(availableRules.data)
} else {
failCallback(availableRules.error)
}
})
.always(function() {
alwaysCallback()

View File

@ -12,15 +12,22 @@
}
function mapIDsToObject($data, $ids) {
$result = [];
foreach ($ids as $id) {
foreach ($data as $i => $entry) {
foreach ($ids as $i => $id) {
foreach ($data as $j => $entry) {
if ($id == $entry['id']) {
$result[] = $entry;
unset($data[$i]);
unset($data[$j]);
unset($ids[$i]);
break;
}
}
}
foreach ($ids as $freetextValue) {
$result[] = [
'name' => $freetextValue,
'id' => $freetextValue
];
}
return $result;
}
?>

View File

@ -1,5 +1,6 @@
<?php
$seed = rand();
$pickerDisplayed = false;
?>
<div>
<div style="display: flex;" class="rules-widget-container container-seed-<?= $seed ?> scope-<?= Inflector::pluralize(h($scope)) ?>" data-funname="initRuleWidgetPicker<?= $seed ?>">
@ -29,32 +30,8 @@ $seed = rand();
</div>
<div style="display: flex; margin: 0 0.5em; flex-shrink: 1; padding-top: 20px;">
<div style="display: flex; flex-direction: column;">
<?php if(!isset($disableFreeText) || !$disableFreeText): ?>
<div class="input-prepend input-append">
<button
class="btn"
type="button"
title="<?= __('Move %s to the list of %s to allow', h($scopeI18n), Inflector::pluralize(h($scopeI18n)));?>"
aria-label="<?= __('Move %s to the list of %s to allow', h($scopeI18n), Inflector::pluralize(h($scopeI18n)));?>"
role="button" tabindex="0"
onClick="<?= sprintf("handleFreetextButtonClick('%s', this); ", 'rules-allow') ?>"
>
<i class="<?= $this->FontAwesome->getClass('caret-left') ?>"></i>
</button>
<input type="text" style="" placeholder="<?= sprintf('Freetext %s name', h($scopeI18n)) ?>">
<button
class="btn"
type="button"
title="<?= __('Move %s to the list of %s to block', h($scopeI18n), Inflector::pluralize(h($scopeI18n)));?>"
aria-label="<?= __('Move %s to the list of %s to block', h($scopeI18n), Inflector::pluralize(h($scopeI18n)));?>"
role="button" tabindex="0"
onClick="<?= sprintf("handleFreetextButtonClick('%s', this); ", 'rules-block') ?>"
>
<i class="<?= $this->FontAwesome->getClass('caret-right') ?>"></i>
</button>
</div>
<?php endif; ?>
<?php if(!empty($options) || $allowEmptyOptions): ?>
<?php $pickerDisplayed = true; ?>
<div class="input-prepend input-append">
<button
class="btn"
@ -91,6 +68,46 @@ $seed = rand();
</button>
</div>
<?php endif; ?>
<?php if(!isset($disableFreeText) || !$disableFreeText): ?>
<?php if ($pickerDisplayed): ?>
<a
data-toggle="collapse" data-target="#collapse-freetext-<?= h($scope) ?>-<?= $seed ?>"
class="text-left useCursorPointer freetext-button-toggle-<?= h($scope) ?>"
title="<?= __('This text input allows you to add custom values to the rules') ?>"
>
<i class="fas fa-caret-down fa-rotate"></i>
<?= __('Show freetext input') ?>
</a>
<?php endif; ?>
<div
id="collapse-freetext-<?= h($scope) ?>-<?= $seed ?>"
class="collapse collapse-freetext-<?= h($scope) ?>"
>
<div class="input-prepend input-append" style="margin: 1px;">
<button
class="btn"
type="button"
title="<?= __('Move %s to the list of %s to allow', h($scopeI18n), Inflector::pluralize(h($scopeI18n)));?>"
aria-label="<?= __('Move %s to the list of %s to allow', h($scopeI18n), Inflector::pluralize(h($scopeI18n)));?>"
role="button" tabindex="0"
onClick="<?= sprintf("handleFreetextButtonClick('%s', this); ", 'rules-allow') ?>"
>
<i class="<?= $this->FontAwesome->getClass('caret-left') ?>"></i>
</button>
<input type="text" style="" placeholder="<?= sprintf('Freetext %s name', h($scopeI18n)) ?>">
<button
class="btn"
type="button"
title="<?= __('Move %s to the list of %s to block', h($scopeI18n), Inflector::pluralize(h($scopeI18n)));?>"
aria-label="<?= __('Move %s to the list of %s to block', h($scopeI18n), Inflector::pluralize(h($scopeI18n)));?>"
role="button" tabindex="0"
onClick="<?= sprintf("handleFreetextButtonClick('%s', this); ", 'rules-block') ?>"
>
<i class="<?= $this->FontAwesome->getClass('caret-right') ?>"></i>
</button>
</div>
</div>
<?php endif; ?>
</div>
</div>
<div style="flex-grow: 1;">
@ -122,15 +139,28 @@ $seed = rand();
<script>
function initRuleWidgetPicker<?= $seed ?>() {
$('.container-seed-<?= $seed ?> select.rules-select-picker').chosen()
$('.container-seed-<?= $seed ?> select.rules-select-data').keydown(function(evt) {
var $baseContainer = $('.container-seed-<?= $seed ?>');
$baseContainer.find('select.rules-select-picker').chosen({
placeholder_text_multiple: "<?= __('Select some %s', Inflector::humanize(Inflector::pluralize(h($scopeI18n)))); ?>"
})
$baseContainer.find('select.rules-select-data').keydown(function(evt) {
var $select = $(this)
var $pickerSelect = $select.closest('.rules-widget-container').find('select.rules-select-picker')
if (evt.keyCode === 46) { // <DELETE>
deleteSelectedRules($select, $pickerSelect)
}
});
rebuildRules($('.container-seed-<?= $seed ?>'))
rebuildRules($baseContainer)
$baseContainer.data('initial-rules-allow', $baseContainer.find('.rules-allow').children())
$baseContainer.data('initial-rules-block', $baseContainer.find('.rules-block').children())
$baseContainer.data('resetrulesfun', function() {
$baseContainer.find('.rules-allow').empty().append(
$baseContainer.data('initial-rules-allow')
)
$baseContainer.find('.rules-block').empty().append(
$baseContainer.data('initial-rules-block')
)
})
}
function deleteSelectedRules($select, $pickerSelect) {

View File

@ -30,6 +30,7 @@
'sightings' => __('Sightings'),
'proposals' => __('Proposals'),
'discussion' => __('Posts'),
'report_count' => __('Report count')
];
$columnsMenu = [];

View File

@ -1,5 +1,5 @@
<div class="events index">
<h2><?php echo __('Event with proposals');?></h2>
<h2><?= __('Event with proposals');?></h2>
<div class="pagination">
<ul>
<?php
@ -14,7 +14,7 @@
<th class="filter">
<?php echo $this->Paginator->sort('published');?>
</th>
<th><?php echo $this->Paginator->sort('id', 'Id', array('direction' => 'desc'));?></th>
<th><?php echo $this->Paginator->sort('id', 'ID', array('direction' => 'desc'));?></th>
<th><?php echo $this->Paginator->sort('attribute_count', __('Proposals'));?></th>
<th><?php echo __('Contributors');?></th>
<?php if ($isSiteAdmin): ?>
@ -30,48 +30,38 @@
<?php echo $this->Paginator->sort('distribution');?>
</th>
</tr>
<?php foreach ($events as $event):?>
<tr <?php if ($event['Event']['distribution'] == 0) echo 'class = "privateRed"'?>>
<td class="short" onclick="document.location.href ='<?php echo $baseurl."/events/view/".$event['Event']['id'];?>'">
<?php
if ($event['Event']['published'] == 1) {
?>
<a href="<?php echo $baseurl."/events/view/".$event['Event']['id'] ?>" class = "icon-ok" title = "<?php echo __('View');?>" aria-label = "<?php echo __('View');?>"></a>
<?php
} else {
?>
<a href="<?php echo $baseurl."/events/view/".$event['Event']['id'] ?>" class = "icon-remove" title = "<?php echo __('View');?>" aria-label = "<?php echo __('View');?>"></a>
<?php
}?>&nbsp;
<?php foreach ($events as $event): ?>
<tr<?php if ($event['Event']['distribution'] == 0) echo ' class="privateRed"'?>>
<td class="short dblclickElement">
<a href="<?= $baseurl."/events/view/".$event['Event']['id'] ?>" title="<?= __('View') ?>" aria-label="<?= __('View') ?>"><i class="<?= $event['Event']['published'] ? 'black fa fa-check' : 'black fa fa-times' ?>"></i></a>
</td>
<td class="short">
<a href="<?php echo $baseurl."/events/view/".$event['Event']['id'] ?>"><?php echo $event['Event']['id'];?></a>
<a href="<?= $baseurl."/events/view/".$event['Event']['id'] ?>" class="dblclickActionElement"><?= $event['Event']['id'] ?></a>
</td>
<td class="short" onclick="location.href ='<?php echo $baseurl."/events/view/".$event['Event']['id'];?>'" style="color:red;font-weight:bold;">
<?php echo count($event['ShadowAttribute']); ?>&nbsp;
<td class="short">
<a href="<?= $baseurl."/events/view/".$event['Event']['id'] . '/proposal:1' ?>" style="color:red;font-weight:bold;"><?= $event['Event']['proposal_count']; ?></a>
</td>
<td class="short">
<?php
foreach ($event['orgArray'] as $k => $org) {
echo $this->OrgImg->getOrgImg(array('name' => $orgs[$org], 'id' => $org, 'size' => 24));
if ((1 + $k) < (count($event['orgArray']))) echo '<br />';
if ((1 + $k) < (count($event['orgArray']))) echo '<br>';
}
?>
&nbsp;
</td>
<?php if ('true' == $isSiteAdmin): ?>
<td class="short" onclick="location.href ='<?php echo $baseurl."/events/view/".$event['Event']['id'];?>'">
<?php echo h($event['User']['email']); ?>&nbsp;
<?php if ($isSiteAdmin): ?>
<td class="short dblclickElement">
<?php echo h($event['User']['email']); ?>
</td>
<?php endif; ?>
<td class="short" onclick="location.href ='<?php echo $baseurl."/events/view/".$event['Event']['id'];?>'">
<?php echo $event['Event']['date']; ?>&nbsp;
<td class="short dblclickElement">
<?php echo $event['Event']['date']; ?>
</td>
<td onclick="location.href ='<?php echo $baseurl."/events/view/".$event['Event']['id'];?>'">
<?php echo nl2br(h($event['Event']['info'])); ?>&nbsp;
<td class="dblclickElement">
<?php echo nl2br(h($event['Event']['info'])); ?>
</td>
<td class="short <?php if ($event['Event']['distribution'] == 0) echo 'privateRedText';?>" onclick="location.href ='<?php echo $baseurl."/events/view/".$event['Event']['id'];?>'">
<?php echo $event['Event']['distribution'] != 3 ? $distributionLevels[$event['Event']['distribution']] : 'All';?>
<td class="short dblclickElement <?php if ($event['Event']['distribution'] == 0) echo 'privateRedText';?>">
<?= $event['Event']['distribution'] != 3 ? $distributionLevels[$event['Event']['distribution']] : __('All');?>
</td>
</tr>
<?php endforeach; ?>
@ -93,5 +83,4 @@
</ul>
</div>
</div>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'event-collection', 'menuItem' => 'viewProposalIndex'));
<?= $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'event-collection', 'menuItem' => 'viewProposalIndex'));

View File

@ -82,8 +82,12 @@
$contributorsContent = [];
foreach ($contributors as $organisationId => $name) {
$org = ['Organisation' => ['id' => $organisationId, 'name' => $name]];
$link = $baseurl . "/logs/event_index/" . $event['Event']['id'] . '/' . h($name);
$contributorsContent[] = $this->OrgImg->getNameWithImg($org, $link);
if (Configure::read('MISP.log_new_audit')) {
$link = $baseurl . "/audit_logs/eventIndex/" . h($event['Event']['id']) . '/' . h($organisationId);
} else {
$link = $baseurl . "/logs/event_index/" . h($event['Event']['id']) . '/' . h($name);
}
$contributorsContent[] = $this->OrgImg->getNameWithImg($org, $link);
}
$table_data[] = array(
'key' => __('Contributors'),

View File

@ -226,6 +226,9 @@
'data' => [
'title' => __('Set PULL rules'),
'content' => [
[
'html' => sprintf('<h5 style="font-weight: normal;"><i>%s</i></h5>', __('Configure the rules to be applied when PULLing data to the server'))
],
[
'html' => $this->element('serverRuleElements/pull', [
'context' => 'feeds',
@ -259,15 +262,24 @@ var modelContext = 'Feed';
$(document).ready(function() {
feedDistributionChange();
$("#pull_modify").click(function() {
$('#genericModal.pull-rule-modal').modal().on('shown', function () {
var $containers = $(this).find('.rules-widget-container')
$containers.each(function() {
var initFun = $(this).data('funname');
if (typeof window[initFun] === 'function') {
window[initFun]()
}
$('#genericModal.pull-rule-modal').modal()
.on('shown', function () {
var $containers = $(this).find('.rules-widget-container')
$containers.each(function() {
var initFun = $(this).data('funname');
if (typeof window[initFun] === 'function') {
window[initFun]()
}
})
})
});
.on('hidden', function () {
var $containers = $(this).find('.rules-widget-container')
$containers.each(function() {
if ($(this).data('resetrulesfun') !== undefined) {
$(this).data('resetrulesfun')()
}
})
});
});
$("#FeedDistribution").change(function() {
feedDistributionChange();

View File

@ -229,6 +229,9 @@
'data' => [
'title' => __('Set PULL rules'),
'content' => [
[
'html' => sprintf('<h5 style="font-weight: normal;"><i>%s</i></h5>', __('Configure the rules to be applied when PULLing data from the server'))
],
[
'html' => $this->element('serverRuleElements/pull', [
'context' => 'feeds',
@ -280,18 +283,27 @@ $(document).ready(function() {
rules = convertServerFilterRules(rules);
feedDistributionChange();
$("#pull_modify").click(function() {
$('#genericModal.pull-rule-modal').modal().on('shown', function () {
var $containers = $(this).find('.rules-widget-container')
$containers.each(function() {
var initFun = $(this).data('funname');
if (typeof window[initFun] === 'function') {
window[initFun]()
$('#genericModal.pull-rule-modal').modal()
.on('shown', function () {
var $containers = $(this).find('.rules-widget-container')
$containers.each(function() {
var initFun = $(this).data('funname');
if (typeof window[initFun] === 'function') {
window[initFun]()
}
})
if (typeof window['cm'] === "object") {
window['cm'].refresh()
}
})
if (typeof window['cm'] === "object") {
window['cm'].refresh()
}
});
.on('hidden', function () {
var $containers = $(this).find('.rules-widget-container')
$containers.each(function() {
if ($(this).data('resetrulesfun') !== undefined) {
$(this).data('resetrulesfun')()
}
})
});
});
$("#FeedDistribution").change(function() {
feedDistributionChange();

View File

@ -1,53 +1,90 @@
<div class="users form">
<?php echo $this->Form->create('Organisation', array('enctype' => 'multipart/form-data'));?>
<fieldset>
<legend><?php echo __('New Organisation'); ?></legend>
<p style="font-weight:bold;"><?php echo __('If the organisation should have access to this instance, make sure that the Local organisation setting is checked. <br />If you would only like to add a known external organisation for inclusion in sharing groups, uncheck the Local organisation setting.');?></p>
<div style="float:left;width:345px;">
<?php echo $this->Form->input('local', array('label' => __('Local organisation'), 'checked' => true));?>
</div>
<div class="clear"></div>
<hr />
<p style="font-weight:bold;"><?php echo __('Mandatory fields.');?></p>
<div style="float:left;width:345px;">
<?php
echo $this->Form->input('name', array('div' => 'clear', 'style' => 'width:320px;','label' => __('Organisation Identifier'), 'placeholder' => __('Brief organisation identifier')));
?>
</div>
<div class="clear"></div>
<div style="float:left;width:425px;">
<?php
echo $this->Form->input('uuid', array('div' => 'clear', 'label' => __('UUID'), 'placeholder' => __('Paste UUID or click generate'), 'style' => 'width:405px;'));
?>
</div>
<span class="btn btn-inverse" role="button" tabindex="0" aria-label="<?php echo __('Generate UUID');?>" title="<?php echo __('Generate a new UUID for the organisation');?>" style="margin-top:25px;" onClick="generateOrgUUID();"><?php echo __('Generate UUID');?></span>
<?php
echo $this->Form->input('description', array('label' => __('A brief description of the organisation'), 'div' => 'clear', 'class' => 'input-xxlarge', 'type' => 'textarea', 'placeholder' => __('A description of the organisation that is purely informational.')));
?>
<?php
echo $this->Form->input('restricted_to_domain', array('label' => __('Bind user accounts to domains (line separated)'), 'div' => 'clear', 'class' => 'input-xxlarge', 'type' => 'textarea', 'placeholder' => __('Enter a (list of) domain name(s) to enforce when creating users.')));
?>
<hr />
<p style="font-weight:bold;"><?php echo __('The following fields are all optional.');?></p>
<?php
echo $this->Form->input('logo', array(
'error' => array('escape' => false),
'type' => 'file',
'label' => __('Logo (48×48 PNG or SVG)')
));
?>
<div class="clear"></div>
<?php
echo $this->Form->input('nationality', array('options' => $countries));
echo $this->Form->input('sector', array('placeholder' => __ ('For example "financial".'), 'style' => 'width:300px;'));
echo $this->Form->input('type', array('class' => 'input-xxlarge', 'label' => __('Type of organisation'), 'div' => 'clear', 'placeholder' => __('Freetext description of the org.')));
echo $this->Form->input('contacts', array('class' => 'input-xxlarge', 'type' => 'textarea', 'div' => 'clear', 'placeholder' => __('You can add some contact details for the organisation here, if applicable.')));
?>
</fieldset>
<?php echo $this->Form->button(__('Submit'), array('class' => 'btn btn-primary'));
echo $this->Form->end();?>
</div>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'admin', 'menuItem' => 'addOrg'));
?>
$modelForForm = 'Organisation';
echo $this->element('genericElements/Form/genericForm', [
'form' => $this->Form,
'formOptions' => [
'enctype' => 'multipart/form-data',
],
'data' => [
'model' => $modelForForm,
'title' => __('%s Organisation', Inflector::Humanize($action)),
'fields' => [
sprintf('<h4>%s</h4>', __('Mandatory Fields')),
[
'default' => true,
'type' => 'checkbox',
'field' => 'local',
'label' => __('Local organisation'),
'description' => __('If the organisation should have access to this instance, make sure that the Local organisation setting is checked. If you would only like to add a known external organisation for inclusion in sharing groups, uncheck the Local organisation setting.')
],
[
'field' => 'name',
'label' => __('Organisation Identifier'),
'placeholder' => __('Brief organisation identifier'),
'class' => 'input-xxlarge',
],
[
'field' => 'uuid',
'label' => __('UUID'),
'placeholder' => __('Paste UUID or click generate'),
'stayInLine' => true,
'class' => 'input-xxlarge'
],
sprintf('<span class="btn btn-inverse" role="button" tabindex="0" aria-label="%s" title="%s" style="margin-top:25px;margin-left: -132px;border-top-left-radius: 0;border-bottom-left-radius: 0;" onClick="generateOrgUUID();">%s</span>', __('Generate UUID'), __('Generate a new UUID for the organisation'), __('Generate UUID')),
sprintf('<h4>%s</h4>', __('Optional Fields')),
[
'field' => 'description',
'type' => 'textarea',
'label' => __('A brief description of the organisation'),
'placeholder' => __('A description of the organisation that is purely informational.'),
'class' => 'input-xxlarge',
],
[
'field' => 'restricted_to_domain',
'type' => 'textarea',
'label' => __('Bind user accounts to domains (line separated)'),
'placeholder' => __('Enter a (list of) domain name(s) to enforce when creating users.'),
'class' => 'input-xxlarge',
],
[
'type' => 'file',
'field' => 'logo',
'error' => array('escape' => false),
'label' => __('Logo (48×48 PNG or SVG)'),
],
[
'field' => 'nationality',
'options' => $countries,
'class' => 'span4',
'stayInLine' => 1,
],
[
'field' => 'sector',
'placeholder' => __('For example "financial".'),
'class' => 'span3',
],
[
'field' => 'type',
'label' => __('Type of organisation'),
'placeholder' => __('Freetext description of the org.'),
'class' => 'input-xxlarge',
],
[
'field' => 'contacts',
'type' => 'textarea',
'label' => __('Type of organisation'),
'placeholder' => __('You can add some contact details for the organisation here, if applicable.'),
'class' => 'input-xxlarge',
],
],
'submit' => [
'action' => $this->request->params['action']
]
]
]);
echo $this->element('/genericElements/SideMenu/side_menu', [
'menuList' => 'admin',
'menuItem' => $action === 'add' ? 'addOrg' : 'editOrg',
'orgId' => $action === 'edit' ? $orgId : 0,
]);

View File

@ -1,64 +0,0 @@
<div class="users form">
<?php echo $this->Form->create('Organisation', array('enctype' => 'multipart/form-data'));?>
<fieldset>
<legend><?php echo __('Edit Organisation'); ?></legend>
<p style="font-weight:bold;"><?php echo __('If the organisation should have access to this instance, make sure that the Local organisation setting is checked. <br />If you would only like to add a known external organisation for inclusion in sharing groups, uncheck the Local organisation setting.');?></p>
<div style="float:left;width:345px;">
<?php echo $this->Form->input('local', array('label' => __('Local organisation')));?>
</div>
<div class="clear"></div>
<hr />
<p style="font-weight:bold;"><?php echo __('Mandatory fields. Leave the UUID field empty if the organisation doesn\'t have a UUID from another instance.');?></p>
<div style="float:left;width:345px;">
<?php
echo $this->Form->input('name', array('div' => 'clear', 'style' => 'width:320px;','label' => __('Organisation Identifier'), 'placeholder' => __('Brief organisation identifier')));
?>
</div>
<div class="clear"></div>
<div style="float:left;width:425px;">
<?php
echo $this->Form->input('uuid', array('div' => 'clear', 'label' => __('UUID'), 'placeholder' => __('Paste UUID or click generate'), 'style' => 'width:405px;'));
?>
</div>
<span role="button" tabindex="0" aria-label="<?php echo __('Generate a new UUID for the organisation');?>" title="<?php echo __('Generate UUID');?>" class="btn btn-inverse" style="margin-top:25px;" onClick="generateOrgUUID();"><?php echo __('Generate UUID');?></span>
<?php
if (!empty($duplicate_org)):
?>
<div class="clear"></div>
<span class="bold red">
<?php echo __('An organisation with the above uuid already exists. Would you like to merge this organisation into the existing one?');?>
</span>
<a href="#" onClick="getPopup('<?php echo h($id) . '/' . h($duplicate_org); ?>', 'organisations', 'merge', 'admin');"><?php echo __('Click here'); ?></a>
<div class="clear"></div>
<?php
endif;
?>
<?php
echo $this->Form->input('description', array('label' => __('A brief description of the organisation'), 'div' => 'clear', 'class' => 'input-xxlarge', 'type' => 'textarea', 'placeholder' => __('A description of the organisation that is purely informational.')));
?>
<?php
echo $this->Form->input('restricted_to_domain', array('label' => __('Bind user accounts to domains (line separated)'), 'div' => 'clear', 'class' => 'input-xxlarge', 'type' => 'textarea', 'placeholder' => __('Enter a (list of) domain name(s) to enforce when creating users.')));
?>
<hr />
<p style="font-weight:bold;"><?php echo __('The following fields are all optional.');?></p>
<?php
echo $this->Form->input('logo', array(
'error' => array('escape' => false),
'type' => 'file',
'label' => __('Logo (48×48 PNG or SVG)')
));
?>
<div class="clear"></div>
<?php
echo $this->Form->input('nationality', array('options' => $countries));
echo $this->Form->input('sector', array('placeholder' => __('For example "financial".'), 'style' => 'width:300px;'));
echo $this->Form->input('type', array('class' => 'input-xxlarge', 'label' => __('Type of organisation'), 'div' => 'clear', 'placeholder' => __('Freetext description of the org.')));
echo $this->Form->input('contacts', array('class' => 'input-xxlarge', 'label' => __('Contacts'), 'type' => 'textarea', 'div' => 'clear', 'placeholder' => __('You can add some contact details for the organisation here, if applicable.')));
?>
</fieldset>
<?php echo $this->Form->button(__('Submit'), array('class' => 'btn btn-primary'));
echo $this->Form->end();?>
</div>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'admin', 'menuItem' => 'editOrg', 'orgId' => $orgId));
?>

View File

@ -1,183 +1,160 @@
<div class="organisations index">
<?php
$texts = array(
'all' => array(
'text' => __('All organisations'),
'extra' => __(', both local and remote')
),
'external' => array(
'text' => __('Known remote organisations'),
'extra' => __(' on other instances')
),
'local' => array(
'text' => __('Local organisations'),
'extra' => __(' having a presence on this instance')
),
);
if (!in_array($scope, array_keys($texts))) $scope = 'local';
$partial = array();
foreach($named as $key => $value):
if ($key == 'page' || $key == 'viewall'):
continue;
endif;
$partial[] = h($key) . ':' . h($value);
endforeach;
$viewall_button_text = __('Paginate');
if (!$viewall):
$viewall_button_text = __('View all');
$partial[] = 'viewall:1';
endif;
?>
<h2><?php echo $texts[$scope]['text'] . $texts[$scope]['extra']; ?></h2>
<div class="pagination">
<ul>
<?php
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
<li class="all">
<a href="<?php echo $baseurl . '/organisations/index/' . implode('/', $partial); ?>"><?php echo $viewall_button_text; ?></a>
</li>
</ul>
</div>
<?php
$data = array(
'children' => array(
array(
'children' => array(
array(
'text' => __('Local organisations'),
$fullTitle = [
'local' => [
'main' => __('Local organisations'),
'extra' => __(', both local and remote'),
],
'external' => [
'main' => __('Known remote organisations'),
'extra' => __(' on other instances'),
],
'all' => [
'main' => __('All organisations'),
'extra' => __(' having a presence on this instance'),
]
];
echo '<div class="index">';
echo $this->element('/genericElements/IndexTable/index_table', [
'data' => [
'data' => $orgs,
'top_bar' => [
'children' => [
[
'children' => [
[
'text' => $fullTitle['local']['main'],
'active' => $scope === 'local',
'url' => $baseurl . '/organisations/index/scope:local'
),
array(
'text' => __('Known remote organisations'),
],
[
'text' => $fullTitle['external']['main'],
'active' => $scope === 'external',
'url' => $baseurl . '/organisations/index/scope:external'
),
array(
'text' => __('All organisations'),
],
[
'text' => $fullTitle['all']['main'],
'active' => $scope === 'all',
'url' => $baseurl . '/organisations/index/scope:all'
),
)
),
array(
],
]
],
[
'type' => 'search',
'button' => __('Filter'),
'placeholder' => __('Enter value to search'),
'data' => '',
)
)
);
echo $this->element('/genericElements/ListTopBar/scaffold', array('data' => $data));
?>
<table class="table table-striped table-hover table-condensed">
<tr>
<th><?php echo $this->Paginator->sort('id');?></th>
<th><?php echo __('Logo');?></th>
<th><?php echo $this->Paginator->sort('name');?></th>
<?php if ($isSiteAdmin): ?>
<th><?php echo $this->Paginator->sort('uuid', 'UUID');?></th>
<?php endif; ?>
<th><?php echo $this->Paginator->sort('description');?></th>
<th><?php echo $this->Paginator->sort('nationality');?></th>
<th><?php echo $this->Paginator->sort('sector');?></th>
<th><?php echo $this->Paginator->sort('type');?></th>
<th><?php echo $this->Paginator->sort('contacts');?></th>
<?php if ($isSiteAdmin): ?>
<th><?php echo __('Added by');?></th>
<?php endif; ?>
<th><?php echo $this->Paginator->sort('local');?></th>
<th><?= $this->Paginator->sort('user_count', __('Users'));?></th>
<th><?php echo $this->Paginator->sort('restrictions');?></th>
<th class="actions"><?php echo __('Actions');?></th>
</tr>
<?php
foreach ($orgs as $org): ?>
<tr>
<td class="short" ondblclick="document.location.href ='<?php echo $baseurl . "/organisations/view/" . $org['Organisation']['id'];?>'"><?php echo h($org['Organisation']['id']); ?></td>
<td class="short" ondblclick="document.location.href ='<?php echo $baseurl . "/organisations/view/" . $org['Organisation']['id'];?>'">
<?= $this->OrgImg->getOrgLogo($org, 24) ?>
</td>
<td class="short" ondblclick="document.location.href ='<?php echo $baseurl . "/organisations/view/" . $org['Organisation']['id'];?>'"><?php echo h($org['Organisation']['name']); ?></td>
<?php if ($isSiteAdmin): ?>
<td class="short" ondblclick="document.location.href ='<?php echo $baseurl . "/organisations/view/" . $org['Organisation']['id'];?>'">
<span class="quickSelect"><?php echo h($org['Organisation']['uuid']); ?></span>
</td>
<?php endif; ?>
<td ondblclick="document.location.href ='<?php echo $baseurl . "/organisations/view/" . $org['Organisation']['id'];?>'"><?php echo h($org['Organisation']['description']); ?></td>
<td class="short" ondblclick="document.location.href ='<?php echo $baseurl . "/organisations/view/" . $org['Organisation']['id'];?>'"><?php
if (isset($org['Organisation']['country_code'])) {
echo $this->Icon->countryFlag($org['Organisation']['country_code']) . '&nbsp;';
}
if ($org['Organisation']['nationality'] !== 'Not specified') {
echo h($org['Organisation']['nationality']);
}
?></td>
<td class="short" ondblclick="document.location.href ='<?php echo $baseurl . "/organisations/view/" . $org['Organisation']['id'];?>'"><?php echo h($org['Organisation']['sector']); ?></td>
<td class="short" ondblclick="document.location.href ='<?php echo $baseurl . "/organisations/view/" . $org['Organisation']['id'];?>'"><?php echo h($org['Organisation']['type']); ?></td>
<td><?php echo h($org['Organisation']['contacts']); ?></td>
<?php if ($isSiteAdmin): ?>
<td class="short" ondblclick="document.location.href ='<?php echo $baseurl . "/organisations/view/" . $org['Organisation']['id'];?>'">
<?php echo (isset($org['Organisation']['created_by_email'])) ? h($org['Organisation']['created_by_email']) : '&nbsp;'; ?>
</td>
<?php endif; ?>
<td class="short <?php echo $org['Organisation']['local'] ? 'green' : 'red';?>" ondblclick="document.location.href ='<?php echo $baseurl . "/organisations/view/" . $org['Organisation']['id'];?>'"><?php echo $org['Organisation']['local'] ? __('Yes') : __('No');?></td>
<td class="short"><?php echo isset($org['Organisation']['user_count']) ? $org['Organisation']['user_count'] : '0';?></td>
<td class="short">
<?php
if (!empty($org['Organisation']['restricted_to_domain'])) {
echo implode('<br />', h($org['Organisation']['restricted_to_domain']));
}
?>
</td>
<td class="short action-links">
<?php if ($isSiteAdmin): ?>
<a href='<?php echo $baseurl . "/admin/organisations/edit/" . $org['Organisation']['id'];?>' class = "fa fa-edit" title = "<?php echo __('Edit');?>" aria-label = "<?php echo __('Edit');?>"></a>
<?php
echo $this->Form->postLink('', array('admin' => true, 'action' => 'delete', $org['Organisation']['id']), array('class' => 'fa fa-trash', 'title' => __('Delete'), 'aria-label' => __('Delete')), __('Are you sure you want to delete %s?', $org['Organisation']['name']));
?>
<?php endif; ?>
<a href='<?php echo $baseurl . "/organisations/view/" . $org['Organisation']['id']; ?>' class = "fa fa-eye" title = "<?php echo __('View');?>" aria-label = "<?php echo __('View');?>"></a>
</td>
</tr>
<?php
endforeach; ?>
</table>
<p>
<?php
echo $this->Paginator->counter(array(
'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}')
));
?>
</p>
<div class="pagination">
<ul>
<?php
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
<li class="all">
<a href="<?php echo $baseurl . '/organisations/index/' . implode('/', $partial); ?>"><?php echo $viewall_button_text; ?></a>
</li>
</ul>
</div>
</div>
<script type="text/javascript">
var passedArgsArray = <?php echo $passedArgs; ?>;
$(document).ready(function() {
$('.searchFilterButton').click(function() {
runIndexFilter(this);
});
$('#quickFilterButton').click(function() {
runIndexQuickFilter('/scope:<?php echo h($scope); ?>');
});
});
</script>
<?php
if ($isSiteAdmin) echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'admin', 'menuItem' => 'indexOrg'));
else echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'globalActions', 'menuItem' => 'indexOrg'));
'cancel' => array(
'fa-icon' => 'times',
'title' => __('Remove filters'),
'onClick' => 'cancelSearch',
)
]
],
],
'title' => $fullTitle[$scope]['main'] . $fullTitle[$scope]['extra'],
'primary_id_path' => 'Organisation.id',
'fields' => [
[
'name' => __('ID'),
'sort' => 'id',
'class' => 'short',
'data_path' => 'Organisation.id',
'element' => 'links',
'url' => $baseurl . '/organisations/view/%s'
],
[
'name' => __('Name'),
'sort' => 'name',
'data_path' => 'Organisation.name',
],
[
'name' => __('UUID'),
'sort' => 'uuid',
'data_path' => 'Organisation.uuid',
'class' => 'quickSelect',
'requirements' => $isSiteAdmin
],
[
'name' => __('Description'),
'data_path' => 'Organisation.description',
],
[
'name' => __('Nationality'),
'data_path' => 'Organisation',
'class' => 'short',
'element' => 'country',
],
[
'name' => __('Sector'),
'data_path' => 'Organisation.sector',
],
[
'name' => __('Type'),
'data_path' => 'Organisation.type',
],
[
'name' => __('Contacts'),
'data_path' => 'Organisation.contacts',
],
[
'name' => __('Added by'),
'sort' => 'created_by_email',
'data_path' => 'Organisation.created_by_email',
'requirements' => $isSiteAdmin
],
[
'name' => __('Local'),
'sort' => 'local',
'element' => 'boolean',
'data_path' => 'Organisation.local',
'colors' => true,
],
[
'name' => __('Users'),
'sort' => 'user_count',
'data_path' => 'Organisation.user_count',
],
[
'name' => __('Restrictions'),
'sort' => 'restricted_to_domain',
'data_path' => 'Organisation.restricted_to_domain',
'array_implode_glue' => '<br/>',
],
],
'actions' => [
[
'url' => '/organisations/view',
'url_params_data_paths' => [
'Organisation.id'
],
'icon' => 'eye',
'title' => __('View'),
'dbclickAction' => true,
],
[
'url' => '/admin/organisations/edit',
'url_params_data_paths' => [
'Organisation.id'
],
'icon' => 'edit',
'title' => __('Edit'),
'requirements' => $isSiteAdmin
],
[
'title' => __('Delete'),
'icon' => 'trash',
'url' => '/admin/organisations/delete',
'url_params_data_paths' => array('Organisation.id'),
'postLink' => true,
'postLinkConfirm' => __('Are you sure you want to delete the Organisation?'),
'requirements' => $isSiteAdmin
],
]
]
]);
echo '</div>';
if ($isSiteAdmin) {
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'admin', 'menuItem' => 'indexOrg'));
} else {
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'globalActions', 'menuItem' => 'indexOrg'));
}

View File

@ -187,6 +187,9 @@
'data' => [
'title' => __('Set PUSH rules'),
'content' => [
[
'html' => sprintf('<h5 style="font-weight: normal;"><i>%s</i></h5>', __('Configure the rules to be applied when PUSHing data to the server'))
],
[
'html' => $this->element('serverRuleElements/push', [
'allTags' => $allTags,
@ -205,7 +208,8 @@
];
echo $this->element('genericElements/infoModal', $modalData);
$modalData['data']['title'] = __('Set PULL rules');
$modalData['data']['content'][0]['html'] = $this->element('serverRuleElements/pull', [
$modalData['data']['content'][1]['html'] = sprintf('<h5 style="font-weight: normal;"><i>%s</i></h5>', __('Configure the rules to be applied when PULLing data from the server'));
$modalData['data']['content'][1]['html'] = $this->element('serverRuleElements/pull', [
'context' => 'servers',
'ruleObject' => $pullRules
]);
@ -275,29 +279,47 @@ $(document).ready(function() {
});
rules = convertServerFilterRules(rules);
$("#push_modify").click(function() {
$('#genericModal.push-rule-modal').modal().on('shown', function () {
var $containers = $(this).find('.rules-widget-container')
$containers.each(function() {
var initFun = $(this).data('funname');
if (typeof window[initFun] === 'function') {
window[initFun]()
}
$('#genericModal.push-rule-modal').modal()
.on('shown', function () {
var $containers = $(this).find('.rules-widget-container')
$containers.each(function() {
var initFun = $(this).data('funname');
if (typeof window[initFun] === 'function') {
window[initFun]()
}
})
})
});
.on('hidden', function () {
var $containers = $(this).find('.rules-widget-container')
$containers.each(function() {
if ($(this).data('resetrulesfun') !== undefined) {
$(this).data('resetrulesfun')()
}
})
});
});
$("#pull_modify").click(function() {
$('#genericModal.pull-rule-modal').modal().on('shown', function () {
var $containers = $(this).find('.rules-widget-container')
$containers.each(function() {
var initFun = $(this).data('funname');
if (typeof window[initFun] === 'function') {
window[initFun]()
$('#genericModal.pull-rule-modal').modal()
.on('shown', function () {
var $containers = $(this).find('.rules-widget-container')
$containers.each(function() {
var initFun = $(this).data('funname');
if (typeof window[initFun] === 'function') {
window[initFun]()
}
})
if (typeof window['cm'] === "object") {
window['cm'].refresh()
}
})
if (typeof window['cm'] === "object") {
window['cm'].refresh()
}
});
.on('hidden', function () {
var $containers = $(this).find('.rules-widget-container')
$containers.each(function() {
if ($(this).data('resetrulesfun') !== undefined) {
$(this).data('resetrulesfun')()
}
})
});
});
$('#add_cert_file').click(function() {

View File

@ -1,181 +1,229 @@
<?php
echo '<div class="index">';
$description = __(
'Click %s to reset the API keys of all sync and org admin users in one shot. This will also automatically inform them of their new API keys.',
$this->Form->postLink(
__('here'),
$baseurl . '/users/resetAllSyncAuthKeys',
array(
'title' => __('Reset all sync user API keys'),
'aria-label' => __('Reset all sync user API keys'),
'class' => 'bold'
),
__('Are you sure you wish to reset the API keys of all users with sync privileges?')
)
);
$multiSelectField = array();
if (!$this->request->is('ajax')) {
// Allow reset Keys, filtering and searching if viewing the /users/index page
echo '<div class="index">';
$description = __(
'Click %s to reset the API keys of all sync and org admin users in one shot. This will also automatically inform them of their new API keys.',
$this->Form->postLink(
__('here'),
$baseurl . '/users/resetAllSyncAuthKeys',
array(
'title' => __('Reset all sync user API keys'),
'aria-label' => __('Reset all sync user API keys'),
'class' => 'bold'
),
__('Are you sure you wish to reset the API keys of all users with sync privileges?')
)
);
$topBar = array(
'children' => array(
array(
'children' => array(
array(
'class' => 'hidden mass-select',
'text' => __('Disable selected users'),
'onClick' => "multiSelectToggleField",
'onClickParams' => array('admin/users', 'massToggleField', 'disabled', '1')
),
array(
'class' => 'hidden mass-select',
'text' => __('Enable selected users'),
'onClick' => "multiSelectToggleField",
'onClickParams' => array('admin/users', 'massToggleField', 'disabled', '0')
),
array(
'class' => 'hidden mass-select',
'text' => __('Disable publish emailing'),
'onClick' => "multiSelectToggleField",
'onClickParams' => array('admin/users', 'massToggleField', 'autoalert', '0')
),
array(
'class' => 'hidden mass-select',
'text' => __('Enable publish emailing'),
'onClick' => "multiSelectToggleField",
'onClickParams' => array('admin/users', 'massToggleField', 'autoalert', '1')
),
)
),
array(
'type' => 'simple',
'children' => array(
array(
'id' => 'create-button',
'title' => __('Modify filters'),
'fa-icon' => 'search',
'onClick' => 'getPopup',
'onClickParams' => array($urlparams, 'admin/users', 'filterUserIndex')
)
)
),
array(
'type' => 'simple',
'children' => array(
array(
'url' => $baseurl . '/admin/users/index',
'text' => __('All'),
'active' => !isset($passedArgsArray['disabled']),
),
array(
'url' => $baseurl . '/admin/users/index/searchdisabled:0',
'text' => __('Active'),
'active' => isset($passedArgsArray['disabled']) && $passedArgsArray['disabled'] === "0",
),
array(
'url' => $baseurl . '/admin/users/index/searchdisabled:1',
'text' => __('Disabled'),
'active' => isset($passedArgsArray['disabled']) && $passedArgsArray['disabled'] === "1",
)
)
),
array(
'type' => 'search',
'button' => __('Filter'),
'placeholder' => __('Enter value to search'),
'searchKey' => 'value',
)
)
);
$multiSelectField = array(array(
'element' => 'selector',
'class' => 'short',
'data' => array(
'id' => array(
'value_path' => 'User.id'
)
)
));
} else {
$description = '';
$topBar = [];
}
echo $this->element('/genericElements/IndexTable/index_table', array(
'data' => array(
'data' => $users,
'top_bar' => array(
'children' => array(
'top_bar' => $topBar,
'fields' => array_merge(
$multiSelectField,
array(
array(
'type' => 'simple',
'children' => array(
array(
'id' => 'create-button',
'title' => __('Modify filters'),
'fa-icon' => 'search',
'onClick' => 'getPopup',
'onClickParams' => array($urlparams, 'admin/users', 'filterUserIndex')
)
)
'name' => __('ID'),
'sort' => 'id',
'class' => 'short',
'data_path' => 'User.id'
),
array(
'type' => 'simple',
'children' => array(
array(
'url' => $baseurl . '/admin/users/index',
'text' => __('All'),
'active' => !isset($passedArgsArray['disabled']),
),
array(
'url' => $baseurl . '/admin/users/index/searchdisabled:0',
'text' => __('Active'),
'active' => isset($passedArgsArray['disabled']) && $passedArgsArray['disabled'] === "0",
),
array(
'url' => $baseurl . '/admin/users/index/searchdisabled:1',
'text' => __('Disabled'),
'active' => isset($passedArgsArray['disabled']) && $passedArgsArray['disabled'] === "1",
)
)
'name' => __('Org'),
'sort' => 'User.org_id',
'element' => 'org',
'data_path' => 'Organisation'
),
array(
'type' => 'search',
'button' => __('Filter'),
'placeholder' => __('Enter value to search'),
'searchKey' => 'value',
'name' => __('Role'),
'sort' => 'User.role_id',
'class' => 'short',
'element' => 'role',
'data_path' => 'Role'
),
array(
'name' => __('Email'),
'sort' => 'User.email',
'data_path' => 'User.email'
),
array(
'name' => __('Authkey'),
'sort' => 'User.authkey',
'class' => 'bold quickSelect',
'data_path' => 'User.authkey',
'privacy' => 1,
'requirement' => empty(Configure::read('Security.advanced_authkeys'))
),
array(
'name' => __('Event alert'),
'element' => 'boolean',
'sort' => 'User.autoalert',
'class' => 'short',
'data_path' => 'User.autoalert'
),
array(
'name' => __('Contact alert'),
'element' => 'boolean',
'sort' => 'User.contactalert',
'class' => 'short',
'data_path' => 'User.contactalert'
),
array(
'name' => __('PGP Key'),
'element' => 'boolean',
'sort' => 'User.gpgkey',
'class' => 'short',
'data_path' => 'User.gpgkey'
),
array(
'name' => __('S/MIME'),
'element' => 'boolean',
'sort' => 'User.certif_public',
'class' => 'short',
'data_path' => 'User.certif_public',
'requirement' => Configure::read('SMIME.enabled')
),
array(
'name' => __('NIDS SID'),
'sort' => 'User.nids_sid',
'class' => 'short',
'data_path' => 'User.nids_sid'
),
array(
'name' => __('Terms Accepted'),
'element' => 'boolean',
'sort' => 'User.termsaccepted',
'class' => 'short',
'data_path' => 'User.termsaccepted'
),
array(
'name' => __('Last Login'),
'sort' => 'User.current_login',
'element' => 'datetime',
'empty' => __('Never'),
'class' => 'short',
'data_path' => 'User.current_login'
),
array(
'name' => __('Created'),
'sort' => 'User.date_created',
'element' => 'datetime',
'class' => 'short',
'data_path' => 'User.date_created'
),
array(
'name' => (Configure::read('Plugin.CustomAuth_name') ? Configure::read('Plugin.CustomAuth_name') : __('External Auth')),
'sort' => 'User.external_auth_required',
'element' => 'boolean',
'class' => 'short',
'data_path' => 'User.external_auth_required',
'requirement' => (Configure::read('Plugin.CustomAuth_enable') && empty(Configure::read('Plugin.CustomAuth_required')))
),
array(
'name' => __('Monitored'),
'element' => 'toggle',
'url' => $baseurl . '/admin/users/monitor',
'url_params_data_paths' => array(
'User.id'
),
'sort' => 'User.disabled',
'class' => 'short',
'data_path' => 'User.monitored',
'requirement' => $isSiteAdmin && Configure::read('Security.user_monitoring_enabled')
),
array(
'name' => __('Disabled'),
'element' => 'boolean',
'sort' => 'User.disabled',
'class' => 'short',
'data_path' => 'User.disabled'
)
)
),
'fields' => array(
array(
'name' => __('ID'),
'sort' => 'id',
'class' => 'short',
'data_path' => 'User.id'
),
array(
'name' => __('Org'),
'sort' => 'User.org_id',
'element' => 'org',
'data_path' => 'Organisation'
),
array(
'name' => __('Role'),
'sort' => 'User.role_id',
'class' => 'short',
'element' => 'role',
'data_path' => 'Role'
),
array(
'name' => __('Email'),
'sort' => 'User.email',
'data_path' => 'User.email'
),
array(
'name' => __('Authkey'),
'sort' => 'User.authkey',
'class' => 'bold quickSelect',
'data_path' => 'User.authkey',
'privacy' => 1,
'requirement' => empty(Configure::read('Security.advanced_authkeys'))
),
array(
'name' => __('Event alert'),
'element' => 'boolean',
'sort' => 'User.autoalert',
'class' => 'short',
'data_path' => 'User.autoalert'
),
array(
'name' => __('Contact alert'),
'element' => 'boolean',
'sort' => 'User.contactalert',
'class' => 'short',
'data_path' => 'User.contactalert'
),
array(
'name' => __('PGP Key'),
'element' => 'boolean',
'sort' => 'User.gpgkey',
'class' => 'short',
'data_path' => 'User.gpgkey'
),
array(
'name' => __('S/MIME'),
'element' => 'boolean',
'sort' => 'User.certif_public',
'class' => 'short',
'data_path' => 'User.certif_public',
'requirement' => Configure::read('SMIME.enabled')
),
array(
'name' => __('NIDS SID'),
'sort' => 'User.nids_sid',
'class' => 'short',
'data_path' => 'User.nids_sid'
),
array(
'name' => __('Terms Accepted'),
'element' => 'boolean',
'sort' => 'User.termsaccepted',
'class' => 'short',
'data_path' => 'User.termsaccepted'
),
array(
'name' => __('Last Login'),
'sort' => 'User.current_login',
'element' => 'datetime',
'empty' => __('Never'),
'class' => 'short',
'data_path' => 'User.current_login'
),
array(
'name' => __('Created'),
'sort' => 'User.date_created',
'element' => 'datetime',
'class' => 'short',
'data_path' => 'User.date_created'
),
array(
'name' => (Configure::read('Plugin.CustomAuth_name') ? Configure::read('Plugin.CustomAuth_name') : __('External Auth')),
'sort' => 'User.external_auth_required',
'element' => 'boolean',
'class' => 'short',
'data_path' => 'User.external_auth_required',
'requirement' => (Configure::read('Plugin.CustomAuth_enable') && empty(Configure::read('Plugin.CustomAuth_required')))
),
array(
'name' => __('Monitored'),
'element' => 'toggle',
'url' => $baseurl . '/admin/users/monitor',
'url_params_data_paths' => array(
'User.id'
),
'sort' => 'User.disabled',
'class' => 'short',
'data_path' => 'User.monitored',
'requirement' => $isSiteAdmin && Configure::read('Security.user_monitoring_enabled')
),
array(
'name' => __('Disabled'),
'element' => 'boolean',
'sort' => 'User.disabled',
'class' => 'short',
'data_path' => 'User.disabled'
)
),
'title' => __('Users index'),
'html' => $description,
'actions' => array(
@ -232,5 +280,7 @@
)
)
));
echo '</div>';
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'admin', 'menuItem' => 'indexUser'));
if (!$this->request->is('ajax')) {
echo '</div>';
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'admin', 'menuItem' => 'indexUser'));
}

Some files were not shown because too many files have changed in this diff Show More