Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117896121
D5220.1775372482.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
56 KB
Referenced Files
None
Subscribers
None
D5220.1775372482.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D5220: Policies code refactoring
Attached
Detach File
Event Timeline