1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-10 08:52:39 +01:00

Use "cluster.mailers" if it is configured

Summary:
Depends on D19004. Ref T13053. Ref T12677. If the new `cluster.mailers` is configured, make use of it. Also use it in the Sengrid/Mailgun inbound stuff.

Also fix a bug where "Must Encrypt" mail to no recipients could fatal because no `$mail` was returned.

Test Plan: Processed some mail locally. The testing on this is still pretty flimsy, but I plan to solidify it in an upcoming change.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13053, T12677

Differential Revision: https://secure.phabricator.com/D19005
This commit is contained in:
epriestley 2018-02-06 06:31:37 -08:00
parent 4236952cdb
commit 994d2e8e15
5 changed files with 126 additions and 20 deletions

View file

@ -3,6 +3,7 @@
abstract class PhabricatorMailImplementationAdapter extends Phobject {
private $key;
private $priority;
private $options = array();
final public function getAdapterType() {
@ -57,6 +58,15 @@ abstract class PhabricatorMailImplementationAdapter extends Phobject {
return $this->key;
}
final public function setPriority($priority) {
$this->priority = $priority;
return $this;
}
final public function getPriority() {
return $this->priority;
}
final public function getOption($key) {
if (!array_key_exists($key, $this->options)) {
throw new Exception(

View file

@ -8,14 +8,31 @@ final class PhabricatorMetaMTAMailgunReceiveController
}
private function verifyMessage() {
$api_key = PhabricatorEnv::getEnvConfig('mailgun.api-key');
$request = $this->getRequest();
$timestamp = $request->getStr('timestamp');
$token = $request->getStr('token');
$sig = $request->getStr('signature');
$hash = hash_hmac('sha256', $timestamp.$token, $api_key);
return phutil_hashes_are_identical($sig, $hash);
// An install may configure multiple Mailgun mailers, and we might receive
// inbound mail from any of them. Test the signature to see if it matches
// any configured Mailgun mailer.
$mailers = PhabricatorMetaMTAMail::newMailers();
$mailgun_type = PhabricatorMailImplementationMailgunAdapter::ADAPTERTYPE;
foreach ($mailers as $mailer) {
if ($mailer->getAdapterType() != $mailgun_type) {
continue;
}
$api_key = $mailer->getOption('api-key');
$hash = hash_hmac('sha256', $timestamp.$token, $api_key);
if (phutil_hashes_are_identical($sig, $hash)) {
return true;
}
}
return false;
}
public function handleRequest(AphrontRequest $request) {

View file

@ -8,6 +8,26 @@ final class PhabricatorMetaMTASendGridReceiveController
}
public function handleRequest(AphrontRequest $request) {
$mailers = PhabricatorMetaMTAMail::newMailers();
$sendgrid_type = PhabricatorMailImplementationSendGridAdapter::ADAPTERTYPE;
// SendGrid doesn't sign payloads so we can't be sure that SendGrid
// actually sent this request, but require a configured SendGrid mailer
// before we activate this endpoint.
$has_sendgrid = false;
foreach ($mailers as $mailer) {
if ($mailer->getAdapterType() != $sendgrid_type) {
continue;
}
$has_sendgrid = true;
break;
}
if (!$has_sendgrid) {
return new Aphront404Response();
}
// No CSRF for SendGrid.
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();

View file

@ -463,33 +463,85 @@ final class PhabricatorMetaMTAMail
throw new Exception(pht('Trying to send an already-sent mail!'));
}
$mailers = $this->newMailers();
$mailers = self::newMailers();
return $this->sendWithMailers($mailers);
}
private function newMailers() {
public static function newMailers() {
$mailers = array();
$mailer = PhabricatorEnv::newObjectFromConfig('metamta.mail-adapter');
$config = PhabricatorEnv::getEnvConfig('cluster.mailers');
if ($config === null) {
$mailer = PhabricatorEnv::newObjectFromConfig('metamta.mail-adapter');
$defaults = $mailer->newDefaultOptions();
$options = $mailer->newLegacyOptions();
$defaults = $mailer->newDefaultOptions();
$options = $mailer->newLegacyOptions();
$options = $options + $defaults;
$options = $options + $defaults;
$mailer
->setKey('default')
->setOptions($options);
$mailer
->setKey('default')
->setPriority(-1)
->setOptions($options);
$mailer->prepareForSend();
$mailers[] = $mailer;
} else {
$adapters = PhabricatorMailImplementationAdapter::getAllAdapters();
$next_priority = -1;
$mailers[] = $mailer;
foreach ($config as $spec) {
$type = $spec['type'];
if (!isset($adapters[$type])) {
throw new Exception(
pht(
'Unknown mailer ("%s")!',
$type));
}
return $mailers;
$key = $spec['key'];
$mailer = id(clone $adapters[$type])
->setKey($key);
$priority = idx($spec, 'priority');
if (!$priority) {
$priority = $next_priority;
$next_priority--;
}
$mailer->setPriority($priority);
$defaults = $mailer->newDefaultOptions();
$options = idx($spec, 'options', array()) + $defaults;
$mailer->setOptions($options);
}
}
$sorted = array();
$groups = mgroup($mailers, 'getPriority');
ksort($groups);
foreach ($groups as $group) {
// Reorder services within the same priority group randomly.
shuffle($group);
foreach ($group as $mailer) {
$sorted[] = $mailer;
}
}
foreach ($sorted as $mailer) {
$mailer->prepareForSend();
}
return $sorted;
}
public function sendWithMailers(array $mailers) {
if (!$mailers) {
return $this
->setStatus(PhabricatorMailOutboundStatus::STATUS_VOID)
->setMessage(pht('No mailers are configured.'))
->save();
}
$exceptions = array();
foreach ($mailers as $template_mailer) {
$mailer = null;
@ -865,6 +917,12 @@ final class PhabricatorMetaMTAMail
$mailer->addCCs($add_cc);
}
// Keep track of which mailer actually ended up accepting the message.
$mailer_key = $mailer->getKey();
if ($mailer_key !== null) {
$this->setParam('mailer.key', $mailer_key);
}
return $mailer;
}

View file

@ -2575,12 +2575,13 @@ abstract class PhabricatorApplicationTransactionEditor
$mail = $this->buildMailForTarget($object, $xactions, $target);
if ($this->mustEncrypt) {
$mail
->setMustEncrypt(true)
->setMustEncryptReasons($this->mustEncrypt);
if ($mail) {
if ($this->mustEncrypt) {
$mail
->setMustEncrypt(true)
->setMustEncryptReasons($this->mustEncrypt);
}
}
} catch (Exception $ex) {
$caught = $ex;
}