mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-23 05:50:55 +01:00
Copy mail sender selection logic into MailReceivers
Summary: Copies sender identification logic into MailReceivers and makes it basically sane. The mess we run into after this try/catch is terrifying so I'm avoiding actually getting rid of any of it quite yet. Ref T1205. Test Plan: Added a bit of test coverage. Used Receiver test console to verify some additional behaviors. Reviewers: btrahan Reviewed By: btrahan CC: Afaque_Hussain, aran Maniphest Tasks: T1205 Differential Revision: https://secure.phabricator.com/D5931
This commit is contained in:
parent
ea43c0bc86
commit
ff4073c2f4
5 changed files with 158 additions and 5 deletions
|
@ -20,4 +20,40 @@ final class ManiphestCreateMailReceiver extends PhabricatorMailReceiver {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function loadSender(PhabricatorMetaMTAReceivedMail $mail) {
|
||||||
|
try {
|
||||||
|
// Try to load the sender normally.
|
||||||
|
return parent::loadSender($mail);
|
||||||
|
} catch (PhabricatorMetaMTAReceivedMailProcessingException $ex) {
|
||||||
|
|
||||||
|
// If we failed to load the sender normally, use this special legacy
|
||||||
|
// black magic.
|
||||||
|
|
||||||
|
// TODO: Deprecate and remove this.
|
||||||
|
|
||||||
|
$default_author_key = 'metamta.maniphest.default-public-author';
|
||||||
|
$default_author = PhabricatorEnv::getEnvConfig($default_author_key);
|
||||||
|
|
||||||
|
if (!strlen($default_author)) {
|
||||||
|
throw $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = id(new PhabricatorUser())->loadOneWhere(
|
||||||
|
'username = %s',
|
||||||
|
$default_author);
|
||||||
|
|
||||||
|
if ($user) {
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new PhabricatorMetaMTAReceivedMailProcessingException(
|
||||||
|
MetaMTAReceivedMailStatus::STATUS_UNKNOWN_SENDER,
|
||||||
|
pht(
|
||||||
|
"Phabricator is misconfigured, the configuration key ".
|
||||||
|
"'metamta.maniphest.default-public-author' is set to user ".
|
||||||
|
"'%s' but that user does not exist.",
|
||||||
|
$default_author));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,5 +7,6 @@ final class MetaMTAReceivedMailStatus
|
||||||
const STATUS_FROM_PHABRICATOR = 'err:self';
|
const STATUS_FROM_PHABRICATOR = 'err:self';
|
||||||
const STATUS_NO_RECEIVERS = 'err:no-receivers';
|
const STATUS_NO_RECEIVERS = 'err:no-receivers';
|
||||||
const STATUS_ABUNDANT_RECEIVERS = 'err:multiple-receivers';
|
const STATUS_ABUNDANT_RECEIVERS = 'err:multiple-receivers';
|
||||||
|
const STATUS_UNKNOWN_SENDER = 'err:unknown-sender';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,83 @@ abstract class PhabricatorMailReceiver {
|
||||||
abstract public function isEnabled();
|
abstract public function isEnabled();
|
||||||
abstract public function canAcceptMail(PhabricatorMetaMTAReceivedMail $mail);
|
abstract public function canAcceptMail(PhabricatorMetaMTAReceivedMail $mail);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifies the sender's user account for a piece of received mail. Note
|
||||||
|
* that this method does not validate that the sender is who they say they
|
||||||
|
* are, just that they've presented some credential which corresponds to a
|
||||||
|
* recognizable user.
|
||||||
|
*/
|
||||||
|
public function loadSender(PhabricatorMetaMTAReceivedMail $mail) {
|
||||||
|
$raw_from = $mail->getHeader('From');
|
||||||
|
$from = self::getRawAddress($raw_from);
|
||||||
|
|
||||||
|
$reasons = array();
|
||||||
|
|
||||||
|
// Try to find a user with this email address.
|
||||||
|
$user = PhabricatorUser::loadOneWithEmailAddress($from);
|
||||||
|
if ($user) {
|
||||||
|
return $user;
|
||||||
|
} else {
|
||||||
|
$reasons[] = pht(
|
||||||
|
"The email was sent from '%s', but this address does not correspond ".
|
||||||
|
"to any user account.",
|
||||||
|
$raw_from);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we missed on "From", try "Reply-To" if we're configured for it.
|
||||||
|
$reply_to_key = 'metamta.insecure-auth-with-reply-to';
|
||||||
|
$allow_reply_to = PhabricatorEnv::getEnvConfig($reply_to_key);
|
||||||
|
if ($allow_reply_to) {
|
||||||
|
$raw_reply_to = $mail->getHeader('Reply-To');
|
||||||
|
$reply_to = self::getRawAddress($raw_reply_to);
|
||||||
|
|
||||||
|
$user = PhabricatorUser::loadOneWithEmailAddress($reply_to);
|
||||||
|
if ($user) {
|
||||||
|
return $user;
|
||||||
|
} else {
|
||||||
|
$reasons[] = pht(
|
||||||
|
"Phabricator is configured to try to authenticate users using ".
|
||||||
|
"'Reply-To', but the reply to address ('%s') does not correspond ".
|
||||||
|
"to any user account.",
|
||||||
|
$raw_reply_to);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$reasons[] = pht(
|
||||||
|
"Phabricator is not configured to authenticate users using ".
|
||||||
|
"'Reply-To' (`metamta.insecure-auth-with-reply-to`), so the ".
|
||||||
|
"'Reply-To' header was not examined.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't know who this user is, load or create an external user
|
||||||
|
// account for them if we're configured for it.
|
||||||
|
$email_key = 'phabricator.allow-email-users';
|
||||||
|
$allow_email_users = PhabricatorEnv::getEnvConfig($email_key);
|
||||||
|
if ($allow_email_users) {
|
||||||
|
$xuser = id(new PhabricatorExternalAccount())->loadOneWhere(
|
||||||
|
'accountType = %s AND accountDomain IS NULL and accountID = %s',
|
||||||
|
'email',
|
||||||
|
$from);
|
||||||
|
if (!$xuser) {
|
||||||
|
$xuser = id(new PhabricatorExternalAccount())
|
||||||
|
->setAccountID($from)
|
||||||
|
->setAccountType('email')
|
||||||
|
->setDisplayName($from)
|
||||||
|
->save();
|
||||||
|
}
|
||||||
|
return $xuser->getPhabricatorUser();
|
||||||
|
} else {
|
||||||
|
$reasons[] = pht(
|
||||||
|
"Phabricator is not configured to allow unknown external users to ".
|
||||||
|
"send mail to the system using just an email address ".
|
||||||
|
"(`phabricator.allow-email-users`), so an implicit external acount ".
|
||||||
|
"could not be created.");
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new PhabricatorMetaMTAReceivedMailProcessingException(
|
||||||
|
MetaMTAReceivedMailStatus::STATUS_UNKNOWN_SENDER,
|
||||||
|
pht('Unknown sender: %s', implode(' ', $reasons)));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if two inbound email addresses are effectively identical. This
|
* Determine if two inbound email addresses are effectively identical. This
|
||||||
* method strips and normalizes addresses so that equivalent variations are
|
* method strips and normalizes addresses so that equivalent variations are
|
||||||
|
@ -22,15 +99,12 @@ abstract class PhabricatorMailReceiver {
|
||||||
* @return bool True if addresses match.
|
* @return bool True if addresses match.
|
||||||
*/
|
*/
|
||||||
public static function matchAddresses($u, $v) {
|
public static function matchAddresses($u, $v) {
|
||||||
$u = id(new PhutilEmailAddress($u))->getAddress();
|
$u = self::getRawAddress($u);
|
||||||
$v = id(new PhutilEmailAddress($v))->getAddress();
|
$v = self::getRawAddress($v);
|
||||||
|
|
||||||
$u = self::stripMailboxPrefix($u);
|
$u = self::stripMailboxPrefix($u);
|
||||||
$v = self::stripMailboxPrefix($v);
|
$v = self::stripMailboxPrefix($v);
|
||||||
|
|
||||||
$u = trim(phutil_utf8_strtolower($u));
|
|
||||||
$v = trim(phutil_utf8_strtolower($v));
|
|
||||||
|
|
||||||
return ($u === $v);
|
return ($u === $v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,4 +143,22 @@ abstract class PhabricatorMailReceiver {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduce an email address to its canonical form. For example, an adddress
|
||||||
|
* like:
|
||||||
|
*
|
||||||
|
* "Abraham Lincoln" < ALincoln@example.com >
|
||||||
|
*
|
||||||
|
* ...will be reduced to:
|
||||||
|
*
|
||||||
|
* alincoln@example.com
|
||||||
|
*
|
||||||
|
* @param string Email address in noncanonical form.
|
||||||
|
* @return string Canonical email address.
|
||||||
|
*/
|
||||||
|
public static function getRawAddress($address) {
|
||||||
|
$address = id(new PhutilEmailAddress($address))->getAddress();
|
||||||
|
return trim(phutil_utf8_strtolower($address));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,6 +175,7 @@ final class PhabricatorMetaMTAReceivedMail extends PhabricatorMetaMTADAO {
|
||||||
$this->dropMailAlreadyReceived();
|
$this->dropMailAlreadyReceived();
|
||||||
|
|
||||||
$receiver = $this->loadReceiver();
|
$receiver = $this->loadReceiver();
|
||||||
|
$sender = $receiver->loadSender($this);
|
||||||
|
|
||||||
} catch (PhabricatorMetaMTAReceivedMailProcessingException $ex) {
|
} catch (PhabricatorMetaMTAReceivedMailProcessingException $ex) {
|
||||||
$this
|
$this
|
||||||
|
|
|
@ -63,5 +63,28 @@ final class PhabricatorMetaMTAReceivedMailTestCase extends PhabricatorTestCase {
|
||||||
$mail->getStatus());
|
$mail->getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testDropUnknownSenderMail() {
|
||||||
|
$env = PhabricatorEnv::beginScopedEnv();
|
||||||
|
$env->overrideEnvConfig(
|
||||||
|
'metamta.maniphest.public-create-email',
|
||||||
|
'bugs@example.com');
|
||||||
|
$env->overrideEnvConfig('phabricator.allow-email-users', false);
|
||||||
|
$env->overrideEnvConfig('metamta.maniphest.default-public-author', null);
|
||||||
|
|
||||||
|
$mail = new PhabricatorMetaMTAReceivedMail();
|
||||||
|
$mail->setHeaders(
|
||||||
|
array(
|
||||||
|
'Message-ID' => 'test@example.com',
|
||||||
|
'To' => 'bugs@example.com',
|
||||||
|
'From' => 'does+not+exist@example.com',
|
||||||
|
));
|
||||||
|
$mail->save();
|
||||||
|
|
||||||
|
$mail->processReceivedMail();
|
||||||
|
|
||||||
|
$this->assertEqual(
|
||||||
|
MetaMTAReceivedMailStatus::STATUS_UNKNOWN_SENDER,
|
||||||
|
$mail->getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue