Merge pull request #10392 from paulwer/fix-inbound-issues

Fix inbound issues
This commit is contained in:
David Bomba 2024-12-17 10:05:49 +11:00 committed by GitHub
commit bb5f10c113
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 66 additions and 39 deletions

View File

@ -11,6 +11,7 @@
namespace App\Http\Controllers;
use App\Services\InboundMail\InboundMailEngine;
use App\Utils\Ninja;
use App\Models\Company;
use App\Libraries\MultiDB;
@ -137,14 +138,15 @@ class MailgunController extends BaseController
return response()->json(['message' => 'Failed. Missing Parameters. Use store and notify!'], 400);
}
/** @var \App\Models\Company $company */
$company = MultiDB::findAndSetDbByExpenseMailbox($input["recipient"]);
$inboundEngine = new InboundMailEngine();
if (!$company) {
return response()->json(['message' => 'Ok'], 200);
} // Fail gracefully
// Spam protection
if ($inboundEngine->isInvalidOrBlocked($input["sender"], $input["recipient"])) {
return;
}
ProcessMailgunInboundWebhook::dispatch($input["sender"], $input["recipient"], $input["message-url"], $company)->delay(rand(2, 10));
// Dispatch Job for processing
ProcessMailgunInboundWebhook::dispatch($input["sender"], $input["recipient"], $input["message-url"])->delay(rand(2, 10));
return response()->json(['message' => 'Success.'], 200);
}

View File

