new: [feeds] Opened up feed inspection to host org users and added servers to overlap matrix

pull/4033/head v2.4.101
iglocska 2019-01-20 10:19:05 +01:00
parent 2f1b8bb9fb
commit 498a7ae77c
13 changed files with 241 additions and 53 deletions

View File

@ -1 +1 @@
{"major":2, "minor":4, "hotfix":100}
{"major":2, "minor":4, "hotfix":101}

View File

@ -407,6 +407,7 @@ class AppController extends Controller
$this->set('me', $this->Auth->user());
$this->set('isAdmin', $role['perm_admin']);
$this->set('isSiteAdmin', $role['perm_site_admin']);
$this->set('hostOrgUser', $this->Auth->user('org_id') == Configure::read('MISP.host_org_id'));
$this->set('isAclAdd', $role['perm_add']);
$this->set('isAclModify', $role['perm_modify']);
$this->set('isAclModifyOrg', $role['perm_modify_org']);

View File

@ -159,7 +159,7 @@ class ACLComponent extends Component
'feeds' => array(
'add' => array(),
'cacheFeeds' => array(),
'compareFeeds' => array(),
'compareFeeds' => array('*'),
'delete' => array(),
'disable' => array(),
'edit' => array(),
@ -169,11 +169,11 @@ class ACLComponent extends Component
'fetchSelectedFromFreetextIndex' => array(),
'getEvent' => array(),
'importFeeds' => array(),
'index' => array(),
'previewEvent' => array(),
'previewIndex' => array(),
'index' => array('*'),
'previewEvent' => array('*'),
'previewIndex' => array('*'),
'toggleSelected' => array('perm_site_admin'),
'view' => array(),
'view' => array('*'),
),
'galaxies' => array(
'attachCluster' => array('perm_tagger'),

View File

@ -23,13 +23,16 @@ class FeedsController extends AppController
{
parent::beforeFilter();
$this->Security->unlockedActions = array('previewIndex');
if (!$this->_isSiteAdmin()) {
if (!$this->_isSiteAdmin() && $this->Auth->user('org_id') != Configure::read('MISP.host_org_id')) {
throw new MethodNotAllowedException(__('You don\'t have the required privileges to do that.'));
}
}
public function index()
{
if (!$this->_isSiteAdmin() && !$this->Auth->user('org_id') == Configure::read('MISP.host_org_id')) {
throw NotAllowedException('You don\'t have access to this feature.');
}
$this->Feed->load_default_feeds();
$scope = isset($this->passedArgs['scope']) ? $this->passedArgs['scope'] : 'all';
if ($scope !== 'all') {
@ -77,6 +80,9 @@ class FeedsController extends AppController
public function view($feedId)
{
if (!$this->_isSiteAdmin() && !$this->Auth->user('org_id') == Configure::read('MISP.host_org_id')) {
throw NotAllowedException('You don\'t have access to this feature.');
}
$feed = $this->Feed->find('first', array(
'conditions' => array('Feed.id' => $feedId),
'recursive' => -1,
@ -500,6 +506,9 @@ class FeedsController extends AppController
public function previewIndex($feedId)
{
if (!$this->_isSiteAdmin() && !$this->Auth->user('org_id') == Configure::read('MISP.host_org_id')) {
throw NotAllowedException('You don\'t have access to this feature.');
}
$this->Feed->id = $feedId;
if (!$this->Feed->exists()) {
throw new NotFoundException(__('Invalid feed.'));
@ -680,6 +689,9 @@ class FeedsController extends AppController
public function previewEvent($feedId, $eventUuid, $all = false)
{
if (!$this->_isSiteAdmin() && !$this->Auth->user('org_id') == Configure::read('MISP.host_org_id')) {
throw NotAllowedException('You don\'t have access to this feature.');
}
$this->Feed->id = $feedId;
if (!$this->Feed->exists()) {
throw new NotFoundException(__('Invalid feed.'));
@ -839,6 +851,9 @@ class FeedsController extends AppController
public function compareFeeds($id = false)
{
if (!$this->_isSiteAdmin() && !$this->Auth->user('org_id') == Configure::read('MISP.host_org_id')) {
throw NotAllowedException('You don\'t have access to this feature.');
}
$feeds = $this->Feed->compareFeeds($id);
if ($this->_isRest()) {
return $this->RestResponse->viewData($feeds, $this->response->type());

View File

@ -54,14 +54,24 @@ class ServersController extends AppController
$params = array(
'recursive' => -1,
'contain' => array(
'Organisation' => array('Organisation.id', 'Organisation.name', 'Organisation.uuid', 'Organisation.nationality', 'Organisation.sector', 'Organisation.type'),
'RemoteOrg' => array('RemoteOrg.id', 'RemoteOrg.name', 'RemoteOrg.uuid', 'RemoteOrg.nationality', 'RemoteOrg.sector', 'RemoteOrg.type'),
)
'User' => array(
'fields' => array('User.id', 'User.org_id', 'User.email'),
),
'Organisation' => array(
'fields' => array('Organisation.id', 'Organisation.name', 'Organisation.uuid', 'Organisation.nationality', 'Organisation.sector', 'Organisation.type'),
),
'RemoteOrg' => array(
'fields' => array('RemoteOrg.id', 'RemoteOrg.name', 'RemoteOrg.uuid', 'RemoteOrg.nationality', 'RemoteOrg.sector', 'RemoteOrg.type'),
),
),
);
$servers = $this->Server->find('all', $params);
$servers = $this->Server->attachServerCacheTimestamps($servers);
return $this->RestResponse->viewData($servers, $this->response->type());
} else {
$this->set('servers', $this->paginate());
$servers = $this->paginate();
$servers = $this->Server->attachServerCacheTimestamps($servers);
$this->set('servers', $servers);
$collection = array();
$collection['orgs'] = $this->Server->Organisation->find('list', array(
'fields' => array('id', 'name'),

View File

@ -1254,6 +1254,25 @@ class Feed extends AppModel
$feeds[$k]['Feed']['values'] = $redis->sCard('misp:feed_cache:' . $feed['Feed']['id']);
}
$feeds = array_values($feeds);
$this->Server = ClassRegistry::init('Server');
$servers = $this->Server->find('all', array(
'recursive' => -1,
'fields' => array('id', 'url', 'name'),
'contain' => array('RemoteOrg' => array('fields' => array('RemoteOrg.id', 'RemoteOrg.name'))),
'conditions' => array('Server.caching_enabled')
));
foreach ($servers as $k => $server) {
if (!$redis->exists('misp:server_cache:' . $server['Server']['id'])) {
unset($servers[$k]);
continue;
}
$servers[$k]['Server']['input_source'] = 'network';
$servers[$k]['Server']['source_format'] = 'misp';
$servers[$k]['Server']['provider'] = $servers[$k]['RemoteOrg']['name'];
$servers[$k]['Server']['default'] = false;
$servers[$k]['Server']['is_misp_server'] = true;
$servers[$k]['Server']['values'] = $redis->sCard('misp:server_cache:' . $server['Server']['id']);
}
foreach ($feeds as $k => $feed) {
foreach ($feeds as $k2 => $feed2) {
if ($k == $k2) {
@ -1265,6 +1284,37 @@ class Feed extends AppModel
'overlap_percentage' => round(100 * count($intersect) / $feeds[$k]['Feed']['values']),
));
}
foreach ($servers as $k2 => $server) {
$intersect = $redis->sInter('misp:feed_cache:' . $feed['Feed']['id'], 'misp:server_cache:' . $server['Server']['id']);
$feeds[$k]['Feed']['ComparedFeed'][] = array_merge(array_intersect_key($server['Server'], $fields), array(
'overlap_count' => count($intersect),
'overlap_percentage' => round(100 * count($intersect) / $feeds[$k]['Feed']['values']),
));
}
}
foreach ($servers as $k => $server) {
foreach ($feeds as $k2 => $feed2) {
$intersect = $redis->sInter('misp:server_cache:' . $server['Server']['id'], 'misp:feed_cache:' . $feed2['Feed']['id']);
$servers[$k]['Server']['ComparedFeed'][] = array_merge(array_intersect_key($feed2['Feed'], $fields), array(
'overlap_count' => count($intersect),
'overlap_percentage' => round(100 * count($intersect) / $servers[$k]['Server']['values']),
));
}
foreach ($servers as $k2 => $server2) {
if ($k == $k2) {
continue;
}
$intersect = $redis->sInter('misp:server_cache:' . $server['Server']['id'], 'misp:server_cache:' . $server2['Server']['id']);
$servers[$k]['Server']['ComparedFeed'][] = array_merge(array_intersect_key($server2['Server'], $fields), array(
'overlap_count' => count($intersect),
'overlap_percentage' => round(100 * count($intersect) / $servers[$k]['Server']['values']),
));
}
}
foreach ($servers as $k => $server) {
$server['Feed'] = $server['Server'];
unset($server['Server']);
$feeds[] = $server;
}
return $feeds;
}

View File

@ -4316,6 +4316,7 @@ class Server extends AppModel
}
$pipe->exec();
}
$redis->set('misp:server_cache_timestamp:' . $server['Server']['id'], time());
return true;
}
@ -4336,4 +4337,16 @@ class Server extends AppModel
}
return $response->body;
}
public function attachServerCacheTimestamps($data)
{
$redis = $this->setupRedis();
if ($redis === false) {
return $data;
}
foreach ($data as $k => $v) {
$data[$k]['Server']['cache_timestamp'] = $redis->get('misp:server_cache_timestamp:' . $data[$k]['Server']['id']);
}
return $data;
}
}

View File

@ -117,15 +117,17 @@
</ul>
</li>
<?php if ($isAclSync || $isAdmin): ?>
<?php if ($isAclSync || $isAdmin || $hostOrgUser): ?>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<?php echo __('Sync Actions');?>
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="<?php echo $baseurl;?>/servers/index"><?php echo __('List Servers');?></a></li>
<?php if ($isSiteAdmin): ?>
<?php if ($isAclSync || $isAdmin): ?>
<li><a href="<?php echo $baseurl;?>/servers/index"><?php echo __('List Servers');?></a></li>
<?php endif; ?>
<?php if ($isSiteAdmin || $hostOrgUser): ?>
<li><a href="<?php echo $baseurl;?>/feeds/index"><?php echo __('List Feeds');?></a></li>
<?php endif;?>
</ul>

View File

@ -356,24 +356,42 @@
break;
case 'sync':
if ($menuItem === 'previewEvent' && $isSiteAdmin) : ?>
<li class="active"><?php echo $this->Html->link(__('Explore Remote Event'), array('controller' => 'servers', 'action' => 'previewEvent', h($server['Server']['id']), h($event['Event']['id']))); ?></li>
<li><?php echo $this->Form->postLink(__('Fetch This Event'), '/servers/pull/' . $server['Server']['id'] . '/' . $event['Event']['id'], null, __('Are you sure you want to fetch and save this event on your instance?', $this->Form->value('Server.id'))); ?></li>
<li><?php echo $this->Html->link(__('Explore Remote Server'), array('controller' => 'servers', 'action' => 'previewIndex', h($server['Server']['id']))); ?></li>
<?php endif;
if ($menuItem === 'previewIndex' && $isSiteAdmin) : ?>
<li class="active"><?php echo $this->Html->link(__('Explore Remote Server'), array('controller' => 'servers', 'action' => 'previewIndex', h($id))); ?></li>
<?php endif; ?>
<?php if ($menuItem === 'edit' && $isSiteAdmin): ?>
<li class="active"><?php echo $this->Html->link(__('Edit Server'), array('controller' => 'servers', 'action' => 'edit')); ?></li>
<li><?php echo $this->Form->postLink(__('Delete'), array('action' => 'delete', $this->Form->value('Server.id')), null, __('Are you sure you want to delete # %s?', $this->Form->value('Server.id'))); ?></li>
<li class="divider"></li>
<?php endif; ?>
<li id='liindex'><?php echo $this->Html->link(__('List Servers'), array('controller' => 'servers', 'action' => 'index'));?></li>
<?php if ($isSiteAdmin): ?>
<li id='liadd'><?php echo $this->Html->link(__('New Server'), array('controller' => 'servers', 'action' => 'add')); ?></li>
<?php endif;?>
<?php
if ($menuItem === 'previewEvent' && ($isSiteAdmin || $hostOrg)) {
echo sprintf(
'<li>%s</li><li class="active">%s</li>',
$this->Html->link(__('Explore Remote Server'), array('controller' => 'servers', 'action' => 'previewIndex', h($server['Server']['id']))),
$this->Html->link(__('Explore Remote Event'), array('controller' => 'servers', 'action' => 'previewEvent', h($server['Server']['id']), h($event['Event']['id'])))
);
}
if ($menuItem === 'previewEvent' && $isSiteAdmin) {
echo sprintf(
'<li>%s</li>',
$this->Form->postLink(__('Fetch This Event'), '/servers/pull/' . $server['Server']['id'] . '/' . $event['Event']['id'], null, __('Are you sure you want to fetch and save this event on your instance?', $this->Form->value('Server.id')))
);
}
if ($menuItem === 'previewIndex' && ($isSiteAdmin || $hostOrg)) {
echo sprintf(
'<li class="active">%s</li>',
$this->Html->link(__('Explore Remote Server'), array('controller' => 'servers', 'action' => 'previewIndex', h($id)))
);
}
if ($menuItem === 'edit' && $isSiteAdmin) {
echo sprintf(
'<li class="active">%s</li><li>%s</li><li class="divider"></li>',
$this->Html->link(__('Edit Server'), array('controller' => 'servers', 'action' => 'edit')),
$this->Form->postLink(__('Delete'), array('action' => 'delete', $this->Form->value('Server.id')), null, __('Are you sure you want to delete # %s?', $this->Form->value('Server.id')))
);
}
echo sprintf(
'<li id="liindex">%s</li>',
$this->Html->link(__('List Servers'), array('controller' => 'servers', 'action' => 'index'))
);
if ($isSiteAdmin) {
echo sprintf(
'<li id="liadd">%s</li>',
$this->Html->link(__('New Server'), array('controller' => 'servers', 'action' => 'add'))
);
}
break;
case 'admin':
@ -515,19 +533,54 @@
endif;
break;
case 'feeds': ?>
<li id='liindex'><a href="<?php echo $baseurl;?>/feeds/index"><?php echo __('List Feeds');?></a></li>
<li id='liadd'><a href="<?php echo $baseurl;?>/feeds/add"><?php echo __('Add Feed');?></a></li>
<li id='liadd'><a href="<?php echo $baseurl;?>/feeds/importFeeds"><?php echo __('Import Feeds from JSON');?></a></li>
<li id='licompare'><a href="<?php echo $baseurl;?>/feeds/compareFeeds"><?php echo __('Feed overlap analysis matrix');?></a></li>
<li id='liexport'><a href="<?php echo $baseurl;?>/feeds/index.json" download="feed_index.json"><?php echo __('Export Feed settings');?></a></li>
<?php if ($menuItem === 'edit'): ?>
<li class="active"><a href="#"><?php echo __('Edit Feed');?></a></li>
<?php elseif ($menuItem === 'previewIndex'): ?>
<li id='lipreviewIndex'><a href="<?php echo $baseurl;?>/feeds/previewIndex/<?php echo h($feed['Feed']['id']); ?>"><?php echo __('PreviewIndex');?></a></li>
<?php elseif ($menuItem === 'previewEvent'): ?>
<li id='lipreviewEvent'><a href="<?php echo $baseurl;?>/feeds/previewEvent/<?php echo h($feed['Feed']['id']); ?>/<?php echo h($id);?>"><?php echo __('PreviewEvent');?></a></li>
<?php endif;
case 'feeds':
echo sprintf(
'<li id="liindex"><a href="%s/feeds/index">%s</a></li>',
$baseurl,
__('List Feeds')
);
if ($isSiteAdmin) {
echo sprintf(
'<li id="liadd"><a href="%s/feeds/add">%s</a></li>',
$baseurl,
__('Add Feed')
);
echo sprintf(
'<li id="liimport"><a href="%s/feeds/importFeeds">%s</a></li>',
$baseurl,
__('Import Feeds from JSON')
);
}
echo sprintf(
'<li id="licompare"><a href="%s/feeds/compareFeeds">%s</a></li>',
$baseurl,
__('Feed overlap analysis matrix')
);
echo sprintf(
'<li id="liexport"><a href="%s/feeds/index.json" download="feed_index.json">%s</a></li>',
$baseurl,
__('Export Feed settings')
);
if ($isSiteAdmin) {
if ($menuItem === 'edit') {
echo '<li class="active"><a href="#">' . __('Edit Feed') . '</a></li>';
} else if ($menuItem === 'previewIndex') {
echo sprintf(
'<li id="lipreviewIndex"><a href="%s/feeds/previewIndex/%s"></a></li>',
$baseurl,
h($feed['Feed']['id']),
__('PreviewIndex')
);
} else if ($menuItem === 'previewEvent') {
echo sprintf(
'<li id="lipreviewEvent"><a href="%s/feeds/previewEvent/%s/%s">%s</a></li>',
$baseurl,
h($feed['Feed']['id']),
h($id),
__('PreviewEvent')
);
}
}
break;
case 'news': ?>

View File

@ -21,7 +21,7 @@
?>
<th>
<div data-toggle="popover" data-content="<?php echo $popover; ?>" data-trigger="hover">
<?php echo h($item['Feed']['id']); ?>
<?php echo (empty($item['Feed']['is_misp_server']) ? 'F' : 'S') . h($item['Feed']['id']); ?>
</div>
</th>
<?php
@ -38,7 +38,15 @@
<tr>
<td class="short">
<div data-toggle="popover" data-content="<?php echo $popover;?>" data-trigger="hover">
<?php echo h($item['Feed']['id']) . ' ' . h($item['Feed']['name']); ?>&nbsp;
<?php
echo sprintf(
'%s%s %s%s',
empty($item['Feed']['is_misp_server']) ? 'Feed #' : 'Server #',
h($item['Feed']['id']),
empty($item['Feed']['is_misp_server']) ? '' : '(<span class="blue bold">MISP</span>) ',
h($item['Feed']['name'])
);
?>
</div>
</td>
<?php

View File

@ -196,7 +196,7 @@ foreach ($feeds as $item):
else:
echo __('Not cached');
endif;
if ($item['Feed']['caching_enabled']):
if ($item['Feed']['caching_enabled'] && $isSiteAdmin):
?>
<a href="<?php echo $baseurl;?>/feeds/cacheFeeds/<?php echo h($item['Feed']['id']); ?>" title="Cache feed"><span class="icon-download-alt"></span></a>
<?php
@ -206,12 +206,14 @@ foreach ($feeds as $item):
<td class="short action-links">
<?php
echo $this->Html->link('', array('action' => 'previewIndex', $item['Feed']['id']), array('class' => 'icon-search', 'title' => __('Explore the events remotely')));
if (!isset($item['Feed']['event_error'])) {
if (!isset($item['Feed']['event_error']) && $isSiteAdmin) {
if ($item['Feed']['enabled']) echo $this->Html->link('', array('action' => 'fetchFromFeed', $item['Feed']['id']), array('class' => 'icon-download', 'title' => __('Fetch all events')));
}
if ($isSiteAdmin):
?>
<a href="<?php echo $baseurl;?>/feeds/edit/<?php echo h($item['Feed']['id']); ?>"><span class="icon-edit" title="Edit">&nbsp;</span></a>
<?php echo $this->Form->postLink('', array('action' => 'delete', h($item['Feed']['id'])), array('class' => 'icon-trash', 'title' => __('Delete')), __('Are you sure you want to permanently remove the feed (%s)?', h($item['Feed']['name']))); ?>
<a href="<?php echo $baseurl;?>/feeds/edit/<?php echo h($item['Feed']['id']); ?>"><span class="icon-edit" title="Edit">&nbsp;</span></a>
<?php echo $this->Form->postLink('', array('action' => 'delete', h($item['Feed']['id'])), array('class' => 'icon-trash', 'title' => __('Delete')), __('Are you sure you want to permanently remove the feed (%s)?', h($item['Feed']['name']))); ?>
<?php endif; ?>
<a href="<?php echo $baseurl;?>/feeds/view/<?php echo h($item['Feed']['id']); ?>.json" title="<?php echo __('Download feed metadata as JSON');?>" download><span class="fa fa-cloud-download black"></span></a>
</td>
</tr><?php

View File

@ -62,7 +62,7 @@
</td>
<td ondblclick="document.location.href ='<?php echo $eventViewURL . h($uuid);?>'" class="short"><?php echo h($event['timestamp']); ?></td>
<td class="short action-links">
<?php if ($feed['Feed']['enabled']) echo $this->Form->postLink('', '/feeds/getEvent/' . $id . '/' . $uuid, array('class' => 'icon-download', 'title' => 'Fetch the event'), __('Are you sure you want to fetch and save this event on your instance?', $this->Form->value('Feed.id'))); ?>
<?php if ($feed['Feed']['enabled'] && $isSiteAdmin) echo $this->Form->postLink('', '/feeds/getEvent/' . $id . '/' . $uuid, array('class' => 'icon-download', 'title' => 'Fetch the event'), __('Are you sure you want to fetch and save this event on your instance?', $this->Form->value('Feed.id'))); ?>
<a href='<?php echo $eventViewURL . h($uuid);?>' class = "icon-list-alt" title = "<?php echo __('View');?>"></a>
</td>
</tr>

View File

@ -73,7 +73,41 @@ foreach ($servers as $server):
<td><span class="<?php echo ($server['Server']['internal']? 'icon-ok' : 'icon-remove'); ?>" title="<?php echo ($server['Server']['internal']? __('Internal instance that ignores distribution level degradation *WARNING: Only use this setting if you have several internal instances and the sync link is to an internal extension of the current MISP community*') : __('Normal sync link to an external MISP instance. Distribution degradation will follow the normal rules.')); ?>"></span></td>
<td><span class="<?php echo ($server['Server']['push']? 'icon-ok' : 'icon-remove'); ?>"></span><span class="short <?php if (!$server['Server']['push'] || empty($ruleDescription['push'])) echo "hidden"; ?>" data-toggle="popover" title="Distribution List" data-content="<?php echo $ruleDescription['push']; ?>"> (<?php echo __('Rules');?>)</span></td>
<td><span class="<?php echo ($server['Server']['pull']? 'icon-ok' : 'icon-remove'); ?>"></span><span class="short <?php if (!$server['Server']['pull'] || empty($ruleDescription['pull'])) echo "hidden"; ?>" data-toggle="popover" title="Distribution List" data-content="<?php echo $ruleDescription['pull']; ?>"> (<?php echo __('Rules');?>)</span></td>
<td><span class="<?php echo ($server['Server']['caching_enabled']? 'icon-ok' : 'icon-remove'); ?>"></span></td>
<td>
<?php
if ($server['Server']['caching_enabled']) {
if (!empty($server['Server']['cache_timestamp'])) {
$units = array('m', 'h', 'd');
$intervals = array(60, 60, 24);
$unit = 's';
$last = time() - $server['Server']['cache_timestamp'];
foreach ($units as $k => $v) {
if ($last > $intervals[$k]) {
$unit = $v;
$last = floor($last / $intervals[$k]);
} else {
break;
}
}
echo sprintf(
'<span class="blue bold">%s%s%s</span> %s',
__('Age: '),
$last,
$unit,
'<span class="icon-ok"></span>'
);
} else {
echo sprintf(
'<span class="red bold">%s</span> %s',
__('Not cached'),
'<span class="icon-ok"></span>'
);
}
} else {
echo '<span class="icon-remove"></span>';
}
?>
</td>
<td class="short"><span class="<?php echo ($server['Server']['unpublish_event'] ? 'icon-ok' : 'icon-remove'); ?>"></span></td>
<td class="short"><span class="<?php echo ($server['Server']['publish_without_email'] ? 'icon-ok' : 'icon-remove'); ?>"></span></td>
<td><?php echo h($server['Server']['url']); ?>&nbsp;</td>