From 46d19f4423bea77fe663415a4388467e12fb0c0a Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sun, 4 Feb 2018 11:24:43 +0200 Subject: [PATCH] Throttle emails --- app/Http/Middleware/ApiCheck.php | 4 +-- app/Ninja/Mailers/ContactMailer.php | 48 ++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/app/Http/Middleware/ApiCheck.php b/app/Http/Middleware/ApiCheck.php index 310ebaaf60aa..7bd19386d6ea 100644 --- a/app/Http/Middleware/ApiCheck.php +++ b/app/Http/Middleware/ApiCheck.php @@ -100,8 +100,8 @@ class ApiCheck return Response::json("Please wait {$wait} second(s)", 403, $headers); } - Cache::put("hour_throttle:{$key}", $new_hour_throttle, 10); - Cache::put("last_api_request:{$key}", time(), 10); + Cache::put("hour_throttle:{$key}", $new_hour_throttle, 60); + Cache::put("last_api_request:{$key}", time(), 60); } return $next($request); diff --git a/app/Ninja/Mailers/ContactMailer.php b/app/Ninja/Mailers/ContactMailer.php index 6eba13206cf6..982d56ec5d8f 100644 --- a/app/Ninja/Mailers/ContactMailer.php +++ b/app/Ninja/Mailers/ContactMailer.php @@ -11,6 +11,8 @@ use App\Services\TemplateService; use App\Jobs\ConvertInvoiceToUbl; use Event; use Utils; +use Cache; +use Mail; class ContactMailer extends Mailer { @@ -150,7 +152,7 @@ class ContactMailer extends Mailer if (! $user->email || ! $user->registered) { return trans('texts.email_error_user_unregistered'); - } elseif (! $user->confirmed) { + } elseif (! $user->confirmed || $this->isThrottled($account)) { return trans('texts.email_error_user_unconfirmed'); } elseif (! $invitation->contact->email) { return trans('texts.email_error_invalid_contact_email'); @@ -355,4 +357,48 @@ class ContactMailer extends Mailer $this->sendTo($contact->email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data); } + + private function isThrottled($account) + { + if (Utils::isSelfHost()) { + return false; + } + + $key = $account->company_id; + + // http://stackoverflow.com/questions/1375501/how-do-i-throttle-my-sites-api-users + $hour = 60 * 60; + $hour_limit = 50; // users are limited to 50 emails/hour + $hour_throttle = Cache::get("email_hour_throttle:{$key}", null); + $last_api_request = Cache::get("last_email_request:{$key}", 0); + $last_api_diff = time() - $last_api_request; + + if (is_null($hour_throttle)) { + $new_hour_throttle = 0; + } else { + $new_hour_throttle = $hour_throttle - $last_api_diff; + $new_hour_throttle = $new_hour_throttle < 0 ? 0 : $new_hour_throttle; + $new_hour_throttle += $hour / $hour_limit; + $hour_hits_remaining = floor(($hour - $new_hour_throttle) * $hour_limit / $hour); + $hour_hits_remaining = $hour_hits_remaining >= 0 ? $hour_hits_remaining : 0; + } + + Cache::put("email_hour_throttle:{$key}", $new_hour_throttle, 60); + Cache::put("last_email_request:{$key}", time(), 60); + + if ($new_hour_throttle > $hour) { + $errorEmail = env('ERROR_EMAIL'); + if ($errorEmail && ! Cache::get("throttle_notified:{$key}")) { + Mail::raw('Account Throttle', function ($message) use ($errorEmail, $account) { + $message->to($errorEmail) + ->from(CONTACT_EMAIL) + ->subject("Email throttle triggered for account " . $account->id); + }); + } + Cache::put("throttle_notified:{$key}", true, 60); + return true; + } + + return false; + } }