Page MenuHomePhorge

D5220.1775372482.diff
No OneTemporary

Authored By
Unknown
Size
56 KB
Referenced Files
None
Subscribers
None

D5220.1775372482.diff

diff --git a/src/app/Console/Commands/Policy/RateLimit/Whitelist/CreateCommand.php b/src/app/Console/Commands/Policy/RateLimit/Whitelist/CreateCommand.php
--- a/src/app/Console/Commands/Policy/RateLimit/Whitelist/CreateCommand.php
+++ b/src/app/Console/Commands/Policy/RateLimit/Whitelist/CreateCommand.php
@@ -51,7 +51,7 @@
$type = \App\User::class;
}
- \App\Policy\RateLimitWhitelist::create(
+ \App\Policy\RateLimit\Whitelist::create(
[
'whitelistable_id' => $id,
'whitelistable_type' => $type
diff --git a/src/app/Console/Commands/Policy/RateLimit/Whitelist/DeleteCommand.php b/src/app/Console/Commands/Policy/RateLimit/Whitelist/DeleteCommand.php
--- a/src/app/Console/Commands/Policy/RateLimit/Whitelist/DeleteCommand.php
+++ b/src/app/Console/Commands/Policy/RateLimit/Whitelist/DeleteCommand.php
@@ -51,7 +51,7 @@
$type = \App\User::class;
}
- \App\Policy\RateLimitWhitelist::where(
+ \App\Policy\RateLimit\Whitelist::where(
[
'whitelistable_id' => $id,
'whitelistable_type' => $type
diff --git a/src/app/Console/Commands/Policy/RateLimit/Whitelist/ReadCommand.php b/src/app/Console/Commands/Policy/RateLimit/Whitelist/ReadCommand.php
--- a/src/app/Console/Commands/Policy/RateLimit/Whitelist/ReadCommand.php
+++ b/src/app/Console/Commands/Policy/RateLimit/Whitelist/ReadCommand.php
@@ -27,7 +27,7 @@
*/
public function handle()
{
- \App\Policy\RateLimitWhitelist::each(
+ \App\Policy\RateLimit\Whitelist::each(
function ($item) {
$whitelistable = $item->whitelistable;
diff --git a/src/app/Http/Controllers/API/V4/PolicyController.php b/src/app/Http/Controllers/API/V4/PolicyController.php
--- a/src/app/Http/Controllers/API/V4/PolicyController.php
+++ b/src/app/Http/Controllers/API/V4/PolicyController.php
@@ -5,11 +5,7 @@
use App\Http\Controllers\Controller;
use App\Policy\Mailfilter\RequestHandler as Mailfilter;
use App\Policy\RateLimit;
-use App\Policy\RateLimitWhitelist;
-use App\Transaction;
use Illuminate\Http\Request;
-use Illuminate\Support\Facades\Auth;
-use Illuminate\Support\Facades\Validator;
class PolicyController extends Controller
{
@@ -76,185 +72,25 @@
return response()->json(['response' => 'DUNNO'], 200);
}
- //
- // Examine the individual sender
- //
+ // Find the Kolab user
$user = \App\User::withTrashed()->where('email', $sender)->first();
if (!$user) {
$alias = \App\UserAlias::where('alias', $sender)->first();
if (!$alias) {
+ // TODO: How about sender is a distlist address?
+
// external sender through where this policy is applied
return response()->json(['response' => 'DUNNO'], 200);
}
- $user = $alias->user;
- }
-
- if (empty($user) || $user->trashed() || $user->isSuspended()) {
- // use HOLD, so that it is silent (as opposed to REJECT)
- return response()->json(['response' => 'HOLD', 'reason' => 'Sender deleted or suspended'], 403);
- }
-
- //
- // Examine the domain
- //
- $domain = \App\Domain::withTrashed()->where('namespace', $domain)->first();
-
- if (!$domain) {
- // external sender through where this policy is applied
- return response()->json(['response' => 'DUNNO'], 200);
- }
-
- if ($domain->trashed() || $domain->isSuspended()) {
- // use HOLD, so that it is silent (as opposed to REJECT)
- return response()->json(['response' => 'HOLD', 'reason' => 'Sender domain deleted or suspended'], 403);
- }
-
- // see if the user or domain is whitelisted
- // use ./artisan policy:ratelimit:whitelist:create <email|namespace>
- if (RateLimitWhitelist::isListed($user) || RateLimitWhitelist::isListed($domain)) {
- return response()->json(['response' => 'DUNNO'], 200);
- }
-
- // user nor domain whitelisted, continue scrutinizing the request
- $recipients = (array)$data['recipients'];
- sort($recipients);
-
- $recipientCount = count($recipients);
- $recipientHash = hash('sha256', implode(',', $recipients));
-
- //
- // Retrieve the wallet to get to the owner
- //
- $wallet = $user->wallet();
-
- // wait, there is no wallet?
- if (!$wallet || !$wallet->owner) {
- return response()->json(['response' => 'HOLD', 'reason' => 'Sender without a wallet'], 403);
+ $user = $alias->user()->withTrashed()->first();
}
- $owner = $wallet->owner;
-
- // find or create the request
- $request = RateLimit::where('recipient_hash', $recipientHash)
- ->where('user_id', $user->id)
- ->where('updated_at', '>=', \Carbon\Carbon::now()->subHour())
- ->first();
-
- if (!$request) {
- $request = RateLimit::create([
- 'user_id' => $user->id,
- 'owner_id' => $owner->id,
- 'recipient_hash' => $recipientHash,
- 'recipient_count' => $recipientCount
- ]);
- } else {
- // ensure the request has an up to date timestamp
- $request->updated_at = \Carbon\Carbon::now();
- $request->save();
- }
-
- // exempt owners that have 100% discount.
- if ($wallet->discount && $wallet->discount->discount == 100) {
- return response()->json(['response' => 'DUNNO'], 200);
- }
-
- // exempt owners that currently maintain a positive balance and made any payments.
- // Because there might be users that pay via external methods (and don't have Payment records)
- // we can't check only the Payments table. Instead we assume that a credit/award transaction
- // is enough to consider the user a "paying user" for purpose of the rate limit.
- if ($wallet->balance > 0) {
- $isPayer = $wallet->transactions()
- ->whereIn('type', [Transaction::WALLET_AWARD, Transaction::WALLET_CREDIT])
- ->where('amount', '>', 0)
- ->exists();
-
- if ($isPayer) {
- return response()->json(['response' => 'DUNNO'], 200);
- }
- }
+ $result = RateLimit::verifyRequest($user, (array) $data['recipients']);
- //
- // Examine the rates at which the owner (or its users) is sending
- //
- $ownerRates = RateLimit::where('owner_id', $owner->id)
- ->where('updated_at', '>=', \Carbon\Carbon::now()->subHour());
-
- if (($count = $ownerRates->count()) >= 10) {
- $result = [
- 'response' => 'DEFER_IF_PERMIT',
- 'reason' => 'The account is at 10 messages per hour, cool down.'
- ];
-
- // automatically suspend (recursively) if 2.5 times over the original limit and younger than two months
- $ageThreshold = \Carbon\Carbon::now()->subMonthsWithoutOverflow(2);
-
- if ($count >= 25 && $owner->created_at > $ageThreshold) {
- $owner->suspendAccount();
- }
-
- return response()->json($result, 403);
- }
-
- if (($recipientCount = $ownerRates->sum('recipient_count')) >= 100) {
- $result = [
- 'response' => 'DEFER_IF_PERMIT',
- 'reason' => 'The account is at 100 recipients per hour, cool down.'
- ];
-
- // automatically suspend if 2.5 times over the original limit and younger than two months
- $ageThreshold = \Carbon\Carbon::now()->subMonthsWithoutOverflow(2);
-
- if ($recipientCount >= 250 && $owner->created_at > $ageThreshold) {
- $owner->suspendAccount();
- }
-
- return response()->json($result, 403);
- }
-
- //
- // Examine the rates at which the user is sending (if not also the owner)
- //
- if ($user->id != $owner->id) {
- $userRates = RateLimit::where('user_id', $user->id)
- ->where('updated_at', '>=', \Carbon\Carbon::now()->subHour());
-
- if (($count = $userRates->count()) >= 10) {
- $result = [
- 'response' => 'DEFER_IF_PERMIT',
- 'reason' => 'User is at 10 messages per hour, cool down.'
- ];
-
- // automatically suspend if 2.5 times over the original limit and younger than two months
- $ageThreshold = \Carbon\Carbon::now()->subMonthsWithoutOverflow(2);
-
- if ($count >= 25 && $user->created_at > $ageThreshold) {
- $user->suspend();
- }
-
- return response()->json($result, 403);
- }
-
- if (($recipientCount = $userRates->sum('recipient_count')) >= 100) {
- $result = [
- 'response' => 'DEFER_IF_PERMIT',
- 'reason' => 'User is at 100 recipients per hour, cool down.'
- ];
-
- // automatically suspend if 2.5 times over the original limit
- $ageThreshold = \Carbon\Carbon::now()->subMonthsWithoutOverflow(2);
-
- if ($recipientCount >= 250 && $user->created_at > $ageThreshold) {
- $user->suspend();
- }
-
- return response()->json($result, 403);
- }
- }
-
- return response()->json(['response' => 'DUNNO'], 200);
+ return $result->jsonResponse();
}
/*
@@ -298,6 +134,10 @@
$senderLocal = 'unknown';
$senderDomain = 'unknown';
+ if (!isset($data['sender'])) {
+ $data['sender'] = '';
+ }
+
if (strpos($data['sender'], '@') !== false) {
list($senderLocal, $senderDomain) = explode('@', $data['sender']);
@@ -306,10 +146,6 @@
}
}
- if ($data['sender'] === null) {
- $data['sender'] = '';
- }
-
// Compose the cache key we want.
$cacheKey = "{$netType}_{$netID}_{$senderDomain}";
diff --git a/src/app/Observers/DomainObserver.php b/src/app/Observers/DomainObserver.php
--- a/src/app/Observers/DomainObserver.php
+++ b/src/app/Observers/DomainObserver.php
@@ -63,7 +63,7 @@
*/
public function deleting(Domain $domain)
{
- \App\Policy\RateLimitWhitelist::where(
+ \App\Policy\RateLimit\Whitelist::where(
[
'whitelistable_id' => $domain->id,
'whitelistable_type' => Domain::class
diff --git a/src/app/Observers/UserObserver.php b/src/app/Observers/UserObserver.php
--- a/src/app/Observers/UserObserver.php
+++ b/src/app/Observers/UserObserver.php
@@ -276,7 +276,7 @@
}
// regardless of force delete, we're always purging whitelists... just in case
- \App\Policy\RateLimitWhitelist::where(
+ \App\Policy\RateLimit\Whitelist::where(
[
'whitelistable_id' => $user->id,
'whitelistable_type' => User::class
diff --git a/src/app/Policy/RateLimit.php b/src/app/Policy/RateLimit.php
--- a/src/app/Policy/RateLimit.php
+++ b/src/app/Policy/RateLimit.php
@@ -2,6 +2,9 @@
namespace App\Policy;
+use App\Transaction;
+use App\User;
+use Carbon\Carbon;
use App\Traits\BelongsToUserTrait;
use Illuminate\Database\Eloquent\Model;
@@ -19,4 +22,166 @@
/** @var string Database table name */
protected $table = 'policy_ratelimit';
+
+
+ /**
+ * Check the submission request agains rate limits
+ *
+ * @param User $user Sender user
+ * @param array $recipients List of mail recipients
+ *
+ * @return Response Policy respone
+ */
+ public static function verifyRequest(User $user, array $recipients = []): Response
+ {
+ if ($user->trashed() || $user->isSuspended()) {
+ // use HOLD, so that it is silent (as opposed to REJECT)
+ return new Response(Response::ACTION_HOLD, 'Sender deleted or suspended', 403);
+ }
+
+ // Examine the domain
+ $domain = $user->domain();
+
+ if (!$domain) {
+ // external sender through where this policy is applied
+ return new Response(); // DUNNO
+ }
+
+ if ($domain->trashed() || $domain->isSuspended()) {
+ // use HOLD, so that it is silent (as opposed to REJECT)
+ return new Response(Response::ACTION_HOLD, 'Sender domain deleted or suspended', 403);
+ }
+
+ // see if the user or domain is whitelisted
+ // use ./artisan policy:ratelimit:whitelist:create <email|namespace>
+ if (RateLimit\Whitelist::isListed($user) || RateLimit\Whitelist::isListed($domain)) {
+ return new Response(); // DUNNO
+ }
+
+ // user nor domain whitelisted, continue scrutinizing the request
+ sort($recipients);
+ $recipientCount = count($recipients);
+ $recipientHash = hash('sha256', implode(',', $recipients));
+
+ // Retrieve the wallet to get to the owner
+ $wallet = $user->wallet();
+
+ // wait, there is no wallet?
+ if (!$wallet || !$wallet->owner) {
+ return new Response(Response::ACTION_HOLD, 'Sender without a wallet', 403);
+ }
+
+ $owner = $wallet->owner;
+
+ // find or create the request
+ $request = RateLimit::where('recipient_hash', $recipientHash)
+ ->where('user_id', $user->id)
+ ->where('updated_at', '>=', Carbon::now()->subHour())
+ ->first();
+
+ if (!$request) {
+ $request = RateLimit::create([
+ 'user_id' => $user->id,
+ 'owner_id' => $owner->id,
+ 'recipient_hash' => $recipientHash,
+ 'recipient_count' => $recipientCount
+ ]);
+ } else {
+ // ensure the request has an up to date timestamp
+ $request->updated_at = Carbon::now();
+ $request->save();
+ }
+
+ // exempt owners that have 100% discount.
+ if ($wallet->discount && $wallet->discount->discount == 100) {
+ return new Response(); // DUNNO
+ }
+
+ // exempt owners that currently maintain a positive balance and made any payments.
+ // Because there might be users that pay via external methods (and don't have Payment records)
+ // we can't check only the Payments table. Instead we assume that a credit/award transaction
+ // is enough to consider the user a "paying user" for purpose of the rate limit.
+ if ($wallet->balance > 0) {
+ $isPayer = $wallet->transactions()
+ ->whereIn('type', [Transaction::WALLET_AWARD, Transaction::WALLET_CREDIT])
+ ->where('amount', '>', 0)
+ ->exists();
+
+ if ($isPayer) {
+ return new Response();
+ }
+ }
+
+ // Examine the rates at which the owner (or its users) is sending
+ $ownerRates = RateLimit::where('owner_id', $owner->id)
+ ->where('updated_at', '>=', Carbon::now()->subHour());
+
+ if (($count = $ownerRates->count()) >= 10) {
+ // automatically suspend (recursively) if 2.5 times over the original limit and younger than two months
+ $ageThreshold = Carbon::now()->subMonthsWithoutOverflow(2);
+
+ if ($count >= 25 && $owner->created_at > $ageThreshold) {
+ $owner->suspendAccount();
+ }
+
+ return new Response(
+ Response::ACTION_DEFER_IF_PERMIT,
+ 'The account is at 10 messages per hour, cool down.',
+ 403
+ );
+ }
+
+ if (($recipientCount = $ownerRates->sum('recipient_count')) >= 100) {
+ // automatically suspend if 2.5 times over the original limit and younger than two months
+ $ageThreshold = Carbon::now()->subMonthsWithoutOverflow(2);
+
+ if ($recipientCount >= 250 && $owner->created_at > $ageThreshold) {
+ $owner->suspendAccount();
+ }
+
+ return new Response(
+ Response::ACTION_DEFER_IF_PERMIT,
+ 'The account is at 100 recipients per hour, cool down.',
+ 403
+ );
+ }
+
+ // Examine the rates at which the user is sending (if not also the owner)
+ if ($user->id != $owner->id) {
+ $userRates = RateLimit::where('user_id', $user->id)
+ ->where('updated_at', '>=', Carbon::now()->subHour());
+
+ if (($count = $userRates->count()) >= 10) {
+ // automatically suspend if 2.5 times over the original limit and younger than two months
+ $ageThreshold = Carbon::now()->subMonthsWithoutOverflow(2);
+
+ if ($count >= 25 && $user->created_at > $ageThreshold) {
+ $user->suspend();
+ }
+
+ return new Response(
+ Response::ACTION_DEFER_IF_PERMIT,
+ 'User is at 10 messages per hour, cool down.',
+ 403
+ );
+ }
+
+ if (($recipientCount = $userRates->sum('recipient_count')) >= 100) {
+ // automatically suspend if 2.5 times over the original limit
+ $ageThreshold = Carbon::now()->subMonthsWithoutOverflow(2);
+
+ if ($recipientCount >= 250 && $user->created_at > $ageThreshold) {
+ $user->suspend();
+ }
+
+ return new Response(
+ Response::ACTION_DEFER_IF_PERMIT,
+ 'The account is at 100 recipients per hour, cool down.',
+ 403
+ );
+ }
+ }
+
+ return new Response(); // DUNNO
+ }
}
diff --git a/src/app/Policy/RateLimitWhitelist.php b/src/app/Policy/RateLimit/Whitelist.php
rename from src/app/Policy/RateLimitWhitelist.php
rename to src/app/Policy/RateLimit/Whitelist.php
--- a/src/app/Policy/RateLimitWhitelist.php
+++ b/src/app/Policy/RateLimit/Whitelist.php
@@ -1,17 +1,17 @@
<?php
-namespace App\Policy;
+namespace App\Policy\RateLimit;
use Illuminate\Database\Eloquent\Model;
/**
- * The eloquent definition of a RateLimitWhitelist entry.
+ * The eloquent definition of a RateLimit Whitelist entry.
*
* @property ?object $whitelistable The whitelistable object
* @property int|string $whitelistable_id The whitelistable object identifier
* @property string $whitelistable_type The whitelistable object type
*/
-class RateLimitWhitelist extends Model
+class Whitelist extends Model
{
/** @var array<int, string> The attributes that are mass assignable */
protected $fillable = [
diff --git a/src/app/Policy/Response.php b/src/app/Policy/Response.php
new file mode 100644
--- /dev/null
+++ b/src/app/Policy/Response.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace App\Policy;
+
+use Illuminate\Http\JsonResponse;
+
+class Response
+{
+ public const ACTION_DEFER_IF_PERMIT = 'DEFER_IF_PERMIT';
+ public const ACTION_DUNNO = 'DUNNO';
+ public const ACTION_HOLD = 'HOLD';
+
+ /** @var string Postfix action */
+ public string $action = self::ACTION_DUNNO;
+
+ /** @var string Optional response reason message */
+ public string $reason = '';
+
+ /** @var int HTTP response code */
+ public int $code = 200;
+
+
+ /**
+ * Object constructor
+ *
+ * @param string $action Action to take on the Postfix side
+ * @param string $reason Optional reason for the action
+ * @param int $code HTTP response code
+ */
+ public function __construct($action = self::ACTION_DUNNO, $reason = '', $code = 200)
+ {
+ $this->action = $action;
+ $this->reason = $reason;
+ $this->code = $code;
+ }
+
+ /**
+ * Convert this object into a JSON response
+ */
+ public function jsonResponse(): JsonResponse
+ {
+ $response = [
+ 'response' => $this->action,
+ ];
+
+ if ($this->reason) {
+ $response['reason'] = $this->reason;
+ }
+
+ if (!empty($this->logs)) {
+ $response['log'] = $this->logs;
+ }
+
+ return response()->json($response, $this->code);
+ }
+}
diff --git a/src/tests/Feature/Controller/PolicyTest.php b/src/tests/Feature/Controller/PolicyTest.php
--- a/src/tests/Feature/Controller/PolicyTest.php
+++ b/src/tests/Feature/Controller/PolicyTest.php
@@ -125,4 +125,65 @@
// TODO: Test two modules that both modify the mail content
$this->markTestIncomplete();
}
+
+ /**
+ * Test ratelimit policy webhook
+ */
+ public function testRatelimit()
+ {
+ // Note: Only basic tests here. More detailed policy handler tests are in another place
+
+ // Test a valid user
+ $post = [
+ 'sender' => $this->testUser->email,
+ 'recipients' => 'someone@sender.domain',
+ ];
+
+ $response = $this->post('/api/webhooks/policy/ratelimit', $post);
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertSame('DUNNO', $json['response']);
+
+ // Test invalid sender
+ $post['sender'] = 'non-existing';
+ $response = $this->post('/api/webhooks/policy/ratelimit', $post);
+ $response->assertStatus(403);
+
+ $json = $response->json();
+
+ $this->assertSame('HOLD', $json['response']);
+ $this->assertSame('Invalid sender email', $json['reason']);
+
+ // Test unknown sender
+ $post['sender'] = 'non-existing@example.com';
+ $response = $this->post('/api/webhooks/policy/ratelimit', $post);
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertSame('DUNNO', $json['response']);
+
+ // Test alias sender
+ $this->testUser->suspend();
+ $this->testUser->aliases()->create(['alias' => 'alias@test.domain']);
+ $post['sender'] = 'alias@test.domain';
+ $response = $this->post('/api/webhooks/policy/ratelimit', $post);
+ $response->assertStatus(403);
+
+ $json = $response->json();
+
+ $this->assertSame('HOLD', $json['response']);
+ $this->assertSame('Sender deleted or suspended', $json['reason']);
+
+ // Test app.ratelimit_whitelist
+ \config(['app.ratelimit_whitelist' => ['alias@test.domain']]);
+ $response = $this->post('/api/webhooks/policy/ratelimit', $post);
+ $response->assertStatus(200);
+
+ $json = $response->json();
+
+ $this->assertSame('DUNNO', $json['response']);
+ }
}
diff --git a/src/tests/Feature/Policy/RateLimitTest.php b/src/tests/Feature/Policy/RateLimitTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Feature/Policy/RateLimitTest.php
@@ -0,0 +1,430 @@
+<?php
+
+namespace Tests\Feature\Policy;
+
+use App\Policy\RateLimit;
+use App\Policy\Response;
+use App\Transaction;
+use App\User;
+use Illuminate\Support\Facades\DB;
+use Tests\TestCase;
+
+/**
+ * @group data
+ */
+class RateLimitTest extends TestCase
+{
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ $this->setUpTest();
+
+ RateLimit::query()->delete();
+ Transaction::query()->delete();
+ }
+
+ public function tearDown(): void
+ {
+ RateLimit::query()->delete();
+ Transaction::query()->delete();
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test verifyRequest() method for an individual account cases
+ */
+ public function testVerifyRequestIndividualAccount()
+ {
+ // Verify an individual can send an email unrestricted, so long as the account is active.
+ $result = RateLimit::verifyRequest($this->publicDomainUser, ['someone@test.domain']);
+
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+
+ // Verify a whitelisted individual account is in fact whitelisted
+ RateLimit::truncate();
+ RateLimit\Whitelist::create([
+ 'whitelistable_id' => $this->publicDomainUser->id,
+ 'whitelistable_type' => User::class
+ ]);
+
+ // first 9 requests
+ for ($i = 1; $i <= 9; $i++) {
+ $result = RateLimit::verifyRequest($this->publicDomainUser, [sprintf("%04d@test.domain", $i)]);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+ }
+
+ // normally, request #10 would get blocked
+ $result = RateLimit::verifyRequest($this->publicDomainUser, ['0010@test.domain']);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+
+ // requests 11 through 26
+ for ($i = 11; $i <= 26; $i++) {
+ $result = RateLimit::verifyRequest($this->publicDomainUser, [sprintf("%04d@test.domain", $i)]);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+ }
+
+ // Verify an individual trial user is automatically suspended.
+ RateLimit::truncate();
+ RateLimit\Whitelist::truncate();
+
+ // first 9 requests
+ for ($i = 1; $i <= 9; $i++) {
+ $result = RateLimit::verifyRequest($this->publicDomainUser, [sprintf("%04d@test.domain", $i)]);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+ }
+
+ // the next 16 requests for 25 total
+ for ($i = 10; $i <= 25; $i++) {
+ $result = RateLimit::verifyRequest($this->publicDomainUser, [sprintf("%04d@test.domain", $i)]);
+ $this->assertSame(403, $result->code);
+ $this->assertSame(Response::ACTION_DEFER_IF_PERMIT, $result->action);
+ $this->assertSame('The account is at 10 messages per hour, cool down.', $result->reason);
+ }
+
+ $this->publicDomainUser->refresh();
+ $this->assertTrue($this->publicDomainUser->isSuspended());
+
+ // Verify a suspended individual can not send an email
+ RateLimit::truncate();
+
+ $result = RateLimit::verifyRequest($this->publicDomainUser, ['someone@test.domain']);
+ $this->assertSame(403, $result->code);
+ $this->assertSame(Response::ACTION_HOLD, $result->action);
+ $this->assertSame('Sender deleted or suspended', $result->reason);
+
+ // Verify an individual can run out of messages per hour
+ RateLimit::truncate();
+ $this->publicDomainUser->unsuspend();
+
+ // first 9 requests
+ for ($i = 1; $i <= 9; $i++) {
+ $result = RateLimit::verifyRequest($this->publicDomainUser, [sprintf("%04d@test.domain", $i)]);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+ }
+
+ // the tenth request should be blocked
+ $result = RateLimit::verifyRequest($this->publicDomainUser, ['0010@test.domain']);
+ $this->assertSame(403, $result->code);
+ $this->assertSame(Response::ACTION_DEFER_IF_PERMIT, $result->action);
+ $this->assertSame('The account is at 10 messages per hour, cool down.', $result->reason);
+
+ // Verify a paid for individual account does not simply run out of messages
+ RateLimit::truncate();
+
+ // first 9 requests
+ for ($i = 1; $i <= 9; $i++) {
+ $result = RateLimit::verifyRequest($this->publicDomainUser, [sprintf("%04d@test.domain", $i)]);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+ }
+
+ // the tenth request should be blocked
+ $result = RateLimit::verifyRequest($this->publicDomainUser, ['0010@test.domain']);
+ $this->assertSame(403, $result->code);
+ $this->assertSame(Response::ACTION_DEFER_IF_PERMIT, $result->action);
+ $this->assertSame('The account is at 10 messages per hour, cool down.', $result->reason);
+
+ // create a credit transaction
+ $this->publicDomainUser->wallets()->first()->credit(1111);
+
+ // the next request should now be allowed
+ $result = RateLimit::verifyRequest($this->publicDomainUser, ['0010@test.domain']);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+
+ // Verify a 100% discount for individual account does not simply run out of messages
+ RateLimit::truncate();
+ $wallet = $this->publicDomainUser->wallets()->first();
+ $wallet->discount()->associate(\App\Discount::where('description', 'Free Account')->first());
+ $wallet->save();
+
+ // first 9 requests
+ for ($i = 1; $i <= 9; $i++) {
+ $result = RateLimit::verifyRequest($this->publicDomainUser, [sprintf("%04d@test.domain", $i)]);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+ }
+
+ // the tenth request should now be allowed
+ $result = RateLimit::verifyRequest($this->publicDomainUser, ['someone@test.domain']);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+
+ // Verify that an individual user in its trial can run out of recipients.
+ RateLimit::truncate();
+ $wallet->discount_id = null;
+ $wallet->balance = 0;
+ $wallet->save();
+
+ // first 2 requests (34 recipients each)
+ for ($x = 1; $x <= 2; $x++) {
+ $recipients = [];
+ for ($y = 1; $y <= 34; $y++) {
+ $recipients[] = sprintf('%04d@test.domain', $x * $y);
+ }
+
+ $result = RateLimit::verifyRequest($this->publicDomainUser, $recipients);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+ }
+
+ // on to the third request, resulting in 102 recipients total
+ $recipients = [];
+ for ($y = 1; $y <= 34; $y++) {
+ $recipients[] = sprintf('%04d@test.domain', 3 * $y);
+ }
+
+ $result = RateLimit::verifyRequest($this->publicDomainUser, $recipients);
+ $this->assertSame(403, $result->code);
+ $this->assertSame(Response::ACTION_DEFER_IF_PERMIT, $result->action);
+ $this->assertSame('The account is at 100 recipients per hour, cool down.', $result->reason);
+
+ // Verify that an individual user that has paid for its account doesn't run out of recipients.
+ RateLimit::truncate();
+ $wallet->balance = 0;
+ $wallet->save();
+
+ // first 2 requests (34 recipients each)
+ for ($x = 0; $x < 2; $x++) {
+ $recipients = [];
+ for ($y = 0; $y < 34; $y++) {
+ $recipients[] = sprintf("%04d@test.domain", $x * $y);
+ }
+
+ $result = RateLimit::verifyRequest($this->publicDomainUser, $recipients);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+ }
+
+ // on to the third request, resulting in 102 recipients total
+ $recipients = [];
+ for ($y = 0; $y < 34; $y++) {
+ $recipients[] = sprintf("%04d@test.domain", 2 * $y);
+ }
+
+ $result = RateLimit::verifyRequest($this->publicDomainUser, $recipients);
+ $this->assertSame(403, $result->code);
+ $this->assertSame(Response::ACTION_DEFER_IF_PERMIT, $result->action);
+ $this->assertSame('The account is at 100 recipients per hour, cool down.', $result->reason);
+
+ $wallet->award(11111);
+
+ // the tenth request should now be allowed
+ $result = RateLimit::verifyRequest($this->publicDomainUser, $recipients);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+ }
+
+ /**
+ * Test verifyRequest() with group account cases
+ */
+ public function testVerifyRequestGroupAccount()
+ {
+ // Verify that a group owner can send email
+ $result = RateLimit::verifyRequest($this->domainOwner, ['someone@test.domain']);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+
+ // Verify that a domain owner can run out of messages
+ RateLimit::truncate();
+
+ // first 9 requests
+ for ($i = 0; $i < 9; $i++) {
+ $result = RateLimit::verifyRequest($this->domainOwner, [sprintf("%04d@test.domain", $i)]);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+ }
+
+ // the tenth request should be blocked
+ $result = RateLimit::verifyRequest($this->domainOwner, ['0010@test.domain']);
+ $this->assertSame(403, $result->code);
+ $this->assertSame(Response::ACTION_DEFER_IF_PERMIT, $result->action);
+ $this->assertSame('The account is at 10 messages per hour, cool down.', $result->reason);
+
+ $this->domainOwner->refresh();
+ $this->assertFalse($this->domainOwner->isSuspended());
+
+ // Verify that a domain owner can run out of recipients
+ RateLimit::truncate();
+ $this->domainOwner->unsuspend();
+
+ // first 2 requests (34 recipients each)
+ for ($x = 0; $x < 2; $x++) {
+ $recipients = [];
+ for ($y = 0; $y < 34; $y++) {
+ $recipients[] = sprintf("%04d@test.domain", $x * $y);
+ }
+
+ $result = RateLimit::verifyRequest($this->domainOwner, $recipients);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+ }
+
+ // on to the third request, resulting in 102 recipients total
+ $recipients = [];
+ for ($y = 0; $y < 34; $y++) {
+ $recipients[] = sprintf("%04d@test.domain", 2 * $y);
+ }
+
+ $result = RateLimit::verifyRequest($this->domainOwner, $recipients);
+ $this->assertSame(403, $result->code);
+ $this->assertSame(Response::ACTION_DEFER_IF_PERMIT, $result->action);
+ $this->assertSame('The account is at 100 recipients per hour, cool down.', $result->reason);
+
+ $this->domainOwner->refresh();
+ $this->assertFalse($this->domainOwner->isSuspended());
+
+ // Verify that a paid for group account can send messages.
+ RateLimit::truncate();
+
+ // first 2 requests (34 recipients each)
+ for ($x = 0; $x < 2; $x++) {
+ $recipients = [];
+ for ($y = 0; $y < 34; $y++) {
+ $recipients[] = sprintf("%04d@test.domain", $x * $y);
+ }
+
+ $result = RateLimit::verifyRequest($this->domainOwner, $recipients);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+ }
+
+ // on to the third request, resulting in 102 recipients total
+ $recipients = [];
+ for ($y = 0; $y < 34; $y++) {
+ $recipients[] = sprintf("%04d@test.domain", 2 * $y);
+ }
+
+ $result = RateLimit::verifyRequest($this->domainOwner, $recipients);
+ $this->assertSame(403, $result->code);
+ $this->assertSame(Response::ACTION_DEFER_IF_PERMIT, $result->action);
+ $this->assertSame('The account is at 100 recipients per hour, cool down.', $result->reason);
+
+ $wallet = $this->domainOwner->wallets()->first();
+ $wallet->credit(1111);
+
+ $result = RateLimit::verifyRequest($this->domainOwner, $recipients);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+
+ // Verify that a user for a domain owner can send email.
+ RateLimit::truncate();
+
+ $result = RateLimit::verifyRequest($this->domainUsers[0], ['someone@test.domain']);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+
+ // Verify that the users in a group account can be limited.
+ RateLimit::truncate();
+ $wallet->balance = 0;
+ $wallet->save();
+
+ // the first eight requests should be accepted
+ for ($i = 0; $i < 8; $i++) {
+ $result = RateLimit::verifyRequest($this->domainUsers[0], [sprintf("%04d@test.domain", $i)]);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+ }
+
+ // the ninth request from another group user should also be accepted
+ $result = RateLimit::verifyRequest($this->domainUsers[1], ['0009@test.domain']);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+
+ // the tenth request from another group user should be rejected
+ $result = RateLimit::verifyRequest($this->domainUsers[1], ['0010@test.domain']);
+ $this->assertSame(403, $result->code);
+ $this->assertSame(Response::ACTION_DEFER_IF_PERMIT, $result->action);
+ $this->assertSame('The account is at 10 messages per hour, cool down.', $result->reason);
+
+ // Test a trial user
+ RateLimit::truncate();
+
+ // first 2 requests (34 recipients each)
+ for ($x = 0; $x < 2; $x++) {
+ $recipients = [];
+ for ($y = 0; $y < 34; $y++) {
+ $recipients[] = sprintf("%04d@test.domain", $x * $y);
+ }
+
+ $result = RateLimit::verifyRequest($this->domainUsers[0], $recipients);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+ }
+
+ // on to the third request, resulting in 102 recipients total
+ $recipients = [];
+ for ($y = 0; $y < 34; $y++) {
+ $recipients[] = sprintf("%04d@test.domain", 2 * $y);
+ }
+
+ $result = RateLimit::verifyRequest($this->domainUsers[0], $recipients);
+ $this->assertSame(403, $result->code);
+ $this->assertSame(Response::ACTION_DEFER_IF_PERMIT, $result->action);
+ $this->assertSame('The account is at 100 recipients per hour, cool down.', $result->reason);
+
+ // Verify a whitelisted group domain is in fact whitelisted
+ RateLimit::truncate();
+ RateLimit\Whitelist::create([
+ 'whitelistable_id' => $this->domainHosted->id,
+ 'whitelistable_type' => \App\Domain::class
+ ]);
+
+ $request = [
+ 'sender' => $this->domainUsers[0]->email,
+ 'recipients' => []
+ ];
+
+ // first 9 requests
+ for ($i = 1; $i <= 9; $i++) {
+ $result = RateLimit::verifyRequest($this->domainUsers[0], [sprintf("%04d@test.domain", $i)]);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+ }
+
+ // normally, request #10 would get blocked
+ $result = RateLimit::verifyRequest($this->domainUsers[0], ['0010@test.domain']);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+
+ // requests 11 through 26
+ for ($i = 11; $i <= 26; $i++) {
+ $result = RateLimit::verifyRequest($this->domainUsers[0], [sprintf("%04d@test.domain", $i)]);
+ $this->assertSame(200, $result->code);
+ $this->assertSame(Response::ACTION_DUNNO, $result->action);
+ $this->assertSame('', $result->reason);
+ }
+ }
+}
diff --git a/src/tests/Feature/Stories/RateLimitTest.php b/src/tests/Feature/Stories/RateLimitTest.php
deleted file mode 100644
--- a/src/tests/Feature/Stories/RateLimitTest.php
+++ /dev/null
@@ -1,533 +0,0 @@
-<?php
-
-namespace Tests\Feature\Stories;
-
-use App\Policy\RateLimit;
-use App\Transaction;
-use Illuminate\Support\Facades\DB;
-use Tests\TestCase;
-
-/**
- * @group data
- * @group ratelimit
- */
-class RateLimitTest extends TestCase
-{
- public function setUp(): void
- {
- parent::setUp();
-
- $this->setUpTest();
- $this->useServicesUrl();
-
- Transaction::query()->delete();
- }
-
- public function tearDown(): void
- {
- Transaction::query()->delete();
-
- parent::tearDown();
- }
-
- /**
- * Verify an individual can send an email unrestricted, so long as the account is active.
- */
- public function testIndividualDunno()
- {
- $request = [
- 'sender' => $this->publicDomainUser->email,
- 'recipients' => [ 'someone@test.domain' ]
- ];
-
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
-
- /**
- * Verify a whitelisted individual account is in fact whitelisted
- */
- public function testIndividualWhitelist()
- {
- \App\Policy\RateLimitWhitelist::create(
- [
- 'whitelistable_id' => $this->publicDomainUser->id,
- 'whitelistable_type' => \App\User::class
- ]
- );
-
- $request = [
- 'sender' => $this->publicDomainUser->email,
- 'recipients' => []
- ];
-
- // first 9 requests
- for ($i = 1; $i <= 9; $i++) {
- $request['recipients'] = [sprintf("%04d@test.domain", $i)];
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
-
- // normally, request #10 would get blocked
- $request['recipients'] = ['0010@test.domain'];
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
- $response->assertStatus(200);
-
- // requests 11 through 26
- for ($i = 11; $i <= 26; $i++) {
- $request['recipients'] = [sprintf("%04d@test.domain", $i)];
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
- }
-
- /**
- * Verify an individual trial user is automatically suspended.
- */
- public function testIndividualAutoSuspendMessages()
- {
- $request = [
- 'sender' => $this->publicDomainUser->email,
- 'recipients' => []
- ];
-
- // first 9 requests
- for ($i = 1; $i <= 9; $i++) {
- $request['recipients'] = [sprintf("%04d@test.domain", $i)];
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
-
- // the next 16 requests for 25 total
- for ($i = 10; $i <= 25; $i++) {
- $request['recipients'] = [sprintf("%04d@test.domain", $i)];
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(403);
- }
-
- $this->assertTrue($this->publicDomainUser->fresh()->isSuspended());
- }
-
- /**
- * Verify a suspended individual can not send an email
- */
- public function testIndividualSuspended()
- {
- $this->publicDomainUser->suspend();
-
- $request = [
- 'sender' => $this->publicDomainUser->email,
- 'recipients' => ['someone@test.domain']
- ];
-
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(403);
- }
-
- /**
- * Verify an individual can run out of messages per hour
- */
- public function testIndividualTrialMessages()
- {
- $request = [
- 'sender' => $this->publicDomainUser->email,
- 'recipients' => []
- ];
-
- // first 9 requests
- for ($i = 1; $i <= 9; $i++) {
- $request['recipients'] = [sprintf("%04d@test.domain", $i)];
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
-
- // the tenth request should be blocked
- $request['recipients'] = ['0010@test.domain'];
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(403);
- }
-
- /**
- * Verify a paid for individual account does not simply run out of messages
- */
- public function testIndividualPaidMessages()
- {
- $wallet = $this->publicDomainUser->wallets()->first();
-
- $request = [
- 'sender' => $this->publicDomainUser->email,
- 'recipients' => ['someone@test.domain']
- ];
-
- // first 9 requests
- for ($i = 1; $i <= 9; $i++) {
- $request['recipients'] = [sprintf("%04d@test.domain", $i)];
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
-
- // the tenth request should be blocked
- $request['recipients'] = ['0010@test.domain'];
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
- $response->assertStatus(403);
-
- // create a credit transaction
- $wallet->credit(1111);
-
- // the next request should now be allowed
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
- $response->assertStatus(200);
- }
-
- /**
- * Verify a 100% discount for individual account does not simply run out of messages
- */
- public function testIndividualDiscountMessages()
- {
- $wallet = $this->publicDomainUser->wallets()->first();
-
- $wallet->discount()->associate(\App\Discount::where('description', 'Free Account')->first());
- $wallet->save();
-
- $request = [
- 'sender' => $this->publicDomainUser->email,
- 'recipients' => ['someone@test.domain']
- ];
-
- // first 9 requests
- for ($i = 1; $i <= 9; $i++) {
- $request['recipients'] = [sprintf("%04d@test.domain", $i)];
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
-
- // the tenth request should now be allowed
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
- $response->assertStatus(200);
- }
-
- /**
- * Verify that an individual user in its trial can run out of recipients.
- */
- public function testIndividualTrialRecipients()
- {
- $request = [
- 'sender' => $this->publicDomainUser->email,
- 'recipients' => []
- ];
-
- // first 2 requests (34 recipients each)
- for ($x = 1; $x <= 2; $x++) {
- $request['recipients'] = [];
-
- for ($y = 1; $y <= 34; $y++) {
- $request['recipients'][] = sprintf("%04d@test.domain", $x * $y);
- }
-
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
-
- // on to the third request, resulting in 102 recipients total
- $request['recipients'] = [];
-
- for ($y = 1; $y <= 34; $y++) {
- $request['recipients'][] = sprintf("%04d@test.domain", 3 * $y);
- }
-
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
- $response->assertStatus(403);
- }
-
- /**
- * Verify that an individual user that has paid for its account doesn't run out of recipients.
- */
- public function testIndividualPaidRecipients()
- {
- $wallet = $this->publicDomainUser->wallets()->first();
-
- $request = [
- 'sender' => $this->publicDomainUser->email,
- 'recipients' => []
- ];
-
- // first 2 requests (34 recipients each)
- for ($x = 0; $x < 2; $x++) {
- $request['recipients'] = [];
-
- for ($y = 0; $y < 34; $y++) {
- $request['recipients'][] = sprintf("%04d@test.domain", $x * $y);
- }
-
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
-
- // on to the third request, resulting in 102 recipients total
- $request['recipients'] = [];
-
- for ($y = 0; $y < 34; $y++) {
- $request['recipients'][] = sprintf("%04d@test.domain", 2 * $y);
- }
-
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(403);
-
- $wallet->award(1111);
-
- // the tenth request should now be allowed
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
-
- /**
- * Verify that a group owner can send email
- */
- public function testGroupOwnerDunno()
- {
- $request = [
- 'sender' => $this->domainOwner->email,
- 'recipients' => [ 'someone@test.domain' ]
- ];
-
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
-
- /**
- * Verify that a domain owner can run out of messages
- */
- public function testGroupTrialOwnerMessages()
- {
- $request = [
- 'sender' => $this->domainOwner->email,
- 'recipients' => []
- ];
-
- // first 9 requests
- for ($i = 0; $i < 9; $i++) {
- $request['recipients'] = [sprintf("%04d@test.domain", $i)];
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
-
- // the tenth request should be blocked
- $request['recipients'] = ['0010@test.domain'];
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(403);
-
- $this->assertFalse($this->domainOwner->fresh()->isSuspended());
- }
-
- /**
- * Verify that a domain owner can run out of recipients
- */
- public function testGroupTrialOwnerRecipients()
- {
- $request = [
- 'sender' => $this->domainOwner->email,
- 'recipients' => []
- ];
-
- // first 2 requests (34 recipients each)
- for ($x = 0; $x < 2; $x++) {
- $request['recipients'] = [];
-
- for ($y = 0; $y < 34; $y++) {
- $request['recipients'][] = sprintf("%04d@test.domain", $x * $y);
- }
-
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
-
- // on to the third request, resulting in 102 recipients total
- $request['recipients'] = [];
-
- for ($y = 0; $y < 34; $y++) {
- $request['recipients'][] = sprintf("%04d@test.domain", 2 * $y);
- }
-
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
- $response->assertStatus(403);
-
- $this->assertFalse($this->domainOwner->fresh()->isSuspended());
- }
-
- /**
- * Verify that a paid for group account can send messages.
- */
- public function testGroupPaidOwnerRecipients()
- {
- $wallet = $this->domainOwner->wallets()->first();
-
- $request = [
- 'sender' => $this->domainOwner->email,
- 'recipients' => []
- ];
-
- // first 2 requests (34 recipients each)
- for ($x = 0; $x < 2; $x++) {
- $request['recipients'] = [];
-
- for ($y = 0; $y < 34; $y++) {
- $request['recipients'][] = sprintf("%04d@test.domain", $x * $y);
- }
-
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
-
- // on to the third request, resulting in 102 recipients total
- $request['recipients'] = [];
-
- for ($y = 0; $y < 34; $y++) {
- $request['recipients'][] = sprintf("%04d@test.domain", 2 * $y);
- }
-
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
- $response->assertStatus(403);
-
- $wallet->credit(1111);
-
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
- $response->assertStatus(200);
- }
-
- /**
- * Verify that a user for a domain owner can send email.
- */
- public function testGroupUserDunno()
- {
- $request = [
- 'sender' => $this->domainUsers[0]->email,
- 'recipients' => [ 'someone@test.domain' ]
- ];
-
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
-
- /**
- * Verify that the users in a group account can be limited.
- */
- public function testGroupTrialUserMessages()
- {
- $user = $this->domainUsers[0];
-
- $request = [
- 'sender' => $user->email,
- 'recipients' => []
- ];
-
- // the first eight requests should be accepted
- for ($i = 0; $i < 8; $i++) {
- $request['recipients'] = [sprintf("%04d@test.domain", $i)];
-
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
- $response->assertStatus(200);
- }
-
- $request['sender'] = $this->domainUsers[1]->email;
-
- // the ninth request from another group user should also be accepted
- $request['recipients'] = ['0009@test.domain'];
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
- $response->assertStatus(200);
-
- // the tenth request from another group user should be rejected
- $request['recipients'] = ['0010@test.domain'];
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
- $response->assertStatus(403);
- }
-
- public function testGroupTrialUserRecipients()
- {
- $request = [
- 'sender' => $this->domainUsers[0]->email,
- 'recipients' => []
- ];
-
- // first 2 requests (34 recipients each)
- for ($x = 0; $x < 2; $x++) {
- $request['recipients'] = [];
-
- for ($y = 0; $y < 34; $y++) {
- $request['recipients'][] = sprintf("%04d@test.domain", $x * $y);
- }
-
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
-
- // on to the third request, resulting in 102 recipients total
- $request['recipients'] = [];
-
- for ($y = 0; $y < 34; $y++) {
- $request['recipients'][] = sprintf("%04d@test.domain", 2 * $y);
- }
-
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
- $response->assertStatus(403);
- }
-
- /**
- * Verify a whitelisted group domain is in fact whitelisted
- */
- public function testGroupDomainWhitelist()
- {
- \App\Policy\RateLimitWhitelist::create(
- [
- 'whitelistable_id' => $this->domainHosted->id,
- 'whitelistable_type' => \App\Domain::class
- ]
- );
-
- $request = [
- 'sender' => $this->domainUsers[0]->email,
- 'recipients' => []
- ];
-
- // first 9 requests
- for ($i = 1; $i <= 9; $i++) {
- $request['recipients'] = [sprintf("%04d@test.domain", $i)];
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
-
- // normally, request #10 would get blocked
- $request['recipients'] = ['0010@test.domain'];
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
- $response->assertStatus(200);
-
- // requests 11 through 26
- for ($i = 11; $i <= 26; $i++) {
- $request['recipients'] = [sprintf("%04d@test.domain", $i)];
- $response = $this->post('api/webhooks/policy/ratelimit', $request);
-
- $response->assertStatus(200);
- }
- }
-}

File Metadata

Mime Type
text/plain
Expires
Sun, Apr 5, 7:01 AM (7 h, 29 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18832707
Default Alt Text
D5220.1775372482.diff (56 KB)

Event Timeline