chg: [user:periodic_notification] General improvements and added CLI support

pull/8575/head
Sami Mokaddem 2022-08-31 11:51:36 +02:00
parent 9d83f8a478
commit 7cd3b35d61
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
7 changed files with 117 additions and 38 deletions

View File

@ -578,6 +578,37 @@ class ServerShell extends AppShell
$this->Task->saveField('message', count($servers) . ' job(s) completed at ' . date('d/m/Y - H:i:s') . '.');
}
public function sendPeriodicSummaryToUsers()
{
$this->ConfigLoad->execute();
$periods = $this->__getPeriodsForToday();
$start_time = time();
echo __n('Started periodic summary generation for the %s period', 'Started periodic summary generation for periods: %s', count($periods), implode(', ', $periods)) . PHP_EOL;
foreach ($periods as $period) {
$users = $this->User->getSubscribedUsersForPeriod($period);
echo __n('%s user has subscribed for the `%s` period', '%s users has subscribed for the `%s` period', count($users), count($users), $period) . PHP_EOL;
foreach ($users as $user) {
echo __('Sending `%s` report to `%s`', $period, $user['User']['email']) . PHP_EOL;
$emailTemplate = $this->User->generatePeriodicSummary($user['User']['id'], $period, false);
$this->User->sendEmail($user, $emailTemplate, false, null);
}
}
echo __('All reports sent. Task took %s secondes', time() - $start_time) . PHP_EOL;
}
private function __getPeriodsForToday(): array
{
$today = new DateTime();
$periods = ['daily'];
if ($today->format('j') == 1) {
$periods[] = 'weekly';
}
if ($today->format('N') == 1) {
$periods[] = 'monthly';
}
return $periods;
}
/**
* @param int $userId
* @return array

View File

@ -471,6 +471,7 @@ class UsersController extends AppController
} else {
$this->set('urlparams', $urlParams);
$this->set('passedArgsArray', $passedArgsArray);
$this->set('periodic_notifications', $this->User::PERIODIC_NOTIFICATIONS);
$conditions = array();
if ($this->_isSiteAdmin()) {
$users = $this->paginate();

View File

@ -1753,6 +1753,7 @@ class User extends AppModel
$renderView = false;
$filtersForRestSearch['publish_timestamp'] = $filtersForRestSearch['last'];
$filtersForRestSearch['returnFormat'] = 'context';
$filtersForRestSearch['staticHtml'] = true;
unset($filtersForRestSearch['last']);
$final = $this->Event->restSearch($user, 'context', $filtersForRestSearch, false, false, $elementCounter, $renderView);
$final = json_decode($final->intoString(), true);
@ -1763,6 +1764,8 @@ class User extends AppModel
$emailTemplate->set('filters', $filters);
$emailTemplate->set('period', $period);
$emailTemplate->set('aggregated_context', $aggregated_context);
$emailTemplate->set('analysisLevels', $this->Event->analysisLevels);
$emailTemplate->set('distributionLevels', $this->Event->distributionLevels);
if (!empty($rendered)) {
$summary = $emailTemplate->render();
return $summary->format() == 'text' ? $summary->text : $summary->html;
@ -1818,7 +1821,6 @@ class User extends AppModel
$timerange = '31d';
}
return $timerange;
return $this->resolveTimeDelta($timerange);
}
private function __getEventsForFilters(array $user, array $filters): array
@ -1830,7 +1832,7 @@ class User extends AppModel
public function prepareEmailTemplate(string $period='daily'): SendEmailTemplate
{
$subject = sprintf('[%s MISP] %s %s', Configure::read('MISP.org'), Inflector::humanize($period), __('Notification'));
$subject = sprintf('[%s MISP] %s %s', Configure::read('MISP.org'), Inflector::humanize($period), __('Notification - %s', (new DateTime())->format('Y-m-d')));
$template = new SendEmailTemplate("notification_$period");
$template->subject($subject);
return $template;

View File

@ -110,11 +110,26 @@ foreach ($events as $event) {
}
}
if (!function_exists('findAndBuildTag')) {
function findAndBuildTag($tag_list, $tag_prefix, $that) {
foreach ($tag_list as $tag) {
if (substr($tag['Tag']['name'], 0, strlen($tag_prefix)) == $tag_prefix) {
return $that->element('tag', ['tag' => $tag]);
}
}
return '';
}
}
$unique_tag_number = count(array_keys($all_tag_amount));
arsort($attribute_types);
arsort($object_types);
arsort($all_tag_amount);
array_splice($attribute_types, 10);
array_splice($object_types, 10);
array_splice($all_tag_amount, 10);
?>
<h2><?= __('Summary of published Events') ?></h2>
@ -171,39 +186,44 @@ arsort($all_tag_amount);
<?php if ($this->fetch('detailed-summary-mitre-attack')): ?>
<?= $this->fetch('detailed-summary-mitre-attack'); ?>
<?php else: ?>
<h4><img src="https://localhost:8443/img/mitre-attack-icon.ico" style="height: 1em; vertical-align: text-top;"> <?= __('Mitre Att&ck techniques') ?></h4>
<ul>
<?php foreach ($mitre_attack_techniques as $technique => $tag) : ?>
<li>
<?php
$tag['Tag']['name'] = $technique;
echo $this->element('tag', ['tag' => $tag])
?>
</li>
<?php endforeach; ?>
</ul>
<?php if (!empty($mitre_attack_techniques)): ?>
<h4><?= __('Mitre Att&ck techniques') ?></h4>
<ul>
<?php foreach ($mitre_attack_techniques as $technique => $tag) : ?>
<li>
<?php
$tag['Tag']['name'] = $technique;
echo $this->element('tag', ['tag' => $tag])
?>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<?php endif; ?>
<?php if ($this->fetch('detailed-summary-type')): ?>
<?= $this->fetch('detailed-summary-type'); ?>
<?php else: ?>
<h4><?= __('Entity type distribution') ?></h4>
<h5><?= __('Attributes') ?></h5>
<ul>
<?php foreach ($attribute_types as $type => $amount) : ?>
<li><strong><?= h($type) ?></strong>: <?= $amount ?></li>
<?php endforeach; ?>
</ul>
<?php if (!empty($attribute_types)): ?>
<h4><?= __('Top 10 Attribute types') ?></h4>
<ul>
<?php foreach ($attribute_types as $type => $amount) : ?>
<li><strong><?= h($type) ?></strong>: <?= $amount ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<h5><?= __('MISP Objects') ?></h5>
<ul>
<?php foreach ($object_types as $name => $amount) : ?>
<li><strong><?= h($name) ?></strong>: <?= $amount ?></li>
<?php endforeach; ?>
</ul>
<?php if (!empty($object_types)): ?>
<h4><?= __('Top 10 MISP Object names') ?></h4>
<ul>
<?php foreach ($object_types as $name => $amount) : ?>
<li><strong><?= h($name) ?></strong>: <?= $amount ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<?php if (!empty($all_event_report)): ?>
<h5><?= __('Event Reports') ?></h5>
<h4><?= __('All Event Reports') ?></h4>
<ul>
<?php foreach ($all_event_report as $report) : ?>
<li>
@ -219,7 +239,7 @@ arsort($all_tag_amount);
<?php if ($this->fetch('detailed-summary-tags')): ?>
<?= $this->fetch('detailed-summary-tags'); ?>
<?php else: ?>
<h4><?= __('Tags distribution') ?></h4>
<h4><?= __('Top 10 Tags') ?></h4>
<ul>
<?php foreach ($all_tag_amount as $tag_name => $amount) : ?>
<li>
@ -235,17 +255,28 @@ arsort($all_tag_amount);
<?php else: ?>
<h3><?= __('Event list') ?></h3>
<table>
<thead>
<tr>
<th>TLP</th>
<th>PAP</th>
<th><?= __('State') ?></th>
<th>ThreatLevel</th>
<th><?= __('Event Info') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($events as $event) : ?>
<?php
$tlpTag = array_filter($event['EventTag'], function ($event_tag) {
return substr($event_tag['Tag']['name'], 0, 4) === 'tlp:';
});
$tlpTagHtml = !empty($tlpTag) ? $this->element('tag', ['tag' => $tlpTag[0]]) : '';
?>
<?php
$workflowTag = findAndBuildTag($event['EventTag'], 'workflow:', $this);
$analysisHtml = !empty($workflowTag) ? $workflowTag : h($analysisLevels[$event['Event']['analysis']]);
$tlpTag = findAndBuildTag($event['EventTag'], 'tlp:', $this);
$tlpHtml = !empty($tlpTag) ? $tlpTag : h($distributionLevels[$event['Event']['distribution']]);
?>
<tr>
<td><?= $tlpTagHtml ?></td>
<td>[<?= h($event['ThreatLevel']['name']); ?>]</td>
<td><?= $tlpHtml ?></td>
<td><?= findAndBuildTag($event['EventTag'], 'PAP:', $this) ?></td>
<td><?= $analysisHtml ?></td>
<td><?= h($event['ThreatLevel']['name']); ?></td>
<td><a href="<?= sprintf('%s/events/view/%s', $baseurl, h($event['Event']['uuid'])) ?>"><?= h($event['Event']['info']); ?></a></td>
</tr>
<?php endforeach; ?>

View File

@ -154,6 +154,20 @@
'data_path' => 'User.contactalert',
'colors' => true,
),
array(
'name' => __('Periodic notif.'),
'element' => 'custom',
'class' => 'short',
'function' => function (array $user) use ($periodic_notifications) {
$period_subscriptions = [];
foreach ($periodic_notifications as $period) {
if (!empty($user['User'][$period])) {
$period_subscriptions[] = substr($period, 13, 1);
}
}
return implode('/', $period_subscriptions);
}
),
array(
'name' => __('PGP Key'),
'element' => 'boolean',

View File

@ -17,7 +17,7 @@ echo $this->element('genericElements/Form/genericForm', [
],
[
'field' => 'notification_monthly',
'label' => __('Subscribe to montly notifications'),
'label' => __('Subscribe to monthly notifications'),
'default' => 0,
'type' => 'checkbox'
],

View File

@ -2,7 +2,7 @@
<div class="btn-group">
<a class="btn <?= $period == 'daily' ? 'btn-primary' : 'btn-inverse' ?>" href="<?= $baseurl . '/users/viewPeriodicSummary/daily' ?>"><?= __('Daily') ?></a>
<a class="btn <?= $period == 'weekly' ? 'btn-primary' : 'btn-inverse' ?>" href="<?= $baseurl . '/users/viewPeriodicSummary/weekly' ?>"><?= __('Weekly') ?></a>
<a class="btn <?= $period == 'monthly' ? 'btn-primary' : 'btn-inverse' ?>" href="<?= $baseurl . '/users/viewPeriodicSummary/monthly' ?>"><?= __('Montly') ?></a>
<a class="btn <?= $period == 'monthly' ? 'btn-primary' : 'btn-inverse' ?>" href="<?= $baseurl . '/users/viewPeriodicSummary/monthly' ?>"><?= __('Monthly') ?></a>
</div>
<h2><?= __('MISP %s summary', h($period)); ?></h2>