@ -282,6 +282,14 @@ class PostMarkController extends BaseController
return response()->json(['message' => 'Failed. Missing/Invalid Parameters.'], 400);
}
$inboundEngine = new InboundMailEngine();
// Spam protection
if ($inboundEngine->isInvalidOrBlocked($input["From"], $input["ToFull"][0]["Email"])) {
return;
}
// match company
$company = MultiDB::findAndSetDbByExpenseMailbox($input["ToFull"][0]["Email"]);
if (!$company) {
@ -289,11 +297,7 @@ class PostMarkController extends BaseController
return response()->json(['message' => 'Ok'], 200);
}
$inboundEngine = new InboundMailEngine($company);
if ($inboundEngine->isInvalidOrBlocked($input["From"], $input["ToFull"][0]["Email"])) {
return response()->json(['message' => 'Blocked.'], 403);
}
$inboundEngine->setCompany($company);
try { // important to save meta if something fails here to prevent spam

View File

@ -115,6 +115,7 @@ class ProcessBrevoInboundWebhook implements ShouldQueue
*/
public function __construct(private array $input)
{
$this->engine = new InboundMailEngine();
}
/**
@ -143,7 +144,7 @@ class ProcessBrevoInboundWebhook implements ShouldQueue
continue;
}
$this->engine = new InboundMailEngine($company);
$this->engine->setCompany($company);
$foundOneRecipient = true;
@ -240,7 +241,7 @@ class ProcessBrevoInboundWebhook implements ShouldQueue
public function failed($exception)
{
nlog("BREVO:: Ingest Exception:: => ".$exception->getMessage());
nlog("BREVO:: Ingest Exception:: => " . $exception->getMessage());
config(['queue.failed.driver' => null]);
}

View File

@ -38,9 +38,9 @@ class ProcessMailgunInboundWebhook implements ShouldQueue
* Create a new job instance.
* $input consists of 3 informations: sender/from|recipient/to|messageUrl
*/
public function __construct(private string $sender, private string $recipient, private string $message_url, private Company $company)
public function __construct(private string $sender, private string $recipient, private string $message_url)
{
$this->engine = new InboundMailEngine($company);
$this->engine = new InboundMailEngine();
}
/**
@ -180,6 +180,15 @@ class ProcessMailgunInboundWebhook implements ShouldQueue
return;
}
// match company
$company = MultiDB::findAndSetDbByExpenseMailbox($to);
if (!$company) {
return;
}
$this->engine->setCompany($company);
// lets assess this at a higher level to ensure that only valid email inboxes are processed.
// match company
// $company = MultiDB::findAndSetDbByExpenseMailbox($to);
@ -192,8 +201,8 @@ class ProcessMailgunInboundWebhook implements ShouldQueue
try { // important to save meta if something fails here to prevent spam
// fetch message from mailgun-api
$company_mailgun_domain = $this->company->getSetting('email_sending_method') == 'client_mailgun' && strlen($this->company->getSetting('mailgun_domain') ?? '') > 2 ? $this->company->getSetting('mailgun_domain') : null;
$company_mailgun_secret = $this->company->getSetting('email_sending_method') == 'client_mailgun' && strlen($this->company->getSetting('mailgun_secret') ?? '') > 2 ? $this->company->getSetting('mailgun_secret') : null;
$company_mailgun_domain = $company->getSetting('email_sending_method') == 'client_mailgun' && strlen($company->getSetting('mailgun_domain') ?? '') > 2 ? $company->getSetting('mailgun_domain') : null;
$company_mailgun_secret = $company->getSetting('email_sending_method') == 'client_mailgun' && strlen($company->getSetting('mailgun_secret') ?? '') > 2 ? $company->getSetting('mailgun_secret') : null;
if (!($company_mailgun_domain && $company_mailgun_secret) && !(config('services.mailgun.domain') && config('services.mailgun.secret'))) {
throw new \Error("[ProcessMailgunInboundWebhook] no mailgun credentials found, we cannot get the attachements and files");
}

View File

@ -39,33 +39,12 @@ class InboundMailEngine
private array $globalWhitelist;
public function __construct(private Company $company)
public function __construct(private ?Company $company = null)
{
$this->globalBlacklist = Ninja::isSelfHost() ? explode(",", config('ninja.inbound_mailbox.global_inbound_blocklist')) : [];
$this->globalWhitelist = Ninja::isSelfHost() ? explode(",", config('ninja.inbound_mailbox.global_inbound_whitelist')) : [];
}
/**
* if there is not a company with an matching mailbox, we only do monitoring
* reuse this method to add more mail-parsing behaviors
*/
public function handleExpenseMailbox(InboundMail $email)
{
if ($this->isInvalidOrBlocked($email->from, $email->to)) {
return;
}
// check if company plan matches requirements
if (Ninja::isHosted() && !($this->company->account->isPaid() && $this->company->account->plan == 'enterprise')) {
return;
}
$this->createExpenses($email);
$this->saveMeta($email->from, $email->to);
}
// SPAM Protection
public function isInvalidOrBlocked(string $from, string $to)
{
@ -163,9 +142,41 @@ class InboundMailEngine
}
}
// COMPANY HANDLING SECTION => these methods require a set company for the engine
public function setCompany(Company $company)
{
$this->company = $company;
}
/**
* if there is not a company with an matching mailbox, we only do monitoring
* reuse this method to add more mail-parsing behaviors
*/
public function handleExpenseMailbox(InboundMail $email)
{
if (empty($this->company))
throw new \Exception('invalid use of inbound mail engine: no company selected');
if ($this->isInvalidOrBlocked($email->from, $email->to)) {
return;
}
// check if company plan matches requirements
if (Ninja::isHosted() && !($this->company->account->isPaid() && $this->company->account->plan == 'enterprise')) {
return;
}
$this->createExpenses($email);
$this->saveMeta($email->from, $email->to);
}
// MAIN-PROCESSORS
protected function createExpenses(InboundMail $email)
{
if (empty($this->company))
throw new \Exception('invalid use of inbound mail engine: no company selected');
// Skipping executions: will not result in not saving Metadata to prevent usage of these conditions, to spam
if (!$this->company->expense_mailbox_active) {
$this->logBlocked('mailbox not active for this company. from: ' . $email->from);