new: [mail] Move contact alert email to templates

pull/6967/head
Jakub Onderka 2021-02-06 10:58:46 +01:00
parent e2b1ba18a3
commit 17fb5db3cf
6 changed files with 148 additions and 81 deletions

View File

@ -405,16 +405,20 @@ class SendEmail
/**
* @param array $user
* @param string $subject
* @param CakeEmailBody $body
* @param CakeEmailBody|null $bodyWithoutEncryption
* @param SendEmailTemplate|string $body
* @param string|false $bodyWithoutEncryption
* @param array $replyToUser
* @return bool True if e-mail is encrypted, false if not.
* @throws Crypt_GPG_BadPassphraseException
* @throws Crypt_GPG_Exception
* @throws SendEmailException
*/
public function sendToUser(array $user, $subject, CakeEmailBody $body, CakeEmailBody $bodyWithoutEncryption = null, array $replyToUser = array())
public function sendToUser(array $user, $subject, $body, $bodyWithoutEncryption = false, array $replyToUser = array())
{
if ($body instanceof SendEmailTemplate && $bodyWithoutEncryption !== false) {
throw new InvalidArgumentException("When body is instance of SendEmailTemplate, \$bodyWithoutEncryption must be false.");
}
if (Configure::read('MISP.disable_emailing')) {
throw new SendEmailException('Emailing is currently disabled on this instance.');
}
@ -431,9 +435,18 @@ class SendEmail
throw new SendEmailException('Encrypted messages are enforced and the message could not be encrypted for this user as no valid encryption key was found.');
}
// If 'bodyonlyencrypted' is enabled and the user has no encryption key, use the alternate body (if it exists)
if (Configure::read('GnuPG.bodyonlyencrypted') && !$canEncryptSmime && !$canEncryptGpg && $bodyWithoutEncryption) {
$body = $bodyWithoutEncryption;
// If 'GnuPG.bodyonlyencrypted' is enabled and the user has no encryption key, use the alternate body
$hideDetails = Configure::read('GnuPG.bodyonlyencrypted') && !$canEncryptSmime && !$canEncryptGpg;
if ($body instanceof SendEmailTemplate) {
$body->set('canEncryptSmime', $canEncryptSmime);
$body->set('canEncryptGpg', $canEncryptGpg);
$body = $body->render($hideDetails);
} else {
if ($hideDetails && $bodyWithoutEncryption) {
$body = $bodyWithoutEncryption;
}
$body = new CakeEmailBody($body);
}
$email = $this->create($user, $subject, $body, [], $replyToUser);

View File

@ -0,0 +1,51 @@
<?php
class SendEmailTemplate
{
/** @var array */
private $viewVars = [];
/** @var string */
private $viewName;
public function __construct($viewName)
{
$this->viewName = $viewName;
}
/**
* @param string $key
* @param mixed $value
*/
public function set($key, $value)
{
$this->viewVars[$key] = $value;
}
/**
* @param bool $hideDetails True when GnuPG.bodyonlyencrypted is enabled and e-mail cannot be send in encrypted form
* @return CakeEmailBody
* @throws CakeException
*/
public function render($hideDetails = false)
{
$View = new View();
$View->helpers = ['TextColour'];
$View->loadHelpers();
$View->set($this->viewVars);
$View->set('hideDetails', $hideDetails);
$View->viewPath = $View->layoutPath = 'Emails' . DS . 'html';
try {
$html = $View->render($this->viewName);
} catch (MissingViewException $e) {
$html = null; // HTMl template is optional
}
$View->viewPath = $View->layoutPath = 'Emails' . DS . 'text';
$View->hasRendered = false;
$text = $View->render($this->viewName);
return new CakeEmailBody($text, $html);
}
}

View File

@ -4,6 +4,7 @@ App::uses('CakeEmail', 'Network/Email');
App::uses('RandomTool', 'Tools');
App::uses('AttachmentTool', 'Tools');
App::uses('TmpFileTool', 'Tools');
App::uses('SendEmailTemplate', 'Tools');
/**
* @property User $User
@ -3202,10 +3203,6 @@ class Event extends AppModel
}
$subject = "[" . Configure::read('MISP.org') . " MISP] Event $id - $subject$threatLevel" . strtoupper($subjMarkingString);
$eventUrl = $this->__getAnnounceBaseurl() . "/events/view/" . $id;
$bodyNoEnc = __("A new or modified event was just published on %s", $eventUrl) . "\n\n";
$bodyNoEnc .= __("If you would like to unsubscribe from receiving such alert e-mails, simply\ndisable publish alerts via %s", $this->__getAnnounceBaseurl() . '/users/edit');
$userCount = count($usersWithAccess);
$this->UserSetting = ClassRegistry::init('UserSetting');
foreach ($usersWithAccess as $k => $user) {
@ -3219,8 +3216,8 @@ class Event extends AppModel
])[0];
if ($this->UserSetting->checkPublishFilter($user, $eventForUser)) {
$body = $this->renderAlertEmail($eventForUser, $user, $oldpublish);
$this->User->sendEmail(['User' => $user], $body, $bodyNoEnc, $subject);
$body = $this->prepareAlertEmail($eventForUser, $user, $oldpublish);
$this->User->sendEmail(['User' => $user], $body, false, $subject);
}
if ($jobId) {
$this->Job->saveProgress($jobId, null, $k / $userCount * 100);
@ -3235,39 +3232,22 @@ class Event extends AppModel
/**
* @param array $event
* @param array $user
* @param array $user E-mail receiver
* @param int|null $oldpublish Timestamp of previous publishing.
* @param bool $contactAlert
* @return CakeEmailBody
* @return SendEmailTemplate
* @throws CakeException
*/
private function renderAlertEmail(array $event, array $user, $oldpublish = null, $contactAlert = false)
private function prepareAlertEmail(array $event, array $user, $oldpublish = null)
{
$View = new View();
$View->helpers = ['TextColour'];
$View->loadHelpers();
$View->set('event', $event);
$View->set('user', $user);
$View->set('oldPublishTimestamp', $oldpublish);
$View->set('baseurl', $this->__getAnnounceBaseurl());
$View->set('distributionLevels', $this->distributionLevels);
$View->set('analysisLevels', $this->analysisLevels);
$View->set('contactAlert', $contactAlert);
try {
$View->viewPath = $View->layoutPath = 'Emails' . DS . 'html';
$html = $View->render('alert');
} catch (MissingViewException $e) {
$html = null; // HTMl template is optional
}
$View->hasRendered = false;
$View->viewPath = $View->layoutPath = 'Emails' . DS . 'text';
$text = $View->render('alert');
require_once APP . '/Lib/Tools/SendEmail.php'; // TODO
return new CakeEmailBody($text, $html);
$template = new SendEmailTemplate('alert');
$template->set('event', $event);
$template->set('user', $user);
$template->set('oldPublishTimestamp', $oldpublish);
$template->set('baseurl', $this->__getAnnounceBaseurl());
$template->set('distributionLevels', $this->distributionLevels);
$template->set('analysisLevels', $this->analysisLevels);
$template->set('tlp', $this->getEmailSubjectMarkForEvent($event));
return $template;
}
/**
@ -3327,42 +3307,33 @@ class Event extends AppModel
$tplColorString = $this->getEmailSubjectMarkForEvent($event);
$subject = "[" . Configure::read('MISP.org') . " MISP] Need info about event $id - " . strtoupper($tplColorString);
$result = true;
foreach ($orgMembers as $reporter) {
list($bodyevent, $body) = $this->__buildContactEventEmailBody($user, $reporter, $message, $event);
$result = $this->User->sendEmail($reporter, $bodyevent, $body, $subject, $user) && $result;
foreach ($orgMembers as $eventReporter) {
$body = $this->prepareContactAlertEmail($user, $eventReporter, $message, $event);
$result = $this->User->sendEmail($eventReporter, $body, false, $subject, $user) && $result;
}
return $result;
}
private function __buildContactEventEmailBody(array $user, array $reporter, $message, array $event)
/**
* @param array $user
* @param array $eventReporter
* @param string $message
* @param array $event
* @return SendEmailTemplate
*/
private function prepareContactAlertEmail(array $user, array $eventReporter, $message, array $event)
{
// The mail body, h() is NOT needed as we are sending plain-text mails.
$body = "";
$body .= "Hello, \n";
$body .= "\n";
$body .= "Someone wants to get in touch with you concerning a MISP event. \n";
$body .= "\n";
$body .= "You can reach them at " . $user['User']['email'] . "\n";
if (!empty($user['User']['gpgkey'])) {
$body .= "Their GnuPG key is added as attachment to this email. \n";
}
if (!empty($user['User']['certif_public'])) {
$body .= "Their Public certificate is added as attachment to this email. \n";
}
$body .= "\n";
$body .= "They wrote the following message: \n";
$body .= $message . "\n";
$body .= "\n";
$body .= "\n";
$body .= "The event is the following: \n";
$full = $body;
$body .= $this->__getAnnounceBaseurl() . '/events/view/' . $event['Event']['id'] . "\n";
$rendered = $this->renderAlertEmail($event, $reporter);
$full .= $rendered->text;
return array($body, $full);
$template = new SendEmailTemplate('alert_contact');
$template->set('event', $event);
$template->set('requestor', $user);
$template->set('message', $message);
$template->set('user', $this->User->rearrangeToAuthForm($eventReporter));
$template->set('baseurl', $this->__getAnnounceBaseurl());
$template->set('distributionLevels', $this->distributionLevels);
$template->set('analysisLevels', $this->analysisLevels);
$template->set('contactAlert', true);
$template->set('tlp', $this->getEmailSubjectMarkForEvent($event));
return $template;
}
public function captureSGForElement($element, $user, $server=false)

View File

@ -807,13 +807,7 @@ class User extends AppModel
$sendEmail = new SendEmail($gpg);
try {
$encrypted = $sendEmail->sendToUser(
$user,
$subject,
is_string($body) ? new CakeEmailBody($body) : $body,
$bodyNoEnc ? (is_string($bodyNoEnc) ? new CakeEmailBody($bodyNoEnc) : $bodyNoEnc): null,
$replyToUser ?: []
);
$encrypted = $sendEmail->sendToUser($user, $subject, $body, $bodyNoEnc,$replyToUser ?: []);
} catch (SendEmailException $e) {
$this->logException("Exception during sending e-mail", $e);

View File

@ -1,4 +1,19 @@
<?php
if (!isset($oldPublishTimestamp)) {
$oldPublishTimestamp = null;
}
if (!isset($contactAlert)) {
$contactAlert = false;
}
if ($hideDetails) { // Used when GnuPG.bodyonlyencrypted is enabled and e-mail cannot be send in encrypted form
$eventUrl = $baseurl . "/events/view/" . $event['Event']['id'];
echo __("A new or modified event was just published on %s", $eventUrl) . PHP_EOL . PHP_EOL;
echo __("If you would like to unsubscribe from receiving such alert e-mails, simply\ndisable publish alerts via %s", $baseurl . '/users/edit');
return;
}
$renderAttributes = function(array $attributes, $indent = ' ') use ($oldPublishTimestamp) {
$appendlen = 20;
foreach ($attributes as $attribute) {
@ -83,12 +98,12 @@ foreach ($event['RelatedEvent'] as $relatedEvent) {
<?php endif; ?>
<?php if (!empty($event['Attribute'])): ?>
Attributes<?= isset($oldPublishTimestamp) ? ' (* indicates a new or modified attribute since last update):' : ':' ?>
Attributes<?= isset($oldPublishTimestamp) ? " (* indicates a new or modified attribute since last update):\n" : ":\n" ?>
<?= $renderAttributes($event['Attribute']) ?>
<?php endif; ?>
<?php if (!empty($event['Object'])): ?>
Objects<?= isset($oldPublishTimestamp) ? ' (* indicates a new or modified object since last update):' : ':' ?>
Objects<?= isset($oldPublishTimestamp) ? " (* indicates a new or modified object since last update):\n" : ":\n" ?>
<?= $renderObjects($event['Object']) ?>
<?php endif; ?>

View File

@ -0,0 +1,23 @@
Hello,
Someone wants to get in touch with you concerning a MISP event.
You can reach them at <?= $requestor['User']['email'] ?>
<?php if (!empty($requestor['User']['gpgkey'])): ?>
Their PGP key is added as attachment to this email.
<?php endif; ?>
<?php if (!empty($requestor['User']['certif_public'])): ?>
Their Public certificate is added as attachment to this email.
<?php endif; ?>
They wrote the following message:
<?= $message ?>
The event is the following:
<?php
if ($hideDetails) {
echo $baseurl . ' /events/view/' . $event['Event']['id'];
} else {
require __DIR__ . '/alert.ctp'; // include event details
}